testit-python-commons 5.1.2__py3-none-any.whl → 5.1.3.post580__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.
@@ -30,6 +30,7 @@ from api_client_adapters.models import (
30
30
  from testit_python_commons.client.client_configuration import ClientConfiguration
31
31
  from testit_python_commons.client.converter import Converter
32
32
  from testit_python_commons.client.helpers.bulk_autotest_helper import BulkAutotestHelper
33
+ from testit_python_commons.client.helpers.test_result_matching import pick_best_in_progress_id
33
34
  from typing import List
34
35
 
35
36
  from testit_python_commons.models.link import Link
@@ -62,6 +63,7 @@ class ApiClientWorker:
62
63
  self.__workflows_api = WorkflowsApi(api_client=api_client)
63
64
  self.__config = config
64
65
  self.__status_codes = self.__get_status_codes()
66
+ self.__claimed_in_progress_ids = set()
65
67
 
66
68
  @staticmethod
67
69
  @adapter_logger
@@ -535,37 +537,38 @@ class ApiClientWorker:
535
537
  logging.debug(f'Autotest was linked with workItem "{work_item_id}" by global id "{autotest_global_id}')
536
538
 
537
539
  @adapter_logger
538
- def find_in_progress_test_result_id(self, external_id: str):
540
+ def find_in_progress_test_result_id(self, external_id: str, parameters=None):
539
541
  if not external_id:
540
542
  return None
541
543
 
542
- matches = []
544
+ candidates = []
543
545
  for item in self.__get_test_results():
544
546
  if item is None:
545
547
  continue
546
- if getattr(item, "autotest_external_id", None) == external_id:
547
- matches.append(item)
548
-
549
- if not matches:
550
- return None
551
-
552
- orphan_id = None
553
- for item in matches:
548
+ if getattr(item, "autotest_external_id", None) != external_id:
549
+ continue
554
550
  result_id = getattr(item, "id", None)
555
551
  if result_id is None:
556
552
  continue
557
553
  result_id_str = str(result_id)
558
- if self.__has_valid_test_point_id_v2(result_id_str):
559
- return result_id_str
560
- if orphan_id is None:
561
- orphan_id = result_id_str
562
- return orphan_id
563
-
564
- def __has_valid_test_point_id_v2(self, test_result_id: str) -> bool:
565
- """Hack: adapters OpenAPI omits testPointId — read GET /api/v2/testResults/{id}."""
554
+ if result_id_str in self.__claimed_in_progress_ids:
555
+ continue
556
+ meta = self.__get_test_result_v2_meta(result_id_str)
557
+ candidates.append({
558
+ "id": result_id_str,
559
+ "has_test_point": self.__is_valid_test_point_id(meta.get("testPointId")),
560
+ "parameters": meta.get("parameters") or {},
561
+ })
562
+
563
+ chosen = pick_best_in_progress_id(candidates, parameters)
564
+ if chosen:
565
+ self.__claimed_in_progress_ids.add(chosen)
566
+ return chosen
567
+
568
+ def __get_test_result_v2_meta(self, test_result_id: str) -> dict:
569
+ """Hack: adapters OpenAPI omits testPointId/parameters — read GET /api/v2/testResults/{id}."""
566
570
  import json
567
571
  import ssl
568
- import urllib.error
569
572
  import urllib.request
570
573
 
571
574
  base = (self.__config.get_url() or "").rstrip("/")
@@ -583,15 +586,21 @@ class ApiClientWorker:
583
586
  if not self.__config.get_cert_validation():
584
587
  context = ssl._create_unverified_context()
585
588
  with urllib.request.urlopen(request, timeout=10, context=context) as response:
586
- payload = json.loads(response.read().decode("utf-8"))
587
- test_point_id = payload.get("testPointId")
588
- if test_point_id is None:
589
- return False
590
- value = str(test_point_id)
591
- return bool(value) and value != "00000000-0000-0000-0000-000000000000"
589
+ return json.loads(response.read().decode("utf-8")) or {}
592
590
  except Exception as exc:
593
591
  logging.debug("v2 getTestResult %s failed: %s", test_result_id, exc)
592
+ return {}
593
+
594
+ @staticmethod
595
+ def __is_valid_test_point_id(test_point_id) -> bool:
596
+ if test_point_id is None:
594
597
  return False
598
+ value = str(test_point_id)
599
+ return bool(value) and value != "00000000-0000-0000-0000-000000000000"
600
+
601
+ def __has_valid_test_point_id_v2(self, test_result_id: str) -> bool:
602
+ meta = self.__get_test_result_v2_meta(test_result_id)
603
+ return self.__is_valid_test_point_id(meta.get("testPointId"))
595
604
 
596
605
  @adapter_logger
597
606
  @retry
@@ -624,7 +633,10 @@ class ApiClientWorker:
624
633
  @adapter_logger
625
634
  @retry
626
635
  def __load_test_result(self, test_result: TestResult) -> str:
627
- existing_id = self.find_in_progress_test_result_id(test_result.get_external_id())
636
+ existing_id = self.find_in_progress_test_result_id(
637
+ test_result.get_external_id(),
638
+ test_result.get_parameters(),
639
+ )
628
640
  if existing_id:
629
641
  return self.__update_existing_test_result(existing_id, test_result)
630
642
 
@@ -0,0 +1,41 @@
1
+ """Match InProgress test results by parameters (parametrized mode 0)."""
2
+
3
+
4
+ def normalize_parameters(parameters) -> dict:
5
+ if not parameters:
6
+ return {}
7
+ if isinstance(parameters, dict):
8
+ return {str(k): "" if v is None else str(v) for k, v in parameters.items()}
9
+ return {}
10
+
11
+
12
+ def parameters_empty(parameters) -> bool:
13
+ return not normalize_parameters(parameters)
14
+
15
+
16
+ def pick_best_in_progress_id(candidates: list, incoming_parameters=None):
17
+ """
18
+ candidates: [{id: str, has_test_point: bool, parameters: dict}, ...]
19
+ Prefer: exact param match > empty TMS params (WI without params) > others skipped if incompatible.
20
+ Among equal match quality: prefer valid testPointId.
21
+ """
22
+ incoming = normalize_parameters(incoming_parameters)
23
+ ranked = []
24
+
25
+ for candidate in candidates:
26
+ if not candidate or not candidate.get("id"):
27
+ continue
28
+ cand_params = normalize_parameters(candidate.get("parameters"))
29
+ if cand_params == incoming:
30
+ match_score = 0
31
+ elif parameters_empty(cand_params):
32
+ match_score = 1
33
+ else:
34
+ continue
35
+ tp_score = 0 if candidate.get("has_test_point") else 1
36
+ ranked.append(((match_score, tp_score), candidate["id"]))
37
+
38
+ if not ranked:
39
+ return None
40
+ ranked.sort(key=lambda item: item[0])
41
+ return ranked[0][1]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: testit-python-commons
3
- Version: 5.1.2
3
+ Version: 5.1.3.post580
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,11 +211,12 @@ 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=iNzv6tXx-X3bWfP8VYU2XtgjuZVlZlijNiDSzKBplY4,27742
214
+ testit_python_commons/client/api_client.py,sha256=dDS0EmWkUDnIuEnFEW-1iy2uvHKVXDpBb5wPLEQYZ9w,28392
215
215
  testit_python_commons/client/client_configuration.py,sha256=1M-OrROU3Xj0YGsUX4venG-l6Ewr43LNvUcyFfY57ys,2776
216
216
  testit_python_commons/client/converter.py,sha256=c4jea5FDSUqt_FeRhAW829aouDJbaHUHIHL7ayro0GE,27172
217
217
  testit_python_commons/client/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
218
218
  testit_python_commons/client/helpers/bulk_autotest_helper.py,sha256=L480JYYA3zphCciJOzefCOg1upWeRgdJWd6qWEKM45k,10920
219
+ testit_python_commons/client/helpers/test_result_matching.py,sha256=XSGzzESs-vg41MrvcijD91snpczL3fiVTywOe2j2WZg,1373
219
220
  testit_python_commons/client/helpers/threads_manager.py,sha256=Ix8NJm3Yp9_7jxGTtojsAWqfuNvtYHEia9CiHFW9b3k,3509
220
221
  testit_python_commons/client/models/__init__.py,sha256=4c3yNkadtebFF1eEZw7JmfwcLtOim2zHkwbZIHvCK0A,284
221
222
  testit_python_commons/client/models/threads_for_create_and_result.py,sha256=qOEZDIhCX0r6uUndzc2SiDcykcMVuv5XTDEvBQGGslY,1458
@@ -248,7 +249,7 @@ testit_python_commons/services/sync_storage/__init__.py,sha256=Qmjs6Xk5l-i5FFItk
248
249
  testit_python_commons/services/sync_storage/sync_storage_runner.py,sha256=9FCbMxzGa84I79X7QOjYO_oSiUUu43LhJqPDs_JEEb0,20549
249
250
  testit_python_commons/utils/__init__.py,sha256=CUHziylwvFAuKyUM1tCp13ik5Z2Mw-VK8n7c92aVy-I,78
250
251
  testit_python_commons/utils/html_escape_utils.py,sha256=wx20lAVrMvhNiQ7bEu_f-SGqDBp85D-EBL2sgTmnJPY,8135
251
- testit_python_commons-5.1.2.dist-info/METADATA,sha256=zc91PBMfgTPiDkwm7AR36Zi8E4raK23LUbG7kNBmGbU,1675
252
- testit_python_commons-5.1.2.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
253
- testit_python_commons-5.1.2.dist-info/top_level.txt,sha256=Jme7mbm0CtX-96d3cUiEonamIeb7kAo3Je4bSlxBndI,72
254
- testit_python_commons-5.1.2.dist-info/RECORD,,
252
+ testit_python_commons-5.1.3.post580.dist-info/METADATA,sha256=s1Iypa6_DERIBc5ohOWcj_TULAvLOW86GexjQ8kl0Fk,1683
253
+ testit_python_commons-5.1.3.post580.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
254
+ testit_python_commons-5.1.3.post580.dist-info/top_level.txt,sha256=Jme7mbm0CtX-96d3cUiEonamIeb7kAo3Je4bSlxBndI,72
255
+ testit_python_commons-5.1.3.post580.dist-info/RECORD,,