diff --git a/src/reportportal/reportportal_client_wrapper.py b/src/reportportal/reportportal_client_wrapper.py index 9f51854..fbd6507 100644 --- a/src/reportportal/reportportal_client_wrapper.py +++ b/src/reportportal/reportportal_client_wrapper.py @@ -248,6 +248,7 @@ def start_test_case(self, name: str, start_time: str, parent_id: str, attributes: Optional[List[Dict]] = None, description: Optional[str] = None, code_ref: Optional[str] = None, + test_case_id: Optional[str] = None, retry: bool = False, retry_of: Optional[str] = None) -> str: """ @@ -260,6 +261,7 @@ def start_test_case(self, name: str, start_time: str, parent_id: str, attributes: Test case attributes description: Test case description code_ref: Code reference for the test (e.g., "path/to/test.py::test_name") + test_case_id: Explicit test case identifier for RP history matching retry: If True, marks this test case as a retry of a previous attempt retry_of: UUID of the original test item this retry references (RP 25.x) @@ -269,7 +271,7 @@ def start_test_case(self, name: str, start_time: str, parent_id: str, if self.dry_run: mock_id = self._generate_mock_id() logger.info(f"[DRY-RUN] Would start test case '{name}' with ID: {mock_id}") - logger.info(f"[DRY-RUN] Parent: {parent_id}, Code ref: {code_ref}, Retry: {retry}, Retry of: {retry_of}") + logger.info(f"[DRY-RUN] Parent: {parent_id}, Code ref: {code_ref}, Test case id: {test_case_id}, Retry: {retry}, Retry of: {retry_of}") return mock_id if not self.client: @@ -284,11 +286,12 @@ def start_test_case(self, name: str, start_time: str, parent_id: str, description=description, parent_item_id=parent_id, code_ref=code_ref, + test_case_id=test_case_id, retry=retry, retry_of=retry_of ) logger.debug(f"Started test case '{name}' with ID: {case_id}") - logger.debug(f"Parent: {parent_id}, Code ref: {code_ref}, Retry: {retry}, Retry of: {retry_of}") + logger.debug(f"Parent: {parent_id}, Code ref: {code_ref}, Test case id: {test_case_id}, Retry: {retry}, Retry of: {retry_of}") return case_id except Exception as e: logger.error(f"Failed to start test case '{name}': {e}") diff --git a/src/reportportal/writer.py b/src/reportportal/writer.py index a0fd695..788119c 100644 --- a/src/reportportal/writer.py +++ b/src/reportportal/writer.py @@ -235,7 +235,8 @@ def _process_test_suite(self, suite_data: dict, suite_properties: list, # Process all test cases in the suite for case_data, case_result in zip(suite_data['test_cases'], case_results): case_runtime = self._process_test_case( - case_data, case_result, suite_id, suite_timestamp + suite_runtime + case_data, case_result, suite_id, suite_timestamp + suite_runtime, + suite_name=suite_data['name'] ) # Disabling suite_runtime tally, due to Interrupted test cases # because of parallel run, total runtime is larger than actual time @@ -248,7 +249,8 @@ def _process_test_suite(self, suite_data: dict, suite_properties: list, attributes=suite_attrs ) - def _create_failed_attempts(self, test_name: str, case_result, + def _create_failed_attempts(self, test_name: str, test_case_id: str, + case_result, suite_id: str, start_time: int, rerun_duration: int) -> str: """ Create all failed rerun attempts before the final test result. @@ -257,7 +259,8 @@ def _create_failed_attempts(self, test_name: str, case_result, are retries referencing the original via retry_of (RP 25.x). Args: - test_name: Full test case name + test_name: Test display name and code reference (e.g. "tests/foo.py::test_bar") + test_case_id: Suite-qualified identifier for RP history matching (e.g. "smoke: tests/foo.py::test_bar") case_result: Filtered case property result suite_id: Parent suite ID start_time: Test case start time @@ -277,6 +280,7 @@ def _create_failed_attempts(self, test_name: str, case_result, attributes=case_result.properties, description=case_result.description, code_ref=test_name, + test_case_id=test_case_id, retry=not is_original, retry_of=None if is_original else original_id ) @@ -297,7 +301,8 @@ def _create_failed_attempts(self, test_name: str, case_result, return original_id def _process_test_case(self, case_data: dict, case_result, - suite_id: str, start_time: int) -> int: + suite_id: str, start_time: int, + suite_name: str = "") -> int: """ Process a single test case. @@ -306,21 +311,25 @@ def _process_test_case(self, case_data: dict, case_result, case_result: Pre-filtered case property result suite_id: Parent suite ID start_time: Test case start time + suite_name: Parent suite name, used to qualify code_ref Returns: Test case runtime in milliseconds """ - # Generate test case name (pytest-style) + # Generate test name (pytest-style) test_name = f"{case_data['converted_classname']}::{case_data['name']}" + # Suite-qualified test_case_id for RP history matching + test_case_id = f"{suite_name}: {test_name}" if suite_name else test_name + # Create failed rerun attempts before the final result original_id = None final_start_time = start_time if case_result.reruns > 0: rerun_duration = case_data['time'] // (case_result.reruns + 1) original_id = self._create_failed_attempts( - test_name, case_result, suite_id, start_time, rerun_duration + test_name, test_case_id, case_result, suite_id, start_time, rerun_duration ) final_start_time = start_time + (case_result.reruns * rerun_duration) @@ -332,6 +341,7 @@ def _process_test_case(self, case_data: dict, case_result, attributes=case_result.properties, description=case_result.description, code_ref=test_name, + test_case_id=test_case_id, retry=original_id is not None, retry_of=original_id ) diff --git a/tests/unit/test_reportportal_client_wrapper.py b/tests/unit/test_reportportal_client_wrapper.py index f874b7e..aed7d05 100644 --- a/tests/unit/test_reportportal_client_wrapper.py +++ b/tests/unit/test_reportportal_client_wrapper.py @@ -230,6 +230,7 @@ def test_start_test_case_success(self): description="Case description", parent_item_id="suite_456", code_ref=None, + test_case_id=None, retry=False, retry_of=None ) @@ -258,6 +259,7 @@ def test_start_test_case_with_code_ref(self): description="Case description", parent_item_id="suite_456", code_ref="path/to/test.py::test_name", + test_case_id=None, retry=False, retry_of=None )