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

testit-tms / adapters-python / 21520296532

30 Jan 2026 03:04PM UTC coverage: 37.233% (+0.05%) from 37.183%
21520296532

push

github

web-flow
release: support tms 5.6. (#226)

Co-authored-by: pavel.butuzov <pavel.butuzov@testit.software>

21 of 45 new or added lines in 7 files covered. (46.67%)

2 existing lines in 2 files now uncovered.

1327 of 3564 relevant lines covered (37.23%)

0.74 hits per line

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

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

5
from testit_api_client import ApiClient, Configuration
2✔
6
from testit_api_client.apis import AttachmentsApi, AutoTestsApi, TestRunsApi, TestResultsApi, WorkItemsApi
2✔
7
from testit_api_client.models import (
2✔
8
    ApiV2TestResultsSearchPostRequest,
9
    AutoTestApiResult,
10
    AutoTestCreateApiModel,
11
    AutoTestUpdateApiModel,
12
    AttachmentPutModel,
13
    TestResultResponse,
14
    TestResultShortResponse,
15
    TestRunV2ApiResult,
16
    LinkAutoTestToWorkItemRequest,
17
    AutoTestWorkItemIdentifierApiResult,
18
)
19

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

28

29
class ApiClientWorker:
2✔
30
    __tests_limit = 100
2✔
31

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

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

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

53
        return api_client_configuration
×
54

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

63
    @adapter_logger
2✔
64
    @retry
2✔
65
    def create_test_run(self, test_run_name: str = None) -> str:
2✔
66
        test_run_name = f'TestRun_{datetime.today().strftime("%Y-%m-%dT%H:%M:%S")}' if \
×
67
            not test_run_name else test_run_name
68
        model = Converter.test_run_to_test_run_short_model(
×
69
            self.__config.get_project_id(),
70
            test_run_name
71
        )
72

73
        response = self.__test_run_api.create_empty(create_empty_request=model)
×
74

75
        return Converter.get_id_from_create_test_run_response(response)
×
76

77
    @retry
2✔
78
    def get_test_run(self, test_run_id: str) -> TestRunV2ApiResult:
2✔
79
        """Function gets test run and returns test run."""
80
        logging.debug(f"Getting test run by id {test_run_id}")
×
81

82
        test_run = self.__test_run_api.get_test_run_by_id(test_run_id)
×
83
        if test_run is not None:
×
84
            logging.debug(f"Got testrun: {test_run}")
×
85
            return test_run
×
86

87
        logging.error(f"Test run by id {test_run_id} not found!")
×
88
        raise Exception(f"Test run by id {test_run_id} not found!")
×
89

90
    @retry
2✔
91
    def update_test_run(self, test_run: TestRunV2ApiResult) -> None:
2✔
92
        """Function updates test run."""
93
        model = Converter.build_update_empty_request(test_run)
×
94
        logging.debug(f"Updating test run with model: {model}")
×
95

96
        self.__test_run_api.update_empty(update_empty_request=model)
×
97

98
        logging.debug(f'Updated testrun (ID: {test_run.id})')
×
99

100
    @adapter_logger
2✔
101
    def set_test_run_id(self, test_run_id: str) -> None:
2✔
102
        self.__config.set_test_run_id(test_run_id)
×
103

104
    @adapter_logger
2✔
105
    def get_external_ids_for_test_run_id(self) -> List[str]:
2✔
106
        test_results: List[TestResultShortResponse] = self.__get_test_results()
×
107
        external_ids: List[str] = Converter.get_external_ids_from_autotest_response_list(
×
108
            test_results,
109
            self.__config.get_configuration_id())
110

111
        if len(external_ids) > 0:
×
112
            return external_ids
×
113

114
        raise Exception('The autotests with the status "InProgress" ' +
×
115
                        f'and the configuration id "{self.__config.get_configuration_id()}" were not found!')
116

117
    @retry
2✔
118
    def __get_test_results(self) -> List[TestResultShortResponse]:
2✔
119
        all_test_results = []
×
120
        skip = 0
×
121
        model: ApiV2TestResultsSearchPostRequest = (
×
122
            Converter.build_test_results_search_post_request_with_in_progress_outcome(
123
                self.__config.get_test_run_id(),
124
                self.__config.get_configuration_id()))
125

126
        while skip >= 0:
×
127
            logging.debug(f"Getting test results with limit {self.__tests_limit}: {model}")
×
128

129
            test_results: List[TestResultShortResponse] = self.__test_results_api.api_v2_test_results_search_post(
×
130
                skip=skip,
131
                take=self.__tests_limit,
132
                api_v2_test_results_search_post_request=model)
133

134
            logging.debug(f"Got {len(test_results)} test results: {test_results}")
×
135

136
            all_test_results.extend(test_results)
×
137
            skip += self.__tests_limit
×
138

139
            if len(test_results) == 0:
×
140
                skip = -1
×
141

142
        return all_test_results
×
143

144
    @adapter_logger
2✔
145
    @retry
2✔
146
    def __get_autotests_by_external_id(self, external_id: str) -> List[AutoTestApiResult]:
2✔
147
        model = Converter.project_id_and_external_id_to_auto_tests_search_post_request(
×
148
            self.__config.get_project_id(),
149
            external_id)
150

151
        return self.__autotest_api.api_v2_auto_tests_search_post(api_v2_auto_tests_search_post_request=model)
×
152

153
    @adapter_logger
2✔
154
    def write_test(self, test_result: TestResult) -> str:
2✔
155
        model = Converter.project_id_and_external_id_to_auto_tests_search_post_request(
×
156
            self.__config.get_project_id(),
157
            test_result.get_external_id())
158

159
        autotests = self.__autotest_api.api_v2_auto_tests_search_post(api_v2_auto_tests_search_post_request=model)
×
160

161
        if autotests:
×
162
            self.__update_test(test_result, autotests[0])
×
163

164
            autotest_id = autotests[0].id
×
165

166
            self.__update_autotest_link_from_work_items(autotest_id, test_result.get_work_item_ids())
×
167
        else:
168
            self.__create_test(test_result)
×
169

170
        return self.__load_test_result(test_result)
×
171

172
    @adapter_logger
2✔
173
    def write_tests(self, test_results: List[TestResult], fixture_containers: dict) -> None:
2✔
174
        bulk_autotest_helper = BulkAutotestHelper(self.__autotest_api, self.__test_run_api, self.__config)
×
175

176
        for test_result in test_results:
×
177
            test_result = self.__add_fixtures_to_test_result(test_result, fixture_containers)
×
178

179
            test_result_model = Converter.test_result_to_testrun_result_post_model(
×
180
                test_result,
181
                self.__config.get_configuration_id())
182

183
            work_item_ids_for_link_with_auto_test = self.__get_work_item_uuids_for_link_with_auto_test(
×
184
                test_result.get_work_item_ids())
185

186
            autotests = self.__get_autotests_by_external_id(test_result.get_external_id())
×
187

188
            if autotests:
×
189
                autotest_links_to_wi_for_update = {}
×
190
                autotest_for_update = Converter.prepare_to_mass_update_autotest(
×
191
                    test_result,
192
                    autotests[0],
193
                    self.__config.get_project_id())
194

195
                autotest_id = autotests[0].id
×
196
                autotest_links_to_wi_for_update[autotest_id] = test_result.get_work_item_ids()
×
197

198
                bulk_autotest_helper.add_for_update(
×
199
                    autotest_for_update,
200
                    test_result_model,
201
                    autotest_links_to_wi_for_update)
202
            else:
203
                autotest_for_create = Converter.prepare_to_mass_create_autotest(
×
204
                    test_result,
205
                    self.__config.get_project_id(),
206
                    work_item_ids_for_link_with_auto_test)
207

208
                bulk_autotest_helper.add_for_create(autotest_for_create, test_result_model)
×
209

210
        bulk_autotest_helper.teardown()
×
211

212
    @staticmethod
2✔
213
    @adapter_logger
2✔
214
    def __add_fixtures_to_test_result(
2✔
215
            test_result: TestResult,
216
            fixtures_containers: dict) -> TestResult:
217
        setup_results = []
×
218
        teardown_results = []
×
219

220
        for uuid, fixtures_container in fixtures_containers.items():
×
221
            if test_result.get_external_id() in fixtures_container.external_ids:
×
222
                if fixtures_container.befores:
×
223
                    setup_results += fixtures_container.befores[0].steps
×
224

225
                if fixtures_container.afters:
×
226
                    teardown_results = fixtures_container.afters[0].steps + teardown_results
×
227

228
        test_result.set_setup_results(setup_results)
×
229
        test_result.set_teardown_results(teardown_results)
×
230

231
        return test_result
×
232

233
    @adapter_logger
2✔
234
    def __get_work_item_uuids_for_link_with_auto_test(
2✔
235
            self,
236
            work_item_ids: List[str],
237
            autotest_global_id: str = None) -> List[str]:
238
        linked_work_items = []
×
239

240
        if autotest_global_id:
×
241
            linked_work_items = self.__get_work_items_linked_to_autotest(autotest_global_id)
×
242

243
        work_item_uuids = self.__prepare_list_of_work_item_uuids(linked_work_items, work_item_ids)
×
244

245
        return work_item_uuids
×
246

247
    @adapter_logger
2✔
248
    def __prepare_list_of_work_item_uuids(
2✔
249
            self,
250
            linked_work_items: List[AutoTestWorkItemIdentifierApiResult],
251
            work_item_ids: List[str]) -> List[str]:
252
        work_item_uuids = []
×
253

254
        for linked_work_item in linked_work_items:
×
255
            linked_work_item_id = str(linked_work_item.global_id)
×
256
            linked_work_item_uuid = linked_work_item.id
×
257

258
            if linked_work_item_id in work_item_ids:
×
259
                work_item_ids.remove(linked_work_item_id)
×
260
                work_item_uuids.append(linked_work_item_uuid)
×
261

262
                continue
×
263

264
            if not self.__config.get_automatic_updation_links_to_test_cases():
×
265
                work_item_uuids.append(linked_work_item_uuid)
×
266

267
        for work_item_id in work_item_ids:
×
268
            work_item_uuid = self.__get_work_item_uuid_by_work_item_id(work_item_id)
×
269

270
            if work_item_uuid:
×
271
                work_item_uuids.append(work_item_uuid)
×
272

273
        return work_item_uuids
×
274

275
    @adapter_logger
2✔
276
    def __get_work_item_uuid_by_work_item_id(self, work_item_id: str) -> str or None:
2✔
277
        logging.debug('Getting workitem by id ' + work_item_id)
×
278

279
        try:
×
280
            work_item = self.__work_items_api.get_work_item_by_id(id=work_item_id)
×
281

282
            logging.debug(f'Got workitem {work_item}')
×
283

284
            return work_item.id
×
285
        except Exception as exc:
×
286
            logging.error(f'Getting workitem by id {work_item_id} status: {exc}')
×
287

288
    @adapter_logger
2✔
289
    @retry
2✔
290
    def __get_work_items_linked_to_autotest(self, autotest_global_id: str) -> List[AutoTestWorkItemIdentifierApiResult]:
2✔
291
        return self.__autotest_api.get_work_items_linked_to_auto_test(id=autotest_global_id)
×
292

293
    @adapter_logger
2✔
294
    def __update_autotest_link_from_work_items(self, autotest_global_id: str, work_item_ids: list):
2✔
295
        linked_work_items = self.__get_work_items_linked_to_autotest(autotest_global_id)
×
296

297
        for linked_work_item in linked_work_items:
×
298
            linked_work_item_id = str(linked_work_item.global_id)
×
299

300
            if linked_work_item_id in work_item_ids:
×
301
                work_item_ids.remove(linked_work_item_id)
×
302

303
                continue
×
304

305
            if self.__config.get_automatic_updation_links_to_test_cases():
×
306
                self.__unlink_test_to_work_item(autotest_global_id, linked_work_item_id)
×
307

308
        for work_item_id in work_item_ids:
×
309
            self.__link_test_to_work_item(autotest_global_id, work_item_id)
×
310

311
    @adapter_logger
2✔
312
    @retry
2✔
313
    def __create_test(self, test_result: TestResult) -> str:
2✔
314
        logging.debug(f'Autotest "{test_result.get_autotest_name()}" was not found')
×
315

316
        work_item_ids_for_link_with_auto_test = self.__get_work_item_uuids_for_link_with_auto_test(
×
317
            test_result.get_work_item_ids())
318

319
        model = Converter.prepare_to_create_autotest(
×
320
            test_result,
321
            self.__config.get_project_id(),
322
            work_item_ids_for_link_with_auto_test)
323

324
        autotest_response = self.__autotest_api.create_auto_test(create_auto_test_request=model)
×
325

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

328
        return autotest_response.id
×
329

330
    @adapter_logger
2✔
331
    @retry
2✔
332
    def __create_tests(self, autotests_for_create: List[AutoTestCreateApiModel]) -> None:
2✔
333
        logging.debug(f'Creating autotests: "{autotests_for_create}')
×
334

NEW
335
        self.__autotest_api.create_multiple(auto_test_create_api_model=autotests_for_create)
×
336

337
        logging.debug(f'Autotests were created')
×
338

339
    @adapter_logger
2✔
340
    @retry
2✔
341
    def __update_test(self, test_result: TestResult, autotest: AutoTestApiResult) -> None:
2✔
342
        logging.debug(f'Autotest "{test_result.get_autotest_name()}" was found')
×
343

344
        model = Converter.prepare_to_update_autotest(test_result, autotest, self.__config.get_project_id())
×
345

346
        try:
×
347
            self.__autotest_api.update_auto_test(update_auto_test_request=model)
×
348
        except Exception as exc:
×
349
            logging.error(f'Cannot update autotest "{test_result.get_autotest_name()}" status: {exc}')
×
350

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

353
    @adapter_logger
2✔
354
    @retry
2✔
355
    def __update_tests(self, autotests_for_update: List[AutoTestUpdateApiModel]) -> None:
2✔
356
        logging.debug(f'Updating autotests: {autotests_for_update}')
×
357

NEW
358
        self.__autotest_api.update_multiple(auto_test_update_api_model=autotests_for_update)
×
359

360
        logging.debug(f'Autotests were updated')
×
361

362
    @adapter_logger
2✔
363
    @retry
2✔
364
    def __unlink_test_to_work_item(self, autotest_global_id: str, work_item_id: str) -> None:
2✔
365
        self.__autotest_api.delete_auto_test_link_from_work_item(
×
366
            id=autotest_global_id,
367
            work_item_id=work_item_id)
368

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

371
    @adapter_logger
2✔
372
    @retry
2✔
373
    def __link_test_to_work_item(self, autotest_global_id: str, work_item_id: str) -> None:
2✔
374
        self.__autotest_api.link_auto_test_to_work_item(
×
375
            autotest_global_id,
376
            link_auto_test_to_work_item_request=LinkAutoTestToWorkItemRequest(id=work_item_id))
377

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

380
    @adapter_logger
2✔
381
    @retry
2✔
382
    def __load_test_result(self, test_result: TestResult) -> str:
2✔
383
        model = Converter.test_result_to_testrun_result_post_model(
×
384
            test_result,
385
            self.__config.get_configuration_id())
386

387
        response = self.__test_run_api.set_auto_test_results_for_test_run(
×
388
            id=self.__config.get_test_run_id(),
389
            auto_test_results_for_test_run_model=[model])
390

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

394
        return Converter.get_test_result_id_from_testrun_result_post_response(response)
×
395

396
    @adapter_logger
2✔
397
    @retry
2✔
398
    def get_test_result_by_id(self, test_result_id: str) -> TestResultResponse:
2✔
399
        return self.__test_results_api.api_v2_test_results_id_get(id=test_result_id)
×
400

401
    @adapter_logger
2✔
402
    def update_test_results(self, fixtures_containers: dict, test_result_ids: dict) -> None:
2✔
403
        test_results = Converter.fixtures_containers_to_test_results_with_all_fixture_step_results(
×
404
            fixtures_containers, test_result_ids)
405

406
        for test_result in test_results:
×
407
            model = Converter.convert_test_result_model_to_test_results_id_put_request(
×
408
                self.get_test_result_by_id(test_result.get_test_result_id()))
409

410
            model.setup_results = Converter.step_results_to_auto_test_step_result_update_request(
×
411
                    test_result.get_setup_results())
412
            model.teardown_results = Converter.step_results_to_auto_test_step_result_update_request(
×
413
                    test_result.get_teardown_results())
414

415
            try:
×
416
                self.__test_results_api.api_v2_test_results_id_put(
×
417
                    id=test_result.get_test_result_id(),
418
                    api_v2_test_results_id_put_request=model)
419
            except Exception as exc:
×
420
                logging.error(f'Cannot update test result with id "{test_result.get_test_result_id()}" status: {exc}')
×
421

422
    @adapter_logger
2✔
423
    def load_attachments(self, attach_paths: list or tuple) -> List[AttachmentPutModel]:
2✔
424
        attachments = []
×
425

426
        for path in attach_paths:
×
427
            if os.path.isfile(path):
×
428
                try:
×
429
                    attachment_response = self.__attachments_api.api_v2_attachments_post(file=open(path, "rb"))
×
430

431
                    attachments.append(AttachmentPutModel(attachment_response['id']))
×
432

433
                    logging.debug(f'Attachment "{path}" was uploaded')
×
434
                except Exception as exc:
×
435
                    logging.error(f'Upload attachment "{path}" status: {exc}')
×
436
            else:
437
                logging.error(f'File "{path}" was not found!')
×
438
        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