From ff2dc6f825bc3437ce2bd06b743ab17d639ace14 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 28 Mar 2024 09:48:53 +0100 Subject: [PATCH 01/13] feat: add dataclasses --- polarion_rest_api_client/data_models.py | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/polarion_rest_api_client/data_models.py b/polarion_rest_api_client/data_models.py index 7789ea98..0a437d49 100644 --- a/polarion_rest_api_client/data_models.py +++ b/polarion_rest_api_client/data_models.py @@ -5,6 +5,7 @@ import base64 import dataclasses +import enum import hashlib import json import typing as t @@ -228,9 +229,43 @@ def __init__( self.home_page_content = home_page_content +class TestRun(BaseItem): + """A data class for all data of a test run.""" + + title: str | None = None + home_page_content: TextContent | None = None + select_test_cases_by: SelectTestCasesBy | None = None + + +@dataclasses.dataclass +class TestRecord: + """A data class for test record data.""" + + work_item_id: str + iteration: int = 0 + duration: int = 0 + result: str | None = None + test_case_revision: str | None = None + comment: TextContent | None = None + additional_properties: dict[str, t.Any] = dataclasses.field( + default_factory=dict + ) + + @dataclasses.dataclass class TextContent: """A data class for home_page_content of a Polarion Document.""" type: str | None = None value: str | None = None + + +class SelectTestCasesBy(str, enum.Enum): + """Test case selection mode enum.""" + + AUTOMATEDPROCESS = "automatedProcess" + DYNAMICLIVEDOC = "dynamicLiveDoc" + DYNAMICQUERYRESULT = "dynamicQueryResult" + MANUALSELECTION = "manualSelection" + STATICLIVEDOC = "staticLiveDoc" + STATICQUERYRESULT = "staticQueryResult" From 04f95793a8661ad41c345471c3b513a0cc428201 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 28 Mar 2024 12:54:10 +0100 Subject: [PATCH 02/13] feat: add get apis --- polarion_rest_api_client/base_client.py | 86 ++++++++++++ polarion_rest_api_client/client.py | 166 +++++++++++++++++++++--- polarion_rest_api_client/data_models.py | 11 +- 3 files changed, 241 insertions(+), 22 deletions(-) diff --git a/polarion_rest_api_client/base_client.py b/polarion_rest_api_client/base_client.py index 9e1f73bf..98c1789a 100644 --- a/polarion_rest_api_client/base_client.py +++ b/polarion_rest_api_client/base_client.py @@ -18,6 +18,8 @@ class DefaultFields: _linkedworkitems: str = "id,role,suspect" _workitem_attachments: str = "@basic" _documents: str = "@basic" + _testrecords: str = "@basic" + _testruns: str = "@basic" @property def workitems(self): @@ -55,6 +57,24 @@ def documents(self): def documents(self, value): self._documents = value + @property + def testruns(self): + """Return the fields dict for document.""" + return {"testruns": self._testruns} + + @testruns.setter + def testruns(self, value): + self._testruns = value + + @property + def testrecords(self): + """Return the fields dict for document.""" + return {"testrecords": self._testrecords} + + @testrecords.setter + def testrecords(self, value): + self._testrecords = value + @property def all_types(self): """Return all fields dicts merged together.""" @@ -63,6 +83,8 @@ def all_types(self): | self.workitems | self.linkedworkitems | self.documents + | self.testruns + | self.testrecords ) @@ -360,3 +382,67 @@ def delete_work_item_link(self, work_item_link: dm.WorkItemLink): """Delete the links between the work items in work_item_link.""" self._set_project(work_item_link) self._delete_work_item_links([work_item_link]) + + def get_all_test_runs( + self, + query: str, + fields: dict[str, str] | None = None, + ) -> list[dm.TestRun]: + """Get all test runs matching the given query. + + Will handle pagination automatically. Define a fields dictionary + as described in the Polarion API documentation to get certain + fields. + """ + return self._request_all_items( + self.get_test_runs, fields=fields, query=query + ) + + @abc.abstractmethod + def get_test_runs( + self, + query: str, + fields: dict[str, str] | None = None, + page_size: int = 100, + page_number: int = 1, + retry: bool = True, + ) -> tuple[list[dm.TestRun], bool]: + """Return the test runs on a defined page matching the given query. + + In addition, a flag whether a next page is available is + returned. Define a fields dictionary as described in the + Polarion API documentation to get certain fields. + """ + raise NotImplementedError + + def get_all_test_records( + self, + test_run_id: str, + fields: dict[str, str] | None = None, + ) -> list[dm.TestRecord]: + """Get all test records matching the given query. + + Will handle pagination automatically. Define a fields dictionary + as described in the Polarion API documentation to get certain + fields. + """ + return self._request_all_items( + self.get_test_records, fields=fields, test_run_id=test_run_id + ) + + @abc.abstractmethod + def get_test_records( + self, + test_run_id: str, + fields: dict[str, str] | None = None, + page_size: int = 100, + page_number: int = 1, + retry: bool = True, + ) -> tuple[list[dm.TestRecord], bool]: + """Return the test records on a defined page matching the given query. + + In addition, a flag whether a next page is available is + returned. Define a fields dictionary as described in the + Polarion API documentation to get certain fields. + """ + raise NotImplementedError diff --git a/polarion_rest_api_client/client.py b/polarion_rest_api_client/client.py index 690ee010..b5dc5025 100644 --- a/polarion_rest_api_client/client.py +++ b/polarion_rest_api_client/client.py @@ -25,6 +25,14 @@ post_linked_work_items, ) from polarion_rest_api_client.open_api_client.api.projects import get_project +from polarion_rest_api_client.open_api_client.api.test_records import ( + get_test_records, + post_test_records, +) +from polarion_rest_api_client.open_api_client.api.test_runs import ( + get_test_runs, + post_test_runs, +) from polarion_rest_api_client.open_api_client.api.work_item_attachments import ( # pylint: disable=line-too-long delete_work_item_attachment, get_work_item_attachments, @@ -747,7 +755,7 @@ def get_document( if not getattr(data.meta, "errors", []): assert (attributes := data.attributes) assert isinstance(data.id, str) - home_page_content = self._handle_home_page_content( + home_page_content = self._handle_text_content( attributes.home_page_content ) @@ -761,29 +769,19 @@ def get_document( ) return None - def _handle_home_page_content( + def _handle_text_content( self, - home_page_content: ( - api_models.DocumentsSingleGetResponseDataAttributesHomePageContent - | oa_types.Unset - ), + polarion_content: api_models.DocumentsSingleGetResponseDataAttributesHomePageContent + | api_models.TestrecordsListGetResponseDataItemAttributesComment + | api_models.TestrunsListGetResponseDataItemAttributesHomePageContent + | oa_types.Unset, ) -> dm.TextContent | None: - if isinstance(home_page_content, oa_types.Unset): + if not polarion_content: return None - home_page_content_type = None - home_page_content_value = None - - if isinstance( - home_page_content.type, - api_models.DocumentsSingleGetResponseDataAttributesHomePageContentType, - ): - home_page_content_type = str(home_page_content.type) - if isinstance(home_page_content.value, str): - home_page_content_value = home_page_content.value return dm.TextContent( - type=home_page_content_type, - value=home_page_content_value, + type=str(polarion_content.type) if polarion_content.type else None, + value=polarion_content.value or None, ) def create_work_items(self, work_items: list[base_client.WorkItemType]): @@ -1024,3 +1022,133 @@ def _delete_work_item_links( if not self._check_response(response, not retry) and retry: sleep_random_time() self._delete_work_item_links(work_item_links, False) + + def get_test_records( + self, + test_run_id: str, + fields: dict[str, str] | None = None, + page_size: int = 100, + page_number: int = 1, + retry: bool = True, + ) -> tuple[list[dm.TestRecord], bool]: + """Return the test records on a defined page matching the given query. + + In addition, a flag whether a next page is available is + returned. Define a fields dictionary as described in the + Polarion API documentation to get certain fields. + """ + if fields is None: + fields = self.default_fields.testrecords + + sparse_fields = _build_sparse_fields(fields) + response = get_test_records.sync_detailed( + self.project_id, + test_run_id, + client=self.client, + fields=sparse_fields, + pagenumber=page_number, + pagesize=page_size, + ) + + if not self._check_response(response, not retry) and retry: + sleep_random_time() + return self.get_test_records( + test_run_id, fields, page_size, page_number, False + ) + + parsed_response = response.parsed + assert parsed_response + + test_records = [] + for data in parsed_response.data or []: + assert isinstance(data.id, str) + assert isinstance( + data.attributes, + api_models.TestrecordsListGetResponseDataItemAttributes, + ) + _, _, project_id, work_item, iteration = data.id.split("/") + test_records.append( + dm.TestRecord( + project_id, + work_item, + data.attributes.test_case_revision or None, + int(iteration), + data.attributes.duration or -1, + data.attributes.result or None, + self._handle_text_content(data.attributes.comment), + data.additional_properties or {}, + ) + ) + next_page = isinstance( + parsed_response.links, + api_models.TestrecordsListGetResponseLinks, + ) and bool(parsed_response.links.next_) + + return test_records, next_page + + def get_test_runs( + self, + query: str, + fields: dict[str, str] | None = None, + page_size: int = 100, + page_number: int = 1, + retry: bool = True, + ) -> tuple[list[dm.TestRun], bool]: + """Return the test runs on a defined page matching the given query. + + In addition, a flag whether a next page is available is + returned. Define a fields dictionary as described in the + Polarion API documentation to get certain fields. + """ + if fields is None: + fields = self.default_fields.testruns + + sparse_fields = _build_sparse_fields(fields) + response = get_test_runs.sync_detailed( + self.project_id, + client=self.client, + query=query, + fields=sparse_fields, + pagenumber=page_number, + pagesize=page_size, + ) + + if not self._check_response(response, not retry) and retry: + sleep_random_time() + return self.get_test_runs( + query, fields, page_size, page_number, False + ) + + parsed_response = response.parsed + assert parsed_response + + test_runs = [] + for data in parsed_response.data or []: + assert isinstance(data.id, str) + assert isinstance( + data.attributes, + api_models.TestrunsListGetResponseDataItemAttributes, + ) + test_runs.append( + dm.TestRun( + data.id.split("/")[-1], + data.attributes.type or None, + data.attributes.status or None, + data.attributes.title or None, + self._handle_text_content( + data.attributes.home_page_content + ), + dm.SelectTestCasesBy( + str(data.attributes.select_test_cases_by) + ) + if data.attributes.select_test_cases_by + else None, + data.attributes.additional_properties or {}, + ) + ) + next_page = isinstance( + parsed_response.links, + api_models.TestrunsListGetResponseLinks, + ) and bool(parsed_response.links.next_) + + return test_runs, next_page diff --git a/polarion_rest_api_client/data_models.py b/polarion_rest_api_client/data_models.py index 0a437d49..051bfe57 100644 --- a/polarion_rest_api_client/data_models.py +++ b/polarion_rest_api_client/data_models.py @@ -229,25 +229,30 @@ def __init__( self.home_page_content = home_page_content +@dataclasses.dataclass class TestRun(BaseItem): """A data class for all data of a test run.""" title: str | None = None home_page_content: TextContent | None = None select_test_cases_by: SelectTestCasesBy | None = None + additional_attributes: dict[str, t.Any] = dataclasses.field( + default_factory=dict + ) @dataclasses.dataclass class TestRecord: """A data class for test record data.""" + work_item_project_id: str work_item_id: str + work_item_revision: str | None = None iteration: int = 0 - duration: int = 0 + duration: float = 0 result: str | None = None - test_case_revision: str | None = None comment: TextContent | None = None - additional_properties: dict[str, t.Any] = dataclasses.field( + additional_attributes: dict[str, t.Any] = dataclasses.field( default_factory=dict ) From 0a78366a9bc005375392fbdef5c42ed48f6c6a2a Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 28 Mar 2024 15:42:11 +0100 Subject: [PATCH 03/13] feat: add post and patch for testruns and records --- polarion_rest_api_client/base_client.py | 25 ++++ polarion_rest_api_client/client.py | 187 +++++++++++++++++++++++- 2 files changed, 211 insertions(+), 1 deletion(-) diff --git a/polarion_rest_api_client/base_client.py b/polarion_rest_api_client/base_client.py index 98c1789a..de52ef02 100644 --- a/polarion_rest_api_client/base_client.py +++ b/polarion_rest_api_client/base_client.py @@ -446,3 +446,28 @@ def get_test_records( Polarion API documentation to get certain fields. """ raise NotImplementedError + + @abc.abstractmethod + def create_test_runs( + self, test_runs: list[dm.TestRun], retry: bool = True + ): + """Create the given list of test runs.""" + raise NotImplementedError + + def create_test_run(self, test_run: dm.TestRun): + """Create the given test run.""" + self.create_test_runs([test_run]) + + @abc.abstractmethod + def create_test_records( + self, + test_run_id: str, + test_records: list[dm.TestRecord], + retry: bool = True, + ): + """Create the given list of test records.""" + raise NotImplementedError + + def create_test_record(self, test_run_id: str, test_record: dm.TestRecord): + """Create the given list of test records.""" + self.create_test_records(test_run_id, [test_record]) diff --git a/polarion_rest_api_client/client.py b/polarion_rest_api_client/client.py index b5dc5025..eb4d058a 100644 --- a/polarion_rest_api_client/client.py +++ b/polarion_rest_api_client/client.py @@ -27,10 +27,12 @@ from polarion_rest_api_client.open_api_client.api.projects import get_project from polarion_rest_api_client.open_api_client.api.test_records import ( get_test_records, + patch_test_record, post_test_records, ) from polarion_rest_api_client.open_api_client.api.test_runs import ( get_test_runs, + patch_test_run, post_test_runs, ) from polarion_rest_api_client.open_api_client.api.work_item_attachments import ( # pylint: disable=line-too-long @@ -771,7 +773,7 @@ def get_document( def _handle_text_content( self, - polarion_content: api_models.DocumentsSingleGetResponseDataAttributesHomePageContent + polarion_content: api_models.DocumentsSingleGetResponseDataAttributesHomePageContent # pylint: disable=line-too-long | api_models.TestrecordsListGetResponseDataItemAttributesComment | api_models.TestrunsListGetResponseDataItemAttributesHomePageContent | oa_types.Unset, @@ -1152,3 +1154,186 @@ def get_test_runs( ) and bool(parsed_response.links.next_) return test_runs, next_page + + def create_test_runs( + self, test_runs: list[dm.TestRun], retry: bool = True + ): + """Create the given list of test runs.""" + polarion_test_runs = [ + api_models.TestrunsListPostRequestDataItem( + api_models.TestrunsListPostRequestDataItemType.TESTRUNS, + self._fill_test_run_attributes( + api_models.TestrunsListPostRequestDataItemAttributes, + test_run, + ), + ) + for test_run in test_runs + ] + + response = post_test_runs.sync_detailed( + self.project_id, + client=self.client, + body=api_models.TestrunsListPostRequest(polarion_test_runs), + ) + + if not self._check_response(response, not retry) and retry: + sleep_random_time() + self.create_test_runs(test_runs, False) + + def update_test_run(self, test_run: dm.TestRun, retry: bool = True): + """Create the given list of test runs.""" + assert test_run.id + response = patch_test_run.sync_detailed( + self.project_id, + test_run.id, + client=self.client, + body=api_models.TestrunsSinglePatchRequest( + api_models.TestrunsSinglePatchRequestData( + api_models.TestrunsSinglePatchRequestDataType.TESTRUNS, + f"{self.project_id}/{test_run.id}", + self._fill_test_run_attributes( + api_models.TestrunsSinglePatchRequestDataAttributes, + test_run, + ), + ) + ), + ) + + if not self._check_response(response, not retry) and retry: + sleep_random_time() + self.update_test_run(test_run, False) + + def _fill_test_run_attributes( + self, + attributes_type: type[ + api_models.TestrunsListPostRequestDataItemAttributes + | api_models.TestrunsSinglePatchRequestDataAttributes + ], + test_run: dm.TestRun, + ): + type_prefix = attributes_type.__name__ + attributes = attributes_type() + if test_run.type: + attributes.type = test_run.type + if test_run.id and hasattr(attributes, "id"): + attributes.id = test_run.id + if test_run.status: + attributes.status = test_run.status + if test_run.title: + attributes.title = test_run.title + if test_run.additional_attributes: + attributes.additional_properties = test_run.additional_attributes + if test_run.select_test_cases_by: + attributes.select_test_cases_by = getattr( + api_models, f"{type_prefix}SelectTestCasesBy" + )(str(test_run.select_test_cases_by)) + if test_run.home_page_content: + attributes.home_page_content = getattr( + api_models, f"{type_prefix}HomePageContent" + )() + assert attributes.home_page_content + if test_run.home_page_content.type: + attributes.home_page_content.type = getattr( + api_models, f"{type_prefix}HomePageContentType" + )(test_run.home_page_content.type) + if test_run.home_page_content.value: + attributes.home_page_content.value = ( + test_run.home_page_content.value + ) + + return attributes + + def _fill_test_record_attributes( + self, + attributes_type: type[ + api_models.TestrecordsListPostRequestDataItemAttributes + | api_models.TestrecordsSinglePatchRequestDataAttributes + ], + test_record: dm.TestRecord, + ): + type_prefix = attributes_type.__name__ + attributes = attributes_type() + if test_record.result: + attributes.result = test_record.result + if test_record.comment: + attributes.comment = getattr(api_models, f"{type_prefix}Comment")() + assert attributes.comment + if test_record.comment.type: + attributes.comment.type = getattr( + api_models, f"{type_prefix}CommentType" + )(test_record.comment.type) + if test_record.comment.value: + attributes.comment.value = test_record.comment.value + if test_record.duration: + attributes.duration = test_record.duration + if test_record.work_item_revision: + attributes.test_case_revision = test_record.work_item_revision + if test_record.additional_attributes: + attributes.additional_properties = ( + test_record.additional_attributes + ) + return attributes + + def create_test_records( + self, + test_run_id: str, + test_records: list[dm.TestRecord], + retry: bool = True, + ): + """Create the given list of test records.""" + response = post_test_records.sync_detailed( + self.project_id, + test_run_id, + client=self.client, + body=api_models.TestrecordsListPostRequest( + [ + api_models.TestrecordsListPostRequestDataItem( + api_models.TestrecordsListPostRequestDataItemType.TESTRECORDS, + self._fill_test_record_attributes( + api_models.TestrecordsListPostRequestDataItemAttributes, + test_record, + ), + api_models.TestrecordsListPostRequestDataItemRelationships( + test_case=api_models.TestrecordsListPostRequestDataItemRelationshipsTestCase( + api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseData( + api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseDataType.WORKITEMS, + f"{test_record.work_item_project_id}/{test_record.work_item_id}", + ) + ) + ), + ) + for test_record in test_records + ] + ), + ) + + if not self._check_response(response, not retry) and retry: + sleep_random_time() + self.create_test_records(test_run_id, test_records, False) + + def update_test_record( + self, test_run_id: str, test_record: dm.TestRecord, retry: bool = True + ): + """Create the given list of test records.""" + response = patch_test_record.sync_detailed( + self.project_id, + test_run_id, + test_record.work_item_project_id, + test_record.work_item_id, + str(test_record.iteration), + client=self.client, + body=api_models.TestrecordsSinglePatchRequest( + api_models.TestrecordsSinglePatchRequestData( + api_models.TestrecordsSinglePatchRequestDataType.TESTRECORDS, + f"{self.project_id}/{test_run_id}/{test_record.work_item_project_id}/{test_record.work_item_id}/{test_record.iteration}", + self._fill_test_record_attributes( + api_models.TestrecordsSinglePatchRequestDataAttributes, + test_record, + ), + ) + ), + ) + + if not self._check_response(response, not retry) and retry: + sleep_random_time() + self.update_test_record(test_run_id, test_record, False) From a374061f3792271d7d810176d5fcf160adf56013 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Mon, 6 May 2024 15:29:21 +0200 Subject: [PATCH 04/13] feat: import new dataclasses on package level --- polarion_rest_api_client/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/polarion_rest_api_client/__init__.py b/polarion_rest_api_client/__init__.py index 52911fbd..1597bd5e 100644 --- a/polarion_rest_api_client/__init__.py +++ b/polarion_rest_api_client/__init__.py @@ -11,6 +11,9 @@ from polarion_rest_api_client.client import OpenAPIPolarionProjectClient from polarion_rest_api_client.data_models import ( + SelectTestCasesBy, + TestRecord, + TestRun, WorkItem, WorkItemAttachment, WorkItemLink, From e797743eb466072755a3b130ff8cffb380d0fecf Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Tue, 14 May 2024 19:37:14 +0200 Subject: [PATCH 05/13] feat: add additional fields for test runs and records and additional minor changes --- polarion_rest_api_client/__init__.py | 1 + polarion_rest_api_client/base_client.py | 4 +- polarion_rest_api_client/client.py | 141 +++++++++++++++++------- polarion_rest_api_client/data_models.py | 13 ++- 4 files changed, 118 insertions(+), 41 deletions(-) diff --git a/polarion_rest_api_client/__init__.py b/polarion_rest_api_client/__init__.py index 1597bd5e..9e1a98a9 100644 --- a/polarion_rest_api_client/__init__.py +++ b/polarion_rest_api_client/__init__.py @@ -14,6 +14,7 @@ SelectTestCasesBy, TestRecord, TestRun, + TextContent, WorkItem, WorkItemAttachment, WorkItemLink, diff --git a/polarion_rest_api_client/base_client.py b/polarion_rest_api_client/base_client.py index de52ef02..d024e051 100644 --- a/polarion_rest_api_client/base_client.py +++ b/polarion_rest_api_client/base_client.py @@ -385,7 +385,7 @@ def delete_work_item_link(self, work_item_link: dm.WorkItemLink): def get_all_test_runs( self, - query: str, + query: str = "", fields: dict[str, str] | None = None, ) -> list[dm.TestRun]: """Get all test runs matching the given query. @@ -401,7 +401,7 @@ def get_all_test_runs( @abc.abstractmethod def get_test_runs( self, - query: str, + query: str = "", fields: dict[str, str] | None = None, page_size: int = 100, page_number: int = 1, diff --git a/polarion_rest_api_client/client.py b/polarion_rest_api_client/client.py index eb4d058a..86fa198e 100644 --- a/polarion_rest_api_client/client.py +++ b/polarion_rest_api_client/client.py @@ -3,6 +3,7 @@ """The actual implementation of the API client using an OpenAPIClient.""" from __future__ import annotations +import datetime import io import json import logging @@ -50,6 +51,7 @@ ) logger = logging.getLogger(__name__) +T = t.TypeVar("T", str, int, float, datetime.datetime, bool, None) def _get_json_content_size(data: dict): @@ -77,7 +79,19 @@ def _build_sparse_fields( return api_models.SparseFields.from_dict(new_field_dict) -def unset_str_builder(value: str | oa_types.Unset) -> str | None: +@t.overload +def unset_to_none(value: oa_types.Unset) -> None: + """Return None if value is Unset, else the value.""" + ... + + +@t.overload +def unset_to_none(value: T) -> T: + """Return None if value is Unset, else the value.""" + ... + + +def unset_to_none(value: t.Any) -> t.Any: """Return None if value is Unset, else the value.""" if isinstance(value, oa_types.Unset): return None @@ -399,8 +413,8 @@ def get_work_item_attachments( dm.WorkItemAttachment( work_item_id, attachment.attributes.id, - unset_str_builder(attachment.attributes.title), - file_name=unset_str_builder( + unset_to_none(attachment.attributes.title), + file_name=unset_to_none( attachment.attributes.file_name ), ) @@ -688,16 +702,16 @@ def _generate_work_item( desctype = None desc = None if work_item.attributes.description: - desctype = unset_str_builder(work_item.attributes.description.type) - desc = unset_str_builder(work_item.attributes.description.value) + desctype = unset_to_none(work_item.attributes.description.type) + desc = unset_to_none(work_item.attributes.description.value) work_item_obj = self._work_item( work_item_id, - unset_str_builder(work_item.attributes.title), + unset_to_none(work_item.attributes.title), desctype, desc, - unset_str_builder(work_item.attributes.type), - unset_str_builder(work_item.attributes.status), + unset_to_none(work_item.attributes.type), + unset_to_none(work_item.attributes.status), work_item.attributes.additional_properties, work_item_links, work_item_attachments, @@ -763,20 +777,22 @@ def get_document( return dm.Document( id=data.id, - module_folder=unset_str_builder(attributes.module_folder), - module_name=unset_str_builder(attributes.module_name), - type=unset_str_builder(attributes.type), - status=unset_str_builder(attributes.status), + module_folder=unset_to_none(attributes.module_folder), + module_name=unset_to_none(attributes.module_name), + type=unset_to_none(attributes.type), + status=unset_to_none(attributes.status), home_page_content=home_page_content, ) return None def _handle_text_content( self, - polarion_content: api_models.DocumentsSingleGetResponseDataAttributesHomePageContent # pylint: disable=line-too-long - | api_models.TestrecordsListGetResponseDataItemAttributesComment - | api_models.TestrunsListGetResponseDataItemAttributesHomePageContent - | oa_types.Unset, + polarion_content: ( + api_models.DocumentsSingleGetResponseDataAttributesHomePageContent # pylint: disable=line-too-long + | api_models.TestrecordsListGetResponseDataItemAttributesComment + | api_models.TestrunsListGetResponseDataItemAttributesHomePageContent + | oa_types.Unset + ), ) -> dm.TextContent | None: if not polarion_content: return None @@ -1059,9 +1075,12 @@ def get_test_records( ) parsed_response = response.parsed - assert parsed_response + assert isinstance( + parsed_response, api_models.TestrecordsListGetResponse + ) test_records = [] + for data in parsed_response.data or []: assert isinstance(data.id, str) assert isinstance( @@ -1073,11 +1092,18 @@ def get_test_records( dm.TestRecord( project_id, work_item, - data.attributes.test_case_revision or None, + unset_to_none(data.attributes.test_case_revision), int(iteration), - data.attributes.duration or -1, - data.attributes.result or None, + ( + data.attributes.duration + if not isinstance( + data.attributes.duration, oa_types.Unset + ) + else -1 + ), + unset_to_none(data.attributes.result), self._handle_text_content(data.attributes.comment), + unset_to_none(data.attributes.executed), data.additional_properties or {}, ) ) @@ -1090,7 +1116,7 @@ def get_test_records( def get_test_runs( self, - query: str, + query: str = "", fields: dict[str, str] | None = None, page_size: int = 100, page_number: int = 1, @@ -1122,7 +1148,7 @@ def get_test_runs( ) parsed_response = response.parsed - assert parsed_response + assert isinstance(parsed_response, api_models.TestrunsListGetResponse) test_runs = [] for data in parsed_response.data or []: @@ -1134,17 +1160,26 @@ def get_test_runs( test_runs.append( dm.TestRun( data.id.split("/")[-1], - data.attributes.type or None, - data.attributes.status or None, - data.attributes.title or None, + unset_to_none(data.attributes.type), + unset_to_none(data.attributes.status), + unset_to_none(data.attributes.title), self._handle_text_content( data.attributes.home_page_content ), - dm.SelectTestCasesBy( - str(data.attributes.select_test_cases_by) - ) - if data.attributes.select_test_cases_by - else None, + unset_to_none(data.attributes.finished_on), + unset_to_none(data.attributes.group_id), + unset_to_none(data.attributes.id_prefix), + unset_to_none(data.attributes.is_template), + unset_to_none(data.attributes.keep_in_history), + unset_to_none(data.attributes.query), + unset_to_none(data.attributes.keep_in_history), + ( + dm.SelectTestCasesBy( + str(data.attributes.select_test_cases_by) + ) + if data.attributes.select_test_cases_by + else None + ), data.attributes.additional_properties or {}, ) ) @@ -1213,20 +1248,38 @@ def _fill_test_run_attributes( ): type_prefix = attributes_type.__name__ attributes = attributes_type() - if test_run.type: + if test_run.type is not None: attributes.type = test_run.type if test_run.id and hasattr(attributes, "id"): attributes.id = test_run.id - if test_run.status: + if test_run.status is not None: attributes.status = test_run.status - if test_run.title: + if test_run.title is not None: attributes.title = test_run.title + if test_run.finished_on is not None: + attributes.finished_on = test_run.finished_on + if test_run.group_id is not None: + attributes.group_id = test_run.group_id + if test_run.id_prefix is not None: + attributes.id_prefix = test_run.id_prefix + if test_run.is_template is not None and hasattr( + attributes, "is_template" + ): + attributes.is_template = test_run.is_template + if test_run.keep_in_history is not None: + attributes.keep_in_history = test_run.keep_in_history + if test_run.query is not None: + attributes.query = test_run.query + if test_run.use_report_from_template is not None: + attributes.use_report_from_template = ( + test_run.use_report_from_template + ) if test_run.additional_attributes: attributes.additional_properties = test_run.additional_attributes if test_run.select_test_cases_by: attributes.select_test_cases_by = getattr( api_models, f"{type_prefix}SelectTestCasesBy" - )(str(test_run.select_test_cases_by)) + )(test_run.select_test_cases_by.value) if test_run.home_page_content: attributes.home_page_content = getattr( api_models, f"{type_prefix}HomePageContent" @@ -1264,10 +1317,12 @@ def _fill_test_record_attributes( )(test_record.comment.type) if test_record.comment.value: attributes.comment.value = test_record.comment.value - if test_record.duration: + if test_record.duration != -1: attributes.duration = test_record.duration if test_record.work_item_revision: attributes.test_case_revision = test_record.work_item_revision + if test_record.executed: + attributes.executed = test_record.executed if test_record.additional_attributes: attributes.additional_properties = ( test_record.additional_attributes @@ -1296,8 +1351,8 @@ def create_test_records( api_models.TestrecordsListPostRequestDataItemRelationships( test_case=api_models.TestrecordsListPostRequestDataItemRelationshipsTestCase( api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseData( - api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseDataType.WORKITEMS, - f"{test_record.work_item_project_id}/{test_record.work_item_id}", + type=api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseDataType.WORKITEMS, + id=f"{test_record.work_item_project_id}/{test_record.work_item_id}", ) ) ), @@ -1311,6 +1366,18 @@ def create_test_records( sleep_random_time() self.create_test_records(test_run_id, test_records, False) + assert ( + isinstance(response.parsed, api_models.TestrecordsListPostResponse) + and response.parsed.data + ) + counter = 0 + for response_item in response.parsed.data: + if response_item.id: + test_records[counter].iteration = int( + response_item.id.split("/")[-1] + ) + counter += 1 + def update_test_record( self, test_run_id: str, test_record: dm.TestRecord, retry: bool = True ): diff --git a/polarion_rest_api_client/data_models.py b/polarion_rest_api_client/data_models.py index 051bfe57..978919b9 100644 --- a/polarion_rest_api_client/data_models.py +++ b/polarion_rest_api_client/data_models.py @@ -5,6 +5,7 @@ import base64 import dataclasses +import datetime import enum import hashlib import json @@ -235,6 +236,13 @@ class TestRun(BaseItem): title: str | None = None home_page_content: TextContent | None = None + finished_on: datetime.datetime | None = None + group_id: str | None = None + id_prefix: str | None = None + is_template: bool | None = None + keep_in_history: bool | None = None + query: str | None = None + use_report_from_template: bool | None = None select_test_cases_by: SelectTestCasesBy | None = None additional_attributes: dict[str, t.Any] = dataclasses.field( default_factory=dict @@ -248,10 +256,11 @@ class TestRecord: work_item_project_id: str work_item_id: str work_item_revision: str | None = None - iteration: int = 0 - duration: float = 0 + iteration: int = -1 + duration: float = -1 result: str | None = None comment: TextContent | None = None + executed: datetime.datetime | None = None additional_attributes: dict[str, t.Any] = dataclasses.field( default_factory=dict ) From 38d5760d71e5f3ad4399e6327cd5ae228473e99f Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Tue, 14 May 2024 19:39:19 +0200 Subject: [PATCH 06/13] test: Implement tests for new API functions --- tests/__init__.py | 13 ++ .../expected_requests/patch_test_record.json | 14 ++ .../patch_test_record.json.license | 2 + .../patch_test_run_fully.json | 22 +++ .../patch_test_run_fully.json.license | 2 + .../patch_test_run_partially.json | 14 ++ .../patch_test_run_partially.json.license | 2 + .../expected_requests/post_test_records.json | 46 +++++ .../post_test_records.json.license | 2 + .../data/expected_requests/post_test_run.json | 46 +++++ .../post_test_run.json.license | 2 + .../created_test_records.json | 18 ++ .../created_test_records.json.license | 2 + .../mock_api_responses/created_test_runs.json | 20 ++ .../created_test_runs.json.license | 2 + .../test_records_next_page.json | 60 ++++++ .../test_records_next_page.json.license | 2 + .../test_records_no_next_page.json | 59 ++++++ .../test_records_no_next_page.json.license | 2 + .../test_runs_next_page.json | 100 ++++++++++ .../test_runs_next_page.json.license | 2 + .../test_runs_no_next_page.json | 96 ++++++++++ .../test_runs_no_next_page.json.license | 2 + tests/test_client_testrecords.py | 135 +++++++++++++ tests/test_client_testruns.py | 180 ++++++++++++++++++ 25 files changed, 845 insertions(+) create mode 100644 tests/data/expected_requests/patch_test_record.json create mode 100644 tests/data/expected_requests/patch_test_record.json.license create mode 100644 tests/data/expected_requests/patch_test_run_fully.json create mode 100644 tests/data/expected_requests/patch_test_run_fully.json.license create mode 100644 tests/data/expected_requests/patch_test_run_partially.json create mode 100644 tests/data/expected_requests/patch_test_run_partially.json.license create mode 100644 tests/data/expected_requests/post_test_records.json create mode 100644 tests/data/expected_requests/post_test_records.json.license create mode 100644 tests/data/expected_requests/post_test_run.json create mode 100644 tests/data/expected_requests/post_test_run.json.license create mode 100644 tests/data/mock_api_responses/created_test_records.json create mode 100644 tests/data/mock_api_responses/created_test_records.json.license create mode 100644 tests/data/mock_api_responses/created_test_runs.json create mode 100644 tests/data/mock_api_responses/created_test_runs.json.license create mode 100644 tests/data/mock_api_responses/test_records_next_page.json create mode 100644 tests/data/mock_api_responses/test_records_next_page.json.license create mode 100644 tests/data/mock_api_responses/test_records_no_next_page.json create mode 100644 tests/data/mock_api_responses/test_records_no_next_page.json.license create mode 100644 tests/data/mock_api_responses/test_runs_next_page.json create mode 100644 tests/data/mock_api_responses/test_runs_next_page.json.license create mode 100644 tests/data/mock_api_responses/test_runs_no_next_page.json create mode 100644 tests/data/mock_api_responses/test_runs_no_next_page.json.license create mode 100644 tests/test_client_testrecords.py create mode 100644 tests/test_client_testruns.py diff --git a/tests/__init__.py b/tests/__init__.py index d42461ad..5336d037 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -68,6 +68,19 @@ TEST_FAULTS_ERROR_RESPONSES = TEST_RESPONSES / "faulty_errors.json" TEST_PROJECT_RESPONSE_JSON = TEST_RESPONSES / "project.json" +TEST_TRUN_PATCH_REQUEST = TEST_REQUESTS / "patch_test_run_partially.json" +TEST_TRUN_FULLY_PATCH_REQUEST = TEST_REQUESTS / "patch_test_run_fully.json" +TEST_TRUN_POST_REQUEST = TEST_REQUESTS / "post_test_run.json" +TEST_TREC_PATCH_REQUEST = TEST_REQUESTS / "patch_test_record.json" +TEST_TREC_POST_REQUEST = TEST_REQUESTS / "post_test_records.json" + +TEST_TREC_CREATED_RESPONSE = TEST_RESPONSES / "created_test_records.json" +TEST_TRUN_CREATED_RESPONSE = TEST_RESPONSES / "created_test_runs.json" +TEST_TREC_NEXT_RESPONSE = TEST_RESPONSES / "test_records_next_page.json" +TEST_TREC_NO_NEXT_RESPONSE = TEST_RESPONSES / "test_records_no_next_page.json" +TEST_TRUN_NEXT_RESPONSE = TEST_RESPONSES / "test_runs_next_page.json" +TEST_TRUN_NO_NEXT_RESPONSE = TEST_RESPONSES / "test_runs_no_next_page.json" + class CustomWorkItem(polarion_api.WorkItem): capella_uuid: str | None diff --git a/tests/data/expected_requests/patch_test_record.json b/tests/data/expected_requests/patch_test_record.json new file mode 100644 index 00000000..331d4eeb --- /dev/null +++ b/tests/data/expected_requests/patch_test_record.json @@ -0,0 +1,14 @@ +{ + "data": { + "type": "testrecords", + "id": "PROJ/asdfg/MyProjectId/MyWorkItemId/4", + "attributes": { + "comment": { + "type": "text/html", + "value": "My text value" + }, + "duration": 1337.5, + "result": "passed" + } + } +} diff --git a/tests/data/expected_requests/patch_test_record.json.license b/tests/data/expected_requests/patch_test_record.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/expected_requests/patch_test_record.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/expected_requests/patch_test_run_fully.json b/tests/data/expected_requests/patch_test_run_fully.json new file mode 100644 index 00000000..62d76d4d --- /dev/null +++ b/tests/data/expected_requests/patch_test_run_fully.json @@ -0,0 +1,22 @@ +{ + "data": { + "type": "testruns", + "id": "PROJ/ID", + "attributes": { + "finishedOn": "1970-01-01T00:00:00", + "groupId": "Group ID", + "homePageContent": { + "type": "text/html", + "value": "My text value" + }, + "idPrefix": "ID Prefix", + "keepInHistory": true, + "query": "Query", + "selectTestCasesBy": "manualSelection", + "status": "open", + "title": "Title", + "type": "manual", + "useReportFromTemplate": true + } + } +} diff --git a/tests/data/expected_requests/patch_test_run_fully.json.license b/tests/data/expected_requests/patch_test_run_fully.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/expected_requests/patch_test_run_fully.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/expected_requests/patch_test_run_partially.json b/tests/data/expected_requests/patch_test_run_partially.json new file mode 100644 index 00000000..df8510ad --- /dev/null +++ b/tests/data/expected_requests/patch_test_run_partially.json @@ -0,0 +1,14 @@ +{ + "data": { + "type": "testruns", + "id": "PROJ/asdfg", + "attributes": { + "finishedOn": "1970-01-01T00:00:00", + "query": "Query", + "status": "passed", + "title": "Title", + "type": "manual", + "useReportFromTemplate": false + } + } +} diff --git a/tests/data/expected_requests/patch_test_run_partially.json.license b/tests/data/expected_requests/patch_test_run_partially.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/expected_requests/patch_test_run_partially.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/expected_requests/post_test_records.json b/tests/data/expected_requests/post_test_records.json new file mode 100644 index 00000000..25ec7a5e --- /dev/null +++ b/tests/data/expected_requests/post_test_records.json @@ -0,0 +1,46 @@ +{ + "data": [ + { + "type": "testrecords", + "attributes": { + "comment": { + "type": "text/html", + "value": "My text value" + }, + "duration": 0, + "executed": "1970-01-01T00:00:00", + "result": "passed", + "testCaseRevision": "0" + }, + "relationships": { + "testCase": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId" + } + } + } + }, + { + "type": "testrecords", + "attributes": { + "comment": { + "type": "text/html", + "value": "My text value 2" + }, + "duration": 1, + "executed": "1970-01-01T00:00:00", + "result": "failed", + "testCaseRevision": "1234" + }, + "relationships": { + "testCase": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId" + } + } + } + } + ] +} diff --git a/tests/data/expected_requests/post_test_records.json.license b/tests/data/expected_requests/post_test_records.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/expected_requests/post_test_records.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/expected_requests/post_test_run.json b/tests/data/expected_requests/post_test_run.json new file mode 100644 index 00000000..c0e236f8 --- /dev/null +++ b/tests/data/expected_requests/post_test_run.json @@ -0,0 +1,46 @@ +{ + "data": [ + { + "type": "testruns", + "attributes": { + "finishedOn": "1970-01-01T00:00:00", + "groupId": "Group ID", + "homePageContent": { + "type": "text/html", + "value": "My text value" + }, + "id": "ID", + "idPrefix": "ID Prefix", + "isTemplate": true, + "keepInHistory": true, + "query": "Query", + "selectTestCasesBy": "manualSelection", + "status": "open", + "title": "Title", + "type": "manual", + "useReportFromTemplate": true + } + }, + { + "type": "testruns", + "attributes": { + "finishedOn": "1970-01-01T00:00:00", + "groupId": "Group ID", + "homePageContent": { + "type": "text/html", + "value": "My text value 2" + }, + "id": "ID2", + "idPrefix": "ID Prefix", + "isTemplate": true, + "keepInHistory": true, + "query": "Query", + "selectTestCasesBy": "manualSelection", + "status": "open", + "title": "Title", + "type": "manual", + "useReportFromTemplate": true + } + } + ] +} diff --git a/tests/data/expected_requests/post_test_run.json.license b/tests/data/expected_requests/post_test_run.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/expected_requests/post_test_run.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/mock_api_responses/created_test_records.json b/tests/data/mock_api_responses/created_test_records.json new file mode 100644 index 00000000..d1d20d48 --- /dev/null +++ b/tests/data/mock_api_responses/created_test_records.json @@ -0,0 +1,18 @@ +{ + "data": [ + { + "type": "testrecords", + "id": "elibrary/MyTestRunId/MyProjectId/MyTestcaseId/0", + "links": { + "self": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId/0" + } + }, + { + "type": "testrecords", + "id": "elibrary/MyTestRunId/MyProjectId/MyTestcaseId/1", + "links": { + "self": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId/0" + } + } + ] +} diff --git a/tests/data/mock_api_responses/created_test_records.json.license b/tests/data/mock_api_responses/created_test_records.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/mock_api_responses/created_test_records.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/mock_api_responses/created_test_runs.json b/tests/data/mock_api_responses/created_test_runs.json new file mode 100644 index 00000000..c89f35c7 --- /dev/null +++ b/tests/data/mock_api_responses/created_test_runs.json @@ -0,0 +1,20 @@ +{ + "data": [ + { + "type": "testruns", + "id": "MyProjectId/MyTestRunId", + "links": { + "self": "server-host-name/application-path/projects/MyProjectId/testruns/MyTestRunId?revision=1234", + "portal": "server-host-name/application-path/polarion/redirect/project/MyProjectId/testrun?id=MyTestRunId&revision=1234" + } + }, + { + "type": "testruns", + "id": "MyProjectId/MyTestRunId2", + "links": { + "self": "server-host-name/application-path/projects/MyProjectId/testruns/MyTestRunId?revision=1234", + "portal": "server-host-name/application-path/polarion/redirect/project/MyProjectId/testrun?id=MyTestRunId&revision=1234" + } + } + ] +} diff --git a/tests/data/mock_api_responses/created_test_runs.json.license b/tests/data/mock_api_responses/created_test_runs.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/mock_api_responses/created_test_runs.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/mock_api_responses/test_records_next_page.json b/tests/data/mock_api_responses/test_records_next_page.json new file mode 100644 index 00000000..5d689142 --- /dev/null +++ b/tests/data/mock_api_responses/test_records_next_page.json @@ -0,0 +1,60 @@ +{ + "meta": { + "totalCount": 0 + }, + "data": [ + { + "type": "testrecords", + "id": "elibrary/MyTestRunId/MyProjectId/MyTestcaseId2/0", + "revision": "1234", + "attributes": { + "comment": { + "type": "text/html", + "value": "My text value" + }, + "duration": 0, + "executed": "1970-01-01T00:00:00Z", + "iteration": 0, + "result": "passed", + "testCaseRevision": "1234" + }, + "relationships": { + "defect": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId", + "revision": "1234" + } + }, + "executedBy": { + "data": { + "type": "users", + "id": "MyUserId", + "revision": "1234" + } + }, + "testCase": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyTestcaseId2", + "revision": "1234" + } + } + }, + "meta": {}, + "links": { + "self": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId/0" + } + } + ], + "included": [ + {} + ], + "links": { + "self": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=5", + "first": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=1", + "prev": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=4", + "next": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=6", + "last": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=9" + } +} diff --git a/tests/data/mock_api_responses/test_records_next_page.json.license b/tests/data/mock_api_responses/test_records_next_page.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/mock_api_responses/test_records_next_page.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/mock_api_responses/test_records_no_next_page.json b/tests/data/mock_api_responses/test_records_no_next_page.json new file mode 100644 index 00000000..8fc7d84f --- /dev/null +++ b/tests/data/mock_api_responses/test_records_no_next_page.json @@ -0,0 +1,59 @@ +{ + "meta": { + "totalCount": 0 + }, + "data": [ + { + "type": "testrecords", + "id": "elibrary/MyTestRunId/MyProjectId/MyTestcaseId/0", + "revision": "1234", + "attributes": { + "comment": { + "type": "text/html", + "value": "My text value" + }, + "duration": 0, + "executed": "1970-01-01T00:00:00Z", + "iteration": 0, + "result": "passed", + "testCaseRevision": "Test Case Revision" + }, + "relationships": { + "defect": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId", + "revision": "1234" + } + }, + "executedBy": { + "data": { + "type": "users", + "id": "MyUserId", + "revision": "1234" + } + }, + "testCase": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId", + "revision": "1234" + } + } + }, + "meta": {}, + "links": { + "self": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId/0" + } + } + ], + "included": [ + {} + ], + "links": { + "self": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=5", + "first": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=1", + "prev": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=4", + "last": "server-host-name/application-path/projects/elibrary/testruns/MyTestRunId/testrecords/MyProjectId/MyTestcaseId?page%5Bsize%5D=10&page%5Bnumber%5D=9" + } +} diff --git a/tests/data/mock_api_responses/test_records_no_next_page.json.license b/tests/data/mock_api_responses/test_records_no_next_page.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/mock_api_responses/test_records_no_next_page.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/mock_api_responses/test_runs_next_page.json b/tests/data/mock_api_responses/test_runs_next_page.json new file mode 100644 index 00000000..5c2078dc --- /dev/null +++ b/tests/data/mock_api_responses/test_runs_next_page.json @@ -0,0 +1,100 @@ +{ + "meta": { + "totalCount": 0 + }, + "data": [ + { + "type": "testruns", + "id": "MyProjectId/MyTestRunId2", + "revision": "1234", + "attributes": { + "created": "1970-01-01T00:00:00Z", + "finishedOn": "1970-01-01T00:00:00Z", + "groupId": "Group ID", + "homePageContent": { + "type": "text/html", + "value": "My text value" + }, + "id": "ID", + "idPrefix": "ID Prefix", + "isTemplate": true, + "keepInHistory": true, + "query": "Query", + "selectTestCasesBy": "manualSelection", + "status": "open", + "title": "Title", + "type": "manual", + "updated": "1970-01-01T00:00:00Z", + "useReportFromTemplate": true + }, + "relationships": { + "author": { + "data": { + "type": "users", + "id": "MyUserId", + "revision": "1234" + } + }, + "document": { + "data": { + "type": "documents", + "id": "MyProjectId/MySpaceId/MyDocumentId", + "revision": "1234" + } + }, + "project": { + "data": { + "type": "projects", + "id": "MyProjectId", + "revision": "1234" + } + }, + "projectSpan": { + "data": [ + { + "type": "projects", + "id": "MyProjectId", + "revision": "1234" + } + ], + "meta": { + "totalCount": 0 + } + }, + "summaryDefect": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId", + "revision": "1234" + } + }, + "template": { + "data": { + "type": "testruns", + "id": "MyProjectId/MyTestRunId", + "revision": "1234" + } + } + }, + "meta": { + "errors": [ + ] + }, + "links": { + "self": "server-host-name/application-path/projects/MyProjectId/testruns/MyTestRunId?revision=1234", + "portal": "server-host-name/application-path/polarion/redirect/project/MyProjectId/testrun?id=MyTestRunId&revision=1234" + } + } + ], + "included": [ + {} + ], + "links": { + "self": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=5", + "first": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=1", + "prev": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=4", + "next": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=6", + "last": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=9", + "portal": "server-host-name/application-path/polarion/redirect/project/MyProjectId/testruns" + } +} diff --git a/tests/data/mock_api_responses/test_runs_next_page.json.license b/tests/data/mock_api_responses/test_runs_next_page.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/mock_api_responses/test_runs_next_page.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/data/mock_api_responses/test_runs_no_next_page.json b/tests/data/mock_api_responses/test_runs_no_next_page.json new file mode 100644 index 00000000..b7439ebd --- /dev/null +++ b/tests/data/mock_api_responses/test_runs_no_next_page.json @@ -0,0 +1,96 @@ +{ + "meta": { + "totalCount": 0 + }, + "data": [ + { + "type": "testruns", + "id": "MyProjectId/MyTestRunId", + "revision": "1234", + "attributes": { + "created": "1970-01-01T00:00:00Z", + "finishedOn": "1970-01-01T00:00:00Z", + "groupId": "Group ID", + "homePageContent": { + "type": "text/html", + "value": "My text value" + }, + "id": "ID", + "idPrefix": "ID Prefix", + "isTemplate": true, + "keepInHistory": true, + "query": "Query", + "selectTestCasesBy": "manualSelection", + "status": "open", + "title": "Title", + "type": "manual", + "updated": "1970-01-01T00:00:00Z", + "useReportFromTemplate": true + }, + "relationships": { + "author": { + "data": { + "type": "users", + "id": "MyUserId", + "revision": "1234" + } + }, + "document": { + "data": { + "type": "documents", + "id": "MyProjectId/MySpaceId/MyDocumentId", + "revision": "1234" + } + }, + "project": { + "data": { + "type": "projects", + "id": "MyProjectId", + "revision": "1234" + } + }, + "projectSpan": { + "data": [ + { + "type": "projects", + "id": "MyProjectId", + "revision": "1234" + } + ], + "meta": { + "totalCount": 0 + } + }, + "summaryDefect": { + "data": { + "type": "workitems", + "id": "MyProjectId/MyWorkItemId", + "revision": "1234" + } + }, + "template": { + "data": { + "type": "testruns", + "id": "MyProjectId/MyTestRunId", + "revision": "1234" + } + } + }, + "meta": {}, + "links": { + "self": "server-host-name/application-path/projects/MyProjectId/testruns/MyTestRunId?revision=1234", + "portal": "server-host-name/application-path/polarion/redirect/project/MyProjectId/testrun?id=MyTestRunId&revision=1234" + } + } + ], + "included": [ + {} + ], + "links": { + "self": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=5", + "first": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=1", + "prev": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=4", + "last": "server-host-name/application-path/projects/MyProjectId/testruns?page%5Bsize%5D=10&page%5Bnumber%5D=9", + "portal": "server-host-name/application-path/polarion/redirect/project/MyProjectId/testruns" + } +} diff --git a/tests/data/mock_api_responses/test_runs_no_next_page.json.license b/tests/data/mock_api_responses/test_runs_no_next_page.json.license new file mode 100644 index 00000000..02c8c230 --- /dev/null +++ b/tests/data/mock_api_responses/test_runs_no_next_page.json.license @@ -0,0 +1,2 @@ +Copyright DB InfraGO AG and contributors +SPDX-License-Identifier: Apache-2.0 diff --git a/tests/test_client_testrecords.py b/tests/test_client_testrecords.py new file mode 100644 index 00000000..4cd4d084 --- /dev/null +++ b/tests/test_client_testrecords.py @@ -0,0 +1,135 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +import datetime +import json + +import pytest_httpx + +import polarion_rest_api_client as polarion_api +from tests import ( + TEST_TREC_CREATED_RESPONSE, + TEST_TREC_NEXT_RESPONSE, + TEST_TREC_NO_NEXT_RESPONSE, + TEST_TREC_PATCH_REQUEST, + TEST_TREC_POST_REQUEST, +) + + +def test_get_test_records_multi_page( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + with open(TEST_TREC_NEXT_RESPONSE, encoding="utf8") as f: + content = json.load(f) + httpx_mock.add_response(json=content) + httpx_mock.add_response(json=content) + + with open(TEST_TREC_NO_NEXT_RESPONSE, encoding="utf8") as f: + httpx_mock.add_response(json=json.load(f)) + + test_records = client.get_all_test_records("123", {"test_records": "@all"}) + + query = { + "page[size]": "100", + "page[number]": "1", + "fields[test_records]": "@all", + } + reqs = httpx_mock.get_requests() + + assert len(reqs) == 3 + assert reqs[0].method == "GET" + assert dict(reqs[0].url.params) == query + query["page[number]"] = "2" + assert dict(reqs[1].url.params) == query + query["page[number]"] = "3" + assert dict(reqs[2].url.params) == query + + assert len(test_records) == 3 + assert test_records[0].result == "passed" + assert test_records[0].iteration == 0 + assert test_records[0].duration == 0 + assert test_records[0].comment + assert test_records[0].comment.value == "My text value" + assert test_records[0].comment.type == "text/html" + assert test_records[0].work_item_id == "MyTestcaseId2" + assert test_records[0].work_item_revision == "1234" + + +def test_create_test_records( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + with open(TEST_TREC_CREATED_RESPONSE) as f: + httpx_mock.add_response(201, json=json.load(f)) + + test_run_id = "asdfg" + + tr_1 = polarion_api.TestRecord( + "MyProjectId", + "MyWorkItemId", + "0", + executed=datetime.datetime.utcfromtimestamp(0), + duration=0, + result="passed", + comment=polarion_api.TextContent("text/html", "My text value"), + ) + tr_2 = polarion_api.TestRecord( + "MyProjectId", + "MyWorkItemId", + "1234", + executed=datetime.datetime.utcfromtimestamp(0), + duration=1, + result="failed", + comment=polarion_api.TextContent("text/html", "My text value 2"), + ) + + client.create_test_records(test_run_id, [tr_1, tr_2]) + + reqs = httpx_mock.get_requests() + assert len(reqs) == 1 + req_data = json.loads(reqs[0].content.decode("utf-8")) + with open(TEST_TREC_POST_REQUEST) as f: + expected_req = json.load(f) + + assert req_data == expected_req + assert ( + reqs[0].url.path + == f"/api/projects/{client.project_id}/testruns/{test_run_id}/testrecords" + ) + assert tr_1.iteration == 0 + assert tr_2.iteration == 1 + + +def test_update_test_record( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + httpx_mock.add_response(204) + + test_run_id = "asdfg" + work_item_id = "MyWorkItemId" + work_item_project = "MyProjectId" + + tr_1 = polarion_api.TestRecord( + work_item_project, + work_item_id, + iteration=4, + duration=1337.5, + result="passed", + comment=polarion_api.TextContent("text/html", "My text value"), + ) + + client.update_test_record(test_run_id, tr_1) + + reqs = httpx_mock.get_requests() + assert len(reqs) == 1 + req_data = json.loads(reqs[0].content.decode("utf-8")) + with open(TEST_TREC_PATCH_REQUEST) as f: + expected_req = json.load(f) + + assert req_data == expected_req + assert ( + reqs[0].url.path + == f"/api/projects/{client.project_id}/testruns/{test_run_id}/testrecords/{work_item_project}/{work_item_id}/4" + ) diff --git a/tests/test_client_testruns.py b/tests/test_client_testruns.py new file mode 100644 index 00000000..aee0131d --- /dev/null +++ b/tests/test_client_testruns.py @@ -0,0 +1,180 @@ +# Copyright DB InfraGO AG and contributors +# SPDX-License-Identifier: Apache-2.0 + +import datetime +import json + +import pytest_httpx + +import polarion_rest_api_client as polarion_api +from tests import ( + TEST_TRUN_CREATED_RESPONSE, + TEST_TRUN_FULLY_PATCH_REQUEST, + TEST_TRUN_NEXT_RESPONSE, + TEST_TRUN_NO_NEXT_RESPONSE, + TEST_TRUN_PATCH_REQUEST, + TEST_TRUN_POST_REQUEST, +) + + +def test_get_test_runs_multi_page( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + with open(TEST_TRUN_NEXT_RESPONSE, encoding="utf8") as f: + content = json.load(f) + httpx_mock.add_response(json=content) + httpx_mock.add_response(json=content) + + with open(TEST_TRUN_NO_NEXT_RESPONSE, encoding="utf8") as f: + httpx_mock.add_response(json=json.load(f)) + + test_runs = client.get_all_test_runs("123", {"test_runs": "@all"}) + + query = { + "page[size]": "100", + "page[number]": "1", + "fields[test_runs]": "@all", + "query": "123", + } + reqs = httpx_mock.get_requests() + + assert len(reqs) == 3 + assert reqs[0].method == "GET" + assert dict(reqs[0].url.params) == query + query["page[number]"] = "2" + assert dict(reqs[1].url.params) == query + query["page[number]"] = "3" + assert dict(reqs[2].url.params) == query + + assert len(test_runs) == 3 + assert test_runs[0].id == "MyTestRunId2" + assert test_runs[0].type == "manual" + assert ( + test_runs[0].select_test_cases_by + == polarion_api.SelectTestCasesBy.MANUALSELECTION + ) + assert test_runs[0].home_page_content + assert test_runs[0].home_page_content.value == "My text value" + assert test_runs[0].home_page_content.type == "text/html" + assert test_runs[0].status == "open" + assert test_runs[0].title == "Title" + + +def test_create_test_runs( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + with open(TEST_TRUN_CREATED_RESPONSE) as f: + httpx_mock.add_response(201, json=json.load(f)) + + tr_1 = polarion_api.TestRun( + "ID", + "manual", + "open", + "Title", + polarion_api.TextContent("text/html", "My text value"), + datetime.datetime.utcfromtimestamp(0), + "Group ID", + "ID Prefix", + True, + True, + "Query", + True, + polarion_api.SelectTestCasesBy.MANUALSELECTION, + {}, + ) + tr_2 = polarion_api.TestRun( + "ID2", + "manual", + "open", + "Title", + polarion_api.TextContent("text/html", "My text value 2"), + datetime.datetime.utcfromtimestamp(0), + "Group ID", + "ID Prefix", + True, + True, + "Query", + True, + polarion_api.SelectTestCasesBy.MANUALSELECTION, + {}, + ) + + client.create_test_runs([tr_1, tr_2]) + + reqs = httpx_mock.get_requests() + assert len(reqs) == 1 + req_data = json.loads(reqs[0].content.decode("utf-8")) + with open(TEST_TRUN_POST_REQUEST) as f: + expected_req = json.load(f) + + assert req_data == expected_req + assert reqs[0].url.path == f"/api/projects/{client.project_id}/testruns" + + +def test_update_test_run( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + httpx_mock.add_response(204) + + test_run_id = "asdfg" + + tr = polarion_api.TestRun( + test_run_id, + "manual", + "passed", + "Title", + finished_on=datetime.datetime.utcfromtimestamp(0), + query="Query", + use_report_from_template=False, + ) + + client.update_test_run(tr) + + reqs = httpx_mock.get_requests() + assert len(reqs) == 1 + req_data = json.loads(reqs[0].content.decode("utf-8")) + with open(TEST_TRUN_PATCH_REQUEST) as f: + expected_req = json.load(f) + + assert req_data == expected_req + assert ( + reqs[0].url.path + == f"/api/projects/{client.project_id}/testruns/{test_run_id}" + ) + + +def test_update_test_run_fully( + client: polarion_api.OpenAPIPolarionProjectClient, + httpx_mock: pytest_httpx.HTTPXMock, +): + httpx_mock.add_response(204) + + tr = polarion_api.TestRun( + "ID", + "manual", + "open", + "Title", + polarion_api.TextContent("text/html", "My text value"), + datetime.datetime.utcfromtimestamp(0), + "Group ID", + "ID Prefix", + True, + True, + "Query", + True, + polarion_api.SelectTestCasesBy.MANUALSELECTION, + {}, + ) + + client.update_test_run(tr) + + reqs = httpx_mock.get_requests() + assert len(reqs) == 1 + req_data = json.loads(reqs[0].content.decode("utf-8")) + with open(TEST_TRUN_FULLY_PATCH_REQUEST) as f: + expected_req = json.load(f) + + assert req_data == expected_req From e2872dd04301b2e83a16a50c036d9fdaf4fd29cd Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Tue, 14 May 2024 19:50:57 +0200 Subject: [PATCH 07/13] refactor: handle pylint errors --- polarion_rest_api_client/client.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/polarion_rest_api_client/client.py b/polarion_rest_api_client/client.py index 86fa198e..140295bb 100644 --- a/polarion_rest_api_client/client.py +++ b/polarion_rest_api_client/client.py @@ -82,13 +82,11 @@ def _build_sparse_fields( @t.overload def unset_to_none(value: oa_types.Unset) -> None: """Return None if value is Unset, else the value.""" - ... @t.overload def unset_to_none(value: T) -> T: """Return None if value is Unset, else the value.""" - ... def unset_to_none(value: t.Any) -> t.Any: @@ -254,7 +252,7 @@ def _build_work_item_post_request( attrs = api_models.WorkitemsListPostRequestDataItemAttributes( type=work_item.type, - description=api_models.WorkitemsListPostRequestDataItemAttributesDescription( + description=api_models.WorkitemsListPostRequestDataItemAttributesDescription( # pylint: disable=line-too-long type=api_models.WorkitemsListPostRequestDataItemAttributesDescriptionType( # pylint: disable=line-too-long work_item.description_type ), @@ -1340,6 +1338,7 @@ def create_test_records( self.project_id, test_run_id, client=self.client, + # pylint: disable=line-too-long body=api_models.TestrecordsListPostRequest( [ api_models.TestrecordsListPostRequestDataItem( @@ -1360,6 +1359,7 @@ def create_test_records( for test_record in test_records ] ), + # pylint: enable=line-too-long ) if not self._check_response(response, not retry) and retry: @@ -1389,6 +1389,7 @@ def update_test_record( test_record.work_item_id, str(test_record.iteration), client=self.client, + # pylint: disable=line-too-long body=api_models.TestrecordsSinglePatchRequest( api_models.TestrecordsSinglePatchRequestData( api_models.TestrecordsSinglePatchRequestDataType.TESTRECORDS, @@ -1399,6 +1400,7 @@ def update_test_record( ), ) ), + # pylint: enable=line-too-long ) if not self._check_response(response, not retry) and retry: From 4bc5d1309a56cb4e34dadd498893c3a954958066 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Wed, 15 May 2024 08:48:47 +0200 Subject: [PATCH 08/13] refactor: use kw based approach when creating objects from the model schema --- polarion_rest_api_client/client.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/polarion_rest_api_client/client.py b/polarion_rest_api_client/client.py index 140295bb..1ac466c9 100644 --- a/polarion_rest_api_client/client.py +++ b/polarion_rest_api_client/client.py @@ -59,7 +59,7 @@ def _get_json_content_size(data: dict): min_wi_request_size = _get_json_content_size( - api_models.WorkitemsListPostRequest([]).to_dict() + api_models.WorkitemsListPostRequest(data=[]).to_dict() ) @@ -1194,8 +1194,8 @@ def create_test_runs( """Create the given list of test runs.""" polarion_test_runs = [ api_models.TestrunsListPostRequestDataItem( - api_models.TestrunsListPostRequestDataItemType.TESTRUNS, - self._fill_test_run_attributes( + type=api_models.TestrunsListPostRequestDataItemType.TESTRUNS, + attributes=self._fill_test_run_attributes( api_models.TestrunsListPostRequestDataItemAttributes, test_run, ), @@ -1221,10 +1221,10 @@ def update_test_run(self, test_run: dm.TestRun, retry: bool = True): test_run.id, client=self.client, body=api_models.TestrunsSinglePatchRequest( - api_models.TestrunsSinglePatchRequestData( - api_models.TestrunsSinglePatchRequestDataType.TESTRUNS, - f"{self.project_id}/{test_run.id}", - self._fill_test_run_attributes( + data=api_models.TestrunsSinglePatchRequestData( + type=api_models.TestrunsSinglePatchRequestDataType.TESTRUNS, # pylint: disable=line-too-long + id=f"{self.project_id}/{test_run.id}", + attributes=self._fill_test_run_attributes( api_models.TestrunsSinglePatchRequestDataAttributes, test_run, ), @@ -1342,14 +1342,14 @@ def create_test_records( body=api_models.TestrecordsListPostRequest( [ api_models.TestrecordsListPostRequestDataItem( - api_models.TestrecordsListPostRequestDataItemType.TESTRECORDS, - self._fill_test_record_attributes( + type=api_models.TestrecordsListPostRequestDataItemType.TESTRECORDS, + attributes=self._fill_test_record_attributes( api_models.TestrecordsListPostRequestDataItemAttributes, test_record, ), - api_models.TestrecordsListPostRequestDataItemRelationships( + relationships=api_models.TestrecordsListPostRequestDataItemRelationships( test_case=api_models.TestrecordsListPostRequestDataItemRelationshipsTestCase( - api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseData( + data=api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseData( type=api_models.TestrecordsListPostRequestDataItemRelationshipsTestCaseDataType.WORKITEMS, id=f"{test_record.work_item_project_id}/{test_record.work_item_id}", ) @@ -1391,10 +1391,10 @@ def update_test_record( client=self.client, # pylint: disable=line-too-long body=api_models.TestrecordsSinglePatchRequest( - api_models.TestrecordsSinglePatchRequestData( - api_models.TestrecordsSinglePatchRequestDataType.TESTRECORDS, - f"{self.project_id}/{test_run_id}/{test_record.work_item_project_id}/{test_record.work_item_id}/{test_record.iteration}", - self._fill_test_record_attributes( + data=api_models.TestrecordsSinglePatchRequestData( + type=api_models.TestrecordsSinglePatchRequestDataType.TESTRECORDS, + id=f"{self.project_id}/{test_run_id}/{test_record.work_item_project_id}/{test_record.work_item_id}/{test_record.iteration}", + attributes=self._fill_test_record_attributes( api_models.TestrecordsSinglePatchRequestDataAttributes, test_record, ), From 482b53012eba68e4df3ec0d45df1122601f57ab2 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Thu, 16 May 2024 11:26:47 +0200 Subject: [PATCH 09/13] ci: Add tests folder to pylint --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index f2ecd109..1e99b994 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -39,4 +39,4 @@ jobs: python -m pip install pylint - name: Run pylint run: |- - pylint -dfixme polarion_rest_api_client || exit $(($? & ~24)) + pylint -dfixme polarion_rest_api_client tests || exit $(($? & ~24)) From 346bf8642a0ee2935a3010872d78936c89843643 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 16 May 2024 11:29:02 +0200 Subject: [PATCH 10/13] refactor: refactor tests --- tests/__init__.py | 84 ------------------------ tests/conftest.py | 75 ++++++++++++++++++++- tests/test_client_documents.py | 2 +- tests/test_client_error_responses.py | 21 +++--- tests/test_client_general.py | 7 +- tests/test_client_testrecords.py | 9 ++- tests/test_client_testruns.py | 15 ++--- tests/test_client_workitemattachments.py | 7 +- tests/test_client_workitemlinks.py | 5 +- tests/test_client_workitems.py | 20 +----- tests/test_client_workitems_custom.py | 3 +- 11 files changed, 110 insertions(+), 138 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 5336d037..dd5d085d 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,86 +1,2 @@ # Copyright DB InfraGO AG and contributors # SPDX-License-Identifier: Apache-2.0 - -from __future__ import annotations - -import pathlib - -import polarion_rest_api_client as polarion_api - -TEST_DATA_ROOT = pathlib.Path(__file__).parent / "data" -TEST_RESPONSES = TEST_DATA_ROOT / "mock_api_responses" -TEST_REQUESTS = TEST_DATA_ROOT / "expected_requests" - -TEST_WIA_NEXT_PAGE_RESPONSE = ( - TEST_RESPONSES / "get_work_item_attachments_next_page.json" -) -TEST_WIA_NO_NEXT_PAGE_RESPONSE = ( - TEST_RESPONSES / "get_work_item_attachments_no_next_page.json" -) -TEST_WIA_CREATED_RESPONSE = ( - TEST_RESPONSES / "created_work_item_attachment.json" -) -TEST_WIA_MULTI_CREATED_RESPONSE = ( - TEST_RESPONSES / "created_work_item_attachments.json" -) - -TEST_WIL_MULTI_POST_REQUEST = TEST_REQUESTS / "post_work_item_links.json" -TEST_WIL_DELETE2_REQUEST = TEST_REQUESTS / "delete_work_item_link_2.json" -TEST_WIL_DELETE_REQUEST = TEST_REQUESTS / "delete_work_item_links.json" -TEST_WIL_DELETED_REQUEST = TEST_REQUESTS / "delete_work_item_link.json" -TEST_WIL_POSTED_REQUEST = TEST_REQUESTS / "post_work_item_link.json" - -TEST_WIL_CREATED_RESPONSE = TEST_RESPONSES / "created_work_item_links.json" -TEST_WIL_NEXT_PAGE_RESPONSE = ( - TEST_RESPONSES / "get_linked_work_items_next_page.json" -) -TEST_WIL_NO_NEXT_PAGE_RESPONSE = ( - TEST_RESPONSES / "get_linked_work_items_no_next_page.json" -) - -TEST_WI_DELETE_REQUEST = TEST_REQUESTS / "delete_work_item.json" -TEST_WI_PATCH_STATUS_DELETED_REQUEST = ( - TEST_REQUESTS / "patch_work_item_status_deleted.json" -) -TEST_WI_PATCH_STATUS_REQUEST = TEST_REQUESTS / "patch_work_item_status.json" -TEST_WI_PATCH_TITLE_REQUEST = TEST_REQUESTS / "patch_work_item_title.json" -TEST_WI_PATCH_DESCRIPTION_REQUEST = ( - TEST_REQUESTS / "patch_work_item_description.json" -) -TEST_WI_PATCH_COMPLETELY_REQUEST = ( - TEST_REQUESTS / "patch_work_item_completely.json" -) -TEST_WI_MULTI_POST_REQUEST = TEST_REQUESTS / "post_workitems.json" -TEST_WI_POST_REQUEST = TEST_REQUESTS / "post_workitem.json" - -TEST_WI_NO_NEXT_PAGE_RESPONSE = TEST_RESPONSES / "workitems_no_next_page.json" -TEST_WI_CREATED_RESPONSE = TEST_RESPONSES / "created_work_items.json" -TEST_WI_ERROR_NEXT_PAGE_RESPONSE = ( - TEST_RESPONSES / "workitems_next_page_error.json" -) -TEST_WI_NEXT_PAGE_RESPONSE = TEST_RESPONSES / "workitems_next_page.json" -TEST_WI_SINGLE_RESPONSE = TEST_RESPONSES / "get_work_item.json" -TEST_WI_NOT_TRUNCATED_RESPONSE = ( - TEST_RESPONSES / "get_work_item_not_truncated.json" -) -TEST_DOCUMENT_RESPONSE = TEST_RESPONSES / "get_document.json" -TEST_ERROR_RESPONSE = TEST_RESPONSES / "error.json" -TEST_FAULTS_ERROR_RESPONSES = TEST_RESPONSES / "faulty_errors.json" -TEST_PROJECT_RESPONSE_JSON = TEST_RESPONSES / "project.json" - -TEST_TRUN_PATCH_REQUEST = TEST_REQUESTS / "patch_test_run_partially.json" -TEST_TRUN_FULLY_PATCH_REQUEST = TEST_REQUESTS / "patch_test_run_fully.json" -TEST_TRUN_POST_REQUEST = TEST_REQUESTS / "post_test_run.json" -TEST_TREC_PATCH_REQUEST = TEST_REQUESTS / "patch_test_record.json" -TEST_TREC_POST_REQUEST = TEST_REQUESTS / "post_test_records.json" - -TEST_TREC_CREATED_RESPONSE = TEST_RESPONSES / "created_test_records.json" -TEST_TRUN_CREATED_RESPONSE = TEST_RESPONSES / "created_test_runs.json" -TEST_TREC_NEXT_RESPONSE = TEST_RESPONSES / "test_records_next_page.json" -TEST_TREC_NO_NEXT_RESPONSE = TEST_RESPONSES / "test_records_no_next_page.json" -TEST_TRUN_NEXT_RESPONSE = TEST_RESPONSES / "test_runs_next_page.json" -TEST_TRUN_NO_NEXT_RESPONSE = TEST_RESPONSES / "test_runs_no_next_page.json" - - -class CustomWorkItem(polarion_api.WorkItem): - capella_uuid: str | None diff --git a/tests/conftest.py b/tests/conftest.py index 0dd8f44a..425052d5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,10 +3,11 @@ from __future__ import annotations +import pathlib + import pytest import polarion_rest_api_client as polarion_api -from tests import TEST_WIA_CREATED_RESPONSE, CustomWorkItem @pytest.fixture(name="client") @@ -69,3 +70,75 @@ def fixture_dummy_work_item_attachment(): "text/plain", "test.json", ) + + +TEST_DATA_ROOT = pathlib.Path(__file__).parent / "data" +TEST_RESPONSES = TEST_DATA_ROOT / "mock_api_responses" +TEST_REQUESTS = TEST_DATA_ROOT / "expected_requests" +TEST_WIA_NEXT_PAGE_RESPONSE = ( + TEST_RESPONSES / "get_work_item_attachments_next_page.json" +) +TEST_WIA_NO_NEXT_PAGE_RESPONSE = ( + TEST_RESPONSES / "get_work_item_attachments_no_next_page.json" +) +TEST_WIA_CREATED_RESPONSE = ( + TEST_RESPONSES / "created_work_item_attachment.json" +) +TEST_WIA_MULTI_CREATED_RESPONSE = ( + TEST_RESPONSES / "created_work_item_attachments.json" +) +TEST_WIL_MULTI_POST_REQUEST = TEST_REQUESTS / "post_work_item_links.json" +TEST_WIL_DELETE2_REQUEST = TEST_REQUESTS / "delete_work_item_link_2.json" +TEST_WIL_DELETE_REQUEST = TEST_REQUESTS / "delete_work_item_links.json" +TEST_WIL_DELETED_REQUEST = TEST_REQUESTS / "delete_work_item_link.json" +TEST_WIL_POSTED_REQUEST = TEST_REQUESTS / "post_work_item_link.json" +TEST_WIL_CREATED_RESPONSE = TEST_RESPONSES / "created_work_item_links.json" +TEST_WIL_NEXT_PAGE_RESPONSE = ( + TEST_RESPONSES / "get_linked_work_items_next_page.json" +) +TEST_WIL_NO_NEXT_PAGE_RESPONSE = ( + TEST_RESPONSES / "get_linked_work_items_no_next_page.json" +) +TEST_WI_DELETE_REQUEST = TEST_REQUESTS / "delete_work_item.json" +TEST_WI_PATCH_STATUS_DELETED_REQUEST = ( + TEST_REQUESTS / "patch_work_item_status_deleted.json" +) +TEST_WI_PATCH_STATUS_REQUEST = TEST_REQUESTS / "patch_work_item_status.json" +TEST_WI_PATCH_TITLE_REQUEST = TEST_REQUESTS / "patch_work_item_title.json" +TEST_WI_PATCH_DESCRIPTION_REQUEST = ( + TEST_REQUESTS / "patch_work_item_description.json" +) +TEST_WI_PATCH_COMPLETELY_REQUEST = ( + TEST_REQUESTS / "patch_work_item_completely.json" +) +TEST_WI_MULTI_POST_REQUEST = TEST_REQUESTS / "post_workitems.json" +TEST_WI_POST_REQUEST = TEST_REQUESTS / "post_workitem.json" +TEST_WI_NO_NEXT_PAGE_RESPONSE = TEST_RESPONSES / "workitems_no_next_page.json" +TEST_WI_CREATED_RESPONSE = TEST_RESPONSES / "created_work_items.json" +TEST_WI_ERROR_NEXT_PAGE_RESPONSE = ( + TEST_RESPONSES / "workitems_next_page_error.json" +) +TEST_WI_NEXT_PAGE_RESPONSE = TEST_RESPONSES / "workitems_next_page.json" +TEST_WI_SINGLE_RESPONSE = TEST_RESPONSES / "get_work_item.json" +TEST_WI_NOT_TRUNCATED_RESPONSE = ( + TEST_RESPONSES / "get_work_item_not_truncated.json" +) +TEST_DOCUMENT_RESPONSE = TEST_RESPONSES / "get_document.json" +TEST_ERROR_RESPONSE = TEST_RESPONSES / "error.json" +TEST_FAULTS_ERROR_RESPONSES = TEST_RESPONSES / "faulty_errors.json" +TEST_PROJECT_RESPONSE_JSON = TEST_RESPONSES / "project.json" +TEST_TRUN_PATCH_REQUEST = TEST_REQUESTS / "patch_test_run_partially.json" +TEST_TRUN_FULLY_PATCH_REQUEST = TEST_REQUESTS / "patch_test_run_fully.json" +TEST_TRUN_POST_REQUEST = TEST_REQUESTS / "post_test_run.json" +TEST_TREC_PATCH_REQUEST = TEST_REQUESTS / "patch_test_record.json" +TEST_TREC_POST_REQUEST = TEST_REQUESTS / "post_test_records.json" +TEST_TREC_CREATED_RESPONSE = TEST_RESPONSES / "created_test_records.json" +TEST_TRUN_CREATED_RESPONSE = TEST_RESPONSES / "created_test_runs.json" +TEST_TREC_NEXT_RESPONSE = TEST_RESPONSES / "test_records_next_page.json" +TEST_TREC_NO_NEXT_RESPONSE = TEST_RESPONSES / "test_records_no_next_page.json" +TEST_TRUN_NEXT_RESPONSE = TEST_RESPONSES / "test_runs_next_page.json" +TEST_TRUN_NO_NEXT_RESPONSE = TEST_RESPONSES / "test_runs_no_next_page.json" + + +class CustomWorkItem(polarion_api.WorkItem): + capella_uuid: str | None diff --git a/tests/test_client_documents.py b/tests/test_client_documents.py index 890ff872..53461f48 100644 --- a/tests/test_client_documents.py +++ b/tests/test_client_documents.py @@ -8,7 +8,7 @@ import pytest_httpx import polarion_rest_api_client as polarion_api -from tests import TEST_DOCUMENT_RESPONSE +from tests.conftest import TEST_DOCUMENT_RESPONSE def test_get_document_with_all_fields( diff --git a/tests/test_client_error_responses.py b/tests/test_client_error_responses.py index b06f8bea..ae76fb6c 100644 --- a/tests/test_client_error_responses.py +++ b/tests/test_client_error_responses.py @@ -5,11 +5,11 @@ import json +import pytest import pytest_httpx import polarion_rest_api_client as polarion_api -from polarion_rest_api_client import data_models as dm -from tests import TEST_FAULTS_ERROR_RESPONSES +from tests.conftest import TEST_FAULTS_ERROR_RESPONSES def test_faulty_error_message( @@ -19,14 +19,15 @@ def test_faulty_error_message( with open(TEST_FAULTS_ERROR_RESPONSES, encoding="utf8") as f: httpx_mock.add_response(400, json=json.load(f)) - try: + with pytest.raises(polarion_api.PolarionApiException) as e_info: client.get_document( "MySpaceId", "MyDocumentName", {"fields[documents]": "@all"} ) - except polarion_api.PolarionApiException as e: - assert len(e.args) == 5 - assert e.args[0][0] == "400" - assert ( - e.args[0][1] - == "Unexpected token, BEGIN_ARRAY expected, but was : BEGIN_OBJECT (at $.data)" - ) + + e = e_info.value + assert len(e.args) == 5 + assert e.args[0][0] == "400" + assert ( + e.args[0][1] + == "Unexpected token, BEGIN_ARRAY expected, but was : BEGIN_OBJECT (at $.data)" + ) diff --git a/tests/test_client_general.py b/tests/test_client_general.py index 2c416611..5ef1a763 100644 --- a/tests/test_client_general.py +++ b/tests/test_client_general.py @@ -7,7 +7,7 @@ import pytest_httpx import polarion_rest_api_client as polarion_api -from tests import TEST_PROJECT_RESPONSE_JSON +from tests.conftest import TEST_PROJECT_RESPONSE_JSON def test_api_authentication( @@ -19,7 +19,8 @@ def test_api_authentication( match_headers={"Authorization": "Bearer PAT123"}, json=json.load(f), ) - client.project_exists() + + assert client.project_exists() def test_check_existing_project( @@ -28,6 +29,7 @@ def test_check_existing_project( ): with open(TEST_PROJECT_RESPONSE_JSON, encoding="utf8") as f: httpx_mock.add_response(json=json.load(f)) + assert client.project_exists() @@ -36,4 +38,5 @@ def test_check_non_existing_project( httpx_mock: pytest_httpx.HTTPXMock, ): httpx_mock.add_response(status_code=404, json={}) + assert not client.project_exists() diff --git a/tests/test_client_testrecords.py b/tests/test_client_testrecords.py index 4cd4d084..82713193 100644 --- a/tests/test_client_testrecords.py +++ b/tests/test_client_testrecords.py @@ -7,7 +7,7 @@ import pytest_httpx import polarion_rest_api_client as polarion_api -from tests import ( +from tests.conftest import ( TEST_TREC_CREATED_RESPONSE, TEST_TREC_NEXT_RESPONSE, TEST_TREC_NO_NEXT_RESPONSE, @@ -44,7 +44,6 @@ def test_get_test_records_multi_page( assert dict(reqs[1].url.params) == query query["page[number]"] = "3" assert dict(reqs[2].url.params) == query - assert len(test_records) == 3 assert test_records[0].result == "passed" assert test_records[0].iteration == 0 @@ -60,7 +59,7 @@ def test_create_test_records( client: polarion_api.OpenAPIPolarionProjectClient, httpx_mock: pytest_httpx.HTTPXMock, ): - with open(TEST_TREC_CREATED_RESPONSE) as f: + with open(TEST_TREC_CREATED_RESPONSE, encoding="utf8") as f: httpx_mock.add_response(201, json=json.load(f)) test_run_id = "asdfg" @@ -89,7 +88,7 @@ def test_create_test_records( reqs = httpx_mock.get_requests() assert len(reqs) == 1 req_data = json.loads(reqs[0].content.decode("utf-8")) - with open(TEST_TREC_POST_REQUEST) as f: + with open(TEST_TREC_POST_REQUEST, encoding="utf8") as f: expected_req = json.load(f) assert req_data == expected_req @@ -125,7 +124,7 @@ def test_update_test_record( reqs = httpx_mock.get_requests() assert len(reqs) == 1 req_data = json.loads(reqs[0].content.decode("utf-8")) - with open(TEST_TREC_PATCH_REQUEST) as f: + with open(TEST_TREC_PATCH_REQUEST, encoding="utf8") as f: expected_req = json.load(f) assert req_data == expected_req diff --git a/tests/test_client_testruns.py b/tests/test_client_testruns.py index aee0131d..766a5984 100644 --- a/tests/test_client_testruns.py +++ b/tests/test_client_testruns.py @@ -7,7 +7,7 @@ import pytest_httpx import polarion_rest_api_client as polarion_api -from tests import ( +from tests.conftest import ( TEST_TRUN_CREATED_RESPONSE, TEST_TRUN_FULLY_PATCH_REQUEST, TEST_TRUN_NEXT_RESPONSE, @@ -38,7 +38,6 @@ def test_get_test_runs_multi_page( "query": "123", } reqs = httpx_mock.get_requests() - assert len(reqs) == 3 assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query @@ -46,7 +45,6 @@ def test_get_test_runs_multi_page( assert dict(reqs[1].url.params) == query query["page[number]"] = "3" assert dict(reqs[2].url.params) == query - assert len(test_runs) == 3 assert test_runs[0].id == "MyTestRunId2" assert test_runs[0].type == "manual" @@ -65,7 +63,7 @@ def test_create_test_runs( client: polarion_api.OpenAPIPolarionProjectClient, httpx_mock: pytest_httpx.HTTPXMock, ): - with open(TEST_TRUN_CREATED_RESPONSE) as f: + with open(TEST_TRUN_CREATED_RESPONSE, encoding="utf8") as f: httpx_mock.add_response(201, json=json.load(f)) tr_1 = polarion_api.TestRun( @@ -106,7 +104,7 @@ def test_create_test_runs( reqs = httpx_mock.get_requests() assert len(reqs) == 1 req_data = json.loads(reqs[0].content.decode("utf-8")) - with open(TEST_TRUN_POST_REQUEST) as f: + with open(TEST_TRUN_POST_REQUEST, encoding="utf8") as f: expected_req = json.load(f) assert req_data == expected_req @@ -118,9 +116,7 @@ def test_update_test_run( httpx_mock: pytest_httpx.HTTPXMock, ): httpx_mock.add_response(204) - test_run_id = "asdfg" - tr = polarion_api.TestRun( test_run_id, "manual", @@ -136,7 +132,7 @@ def test_update_test_run( reqs = httpx_mock.get_requests() assert len(reqs) == 1 req_data = json.loads(reqs[0].content.decode("utf-8")) - with open(TEST_TRUN_PATCH_REQUEST) as f: + with open(TEST_TRUN_PATCH_REQUEST, encoding="utf8") as f: expected_req = json.load(f) assert req_data == expected_req @@ -151,7 +147,6 @@ def test_update_test_run_fully( httpx_mock: pytest_httpx.HTTPXMock, ): httpx_mock.add_response(204) - tr = polarion_api.TestRun( "ID", "manual", @@ -174,7 +169,7 @@ def test_update_test_run_fully( reqs = httpx_mock.get_requests() assert len(reqs) == 1 req_data = json.loads(reqs[0].content.decode("utf-8")) - with open(TEST_TRUN_FULLY_PATCH_REQUEST) as f: + with open(TEST_TRUN_FULLY_PATCH_REQUEST, encoding="utf8") as f: expected_req = json.load(f) assert req_data == expected_req diff --git a/tests/test_client_workitemattachments.py b/tests/test_client_workitemattachments.py index f25c00c3..5eb0bf1d 100644 --- a/tests/test_client_workitemattachments.py +++ b/tests/test_client_workitemattachments.py @@ -10,7 +10,7 @@ from httpx import _multipart import polarion_rest_api_client as polarion_api -from tests import ( +from tests.conftest import ( TEST_WIA_CREATED_RESPONSE, TEST_WIA_MULTI_CREATED_RESPONSE, TEST_WIA_NEXT_PAGE_RESPONSE, @@ -32,14 +32,13 @@ def test_get_work_item_attachments_single_page( "MyWorkItemId", fields={"fields[workitem_attachments]": "id,title"}, ) + query = { "fields[workitem_attachments]": "id,title", "page[size]": "100", "page[number]": "1", } - reqs = httpx_mock.get_requests() - assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query assert len(work_item_attachments) == 1 @@ -67,13 +66,13 @@ def test_get_work_item_attachments_multi_page( work_items_attachments = client.get_all_work_item_attachments( "MyWorkItemId" ) + query = { "fields[workitem_attachments]": "@basic", "page[size]": "100", "page[number]": "1", } reqs = httpx_mock.get_requests() - assert len(reqs) == 2 assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query diff --git a/tests/test_client_workitemlinks.py b/tests/test_client_workitemlinks.py index f5777dd2..2d52734d 100644 --- a/tests/test_client_workitemlinks.py +++ b/tests/test_client_workitemlinks.py @@ -9,7 +9,7 @@ import pytest_httpx import polarion_rest_api_client as polarion_api -from tests import ( +from tests.conftest import ( TEST_WIL_CREATED_RESPONSE, TEST_WIL_DELETE2_REQUEST, TEST_WIL_DELETE_REQUEST, @@ -36,15 +36,14 @@ def test_get_work_item_links_single_page( include="workitem", fields={"fields[linkedworkitems]": "id,role"}, ) + query = { "fields[linkedworkitems]": "id,role", "page[size]": "100", "page[number]": "1", "include": "workitem", } - reqs = httpx_mock.get_requests() - assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query assert len(work_item_links) == 1 diff --git a/tests/test_client_workitems.py b/tests/test_client_workitems.py index 3fe005f4..9b4b1430 100644 --- a/tests/test_client_workitems.py +++ b/tests/test_client_workitems.py @@ -12,7 +12,7 @@ import polarion_rest_api_client as polarion_api from polarion_rest_api_client.open_api_client import models as api_models -from tests import ( +from tests.conftest import ( TEST_ERROR_RESPONSE, TEST_WI_CREATED_RESPONSE, TEST_WI_DELETE_REQUEST, @@ -46,11 +46,9 @@ def test_get_one_work_item( "fields[linkedworkitems]": "@all", } reqs = httpx_mock.get_requests() - assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query assert len(reqs) == 1 - assert work_item is not None assert len(work_item.linked_work_items) == 1 assert len(work_item.attachments) == 1 @@ -74,7 +72,6 @@ def test_get_one_work_item_not_truncated( "fields[linkedworkitems]": "@all", } reqs = httpx_mock.get_requests() - assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query assert len(reqs) == 1 @@ -100,6 +97,7 @@ def test_get_all_work_items_multi_page( "", {"fields[workitems]": "id"}, ) + query = { "fields[workitems]": "id", "page[size]": "100", @@ -107,7 +105,6 @@ def test_get_all_work_items_multi_page( "query": "", } reqs = httpx_mock.get_requests() - assert reqs[0].method == "GET" assert dict(reqs[0].url.params) == query assert reqs[1].method == "GET" @@ -212,7 +209,6 @@ def test_create_work_item_checksum( client.create_work_item(work_item) req = httpx_mock.get_request() - with open(TEST_WI_POST_REQUEST, encoding="utf8") as f: expected = json.load(f) @@ -305,7 +301,6 @@ def test_create_work_items_content_exceed_successfully( client.create_work_items(work_items) reqs = httpx_mock.get_requests() - assert len(reqs) == 3 assert reqs[0] is not None and reqs[0].method == "POST" assert len(json.loads(reqs[0].content.decode("utf-8"))["data"]) == 3 @@ -313,8 +308,7 @@ def test_create_work_items_content_exceed_successfully( assert len(json.loads(reqs[1].content.decode("utf-8"))["data"]) == 2 assert reqs[2] is not None and reqs[2].method == "POST" assert len(json.loads(reqs[2].content.decode("utf-8"))["data"]) == 1 - - assert all([wi.id == "MyWorkItemId" for wi in work_items]) + assert all(wi.id == "MyWorkItemId" for wi in work_items) def test_create_work_items_content_exceed_error( @@ -357,7 +351,6 @@ def test_work_item_single_request_size( client.create_work_items([work_item]) req = httpx_mock.get_request() - assert len(req.content) == size @@ -390,7 +383,6 @@ def test_work_item_multi_request_size( client.create_work_items(2 * [work_item]) req = httpx_mock.get_request() - assert len(req.content) == size @@ -463,7 +455,6 @@ def test_update_work_item_completely_checksum( client.update_work_item(work_item_patch) req = httpx_mock.get_request() - with open(TEST_WI_PATCH_COMPLETELY_REQUEST, encoding="utf8") as f: request = json.load(f) @@ -487,7 +478,6 @@ def test_update_work_item_description( ) req = httpx_mock.get_request() - assert req is not None assert req.url.path.endswith("PROJ/workitems/MyWorkItemId") assert req.method == "PATCH" @@ -509,7 +499,6 @@ def test_update_work_item_title( ) req = httpx_mock.get_request() - assert req is not None assert req.url.path.endswith("PROJ/workitems/MyWorkItemId") assert req.method == "PATCH" @@ -555,7 +544,6 @@ def test_update_work_item_type( ) req = httpx_mock.get_request() - assert req is not None assert req.url.path.endswith("PROJ/workitems/MyWorkItemId") assert req.url.params["changeTypeTo"] == "newType" @@ -573,7 +561,6 @@ def test_delete_work_item_status_mode( client.delete_work_item("MyWorkItemId") req = httpx_mock.get_request() - assert req is not None and req.method == "PATCH" with open(TEST_WI_PATCH_STATUS_DELETED_REQUEST, encoding="utf8") as f: assert json.loads(req.content.decode()) == json.load(f) @@ -590,7 +577,6 @@ def test_delete_work_item_delete_mode( client.delete_work_item("MyWorkItemId") req = httpx_mock.get_request() - assert req is not None and req.method == "DELETE" with open(TEST_WI_DELETE_REQUEST, encoding="utf8") as f: assert json.loads(req.content.decode()) == json.load(f) diff --git a/tests/test_client_workitems_custom.py b/tests/test_client_workitems_custom.py index 1d98ae78..1228ebaf 100644 --- a/tests/test_client_workitems_custom.py +++ b/tests/test_client_workitems_custom.py @@ -8,7 +8,7 @@ import pytest_httpx import polarion_rest_api_client as polarion_api -from tests import ( +from tests.conftest import ( TEST_WI_CREATED_RESPONSE, TEST_WI_NO_NEXT_PAGE_RESPONSE, TEST_WI_POST_REQUEST, @@ -24,6 +24,7 @@ def test_get_all_work_items_single_page_custom_work_item( httpx_mock.add_response(json=json.load(f)) work_items = client_custom_work_item.get_all_work_items("") + assert isinstance(work_items[0], CustomWorkItem) assert work_items[0].capella_uuid == "asdfgh" From 77e250044a475675adabb08b8a96de4c381c5b6a Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 16 May 2024 11:35:44 +0200 Subject: [PATCH 11/13] fix: line too long error --- tests/test_client_error_responses.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_client_error_responses.py b/tests/test_client_error_responses.py index ae76fb6c..b93a91bb 100644 --- a/tests/test_client_error_responses.py +++ b/tests/test_client_error_responses.py @@ -28,6 +28,6 @@ def test_faulty_error_message( assert len(e.args) == 5 assert e.args[0][0] == "400" assert ( - e.args[0][1] - == "Unexpected token, BEGIN_ARRAY expected, but was : BEGIN_OBJECT (at $.data)" + e.args[0][1] == "Unexpected token, BEGIN_ARRAY expected, but was : " + "BEGIN_OBJECT (at $.data)" ) From c9825eeae4167616d217fc8908ca60c1871085d2 Mon Sep 17 00:00:00 2001 From: ewuerger Date: Thu, 16 May 2024 11:35:48 +0200 Subject: [PATCH 12/13] ci: Ignore duplicate-code lines --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 6f7b1598..7d4a386e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -128,6 +128,7 @@ ignore-paths = "polarion_rest_api_client/open_api_client/*" [tool.pylint.messages_control] disable = [ + "duplicate-code", # XXX: We should remove this again! "broad-except", "global-statement", "import-outside-toplevel", From 81f601218212df657e88d9991fdfa87aea7ad032 Mon Sep 17 00:00:00 2001 From: Michael Harbarth Date: Thu, 16 May 2024 11:42:42 +0200 Subject: [PATCH 13/13] fix: line too long# --- tests/test_client_testrecords.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_client_testrecords.py b/tests/test_client_testrecords.py index 82713193..1f0c5c4a 100644 --- a/tests/test_client_testrecords.py +++ b/tests/test_client_testrecords.py @@ -93,8 +93,8 @@ def test_create_test_records( assert req_data == expected_req assert ( - reqs[0].url.path - == f"/api/projects/{client.project_id}/testruns/{test_run_id}/testrecords" + reqs[0].url.path == f"/api/projects/{client.project_id}/testruns" + f"/{test_run_id}/testrecords" ) assert tr_1.iteration == 0 assert tr_2.iteration == 1 @@ -128,7 +128,7 @@ def test_update_test_record( expected_req = json.load(f) assert req_data == expected_req - assert ( - reqs[0].url.path - == f"/api/projects/{client.project_id}/testruns/{test_run_id}/testrecords/{work_item_project}/{work_item_id}/4" + assert reqs[0].url.path == ( + f"/api/projects/{client.project_id}/testruns/{test_run_id}" + f"/testrecords/{work_item_project}/{work_item_id}/4" )