diff --git a/CHANGELOG.md b/CHANGELOG.md index b7782cf..cb5a684 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Changelog ## [Unreleased] +### Removed +- Retries of requests ended with `504` HTTP status code, since it's not clear if the request was delivered or not, by @HardNorth +### Changed +- `client.RP.start_test_item` method and all its children now accept `retry_of` argument, by @HardNorth +- `client.RP.finish_test_item` method and all its children now accept `retry_of` argument, by @HardNorth +- `client.RP.finish_test_item` method and all its children now accept `test_case_id` argument, by @HardNorth + +## [5.5.7] ### Added - `helpers.to_bool` function, by @HardNorth - Official `Python 3.12` support, by @HardNorth diff --git a/reportportal_client/_internal/aio/http.py b/reportportal_client/_internal/aio/http.py index c9c6d34..d0fd70d 100644 --- a/reportportal_client/_internal/aio/http.py +++ b/reportportal_client/_internal/aio/http.py @@ -33,7 +33,7 @@ DEFAULT_RETRY_NUMBER: int = 5 DEFAULT_RETRY_DELAY: float = 0.005 THROTTLING_STATUSES: set = {425, 429} -RETRY_STATUSES: set = {408, 500, 502, 503, 504, 507}.union(THROTTLING_STATUSES) +RETRY_STATUSES: set = {408, 500, 502, 503, 507}.union(THROTTLING_STATUSES) class RetryClass(int, Enum): diff --git a/reportportal_client/aio/client.py b/reportportal_client/aio/client.py index 126f1ec..3ae4a2a 100644 --- a/reportportal_client/aio/client.py +++ b/reportportal_client/aio/client.py @@ -109,7 +109,7 @@ def __init__( launch_uuid_print: bool = False, print_output: OutputType = OutputType.STDOUT, truncate_attributes: bool = True, - **kwargs: Any + **_: Any ) -> None: """Initialize the class instance with arguments. @@ -157,7 +157,7 @@ async def session(self) -> RetryingClientSession: if self._session: return self._session - if self.verify_ssl is None or (type(self.verify_ssl) == bool and not self.verify_ssl): + if self.verify_ssl is None or (type(self.verify_ssl) is bool and not self.verify_ssl): ssl_config = False else: if type(self.verify_ssl) is str: @@ -183,7 +183,7 @@ async def session(self) -> RetryingClientSession: } if self.http_timeout: - if type(self.http_timeout) == tuple: + if type(self.http_timeout) is tuple: connect_timeout, read_timeout = self.http_timeout else: connect_timeout, read_timeout = self.http_timeout, self.http_timeout @@ -230,7 +230,7 @@ async def start_launch(self, attributes: Optional[Union[list, dict]] = None, rerun: bool = False, rerun_of: Optional[str] = None, - **kwargs) -> Optional[str]: + **_) -> Optional[str]: """Start a new Launch with the given arguments. :param name: Launch name. @@ -279,8 +279,9 @@ async def start_test_item(self, parameters: Optional[dict] = None, code_ref: Optional[str] = None, test_case_id: Optional[str] = None, - has_stats: bool = True, - retry: bool = False, + has_stats: Optional[bool] = True, + retry: Optional[bool] = False, + retry_of: Optional[str] = None, **_: Any) -> Optional[str]: """Start Test Case/Suite/Step/Nested Step Item. @@ -299,6 +300,8 @@ async def start_test_item(self, :param test_case_id: A unique ID of the current Step. :param has_stats: Set to False if test item is a Nested Step. :param retry: Used to report retry of the test. Allowed values: "True" or "False". + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. :return: Test Item UUID if successfully started or None. """ if parent_item_id: @@ -316,7 +319,8 @@ async def start_test_item(self, has_stats=has_stats, parameters=parameters, retry=retry, - test_case_id=test_case_id + test_case_id=test_case_id, + retry_of=retry_of ).payload response = await AsyncHttpRequest((await self.session()).post, url=url, json=request_payload).make() @@ -334,25 +338,30 @@ async def finish_test_item(self, item_id: Union[str, Task[str]], end_time: str, *, - status: str = None, - description: str = None, + status: Optional[str] = None, + description: Optional[str] = None, attributes: Optional[Union[list, dict]] = None, + test_case_id: Optional[str] = None, issue: Optional[Issue] = None, - retry: bool = False, - **kwargs: Any) -> Optional[str]: + retry: Optional[bool] = False, + retry_of: Optional[str] = None, + **_: Any) -> Optional[str]: """Finish Test Suite/Case/Step/Nested Step Item. - :param launch_uuid: A launch UUID where to finish the Test Item. - :param item_id: ID of the Test Item. - :param end_time: The Item end time. - :param status: Test status. Allowed values: - PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. - :param description: Test Item description. Overrides description from start request. - :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override - attributes on start Test Item call. - :param issue: Issue which will be attached to the current Item. - :param retry: Used to report retry of the test. Allowed values: "True" or "False". - :return: Response message. + :param launch_uuid: A launch UUID where to finish the Test Item. + :param item_id: ID of the Test Item. + :param end_time: The Item end time. + :param status: Test status. Allowed values: + PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. + :param description: Test Item description. Overrides description from start request. + :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override + attributes on start Test Item call. + :param test_case_id: A unique ID of the current Step. Overrides passed on start request. + :param issue: Issue which will be attached to the current Item. + :param retry: Used to report retry of the test. Allowed values: "True" or "False". + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. + :return: Response message. """ url = self.__get_item_url(item_id) request_payload = AsyncItemFinishRequest( @@ -361,9 +370,11 @@ async def finish_test_item(self, status, attributes=verify_value_length(attributes) if self.truncate_attributes else attributes, description=description, + test_case_id=test_case_id, is_skipped_an_issue=self.is_skipped_an_issue, issue=issue, - retry=retry + retry=retry, + retry_of=retry_of ).payload response = await AsyncHttpRequest((await self.session()).put, url=url, json=request_payload).make() if not response: @@ -377,7 +388,7 @@ async def finish_launch(self, launch_uuid: Union[str, Task[str]], end_time: str, *, - status: str = None, + status: Optional[str] = None, attributes: Optional[Union[list, dict]] = None, **kwargs: Any) -> Optional[str]: """Finish a Launch. @@ -385,7 +396,7 @@ async def finish_launch(self, :param launch_uuid: A Launch UUID to finish. :param end_time: Launch end time. :param status: Launch status. Can be one of the followings: - PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED. + PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED or None. :param attributes: Launch attributes. These attributes override attributes on Start Launch call. :return: Response message or None. """ @@ -530,7 +541,7 @@ def clone(self) -> 'Client': """Clone the client object, set current Item ID as cloned item ID. :return: Cloned client object - :rtype: AsyncRPClient + :rtype: Client """ cloned = Client( endpoint=self.endpoint, @@ -721,6 +732,7 @@ async def start_test_item(self, code_ref: Optional[str] = None, retry: bool = False, test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Optional[str]: """Start Test Case/Suite/Step/Nested Step Item. @@ -738,13 +750,15 @@ async def start_test_item(self, :param code_ref: Physical location of the Test Item. :param retry: Used to report retry of the test. Allowed values: "True" or "False". :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. :return: Test Item UUID if successfully started or None. """ item_id = await self.__client.start_test_item(self.launch_uuid, name, start_time, item_type, description=description, attributes=attributes, parameters=parameters, parent_item_id=parent_item_id, has_stats=has_stats, code_ref=code_ref, retry=retry, - test_case_id=test_case_id, **kwargs) + test_case_id=test_case_id, retry_of=retry_of, **kwargs) if item_id and item_id is not NOT_FOUND: logger.debug('start_test_item - ID: %s', item_id) self._add_current_item(item_id) @@ -753,35 +767,39 @@ async def start_test_item(self, async def finish_test_item(self, item_id: str, end_time: str, - status: str = None, + status: Optional[str] = None, issue: Optional[Issue] = None, attributes: Optional[Union[list, dict]] = None, description: str = None, retry: bool = False, + test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Optional[str]: """Finish Test Suite/Case/Step/Nested Step Item. - :param item_id: ID of the Test Item. - :param end_time: The Item end time. - :param status: Test status. Allowed values: - PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. - :param issue: Issue which will be attached to the current Item. - :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override - attributes on start Test Item call. - :param description: Test Item description. Overrides description from start request. - :param retry: Used to report retry of the test. Allowed values: "True" or "False". - :return: Response message. - """ - result = await self.__client.finish_test_item(self.launch_uuid, item_id, end_time, status=status, - issue=issue, attributes=attributes, - description=description, - retry=retry, **kwargs) + :param item_id: ID of the Test Item. + :param end_time: The Item end time. + :param status: Test status. Allowed values: + PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. + :param issue: Issue which will be attached to the current Item. + :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override + attributes on start Test Item call. + :param description: Test Item description. Overrides description from start request. + :param retry: Used to report retry of the test. Allowed values: "True" or "False". + :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. + :return: Response message. + """ + result = await self.__client.finish_test_item( + self.launch_uuid, item_id, end_time, status=status, issue=issue, attributes=attributes, + description=description, retry=retry, test_case_id=test_case_id, retry_of=retry_of, **kwargs) self._remove_current_item() return result async def finish_launch(self, end_time: str, - status: str = None, + status: Optional[str] = None, attributes: Optional[Union[list, dict]] = None, **kwargs: Any) -> Optional[str]: """Finish a Launch. @@ -1132,6 +1150,7 @@ def start_test_item(self, code_ref: Optional[str] = None, retry: bool = False, test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Task[str]: """Start Test Case/Suite/Step/Nested Step Item. @@ -1149,13 +1168,15 @@ def start_test_item(self, :param code_ref: Physical location of the Test Item. :param retry: Used to report retry of the test. Allowed values: "True" or "False". :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. :return: Test Item UUID if successfully started or None. """ item_id_coro = self.__client.start_test_item(self.launch_uuid, name, start_time, item_type, description=description, attributes=attributes, parameters=parameters, parent_item_id=parent_item_id, has_stats=has_stats, code_ref=code_ref, retry=retry, - test_case_id=test_case_id, **kwargs) + test_case_id=test_case_id, retry_of=retry_of, **kwargs) item_id_task = self.create_task(item_id_coro) self._add_current_item(item_id_task) return item_id_task @@ -1163,36 +1184,40 @@ def start_test_item(self, def finish_test_item(self, item_id: Task[str], end_time: str, - status: str = None, + status: Optional[str] = None, issue: Optional[Issue] = None, attributes: Optional[Union[list, dict]] = None, description: str = None, retry: bool = False, + test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Task[str]: """Finish Test Suite/Case/Step/Nested Step Item. - :param item_id: ID of the Test Item. - :param end_time: The Item end time. - :param status: Test status. Allowed values: - PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. - :param issue: Issue which will be attached to the current Item. - :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override - attributes on start Test Item call. - :param description: Test Item description. Overrides description from start request. - :param retry: Used to report retry of the test. Allowed values: "True" or "False". - :return: Response message. - """ - result_coro = self.__client.finish_test_item(self.launch_uuid, item_id, end_time, status=status, - issue=issue, attributes=attributes, - description=description, - retry=retry, **kwargs) + :param item_id: ID of the Test Item. + :param end_time: The Item end time. + :param status: Test status. Allowed values: + PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. + :param issue: Issue which will be attached to the current Item. + :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override + attributes on start Test Item call. + :param description: Test Item description. Overrides description from start request. + :param retry: Used to report retry of the test. Allowed values: "True" or "False". + :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. + :return: Response message. + """ + result_coro = self.__client.finish_test_item( + self.launch_uuid, item_id, end_time, status=status, issue=issue, attributes=attributes, + description=description, retry=retry, test_case_id=test_case_id, retry_of=retry_of, **kwargs) result_task = self.create_task(result_coro) self._remove_current_item() return result_task def finish_launch(self, end_time: str, - status: str = None, + status: Optional[str] = None, attributes: Optional[Union[list, dict]] = None, **kwargs: Any) -> Task[str]: """Finish a Launch. diff --git a/reportportal_client/client.py b/reportportal_client/client.py index bb05645..d365e22 100644 --- a/reportportal_client/client.py +++ b/reportportal_client/client.py @@ -151,10 +151,11 @@ def start_test_item(self, attributes: Optional[Union[List[dict], dict]] = None, parameters: Optional[dict] = None, parent_item_id: Optional[str] = None, - has_stats: bool = True, + has_stats: Optional[bool] = True, code_ref: Optional[str] = None, - retry: bool = False, + retry: Optional[bool] = False, test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Optional[str]: """Start Test Case/Suite/Step/Nested Step Item. @@ -172,6 +173,8 @@ def start_test_item(self, :param code_ref: Physical location of the Test Item. :param retry: Used to report retry of the test. Allowed values: "True" or "False". :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. :return: Test Item UUID if successfully started or None. """ raise NotImplementedError('"start_test_item" method is not implemented!') @@ -180,31 +183,36 @@ def start_test_item(self, def finish_test_item(self, item_id: str, end_time: str, - status: str = None, + status: Optional[str] = None, issue: Optional[Issue] = None, attributes: Optional[Union[list, dict]] = None, - description: str = None, - retry: bool = False, + description: Optional[str] = None, + retry: Optional[bool] = False, + test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Optional[str]: """Finish Test Suite/Case/Step/Nested Step Item. - :param item_id: ID of the Test Item. - :param end_time: The Item end time. - :param status: Test status. Allowed values: - PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. - :param issue: Issue which will be attached to the current Item. - :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override - attributes on start Test Item call. - :param description: Test Item description. Overrides description from start request. - :param retry: Used to report retry of the test. Allowed values: "True" or "False". - :return: Response message. + :param item_id: ID of the Test Item. + :param end_time: The Item end time. + :param status: Test status. Allowed values: + PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. + :param issue: Issue which will be attached to the current Item. + :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override + attributes on start Test Item call. + :param description: Test Item description. Overrides description from start request. + :param retry: Used to report retry of the test. Allowed values: "True" or "False". + :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. + :return: Response message. """ raise NotImplementedError('"finish_test_item" method is not implemented!') @abstractmethod def finish_launch(self, end_time: str, - status: str = None, + status: Optional[str] = None, attributes: Optional[Union[list, dict]] = None, **kwargs: Any) -> Optional[str]: """Finish a Launch. @@ -574,6 +582,7 @@ def start_test_item(self, code_ref: Optional[str] = None, retry: bool = False, test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **_: Any) -> Optional[str]: """Start Test Case/Suite/Step/Nested Step Item. @@ -591,6 +600,8 @@ def start_test_item(self, :param code_ref: Physical location of the Test Item. :param retry: Used to report retry of the test. Allowed values: "True" or "False". :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. :return: Test Item UUID if successfully started or None. """ if parent_item_id is NOT_FOUND: @@ -611,7 +622,8 @@ def start_test_item(self, has_stats=has_stats, parameters=parameters, retry=retry, - test_case_id=test_case_id + test_case_id=test_case_id, + retry_of=retry_of ).payload response = HttpRequest(self.session.post, url=url, json=request_payload, verify_ssl=self.verify_ssl, @@ -630,24 +642,29 @@ def start_test_item(self, def finish_test_item(self, item_id: str, end_time: str, - status: str = None, + status: Optional[str] = None, issue: Optional[Issue] = None, attributes: Optional[Union[list, dict]] = None, - description: str = None, - retry: bool = False, + description: Optional[str] = None, + retry: Optional[bool] = False, + test_case_id: Optional[str] = None, + retry_of: Optional[str] = None, **kwargs: Any) -> Optional[str]: """Finish Test Suite/Case/Step/Nested Step Item. - :param item_id: ID of the Test Item. - :param end_time: The Item end time. - :param status: Test status. Allowed values: - PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. - :param issue: Issue which will be attached to the current Item. - :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override - attributes on start Test Item call. - :param description: Test Item description. Overrides description from start request. - :param retry: Used to report retry of the test. Allowed values: "True" or "False". - :return: Response message. + :param item_id: ID of the Test Item. + :param end_time: The Item end time. + :param status: Test status. Allowed values: + PASSED, FAILED, STOPPED, SKIPPED, INTERRUPTED, CANCELLED, INFO, WARN or None. + :param issue: Issue which will be attached to the current Item. + :param attributes: Test Item attributes(tags). Pairs of key and value. These attributes override + attributes on start Test Item call. + :param description: Test Item description. Overrides description from start request. + :param retry: Used to report retry of the test. Allowed values: "True" or "False". + :param test_case_id: A unique ID of the current Step. + :param retry_of: For retry mode specifies which test item will be marked as retried. Should be used + with the 'retry' parameter. + :return: Response message. """ if item_id is NOT_FOUND or not item_id: logger.warning('Attempt to finish non-existent item') @@ -661,7 +678,9 @@ def finish_test_item(self, description=description, is_skipped_an_issue=self.is_skipped_an_issue, issue=issue, - retry=retry + retry=retry, + test_case_id=test_case_id, + retry_of=retry_of ).payload response = HttpRequest(self.session.put, url=url, json=request_payload, verify_ssl=self.verify_ssl, http_timeout=self.http_timeout).make() @@ -674,15 +693,14 @@ def finish_test_item(self, def finish_launch(self, end_time: str, - status: str = None, + status: Optional[str] = None, attributes: Optional[Union[list, dict]] = None, **kwargs: Any) -> Optional[str]: """Finish launch. :param end_time: Launch end time :param status: Launch status. Can be one of the followings: - PASSED, FAILED, STOPPED, SKIPPED, RESETED, - CANCELLED + PASSED, FAILED, STOPPED, SKIPPED, CANCELLED :param attributes: Launch attributes """ if self.use_own_launch: diff --git a/reportportal_client/core/rp_requests.py b/reportportal_client/core/rp_requests.py index a7482ce..75d8aeb 100644 --- a/reportportal_client/core/rp_requests.py +++ b/reportportal_client/core/rp_requests.py @@ -270,9 +270,10 @@ class ItemStartRequest(RPRequestBase): attributes: Optional[Union[list, dict]] code_ref: Optional[str] description: Optional[str] - has_stats: bool + has_stats: Optional[bool] parameters: Optional[Union[list, dict]] - retry: bool + retry: Optional[bool] + retry_of: Optional[str] test_case_id: Optional[str] @staticmethod @@ -283,6 +284,7 @@ def _create_request(**kwargs) -> dict: 'hasStats': kwargs.get('has_stats'), 'name': kwargs['name'], 'retry': kwargs.get('retry'), + 'retryOf': kwargs.get('retry_of'), 'startTime': kwargs['start_time'], 'testCaseId': kwargs.get('test_case_id'), 'type': kwargs['type'], @@ -340,12 +342,14 @@ class ItemFinishRequest(RPRequestBase): end_time: str launch_uuid: Any - status: str + status: Optional[str] attributes: Optional[Union[list, dict]] - description: str - is_skipped_an_issue: bool - issue: Issue - retry: bool + description: Optional[str] + is_skipped_an_issue: Optional[bool] + issue: Optional[Issue] + retry: Optional[bool] + retry_of: Optional[str] + test_case_id: Optional[str] @staticmethod def _create_request(**kwargs) -> dict: @@ -354,21 +358,22 @@ def _create_request(**kwargs) -> dict: 'endTime': kwargs['end_time'], 'launchUuid': kwargs['launch_uuid'], 'status': kwargs.get('status'), - 'retry': kwargs.get('retry') + 'retry': kwargs.get('retry'), + 'retryOf': kwargs.get('retry_of'), + 'testCaseId': kwargs.get('test_case_id'), } attributes = kwargs.get('attributes') if attributes and isinstance(attributes, dict): attributes = dict_to_payload(kwargs['attributes']) request['attributes'] = attributes + issue_payload = None if kwargs.get('issue') is None and ( kwargs.get('status') is not None and kwargs.get('status').lower() == 'skipped' ) and not kwargs.get('is_skipped_an_issue'): issue_payload = {'issue_type': 'NOT_ISSUE'} elif kwargs.get('issue') is not None: issue_payload = kwargs.get('issue').payload - else: - issue_payload = None request['issue'] = issue_payload return request diff --git a/setup.py b/setup.py index 27b3e49..679b343 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup, find_packages -__version__ = '5.5.7' +__version__ = '5.5.8' TYPE_STUBS = ['*.pyi']