testit-python-commons 4.2.8__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.
Files changed (71) hide show
  1. api_client_syncstorage/__init__.py +27 -0
  2. api_client_syncstorage/api/__init__.py +3 -0
  3. api_client_syncstorage/api/completion_api.py +302 -0
  4. api_client_syncstorage/api/health_api.py +158 -0
  5. api_client_syncstorage/api/system_api.py +158 -0
  6. api_client_syncstorage/api/test_results_api.py +182 -0
  7. api_client_syncstorage/api/workers_api.py +308 -0
  8. api_client_syncstorage/api_client.py +898 -0
  9. api_client_syncstorage/apis/__init__.py +21 -0
  10. api_client_syncstorage/configuration.py +450 -0
  11. api_client_syncstorage/exceptions.py +158 -0
  12. api_client_syncstorage/model/__init__.py +5 -0
  13. api_client_syncstorage/model/completion_response.py +267 -0
  14. api_client_syncstorage/model/health_status_response.py +267 -0
  15. api_client_syncstorage/model/register_request.py +275 -0
  16. api_client_syncstorage/model/register_response.py +279 -0
  17. api_client_syncstorage/model/set_worker_status_request.py +271 -0
  18. api_client_syncstorage/model/set_worker_status_response.py +267 -0
  19. api_client_syncstorage/model/shutdown_response.py +267 -0
  20. api_client_syncstorage/model/test_result_cut_api_model.py +279 -0
  21. api_client_syncstorage/model/test_result_save_response.py +267 -0
  22. api_client_syncstorage/model_utils.py +2058 -0
  23. api_client_syncstorage/models/__init__.py +20 -0
  24. api_client_syncstorage/rest.py +356 -0
  25. testit.py +69 -0
  26. testit_python_commons/__init__.py +0 -0
  27. testit_python_commons/app_properties.py +257 -0
  28. testit_python_commons/client/__init__.py +9 -0
  29. testit_python_commons/client/api_client.py +560 -0
  30. testit_python_commons/client/client_configuration.py +74 -0
  31. testit_python_commons/client/converter.py +618 -0
  32. testit_python_commons/client/helpers/__init__.py +0 -0
  33. testit_python_commons/client/helpers/bulk_autotest_helper.py +246 -0
  34. testit_python_commons/client/helpers/threads_manager.py +86 -0
  35. testit_python_commons/client/models/__init__.py +8 -0
  36. testit_python_commons/client/models/threads_for_create_and_result.py +35 -0
  37. testit_python_commons/client/models/threads_for_update_and_result.py +45 -0
  38. testit_python_commons/configurations/__init__.py +0 -0
  39. testit_python_commons/configurations/properties_names.py +51 -0
  40. testit_python_commons/decorators.py +221 -0
  41. testit_python_commons/dynamic_methods.py +237 -0
  42. testit_python_commons/models/__init__.py +5 -0
  43. testit_python_commons/models/adapter_mode.py +4 -0
  44. testit_python_commons/models/fixture.py +24 -0
  45. testit_python_commons/models/link.py +53 -0
  46. testit_python_commons/models/link_type.py +10 -0
  47. testit_python_commons/models/outcome_type.py +5 -0
  48. testit_python_commons/models/status_type.py +6 -0
  49. testit_python_commons/models/step_result.py +90 -0
  50. testit_python_commons/models/test_result.py +297 -0
  51. testit_python_commons/models/test_result_with_all_fixture_step_results_model.py +28 -0
  52. testit_python_commons/services/__init__.py +41 -0
  53. testit_python_commons/services/adapter_manager.py +318 -0
  54. testit_python_commons/services/adapter_manager_configuration.py +42 -0
  55. testit_python_commons/services/fixture_manager.py +60 -0
  56. testit_python_commons/services/fixture_storage.py +40 -0
  57. testit_python_commons/services/logger.py +32 -0
  58. testit_python_commons/services/plugin_manager.py +101 -0
  59. testit_python_commons/services/retry.py +92 -0
  60. testit_python_commons/services/step_manager.py +36 -0
  61. testit_python_commons/services/step_result_storage.py +26 -0
  62. testit_python_commons/services/sync_storage/__init__.py +9 -0
  63. testit_python_commons/services/sync_storage/sync_storage_runner.py +589 -0
  64. testit_python_commons/services/utils.py +132 -0
  65. testit_python_commons/step.py +101 -0
  66. testit_python_commons/utils/__init__.py +3 -0
  67. testit_python_commons/utils/html_escape_utils.py +219 -0
  68. testit_python_commons-4.2.8.dist-info/METADATA +52 -0
  69. testit_python_commons-4.2.8.dist-info/RECORD +71 -0
  70. testit_python_commons-4.2.8.dist-info/WHEEL +5 -0
  71. testit_python_commons-4.2.8.dist-info/top_level.txt +3 -0
@@ -0,0 +1,42 @@
1
+ from testit_python_commons.models.adapter_mode import AdapterMode
2
+ from testit_python_commons.services.logger import adapter_logger
3
+ from testit_python_commons.services.utils import Utils
4
+ from testit_python_commons.configurations.properties_names import PropertiesNames
5
+
6
+ class AdapterManagerConfiguration:
7
+ __test_run_id = None
8
+
9
+ def __init__(self, app_properties: dict):
10
+ if app_properties.get(PropertiesNames.TEST_RUN_ID):
11
+ self.__test_run_id = Utils.uuid_check(app_properties.get(PropertiesNames.TEST_RUN_ID))
12
+
13
+ self.__adapter_mode = app_properties.get(PropertiesNames.ADAPTER_MODE, AdapterMode.USE_FILTER)
14
+ self.__automatic_creation_test_cases = Utils.convert_value_str_to_bool(
15
+ app_properties.get(PropertiesNames.AUTOMATIC_CREATION_TEST_CASES).lower())
16
+
17
+ self.__import_realtime = Utils.convert_value_str_to_bool(app_properties.get(PropertiesNames.IMPORT_REALTIME).lower())
18
+ self.__test_run_name = app_properties.get(PropertiesNames.TEST_RUN_NAME)
19
+
20
+ @adapter_logger
21
+ def get_test_run_id(self):
22
+ return self.__test_run_id
23
+
24
+ @adapter_logger
25
+ def set_test_run_id(self, test_run_id: str):
26
+ self.__test_run_id = test_run_id
27
+
28
+ @adapter_logger
29
+ def get_test_run_name(self):
30
+ return self.__test_run_name
31
+
32
+ @adapter_logger
33
+ def get_mode(self):
34
+ return self.__adapter_mode
35
+
36
+ @adapter_logger
37
+ def should_automatic_creation_test_cases(self) -> bool:
38
+ return self.__automatic_creation_test_cases
39
+
40
+ @adapter_logger
41
+ def should_import_realtime(self) -> bool:
42
+ return self.__import_realtime
@@ -0,0 +1,60 @@
1
+ from testit_python_commons.services.fixture_storage import ThreadContextFixtures
2
+ from testit_python_commons.models.step_result import StepResult
3
+
4
+
5
+ class FixtureManager:
6
+ def __init__(self):
7
+ self._items = ThreadContextFixtures()
8
+ self._orphan_items = []
9
+
10
+ def _update_item(self, uuid, **kwargs):
11
+ item = self._items[uuid] if uuid else self._items[next(reversed(self._items))]
12
+ for name, value in kwargs.items():
13
+ attr = getattr(item, name)
14
+ if isinstance(attr, list):
15
+ attr.append(value)
16
+ else:
17
+ setattr(item, name, value)
18
+
19
+ def _last_executable(self):
20
+ for _uuid in reversed(self._items):
21
+ if isinstance(self._items[_uuid], StepResult):
22
+ return _uuid
23
+
24
+ def get_item(self, uuid):
25
+ return self._items.get(uuid)
26
+
27
+ def get_last_item(self, item_type=None):
28
+ for _uuid in reversed(self._items):
29
+ if item_type is None:
30
+ return self._items.get(_uuid)
31
+ if type(self._items[_uuid]) == item_type:
32
+ return self._items.get(_uuid)
33
+
34
+ def start_group(self, uuid, group):
35
+ self._items[uuid] = group
36
+
37
+ def stop_group(self, uuid, **kwargs):
38
+ self._update_item(uuid, **kwargs)
39
+
40
+ def update_group(self, uuid, **kwargs):
41
+ self._update_item(uuid, **kwargs)
42
+
43
+ def start_before_fixture(self, parent_uuid, uuid, fixture):
44
+ self._items.get(parent_uuid).befores.append(fixture)
45
+ self._items[uuid] = fixture
46
+
47
+ def stop_before_fixture(self, uuid, **kwargs):
48
+ self._update_item(uuid, **kwargs)
49
+ self._items.pop(uuid)
50
+
51
+ def start_after_fixture(self, parent_uuid, uuid, fixture):
52
+ self._items.get(parent_uuid).afters.append(fixture)
53
+ self._items[uuid] = fixture
54
+
55
+ def stop_after_fixture(self, uuid, **kwargs):
56
+ self._update_item(uuid, **kwargs)
57
+ self._items.pop(uuid)
58
+
59
+ def get_all_items(self):
60
+ return self._items.get_all()
@@ -0,0 +1,40 @@
1
+ import threading
2
+ from collections import OrderedDict, defaultdict
3
+
4
+
5
+ class ThreadContextFixtures:
6
+ _thread_context = defaultdict(OrderedDict)
7
+ _init_thread: threading.Thread
8
+
9
+ @property
10
+ def thread_context(self):
11
+ context = self._thread_context[threading.current_thread()]
12
+ if not context and threading.current_thread() is not self._init_thread:
13
+ uuid, last_item = next(reversed(self._thread_context[self._init_thread].items()))
14
+ context[uuid] = last_item
15
+ return context
16
+
17
+ def __init__(self, *args, **kwargs):
18
+ self._init_thread = threading.current_thread()
19
+ super().__init__(*args, **kwargs)
20
+
21
+ def __setitem__(self, key, value):
22
+ self.thread_context.__setitem__(key, value)
23
+
24
+ def __getitem__(self, item):
25
+ return self.thread_context.__getitem__(item)
26
+
27
+ def __iter__(self):
28
+ return self.thread_context.__iter__()
29
+
30
+ def __reversed__(self):
31
+ return self.thread_context.__reversed__()
32
+
33
+ def get(self, key):
34
+ return self.thread_context.get(key)
35
+
36
+ def pop(self, key):
37
+ return self.thread_context.pop(key)
38
+
39
+ def get_all(self):
40
+ return dict(self.thread_context.items())
@@ -0,0 +1,32 @@
1
+ from functools import wraps
2
+
3
+ from testit_python_commons.services.plugin_manager import TmsPluginManager
4
+
5
+
6
+ def adapter_logger(function):
7
+ @wraps(function)
8
+ def wrapper(*args, **kwargs):
9
+ from testit_python_commons.services.utils import Utils
10
+
11
+ logger = TmsPluginManager.get_logger()
12
+
13
+ parameters = Utils.get_function_parameters(function, *args, **kwargs)
14
+
15
+ message = f'Method "{function.__name__}" started'
16
+
17
+ if parameters:
18
+ message += f' with parameters: {parameters}'
19
+
20
+ logger.debug(message)
21
+
22
+ result = function(*args, **kwargs)
23
+
24
+ message = f'Method "{function.__name__}" finished'
25
+
26
+ if result is not None:
27
+ message += f' with result: {result}'
28
+
29
+ logger.debug(message)
30
+
31
+ return result
32
+ return wrapper
@@ -0,0 +1,101 @@
1
+ import logging
2
+
3
+ from pluggy import PluginManager
4
+
5
+ from testit_python_commons.app_properties import AppProperties
6
+ from testit_python_commons.services.step_manager import StepManager
7
+
8
+
9
+ class TmsPluginManager:
10
+ __plugin_manager = None
11
+ __adapter_manager = None
12
+ __fixture_manager = None
13
+ __step_manager = None
14
+ __logger = None
15
+ # None: non-pytest or undecided; False: pytest without --testit; True: pytest with --testit
16
+ __pytest_tms_report = None
17
+
18
+ @classmethod
19
+ def set_pytest_tms_report(cls, enabled):
20
+ cls.__pytest_tms_report = enabled
21
+
22
+ @classmethod
23
+ def get_plugin_manager(cls):
24
+ if cls.__plugin_manager is None:
25
+ cls.__plugin_manager = PluginManager("testit")
26
+
27
+ return cls.__plugin_manager
28
+
29
+ @classmethod
30
+ def get_adapter_manager(cls, option=None):
31
+ if cls.__pytest_tms_report is False:
32
+ return None
33
+
34
+ if cls.__adapter_manager is None:
35
+ from testit_python_commons.client.client_configuration import (
36
+ ClientConfiguration,
37
+ )
38
+ from testit_python_commons.services.adapter_manager import AdapterManager
39
+ from testit_python_commons.services.adapter_manager_configuration import (
40
+ AdapterManagerConfiguration,
41
+ )
42
+
43
+ app_properties = AppProperties.load_properties(option)
44
+
45
+ cls.get_logger(app_properties.get("logs") == "true")
46
+
47
+ client_configuration = ClientConfiguration(app_properties)
48
+ adapter_configuration = AdapterManagerConfiguration(app_properties)
49
+ fixture_manager = cls.get_fixture_manager()
50
+
51
+ cls.__adapter_manager = AdapterManager(
52
+ adapter_configuration, client_configuration, fixture_manager
53
+ )
54
+
55
+ return cls.__adapter_manager
56
+
57
+ @classmethod
58
+ def get_fixture_manager(cls):
59
+ if cls.__fixture_manager is None:
60
+ from testit_python_commons.services.fixture_manager import FixtureManager
61
+
62
+ cls.__fixture_manager = FixtureManager()
63
+
64
+ return cls.__fixture_manager
65
+
66
+ @classmethod
67
+ def get_step_manager(cls) -> StepManager:
68
+ if cls.__step_manager is None:
69
+ cls.__step_manager = StepManager()
70
+
71
+ return cls.__step_manager
72
+
73
+ @classmethod
74
+ def get_logger(cls, debug: bool = False):
75
+ if cls.__logger is None:
76
+ # Fix for live log duplication issue
77
+ # Create a dedicated handler for TmsLogger to avoid affecting global logging configuration
78
+ cls.__logger = logging.getLogger("TmsLogger")
79
+
80
+ # Only add handler if none exists to prevent duplicates
81
+ if not cls.__logger.handlers:
82
+ handler = logging.StreamHandler()
83
+ formatter = logging.Formatter(
84
+ "\n%(levelname)s (%(asctime)s): %(message)s"
85
+ )
86
+ handler.setFormatter(formatter)
87
+ cls.__logger.addHandler(handler)
88
+
89
+ # Always show WARNING and above (WARNING, ERROR, CRITICAL)
90
+ # Only show DEBUG and INFO when debug mode is enabled
91
+ level = logging.DEBUG if debug else logging.WARNING
92
+ cls.__logger.setLevel(level)
93
+
94
+ # Prevent propagation to root logger to avoid interference with user loggers
95
+ cls.__logger.propagate = False
96
+
97
+ return cls.__logger
98
+
99
+ @classmethod
100
+ def __getattr__(cls, attribute):
101
+ return getattr(cls.get_plugin_manager(), attribute)
@@ -0,0 +1,92 @@
1
+ import logging
2
+ import random
3
+ import time
4
+ from http.client import RemoteDisconnected
5
+
6
+ import testit_api_client
7
+ import urllib3
8
+
9
+ CONNECTION_RETRIES = 3
10
+ CONNECTION_RETRY_DELAY_SEC = 1
11
+ API_EXCEPTION_RETRIES = 10
12
+ NON_RETRIABLE_API_STATUS_CODES = (400, 404)
13
+
14
+ _RETRIABLE_CONNECTION_TYPES = (
15
+ urllib3.exceptions.ProtocolError,
16
+ urllib3.exceptions.NewConnectionError,
17
+ ConnectionError,
18
+ ConnectionResetError,
19
+ ConnectionAbortedError,
20
+ RemoteDisconnected,
21
+ TimeoutError,
22
+ )
23
+
24
+
25
+ def is_non_retriable_api_exception(exc: BaseException) -> bool:
26
+ return (
27
+ isinstance(exc, testit_api_client.exceptions.ApiException)
28
+ and int(exc.status) in NON_RETRIABLE_API_STATUS_CODES
29
+ )
30
+
31
+
32
+ def is_retriable_connection_error(exc: BaseException) -> bool:
33
+ seen = set()
34
+ current = exc
35
+ while current is not None and id(current) not in seen:
36
+ seen.add(id(current))
37
+ if isinstance(current, _RETRIABLE_CONNECTION_TYPES):
38
+ return True
39
+ current = current.__cause__ or current.__context__
40
+ return False
41
+
42
+
43
+ def _execute_with_connection_retries(func, args, kwargs):
44
+ connection_attempts = 0
45
+
46
+ while True:
47
+ try:
48
+ return func(*args, **kwargs)
49
+ except BaseException as e:
50
+ if not is_retriable_connection_error(e):
51
+ raise
52
+
53
+ connection_attempts += 1
54
+ logging.warning(
55
+ 'Connection error in %s (attempt %d/%d): %s',
56
+ func.__name__,
57
+ connection_attempts,
58
+ CONNECTION_RETRIES,
59
+ e,
60
+ )
61
+ if connection_attempts > CONNECTION_RETRIES:
62
+ raise
63
+
64
+ time.sleep(CONNECTION_RETRY_DELAY_SEC)
65
+
66
+
67
+ def retry_on_connection_error(func):
68
+ def retry_wrapper(*args, **kwargs):
69
+ return _execute_with_connection_retries(func, args, kwargs)
70
+
71
+ return retry_wrapper
72
+
73
+
74
+ def retry(func):
75
+ def retry_wrapper(*args, **kwargs):
76
+ api_attempts = 0
77
+
78
+ while True:
79
+ try:
80
+ return _execute_with_connection_retries(func, args, kwargs)
81
+ except testit_api_client.exceptions.ApiException as e:
82
+ if is_non_retriable_api_exception(e):
83
+ raise
84
+
85
+ api_attempts += 1
86
+ logging.error(e)
87
+ if api_attempts >= API_EXCEPTION_RETRIES:
88
+ raise
89
+
90
+ time.sleep(random.randrange(0, 100) / 100)
91
+
92
+ return retry_wrapper
@@ -0,0 +1,36 @@
1
+ from typing import List
2
+
3
+ from testit_python_commons.models.step_result import StepResult
4
+ from testit_python_commons.services.step_result_storage import StepResultStorage
5
+
6
+
7
+ class StepManager:
8
+ __steps_tree: List[StepResult] = []
9
+
10
+ def __init__(self):
11
+ self.__storage = StepResultStorage()
12
+
13
+ def start_step(self, step: StepResult):
14
+ if self.__storage.get_count():
15
+ parent_step: StepResult = self.__storage.get_last()
16
+
17
+ step_results_from_parent_step = parent_step.get_step_results()
18
+ step_results_from_parent_step.append(step)
19
+ parent_step.set_step_results(step_results_from_parent_step)
20
+
21
+ self.__storage.add(step)
22
+
23
+ def stop_step(self):
24
+ if self.__storage.get_count() == 1:
25
+ self.__steps_tree.append(self.__storage.get_last())
26
+
27
+ self.__storage.remove_last()
28
+
29
+ def get_active_step(self) -> StepResult:
30
+ return self.__storage.get_last()
31
+
32
+ def get_steps_tree(self) -> List[StepResult]:
33
+ steps_tree = self.__steps_tree.copy()
34
+ self.__steps_tree.clear()
35
+
36
+ return steps_tree
@@ -0,0 +1,26 @@
1
+ import logging
2
+ from typing import List
3
+
4
+ from testit_python_commons.models.step_result import StepResult
5
+
6
+
7
+ class StepResultStorage:
8
+ __storage: List[StepResult] = []
9
+
10
+ def add(self, step_result: StepResult):
11
+ self.__storage.append(step_result)
12
+
13
+ def get_last(self):
14
+ if not self.__storage:
15
+ return
16
+
17
+ return self.__storage[-1]
18
+
19
+ def remove_last(self):
20
+ try:
21
+ self.__storage.pop()
22
+ except Exception as exc:
23
+ logging.error(f'Cannot remove last step from storage. Storage is empty. {exc}')
24
+
25
+ def get_count(self) -> int:
26
+ return len(self.__storage)
@@ -0,0 +1,9 @@
1
+ """
2
+ Sync Storage integration for Test IT Python adapters.
3
+ """
4
+
5
+ from .sync_storage_runner import SyncStorageRunner
6
+
7
+ __all__ = [
8
+ "SyncStorageRunner",
9
+ ]