testit-python-commons 5.0.0__py3-none-any.whl → 5.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -481,9 +481,99 @@ class ApiClientWorker:
481
481
 
482
482
  logging.debug(f'Autotest was linked with workItem "{work_item_id}" by global id "{autotest_global_id}')
483
483
 
484
+ @adapter_logger
485
+ def find_in_progress_test_result_id(self, external_id: str):
486
+ if not external_id:
487
+ return None
488
+
489
+ matches = []
490
+ for item in self.__get_test_results():
491
+ if item is None:
492
+ continue
493
+ if getattr(item, "autotest_external_id", None) == external_id:
494
+ matches.append(item)
495
+
496
+ if not matches:
497
+ return None
498
+
499
+ orphan_id = None
500
+ for item in matches:
501
+ result_id = getattr(item, "id", None)
502
+ if result_id is None:
503
+ continue
504
+ result_id_str = str(result_id)
505
+ if self.__has_valid_test_point_id_v2(result_id_str):
506
+ return result_id_str
507
+ if orphan_id is None:
508
+ orphan_id = result_id_str
509
+ return orphan_id
510
+
511
+ def __has_valid_test_point_id_v2(self, test_result_id: str) -> bool:
512
+ """Hack: adapters OpenAPI omits testPointId — read GET /api/v2/testResults/{id}."""
513
+ import requests
514
+
515
+ base = (self.__config.get_url() or "").rstrip("/")
516
+ url = f"{base}/api/v2/testResults/{test_result_id}"
517
+ try:
518
+ response = requests.get(
519
+ url,
520
+ headers={
521
+ "Accept": "application/json",
522
+ "Authorization": f"PrivateToken {self.__config.get_private_token()}",
523
+ },
524
+ timeout=10,
525
+ verify=self.__config.get_cert_validation(),
526
+ )
527
+ if response.status_code < 200 or response.status_code >= 300:
528
+ logging.debug(
529
+ "v2 getTestResult %s HTTP %s", test_result_id, response.status_code
530
+ )
531
+ return False
532
+ payload = response.json()
533
+ test_point_id = payload.get("testPointId")
534
+ if test_point_id is None:
535
+ return False
536
+ value = str(test_point_id)
537
+ return bool(value) and value != "00000000-0000-0000-0000-000000000000"
538
+ except Exception as exc:
539
+ logging.debug("v2 getTestResult %s failed: %s", test_result_id, exc)
540
+ return False
541
+
542
+ @adapter_logger
543
+ @retry
544
+ def __update_existing_test_result(self, test_result_id: str, test_result: TestResult) -> str:
545
+ existing = self.get_test_result_by_id(test_result_id)
546
+ model = Converter.convert_test_result_model_to_test_results_id_put_request(existing)
547
+
548
+ outcome = test_result.get_outcome()
549
+ if outcome and outcome.upper() in self.__status_codes:
550
+ model.status_code = outcome
551
+
552
+ if test_result.get_duration() is not None:
553
+ model.duration_in_ms = round(test_result.get_duration())
554
+ if test_result.get_message() is not None:
555
+ model.message = test_result.get_message()
556
+ if test_result.get_traces() is not None:
557
+ model.trace = test_result.get_traces()
558
+
559
+ self.__test_results_api.adapters_test_results_id_put(
560
+ id=test_result_id,
561
+ adapters_test_results_id_put_request=model,
562
+ )
563
+ logging.debug(
564
+ 'Updated existing test result "%s" for autotest "%s"',
565
+ test_result_id,
566
+ test_result.get_autotest_name(),
567
+ )
568
+ return test_result_id
569
+
484
570
  @adapter_logger
485
571
  @retry
486
572
  def __load_test_result(self, test_result: TestResult) -> str:
573
+ existing_id = self.find_in_progress_test_result_id(test_result.get_external_id())
574
+ if existing_id:
575
+ return self.__update_existing_test_result(existing_id, test_result)
576
+
487
577
  model = Converter.test_result_to_testrun_result_post_model(
488
578
  test_result,
489
579
  self.__config.get_configuration_id(),
@@ -18,7 +18,6 @@ from testit_python_commons.services.sync_storage.sync_storage_runner import (
18
18
  )
19
19
 
20
20
  SYNC_STORAGE_AVAILABLE = True
21
- IN_PROGRESS_LITERAL = "InProgress"
22
21
 
23
22
 
24
23
  class AdapterManager:
@@ -187,9 +186,12 @@ class AdapterManager:
187
186
  self.__sync_storage_runner.set_is_already_in_progress(True)
188
187
 
189
188
  try:
190
- # Write test result normally (mark as IN PROGRESS in Test IT)
191
- logging.debug("Write internally, change status to in progress")
192
- test_result.set_outcome(IN_PROGRESS_LITERAL)
189
+ # Keep final outcome. Prefer PUT on existing TP-bound InProgress (mode=0);
190
+ # never invent a second InProgress via setAutoTestResultsForTestRun.
191
+ logging.debug(
192
+ "SyncStorage accepted %s; write final status (update existing if present)",
193
+ test_result.get_external_id(),
194
+ )
193
195
  self._write_test_realtime_internal(test_result)
194
196
  return True
195
197
  except Exception as e:
@@ -40,7 +40,7 @@ class SyncStorageRunner:
40
40
  across multiple workers.
41
41
  """
42
42
 
43
- SYNC_STORAGE_VERSION = "v0.3.7-tms-5.7"
43
+ SYNC_STORAGE_VERSION = "v0.3.8-rc-tms-5.7"
44
44
  SYNC_STORAGE_REPO_URL = (
45
45
  "https://github.com/testit-tms/sync-storage-public/releases/download/"
46
46
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testit-python-commons
3
- Version: 5.0.0
3
+ Version: 5.1.0
4
4
  Summary: Python commons for Test IT
5
5
  Home-page: https://github.com/testit-tms/adapters-python/
6
6
  Author: Integration team
@@ -211,7 +211,7 @@ testit_python_commons/decorators.py,sha256=QI8qMjDincTRZmcZByP5y5wHsf_Fwd1oc67Wu
211
211
  testit_python_commons/dynamic_methods.py,sha256=WKHHA6Uj61tWXI3DphALYOi2BV6TnQKvQy0-Vt2JR2I,8816
212
212
  testit_python_commons/step.py,sha256=CYUs_S8opmRg1tsTY1dVJOPzzt-HYR3_uLTBpYtHSfQ,3244
213
213
  testit_python_commons/client/__init__.py,sha256=zSJhxDHzWWTFHJ0s8bqCaBtM5FK2GcPgZxaKdI8zf-o,165
214
- testit_python_commons/client/api_client.py,sha256=WurOr4cp6csmQq30cuzDpmsKGSgU2-n3ppKZkcVbZdQ,22546
214
+ testit_python_commons/client/api_client.py,sha256=MQIqTE4VyBTugkpAP7wb3Kr27_2KfSHAIr9th4lcSTs,26025
215
215
  testit_python_commons/client/client_configuration.py,sha256=1M-OrROU3Xj0YGsUX4venG-l6Ewr43LNvUcyFfY57ys,2776
216
216
  testit_python_commons/client/converter.py,sha256=rVleh2OC2rKd3iR-BVO1-StP9kVqRSsNcp_HcPoNr2E,24653
217
217
  testit_python_commons/client/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -233,7 +233,7 @@ testit_python_commons/models/step_result.py,sha256=T9BbcPB6BCz3ZkrWXy2Z2Guj2wecv
233
233
  testit_python_commons/models/test_result.py,sha256=z6OB3V_whCQDJvr4jQdgVanoJSoeKh8ZXgPAACkNrdw,7515
234
234
  testit_python_commons/models/test_result_with_all_fixture_step_results_model.py,sha256=aI5nfJ_4EAhzZg7eIV1JhSf9i3EENiRiuQuqJvSUs3M,866
235
235
  testit_python_commons/services/__init__.py,sha256=yVVmiCgMyike_8OoNPiHLNoMqKhCDlWYhvmGW0IRHJM,1197
236
- testit_python_commons/services/adapter_manager.py,sha256=imAogzM7IVv1970lyz3sQXbPvxzVF6nWeKXu4NWVMKE,11435
236
+ testit_python_commons/services/adapter_manager.py,sha256=CtzPoTjGHKKCrjMWk_HEpF26Ww56OI6mwcsRXrTpx2Q,11541
237
237
  testit_python_commons/services/adapter_manager_configuration.py,sha256=KyLhB_iNUQaVEurfLQ0RANIZfhJPB19c_WUkGpqf0xo,1619
238
238
  testit_python_commons/services/fixture_manager.py,sha256=PCSLBEPa3yJVbCPsMt_Y5badWLAhKrJsZTaUesq7oNI,2006
239
239
  testit_python_commons/services/fixture_storage.py,sha256=nAIW0p9lVvVWtiVdFNqaD7-gCBislY8DLrfdlh8OqFw,1205
@@ -244,10 +244,10 @@ testit_python_commons/services/step_manager.py,sha256=pKTtEgpwOS_KJV_9kcBmKA8WRJ
244
244
  testit_python_commons/services/step_result_storage.py,sha256=4axfi2dnP3LABlEql9k6lydftOgDFV3c2cXN-1uEvXU,633
245
245
  testit_python_commons/services/utils.py,sha256=5LBymIxEC1AKtGQz-S1-ipo3OYR-JzRUAC4WWFQylHc,4060
246
246
  testit_python_commons/services/sync_storage/__init__.py,sha256=Qmjs6Xk5l-i5FFItk0-XPrlVK_HkDuX6FD8FID1iKu0,154
247
- testit_python_commons/services/sync_storage/sync_storage_runner.py,sha256=93OXrGlGKik9359j8novwtG_hud8VL_Ksznts8sRdjE,20546
247
+ testit_python_commons/services/sync_storage/sync_storage_runner.py,sha256=9FCbMxzGa84I79X7QOjYO_oSiUUu43LhJqPDs_JEEb0,20549
248
248
  testit_python_commons/utils/__init__.py,sha256=CUHziylwvFAuKyUM1tCp13ik5Z2Mw-VK8n7c92aVy-I,78
249
249
  testit_python_commons/utils/html_escape_utils.py,sha256=wx20lAVrMvhNiQ7bEu_f-SGqDBp85D-EBL2sgTmnJPY,8135
250
- testit_python_commons-5.0.0.dist-info/METADATA,sha256=kzagJEhwz9gub_JiMUnA6fwDWcv3ojRF__aTCLSfw5c,1675
251
- testit_python_commons-5.0.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
252
- testit_python_commons-5.0.0.dist-info/top_level.txt,sha256=Jme7mbm0CtX-96d3cUiEonamIeb7kAo3Je4bSlxBndI,72
253
- testit_python_commons-5.0.0.dist-info/RECORD,,
250
+ testit_python_commons-5.1.0.dist-info/METADATA,sha256=xxhtb3Ug-X3Xfu-CjyZ2EwK7_uOkpSwXXWFcN2uJcUI,1675
251
+ testit_python_commons-5.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
252
+ testit_python_commons-5.1.0.dist-info/top_level.txt,sha256=Jme7mbm0CtX-96d3cUiEonamIeb7kAo3Je4bSlxBndI,72
253
+ testit_python_commons-5.1.0.dist-info/RECORD,,