• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

testit-tms / adapters-python / 17395550471

02 Sep 2025 06:45AM UTC coverage: 36.554% (-0.3%) from 36.848%
17395550471

Pull #200

github

web-flow
Merge 2b90378f4 into 6559ad79f
Pull Request #200: feat: TMS-33268: support the "status code" field.

13 of 60 new or added lines in 4 files covered. (21.67%)

1 existing line in 1 file now uncovered.

1241 of 3395 relevant lines covered (36.55%)

0.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

29.53
/testit-python-commons/src/testit_python_commons/client/api_client.py
1
import logging
2✔
2
import os
2✔
3
import typing
2✔
4
from datetime import datetime
2✔
5

6
from testit_api_client import ApiClient, Configuration
2✔
7
from testit_api_client.apis import AttachmentsApi, AutoTestsApi, TestRunsApi, TestResultsApi, WorkItemsApi
2✔
8
from testit_api_client.models import (
2✔
9
    ApiV2TestResultsSearchPostRequest,
10
    AutoTestApiResult,
11
    AutoTestPostModel,
12
    AutoTestPutModel,
13
    AttachmentPutModel,
14
    ApiV2AutoTestsSearchPostRequest,
15
    TestResultResponse,
16
    TestResultShortResponse,
17
    LinkAutoTestToWorkItemRequest,
18
    WorkItemIdentifierModel
19
)
20

21
from testit_python_commons.client.client_configuration import ClientConfiguration
2✔
22
from testit_python_commons.client.converter import Converter
2✔
23
from testit_python_commons.client.helpers.bulk_autotest_helper import BulkAutotestHelper
2✔
24
from testit_python_commons.models.test_result import TestResult
2✔
25
from testit_python_commons.services.logger import adapter_logger
2✔
26
from testit_python_commons.services.retry import retry
2✔
27
from testit_python_commons.utils.html_escape_utils import HtmlEscapeUtils
2✔
28

29

30
class ApiClientWorker:
2✔
31
    __max_tests_for_write = 100
2✔
32
    __tests_limit = 100
2✔
33

34
    def __init__(self, config: ClientConfiguration):
2✔
35
        api_client_config = self.__get_api_client_configuration(
×
36
            url=config.get_url(),
37
            verify_ssl=config.get_cert_validation() != 'false',
38
            proxy=config.get_proxy())
39
        api_client = self.__get_api_client(api_client_config, config.get_private_token())
×
40

41
        self.__test_run_api = TestRunsApi(api_client=api_client)
×
42
        self.__autotest_api = AutoTestsApi(api_client=api_client)
×
43
        self.__attachments_api = AttachmentsApi(api_client=api_client)
×
44
        self.__test_results_api = TestResultsApi(api_client=api_client)
×
45
        self.__work_items_api = WorkItemsApi(api_client=api_client)
×
46
        self.__config = config
×
47

48
    @staticmethod
2✔
49
    @adapter_logger
2✔
50
    def __get_api_client_configuration(url: str, verify_ssl: bool = True, proxy: str = None) -> Configuration:
2✔
51
        api_client_configuration = Configuration(host=url)
×
52
        api_client_configuration.verify_ssl = verify_ssl
×
53
        api_client_configuration.proxy = proxy
×
54

55
        return api_client_configuration
×
56

57
    @staticmethod
2✔
58
    @adapter_logger
2✔
59
    def __get_api_client(api_client_config: Configuration, token: str) -> ApiClient:
2✔
60
        return ApiClient(
×
61
            configuration=api_client_config,
62
            header_name='Authorization',
63
            header_value='PrivateToken ' + token)
64

65
    @staticmethod
2✔
66
    def _escape_html_in_model(model):
2✔
67
        """Apply HTML escaping to all models before sending to API"""
68
        return HtmlEscapeUtils.escape_html_in_object(model)
×
69

70
    @adapter_logger
2✔
71
    def create_test_run(self):
2✔
72
        test_run_name = f'TestRun_{datetime.today().strftime("%Y-%m-%dT%H:%M:%S")}' if \
×
73
            not self.__config.get_test_run_name() else self.__config.get_test_run_name()
74
        model = Converter.test_run_to_test_run_short_model(
×
75
            self.__config.get_project_id(),
76
            test_run_name
77
        )
78
        model = self._escape_html_in_model(model)
×
79

80
        response = self.__test_run_api.create_empty(create_empty_request=model)
×
81

82
        return Converter.get_id_from_create_test_run_response(response)
×
83

84
    @adapter_logger
2✔
85
    def set_test_run_id(self, test_run_id: str):
2✔
86
        self.__config.set_test_run_id(test_run_id)
×
87

88
    @adapter_logger
2✔
89
    def get_autotests_by_test_run_id(self) -> typing.List[str]:
2✔
NEW
90
        test_results: typing.List[TestResultShortResponse] = self.__get_test_results()
×
NEW
91
        autotest_ids: typing.List[int] = Converter.test_result_short_get_models_to_autotest_ids(
×
92
            test_results,
93
            self.__config.get_configuration_id())
94

NEW
95
        if len(autotest_ids) == 0:
×
NEW
96
            raise Exception('The autotests with the status "InProgress" ' +
×
97
                            f'and the configuration id "{self.__config.get_configuration_id()}" were not found!')
98

NEW
99
        autotests_search_post_request: ApiV2AutoTestsSearchPostRequest = (
×
100
            Converter.autotest_ids_to_autotests_search_post_request(autotest_ids))
NEW
101
        autotests: typing.List[AutoTestApiResult] = self.__get_autotests(autotests_search_post_request)
×
NEW
102
        external_ids: typing.List[str] = Converter.autotest_models_to_external_ids(autotests)
×
103

NEW
104
        return external_ids
×
105

106
    def __get_test_results(self) -> typing.List[TestResultShortResponse]:
2✔
NEW
107
        all_test_results = []
×
NEW
108
        skip = 0
×
NEW
109
        model: ApiV2TestResultsSearchPostRequest = (
×
110
            Converter.testrun_id_and_configuration_id_and_in_progress_outcome_to_test_results_search_post_request(
111
                self.__config.get_test_run_id(),
112
                self.__config.get_configuration_id()))
113

NEW
114
        while skip >= 0:
×
NEW
115
            logging.debug(f"Getting test results with limit {self.__tests_limit}: {model}")
×
116

NEW
117
            test_results: typing.List[TestResultShortResponse] = self.__test_results_api.api_v2_test_results_search_post(
×
118
                skip=skip,
119
                take=self.__tests_limit,
120
                api_v2_test_results_search_post_request=model)
121

NEW
122
            logging.debug(f"Got {len(test_results)} test results in {skip} page: {test_results}")
×
123

NEW
124
            all_test_results.extend(test_results)
×
NEW
125
            skip += 1
×
126

NEW
127
            if len(test_results) == 0:
×
NEW
128
                skip = -1
×
129

NEW
130
        return all_test_results
×
131

132
    def __get_autotests(self, model: ApiV2AutoTestsSearchPostRequest) \
2✔
133
            -> typing.List[AutoTestApiResult]:
134
        """Function returns list of AutoTestApiResult."""
NEW
135
        all_autotests = []
×
NEW
136
        skip = 0
×
137

NEW
138
        while skip >= 0:
×
NEW
139
            logging.debug(f"Getting autotests with limit {self.__tests_limit}: {model}")
×
140

NEW
141
            autotests: typing.List[AutoTestApiResult] = self.__autotest_api.api_v2_auto_tests_search_post(
×
142
                skip=skip,
143
                take=self.__tests_limit,
144
                api_v2_auto_tests_search_post_request=model)
145

NEW
146
            logging.debug(f"Got autotests: {autotests}")
×
147

NEW
148
            all_autotests.extend(autotests)
×
NEW
149
            skip += 1
×
150

NEW
151
            if len(autotests) == 0:
×
NEW
152
                skip = -1
×
153

NEW
154
        return all_autotests
×
155

156
    @adapter_logger
2✔
157
    def __get_autotests_by_external_id(self, external_id: str) -> typing.List[AutoTestApiResult]:
2✔
158
        model = Converter.project_id_and_external_id_to_auto_tests_search_post_request(
×
159
            self.__config.get_project_id(),
160
            external_id)
161

162
        return self.__autotest_api.api_v2_auto_tests_search_post(api_v2_auto_tests_search_post_request=model)
×
163

164
    @adapter_logger
2✔
165
    def write_test(self, test_result: TestResult) -> str:
2✔
166
        model = Converter.project_id_and_external_id_to_auto_tests_search_post_request(
×
167
            self.__config.get_project_id(),
168
            test_result.get_external_id())
169

170
        autotests = self.__autotest_api.api_v2_auto_tests_search_post(api_v2_auto_tests_search_post_request=model)
×
171

172
        if autotests:
×
173
            self.__update_test(test_result, autotests[0])
×
174

175
            autotest_id = autotests[0].id
×
176

177
            self.__update_autotest_link_from_work_items(autotest_id, test_result.get_work_item_ids())
×
178
        else:
179
            self.__create_test(test_result)
×
180

181
        return self.__load_test_result(test_result)
×
182

183
    @adapter_logger
2✔
184
    def write_tests(self, test_results: typing.List[TestResult], fixture_containers: dict):
2✔
185
        bulk_autotest_helper = BulkAutotestHelper(self.__autotest_api, self.__test_run_api, self.__config)
×
186

187
        for test_result in test_results:
×
188
            test_result = self.__add_fixtures_to_test_result(test_result, fixture_containers)
×
189

190
            test_result_model = Converter.test_result_to_testrun_result_post_model(
×
191
                test_result,
192
                self.__config.get_configuration_id())
193

194
            work_item_ids_for_link_with_auto_test = self.__get_work_item_uuids_for_link_with_auto_test(
×
195
                test_result.get_work_item_ids())
196

197
            autotests = self.__get_autotests_by_external_id(test_result.get_external_id())
×
198

199
            if autotests:
×
200
                autotest_links_to_wi_for_update = {}
×
201
                autotest_for_update = Converter.prepare_to_mass_update_autotest(
×
202
                    test_result,
203
                    autotests[0],
204
                    self.__config.get_project_id())
205

206
                autotest_id = autotests[0].id
×
207
                autotest_links_to_wi_for_update[autotest_id] = test_result.get_work_item_ids()
×
208

209
                bulk_autotest_helper.add_for_update(
×
210
                    autotest_for_update,
211
                    test_result_model,
212
                    autotest_links_to_wi_for_update)
213
            else:
214
                autotest_for_create = Converter.prepare_to_mass_create_autotest(
×
215
                    test_result,
216
                    self.__config.get_project_id(),
217
                    work_item_ids_for_link_with_auto_test)
218

219
                bulk_autotest_helper.add_for_create(autotest_for_create, test_result_model)
×
220

221
        bulk_autotest_helper.teardown()
×
222

223
    @staticmethod
2✔
224
    @adapter_logger
2✔
225
    def __add_fixtures_to_test_result(
2✔
226
            test_result: TestResult,
227
            fixtures_containers: dict) -> TestResult:
228
        setup_results = []
×
229
        teardown_results = []
×
230

231
        for uuid, fixtures_container in fixtures_containers.items():
×
232
            if test_result.get_external_id() in fixtures_container.external_ids:
×
233
                if fixtures_container.befores:
×
234
                    setup_results += fixtures_container.befores[0].steps
×
235

236
                if fixtures_container.afters:
×
237
                    teardown_results = fixtures_container.afters[0].steps + teardown_results
×
238

239
        test_result.set_setup_results(setup_results)
×
240
        test_result.set_teardown_results(teardown_results)
×
241

242
        return test_result
×
243

244
    @adapter_logger
2✔
245
    def __get_work_item_uuids_for_link_with_auto_test(
2✔
246
            self,
247
            work_item_ids: list,
248
            autotest_global_id: str = None) -> list:
249
        linked_work_items = []
×
250

251
        if autotest_global_id:
×
252
            linked_work_items = self.__get_work_items_linked_to_autotest(autotest_global_id)
×
253

254
        work_item_uuids = self.__prepare_list_of_work_item_uuids(linked_work_items, work_item_ids)
×
255

256
        return work_item_uuids
×
257

258
    @adapter_logger
2✔
259
    def __prepare_list_of_work_item_uuids(
2✔
260
            self,
261
            linked_work_items: list,
262
            work_item_ids: list) -> list:
263
        work_item_uuids = []
×
264

265
        for linked_work_item in linked_work_items:
×
266
            linked_work_item_id = str(linked_work_item.global_id)
×
267
            linked_work_item_uuid = linked_work_item.id
×
268

269
            if linked_work_item_id in work_item_ids:
×
270
                work_item_ids.remove(linked_work_item_id)
×
271
                work_item_uuids.append(linked_work_item_uuid)
×
272

273
                continue
×
274

275
            if self.__config.get_automatic_updation_links_to_test_cases() != 'true':
×
276
                work_item_uuids.append(linked_work_item_uuid)
×
277

278
        for work_item_id in work_item_ids:
×
279
            work_item_uuid = self.__get_work_item_uuid_by_work_item_id(work_item_id)
×
280

281
            if work_item_uuid:
×
282
                work_item_uuids.append(work_item_uuid)
×
283

284
        return work_item_uuids
×
285

286
    @adapter_logger
2✔
287
    def __get_work_item_uuid_by_work_item_id(self, work_item_id: str) -> str or None:
2✔
288
        logging.debug('Getting workitem by id ' + work_item_id)
×
289

290
        try:
×
291
            work_item = self.__work_items_api.get_work_item_by_id(id=work_item_id)
×
292

293
            logging.debug(f'Got workitem {work_item}')
×
294

295
            return work_item.id
×
296
        except Exception as exc:
×
297
            logging.error(f'Getting workitem by id {work_item_id} status: {exc}')
×
298

299
    @adapter_logger
2✔
300
    def __get_work_items_linked_to_autotest(self, autotest_global_id: str) -> typing.List[WorkItemIdentifierModel]:
2✔
301
        return self.__autotest_api.get_work_items_linked_to_auto_test(id=autotest_global_id)
×
302

303
    @adapter_logger
2✔
304
    def __update_autotest_link_from_work_items(self, autotest_global_id: str, work_item_ids: list):
2✔
305
        linked_work_items = self.__get_work_items_linked_to_autotest(autotest_global_id)
×
306

307
        for linked_work_item in linked_work_items:
×
308
            linked_work_item_id = str(linked_work_item.global_id)
×
309

310
            if linked_work_item_id in work_item_ids:
×
311
                work_item_ids.remove(linked_work_item_id)
×
312

313
                continue
×
314

315
            if self.__config.get_automatic_updation_links_to_test_cases() != 'false':
×
316
                self.__unlink_test_to_work_item(autotest_global_id, linked_work_item_id)
×
317

318
        for work_item_id in work_item_ids:
×
319
            self.__link_test_to_work_item(autotest_global_id, work_item_id)
×
320

321
    @adapter_logger
2✔
322
    def __create_test(self, test_result: TestResult) -> str:
2✔
323
        logging.debug(f'Autotest "{test_result.get_autotest_name()}" was not found')
×
324

325
        work_item_ids_for_link_with_auto_test = self.__get_work_item_uuids_for_link_with_auto_test(
×
326
            test_result.get_work_item_ids())
327

328
        model = Converter.prepare_to_create_autotest(
×
329
            test_result,
330
            self.__config.get_project_id(),
331
            work_item_ids_for_link_with_auto_test)
332
        model = self._escape_html_in_model(model)
×
333

334
        autotest_response = self.__autotest_api.create_auto_test(create_auto_test_request=model)
×
335

336
        logging.debug(f'Autotest "{test_result.get_autotest_name()}" was created')
×
337

338
        return autotest_response.id
×
339

340
    @adapter_logger
2✔
341
    def __create_tests(self, autotests_for_create: typing.List[AutoTestPostModel]):
2✔
342
        logging.debug(f'Creating autotests: "{autotests_for_create}')
×
343

344
        autotests_for_create = self._escape_html_in_model(autotests_for_create)
×
345
        self.__autotest_api.create_multiple(auto_test_post_model=autotests_for_create)
×
346

347
        logging.debug(f'Autotests were created')
×
348

349
    @adapter_logger
2✔
350
    def __update_test(self, test_result: TestResult, autotest: AutoTestApiResult):
2✔
351
        logging.debug(f'Autotest "{test_result.get_autotest_name()}" was found')
×
352

353
        model = Converter.prepare_to_update_autotest(test_result, autotest, self.__config.get_project_id())
×
354
        model = self._escape_html_in_model(model)
×
355

356
        try:
×
357
            self.__autotest_api.update_auto_test(update_auto_test_request=model)
×
358
        except Exception as exc:
×
359
            logging.error(f'Cannot update autotest "{test_result.get_autotest_name()}" status: {exc}')
×
360

361
        logging.debug(f'Autotest "{test_result.get_autotest_name()}" was updated')
×
362

363
    @adapter_logger
2✔
364
    def __update_tests(self, autotests_for_update: typing.List[AutoTestPutModel]):
2✔
365
        logging.debug(f'Updating autotests: {autotests_for_update}')
×
366

367
        autotests_for_update = self._escape_html_in_model(autotests_for_update)
×
368
        self.__autotest_api.update_multiple(auto_test_put_model=autotests_for_update)
×
369

370
        logging.debug(f'Autotests were updated')
×
371

372
    @adapter_logger
2✔
373
    @retry
2✔
374
    def __unlink_test_to_work_item(self, autotest_global_id: str, work_item_id: str):
2✔
375
        self.__autotest_api.delete_auto_test_link_from_work_item(
×
376
            id=autotest_global_id,
377
            work_item_id=work_item_id)
378

379
        logging.debug(f'Autotest was unlinked with workItem "{work_item_id}" by global id "{autotest_global_id}')
×
380

381
    @adapter_logger
2✔
382
    @retry
2✔
383
    def __link_test_to_work_item(self, autotest_global_id: str, work_item_id: str):
2✔
384
        self.__autotest_api.link_auto_test_to_work_item(
×
385
            autotest_global_id,
386
            link_auto_test_to_work_item_request=LinkAutoTestToWorkItemRequest(id=work_item_id))
387

388
        logging.debug(f'Autotest was linked with workItem "{work_item_id}" by global id "{autotest_global_id}')
×
389

390
    @adapter_logger
2✔
391
    def __load_test_result(self, test_result: TestResult) -> str:
2✔
392
        model = Converter.test_result_to_testrun_result_post_model(
×
393
            test_result,
394
            self.__config.get_configuration_id())
395
        model = self._escape_html_in_model(model)
×
396

397
        response = self.__test_run_api.set_auto_test_results_for_test_run(
×
398
            id=self.__config.get_test_run_id(),
399
            auto_test_results_for_test_run_model=[model])
400

401
        logging.debug(f'Result of the autotest "{test_result.get_autotest_name()}" was set '
×
402
                      f'in the test run "{self.__config.get_test_run_id()}"')
403

404
        return Converter.get_test_result_id_from_testrun_result_post_response(response)
×
405

406
    @adapter_logger
2✔
407
    def get_test_result_by_id(self, test_result_id: str) -> TestResultResponse:
2✔
408
        return self.__test_results_api.api_v2_test_results_id_get(id=test_result_id)
×
409

410
    @adapter_logger
2✔
411
    def update_test_results(self, fixtures_containers: dict, test_result_ids: dict):
2✔
412
        test_results = Converter.fixtures_containers_to_test_results_with_all_fixture_step_results(
×
413
            fixtures_containers, test_result_ids)
414

415
        for test_result in test_results:
×
416
            model = Converter.convert_test_result_model_to_test_results_id_put_request(
×
417
                self.get_test_result_by_id(test_result.get_test_result_id()))
418

419
            model.setup_results = Converter.step_results_to_auto_test_step_result_update_request(
×
420
                    test_result.get_setup_results())
421
            model.teardown_results = Converter.step_results_to_auto_test_step_result_update_request(
×
422
                    test_result.get_teardown_results())
423
            
424
            model = self._escape_html_in_model(model)
×
425

426
            try:
×
427
                self.__test_results_api.api_v2_test_results_id_put(
×
428
                    id=test_result.get_test_result_id(),
429
                    api_v2_test_results_id_put_request=model)
430
            except Exception as exc:
×
431
                logging.error(f'Cannot update test result with id "{test_result.get_test_result_id()}" status: {exc}')
×
432

433
    @adapter_logger
2✔
434
    def load_attachments(self, attach_paths: list or tuple):
2✔
435
        attachments = []
×
436

437
        for path in attach_paths:
×
438
            if os.path.isfile(path):
×
439
                try:
×
440
                    attachment_response = self.__attachments_api.api_v2_attachments_post(file=open(path, "rb"))
×
441

442
                    attachments.append(AttachmentPutModel(attachment_response['id']))
×
443

444
                    logging.debug(f'Attachment "{path}" was uploaded')
×
445
                except Exception as exc:
×
446
                    logging.error(f'Upload attachment "{path}" status: {exc}')
×
447
            else:
448
                logging.error(f'File "{path}" was not found!')
×
449
        return attachments
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc