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.
- api_client_syncstorage/__init__.py +27 -0
- api_client_syncstorage/api/__init__.py +3 -0
- api_client_syncstorage/api/completion_api.py +302 -0
- api_client_syncstorage/api/health_api.py +158 -0
- api_client_syncstorage/api/system_api.py +158 -0
- api_client_syncstorage/api/test_results_api.py +182 -0
- api_client_syncstorage/api/workers_api.py +308 -0
- api_client_syncstorage/api_client.py +898 -0
- api_client_syncstorage/apis/__init__.py +21 -0
- api_client_syncstorage/configuration.py +450 -0
- api_client_syncstorage/exceptions.py +158 -0
- api_client_syncstorage/model/__init__.py +5 -0
- api_client_syncstorage/model/completion_response.py +267 -0
- api_client_syncstorage/model/health_status_response.py +267 -0
- api_client_syncstorage/model/register_request.py +275 -0
- api_client_syncstorage/model/register_response.py +279 -0
- api_client_syncstorage/model/set_worker_status_request.py +271 -0
- api_client_syncstorage/model/set_worker_status_response.py +267 -0
- api_client_syncstorage/model/shutdown_response.py +267 -0
- api_client_syncstorage/model/test_result_cut_api_model.py +279 -0
- api_client_syncstorage/model/test_result_save_response.py +267 -0
- api_client_syncstorage/model_utils.py +2058 -0
- api_client_syncstorage/models/__init__.py +20 -0
- api_client_syncstorage/rest.py +356 -0
- testit.py +69 -0
- testit_python_commons/__init__.py +0 -0
- testit_python_commons/app_properties.py +257 -0
- testit_python_commons/client/__init__.py +9 -0
- testit_python_commons/client/api_client.py +560 -0
- testit_python_commons/client/client_configuration.py +74 -0
- testit_python_commons/client/converter.py +618 -0
- testit_python_commons/client/helpers/__init__.py +0 -0
- testit_python_commons/client/helpers/bulk_autotest_helper.py +246 -0
- testit_python_commons/client/helpers/threads_manager.py +86 -0
- testit_python_commons/client/models/__init__.py +8 -0
- testit_python_commons/client/models/threads_for_create_and_result.py +35 -0
- testit_python_commons/client/models/threads_for_update_and_result.py +45 -0
- testit_python_commons/configurations/__init__.py +0 -0
- testit_python_commons/configurations/properties_names.py +51 -0
- testit_python_commons/decorators.py +221 -0
- testit_python_commons/dynamic_methods.py +237 -0
- testit_python_commons/models/__init__.py +5 -0
- testit_python_commons/models/adapter_mode.py +4 -0
- testit_python_commons/models/fixture.py +24 -0
- testit_python_commons/models/link.py +53 -0
- testit_python_commons/models/link_type.py +10 -0
- testit_python_commons/models/outcome_type.py +5 -0
- testit_python_commons/models/status_type.py +6 -0
- testit_python_commons/models/step_result.py +90 -0
- testit_python_commons/models/test_result.py +297 -0
- testit_python_commons/models/test_result_with_all_fixture_step_results_model.py +28 -0
- testit_python_commons/services/__init__.py +41 -0
- testit_python_commons/services/adapter_manager.py +318 -0
- testit_python_commons/services/adapter_manager_configuration.py +42 -0
- testit_python_commons/services/fixture_manager.py +60 -0
- testit_python_commons/services/fixture_storage.py +40 -0
- testit_python_commons/services/logger.py +32 -0
- testit_python_commons/services/plugin_manager.py +101 -0
- testit_python_commons/services/retry.py +92 -0
- testit_python_commons/services/step_manager.py +36 -0
- testit_python_commons/services/step_result_storage.py +26 -0
- testit_python_commons/services/sync_storage/__init__.py +9 -0
- testit_python_commons/services/sync_storage/sync_storage_runner.py +589 -0
- testit_python_commons/services/utils.py +132 -0
- testit_python_commons/step.py +101 -0
- testit_python_commons/utils/__init__.py +3 -0
- testit_python_commons/utils/html_escape_utils.py +219 -0
- testit_python_commons-4.2.8.dist-info/METADATA +52 -0
- testit_python_commons-4.2.8.dist-info/RECORD +71 -0
- testit_python_commons-4.2.8.dist-info/WHEEL +5 -0
- testit_python_commons-4.2.8.dist-info/top_level.txt +3 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import logging
|
|
3
|
+
import re
|
|
4
|
+
import warnings
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from testit_python_commons.models.link import Link
|
|
8
|
+
from testit_python_commons.models.link_type import LinkType
|
|
9
|
+
from testit_python_commons.services.logger import adapter_logger
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from testit_python_commons.models.link import Link
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Utils:
|
|
16
|
+
@staticmethod
|
|
17
|
+
def uuid_check(uuid: str):
|
|
18
|
+
if not re.fullmatch(r'[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12}', uuid):
|
|
19
|
+
logging.error(f'The wrong {uuid}!')
|
|
20
|
+
raise SystemExit
|
|
21
|
+
|
|
22
|
+
return uuid
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def url_check(url: str):
|
|
26
|
+
if not re.fullmatch(
|
|
27
|
+
r"^(ht|f)tp(s?)\:\/\/[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(:(0-9)*)*(\/?)"
|
|
28
|
+
r"([a-zA-Z0-9\-\.\?\,\'\/\\\+&%\$#_]*)?$",
|
|
29
|
+
url):
|
|
30
|
+
logging.error('The wrong URL!')
|
|
31
|
+
raise SystemExit
|
|
32
|
+
|
|
33
|
+
if url[-1] == '/':
|
|
34
|
+
return url[:-1]
|
|
35
|
+
|
|
36
|
+
return url
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def deprecated(message):
|
|
40
|
+
def deprecated_decorator(func): # noqa: N802
|
|
41
|
+
def deprecated_func(*args, **kwargs):
|
|
42
|
+
warnings.warn(
|
|
43
|
+
'"{}" is deprecated.\n{}'.format(func.__name__, message),
|
|
44
|
+
category=DeprecationWarning,
|
|
45
|
+
stacklevel=2)
|
|
46
|
+
warnings.simplefilter('default', DeprecationWarning)
|
|
47
|
+
return func(*args, **kwargs)
|
|
48
|
+
|
|
49
|
+
return deprecated_func
|
|
50
|
+
|
|
51
|
+
return deprecated_decorator
|
|
52
|
+
|
|
53
|
+
@staticmethod
|
|
54
|
+
def get_function_parameters(function, *args, **kwargs):
|
|
55
|
+
parameters = {}
|
|
56
|
+
args_default_values = inspect.getfullargspec(function).defaults
|
|
57
|
+
|
|
58
|
+
if args or args_default_values:
|
|
59
|
+
all_keys = inspect.getfullargspec(function).args
|
|
60
|
+
all_args = list(args)
|
|
61
|
+
|
|
62
|
+
if args_default_values:
|
|
63
|
+
all_args += list(args_default_values[len(args) - (len(all_keys) - len(args_default_values)):])
|
|
64
|
+
|
|
65
|
+
method_args = [arg_name for arg_name in all_keys if arg_name not in list(kwargs)]
|
|
66
|
+
|
|
67
|
+
if len(method_args) == len(all_args):
|
|
68
|
+
for index in range(0, len(method_args)):
|
|
69
|
+
parameters[method_args[index]] = str(all_args[index])
|
|
70
|
+
|
|
71
|
+
if kwargs:
|
|
72
|
+
for key, parameter in kwargs.items():
|
|
73
|
+
parameters[key] = str(parameter)
|
|
74
|
+
|
|
75
|
+
return parameters
|
|
76
|
+
|
|
77
|
+
@staticmethod
|
|
78
|
+
def exclude_self_parameter(all_parameters: dict) -> dict:
|
|
79
|
+
if all_parameters.get('self') is not None:
|
|
80
|
+
all_parameters.pop('self')
|
|
81
|
+
return all_parameters
|
|
82
|
+
|
|
83
|
+
@staticmethod
|
|
84
|
+
def collect_parameters_in_string_attribute(attribute: str, all_parameters: dict) -> str:
|
|
85
|
+
param_keys = []
|
|
86
|
+
|
|
87
|
+
if attribute:
|
|
88
|
+
param_keys = re.findall(r"\{(.*?)\}", attribute)
|
|
89
|
+
|
|
90
|
+
if len(param_keys) > 0:
|
|
91
|
+
for param_key in param_keys:
|
|
92
|
+
parameter = all_parameters.get(param_key)
|
|
93
|
+
|
|
94
|
+
if parameter is not None:
|
|
95
|
+
attribute = attribute.replace("{" + param_key + "}", str(parameter))
|
|
96
|
+
|
|
97
|
+
return attribute
|
|
98
|
+
|
|
99
|
+
@staticmethod
|
|
100
|
+
@adapter_logger
|
|
101
|
+
def convert_link_dict_to_link_model(link_dict: dict) -> "Link":
|
|
102
|
+
from testit_python_commons.models.link import Link
|
|
103
|
+
|
|
104
|
+
link_model = Link()
|
|
105
|
+
link_model.set_url(link_dict['url'])
|
|
106
|
+
|
|
107
|
+
if 'title' in link_dict:
|
|
108
|
+
link_model.set_title(link_dict['title'])
|
|
109
|
+
|
|
110
|
+
if 'type' in link_dict:
|
|
111
|
+
link_model.set_link_type(link_dict.get('type', LinkType.RELATED))
|
|
112
|
+
|
|
113
|
+
if 'description' in link_dict:
|
|
114
|
+
link_model.set_description(link_dict['description'])
|
|
115
|
+
|
|
116
|
+
return link_model
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
@adapter_logger
|
|
120
|
+
def convert_body_of_attachment(body):
|
|
121
|
+
if isinstance(body, bytes):
|
|
122
|
+
return body
|
|
123
|
+
|
|
124
|
+
return str(body).encode('utf-8')
|
|
125
|
+
|
|
126
|
+
@staticmethod
|
|
127
|
+
@adapter_logger
|
|
128
|
+
def convert_value_str_to_bool(value: str) -> bool:
|
|
129
|
+
if value:
|
|
130
|
+
return value == 'true'
|
|
131
|
+
|
|
132
|
+
return False
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
import logging
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Any, Callable, TypeVar
|
|
5
|
+
|
|
6
|
+
from testit_python_commons.models.step_result import StepResult
|
|
7
|
+
from testit_python_commons.services import (
|
|
8
|
+
TmsPluginManager,
|
|
9
|
+
Utils
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Func = TypeVar("Func", bound=Callable[..., Any])
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# if we return title: "", it will be replaced to
|
|
17
|
+
# step name in fact later
|
|
18
|
+
def step(*args, **kwargs):
|
|
19
|
+
if len(args) == 0:
|
|
20
|
+
return StepContext("", None, {})
|
|
21
|
+
|
|
22
|
+
if callable(args[0]):
|
|
23
|
+
function = args[0]
|
|
24
|
+
return StepContext(function.__name__, None, {})(function)
|
|
25
|
+
else:
|
|
26
|
+
title = get_title(args, kwargs)
|
|
27
|
+
description = get_description(args, kwargs)
|
|
28
|
+
|
|
29
|
+
return StepContext(title, description, {})
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_title(args: tuple, kwargs: dict):
|
|
33
|
+
if 'title' in kwargs:
|
|
34
|
+
return kwargs['title']
|
|
35
|
+
|
|
36
|
+
if len(args) > 0:
|
|
37
|
+
if isinstance(args[0], str):
|
|
38
|
+
return args[0]
|
|
39
|
+
|
|
40
|
+
logging.error(f'Cannot to get step title: {args[1]}. The title must be of string type.')
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_description(args: tuple, kwargs: dict):
|
|
44
|
+
if 'description' in kwargs:
|
|
45
|
+
return kwargs['description']
|
|
46
|
+
|
|
47
|
+
if len(args) > 1:
|
|
48
|
+
if isinstance(args[1], str):
|
|
49
|
+
return args[1]
|
|
50
|
+
logging.error(f'Cannot to get step description: {args[1]}. The description must be of string type.')
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class StepContext:
|
|
54
|
+
def __init__(self, title, description, parameters):
|
|
55
|
+
self.__title = title
|
|
56
|
+
self.__description = description
|
|
57
|
+
self.__parameters = parameters
|
|
58
|
+
|
|
59
|
+
def __enter__(self):
|
|
60
|
+
self.__start_time = round(datetime.utcnow().timestamp() * 1000)
|
|
61
|
+
self.__step_result = StepResult()
|
|
62
|
+
|
|
63
|
+
self.__title = Utils.collect_parameters_in_string_attribute(self.__title, self.__parameters)
|
|
64
|
+
self.__description = Utils.collect_parameters_in_string_attribute(self.__description, self.__parameters)
|
|
65
|
+
|
|
66
|
+
self.__step_result\
|
|
67
|
+
.set_title(self.__title)\
|
|
68
|
+
.set_description(self.__description)\
|
|
69
|
+
.set_parameters(
|
|
70
|
+
Utils.exclude_self_parameter(self.__parameters)
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
logging.debug(f'Step "{self.__title}" was started')
|
|
74
|
+
|
|
75
|
+
TmsPluginManager.get_step_manager().start_step(self.__step_result)
|
|
76
|
+
|
|
77
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
78
|
+
outcome = 'Failed' if exc_type \
|
|
79
|
+
else TmsPluginManager.get_plugin_manager().hook.get_pytest_check_outcome()[0] if \
|
|
80
|
+
hasattr(TmsPluginManager.get_plugin_manager().hook, 'get_pytest_check_outcome') \
|
|
81
|
+
else 'Passed'
|
|
82
|
+
duration = round(datetime.utcnow().timestamp() * 1000) - self.__start_time
|
|
83
|
+
|
|
84
|
+
self.__step_result\
|
|
85
|
+
.set_outcome(outcome)\
|
|
86
|
+
.set_duration(duration)
|
|
87
|
+
|
|
88
|
+
TmsPluginManager.get_step_manager().stop_step()
|
|
89
|
+
|
|
90
|
+
def __call__(self, function: Func) -> Func:
|
|
91
|
+
@wraps(function)
|
|
92
|
+
def impl(*args, **kwargs):
|
|
93
|
+
__tracebackhide__ = True
|
|
94
|
+
parameters = Utils.get_function_parameters(function, *args, **kwargs)
|
|
95
|
+
|
|
96
|
+
title = self.__title if self.__title else function.__name__
|
|
97
|
+
|
|
98
|
+
with StepContext(title, self.__description, parameters):
|
|
99
|
+
return function(*args, **kwargs)
|
|
100
|
+
|
|
101
|
+
return impl
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, List, Optional, Union
|
|
5
|
+
from datetime import datetime, date, time, timedelta
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class HtmlEscapeUtils:
|
|
11
|
+
"""
|
|
12
|
+
HTML escape utilities for preventing XSS attacks.
|
|
13
|
+
Escapes HTML tags in strings and objects using reflection.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
NO_ESCAPE_HTML_ENV_VAR = "NO_ESCAPE_HTML"
|
|
17
|
+
|
|
18
|
+
# Regex pattern to detect HTML tags (requires at least one non-whitespace character after <)
|
|
19
|
+
# This ensures empty <> brackets are not considered HTML tags
|
|
20
|
+
_HTML_TAG_PATTERN = re.compile(r'<\S.*?(?:>|/>)')
|
|
21
|
+
|
|
22
|
+
# Regex patterns to escape only non-escaped characters
|
|
23
|
+
# Using negative lookbehind to avoid double escaping
|
|
24
|
+
_LESS_THAN_PATTERN = re.compile('<')
|
|
25
|
+
_GREATER_THAN_PATTERN = re.compile('>')
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def escape_html_tags(text: Optional[str]) -> Optional[str]:
|
|
29
|
+
"""
|
|
30
|
+
Escapes HTML tags to prevent XSS attacks.
|
|
31
|
+
First checks if the string contains HTML tags using regex pattern.
|
|
32
|
+
Only performs escaping if HTML tags are detected.
|
|
33
|
+
Escapes all < as \\< and > as \\> only if they are not already escaped.
|
|
34
|
+
Uses regex with negative lookbehind to avoid double escaping.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
text: The text to escape
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
Escaped text or original text if escaping is disabled
|
|
41
|
+
"""
|
|
42
|
+
if text is None:
|
|
43
|
+
return None
|
|
44
|
+
|
|
45
|
+
# Check if escaping is disabled via environment variable
|
|
46
|
+
no_escape_html = os.environ.get(HtmlEscapeUtils.NO_ESCAPE_HTML_ENV_VAR, "").lower()
|
|
47
|
+
if no_escape_html == "true":
|
|
48
|
+
return text
|
|
49
|
+
|
|
50
|
+
# First check if the string contains HTML tags
|
|
51
|
+
if not HtmlEscapeUtils._HTML_TAG_PATTERN.search(text):
|
|
52
|
+
return text # No HTML tags found, return original string
|
|
53
|
+
|
|
54
|
+
# Use regex with negative lookbehind to escape only non-escaped characters
|
|
55
|
+
result = HtmlEscapeUtils._LESS_THAN_PATTERN.sub('<', text)
|
|
56
|
+
result = HtmlEscapeUtils._GREATER_THAN_PATTERN.sub('>', result)
|
|
57
|
+
|
|
58
|
+
return result
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def escape_html_in_object(obj: Any) -> Any:
|
|
62
|
+
"""
|
|
63
|
+
Escapes HTML tags in all string attributes of an object using reflection.
|
|
64
|
+
Also processes list attributes: if list of objects - calls escape_html_in_object_list,
|
|
65
|
+
if list of strings - escapes each string.
|
|
66
|
+
Can be disabled by setting NO_ESCAPE_HTML environment variable to "true".
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
obj: The object to process
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The processed object with escaped strings
|
|
73
|
+
"""
|
|
74
|
+
if obj is None:
|
|
75
|
+
return None
|
|
76
|
+
|
|
77
|
+
# Check if escaping is disabled via environment variable
|
|
78
|
+
no_escape_html = os.environ.get(HtmlEscapeUtils.NO_ESCAPE_HTML_ENV_VAR, "").lower()
|
|
79
|
+
if no_escape_html == "true":
|
|
80
|
+
return obj
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
HtmlEscapeUtils._process_object_attributes(obj)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
# Silently ignore reflection errors
|
|
86
|
+
logging.debug(f"Error processing object attributes: {e}")
|
|
87
|
+
|
|
88
|
+
return obj
|
|
89
|
+
|
|
90
|
+
@staticmethod
|
|
91
|
+
def escape_html_in_object_list(obj_list: Optional[List[Any]]) -> Optional[List[Any]]:
|
|
92
|
+
"""
|
|
93
|
+
Escapes HTML tags in all string attributes of objects in a list using reflection.
|
|
94
|
+
Can be disabled by setting NO_ESCAPE_HTML environment variable to "true".
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
obj_list: The list of objects to process
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The processed list with escaped strings in all objects
|
|
101
|
+
"""
|
|
102
|
+
if obj_list is None:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
# Check if escaping is disabled via environment variable
|
|
106
|
+
no_escape_html = os.environ.get(HtmlEscapeUtils.NO_ESCAPE_HTML_ENV_VAR, "").lower()
|
|
107
|
+
if no_escape_html == "true":
|
|
108
|
+
return obj_list
|
|
109
|
+
|
|
110
|
+
for obj in obj_list:
|
|
111
|
+
HtmlEscapeUtils.escape_html_in_object(obj)
|
|
112
|
+
|
|
113
|
+
return obj_list
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def _process_object_attributes(obj: Any) -> None:
|
|
117
|
+
"""
|
|
118
|
+
Process all attributes of an object for HTML escaping.
|
|
119
|
+
"""
|
|
120
|
+
# Handle dictionary-like objects (common in API models)
|
|
121
|
+
if hasattr(obj, '__dict__'):
|
|
122
|
+
for attr_name in dir(obj):
|
|
123
|
+
# Skip private/protected attributes and methods
|
|
124
|
+
if attr_name.startswith('_') or callable(getattr(obj, attr_name, None)):
|
|
125
|
+
continue
|
|
126
|
+
if (
|
|
127
|
+
attr_name.startswith("external_id")
|
|
128
|
+
or attr_name.startswith("externalId")
|
|
129
|
+
or attr_name.startswith("auto_test_external_id")
|
|
130
|
+
or attr_name.startswith("autoTestExternalId")
|
|
131
|
+
):
|
|
132
|
+
continue
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
value = getattr(obj, attr_name)
|
|
136
|
+
HtmlEscapeUtils._process_attribute_value(obj, attr_name, value)
|
|
137
|
+
except Exception as e:
|
|
138
|
+
# Silently ignore attribute errors
|
|
139
|
+
logging.debug(f"Error processing attribute {attr_name}: {e}")
|
|
140
|
+
|
|
141
|
+
# Handle dictionary objects
|
|
142
|
+
elif isinstance(obj, dict):
|
|
143
|
+
for key, value in obj.items():
|
|
144
|
+
if isinstance(value, str):
|
|
145
|
+
obj[key] = HtmlEscapeUtils.escape_html_tags(value)
|
|
146
|
+
elif isinstance(value, list):
|
|
147
|
+
HtmlEscapeUtils._process_list(value)
|
|
148
|
+
elif not HtmlEscapeUtils._is_simple_type(type(value)):
|
|
149
|
+
HtmlEscapeUtils.escape_html_in_object(value)
|
|
150
|
+
|
|
151
|
+
@staticmethod
|
|
152
|
+
def _process_attribute_value(obj: Any, attr_name: str, value: Any) -> None:
|
|
153
|
+
"""
|
|
154
|
+
Process a single attribute value for HTML escaping.
|
|
155
|
+
"""
|
|
156
|
+
if isinstance(value, str):
|
|
157
|
+
# Escape string attributes
|
|
158
|
+
try:
|
|
159
|
+
setattr(obj, attr_name, HtmlEscapeUtils.escape_html_tags(value))
|
|
160
|
+
except AttributeError:
|
|
161
|
+
# Attribute might be read-only
|
|
162
|
+
pass
|
|
163
|
+
elif isinstance(value, list):
|
|
164
|
+
HtmlEscapeUtils._process_list(value)
|
|
165
|
+
elif value is not None and not HtmlEscapeUtils._is_simple_type(type(value)):
|
|
166
|
+
# Process nested objects (but not simple types)
|
|
167
|
+
HtmlEscapeUtils.escape_html_in_object(value)
|
|
168
|
+
|
|
169
|
+
@staticmethod
|
|
170
|
+
def _process_list(lst: List[Any]) -> None:
|
|
171
|
+
"""
|
|
172
|
+
Process a list for HTML escaping.
|
|
173
|
+
"""
|
|
174
|
+
if not lst:
|
|
175
|
+
return
|
|
176
|
+
|
|
177
|
+
first_element = lst[0]
|
|
178
|
+
|
|
179
|
+
if isinstance(first_element, str):
|
|
180
|
+
# List of strings - escape each string
|
|
181
|
+
for i, item in enumerate(lst):
|
|
182
|
+
if isinstance(item, str):
|
|
183
|
+
lst[i] = HtmlEscapeUtils.escape_html_tags(item)
|
|
184
|
+
elif first_element is not None:
|
|
185
|
+
# List of objects - process each object
|
|
186
|
+
for item in lst:
|
|
187
|
+
HtmlEscapeUtils.escape_html_in_object(item)
|
|
188
|
+
|
|
189
|
+
@staticmethod
|
|
190
|
+
def _is_simple_type(obj_type: type) -> bool:
|
|
191
|
+
"""
|
|
192
|
+
Checks if a type is a simple type that doesn't need HTML escaping.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
obj_type: Type to check
|
|
196
|
+
|
|
197
|
+
Returns:
|
|
198
|
+
True if it's a simple type
|
|
199
|
+
"""
|
|
200
|
+
simple_types = {
|
|
201
|
+
# Basic types
|
|
202
|
+
bool, int, float, complex, bytes, bytearray,
|
|
203
|
+
# String type (handled separately)
|
|
204
|
+
str,
|
|
205
|
+
# Date/time types
|
|
206
|
+
datetime, date, time, timedelta,
|
|
207
|
+
# Other common types
|
|
208
|
+
Decimal, UUID,
|
|
209
|
+
# None type
|
|
210
|
+
type(None)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
obj_type in simple_types or
|
|
215
|
+
# Check for enums
|
|
216
|
+
(hasattr(obj_type, '__bases__') and any(
|
|
217
|
+
base.__name__ == 'Enum' for base in obj_type.__bases__
|
|
218
|
+
))
|
|
219
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: testit-python-commons
|
|
3
|
+
Version: 4.2.8
|
|
4
|
+
Summary: Python commons for Test IT
|
|
5
|
+
Home-page: https://github.com/testit-tms/adapters-python/
|
|
6
|
+
Author: Integration team
|
|
7
|
+
Author-email: integrations@testit.software
|
|
8
|
+
License: Apache-2.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Requires-Dist: pluggy
|
|
19
|
+
Requires-Dist: tomli
|
|
20
|
+
Requires-Dist: testit-api-client==7.5.10
|
|
21
|
+
Dynamic: author
|
|
22
|
+
Dynamic: author-email
|
|
23
|
+
Dynamic: classifier
|
|
24
|
+
Dynamic: description
|
|
25
|
+
Dynamic: description-content-type
|
|
26
|
+
Dynamic: home-page
|
|
27
|
+
Dynamic: license
|
|
28
|
+
Dynamic: requires-dist
|
|
29
|
+
Dynamic: summary
|
|
30
|
+
|
|
31
|
+
# Sync Storage Integration
|
|
32
|
+
|
|
33
|
+
This library includes built-in support for Sync Storage, enabling coordinated test execution across multiple workers in distributed testing environments.
|
|
34
|
+
|
|
35
|
+
See [SYNC_STORAGE_INTEGRATION.md](SYNC_STORAGE_INTEGRATION.md) for detailed documentation on how the integration works.
|
|
36
|
+
|
|
37
|
+
# How to enable debug logging?
|
|
38
|
+
1. Add in **connection_config.ini** file from the root directory of the project:
|
|
39
|
+
```
|
|
40
|
+
[debug]
|
|
41
|
+
__DEV = true
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## How to install local modified version?
|
|
45
|
+
```
|
|
46
|
+
cd adapters-python/testit-python-commons
|
|
47
|
+
pip install .
|
|
48
|
+
```
|
|
49
|
+
## How to install version from some branch?
|
|
50
|
+
```
|
|
51
|
+
git+https://github.com/testit-tms/adapters-python.git@develop#subdirectory=testit-python-commons
|
|
52
|
+
```
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
testit.py,sha256=DfWIpqiZdbZV86cKB15RrhP1orXoJA0iwVygrsdzJJk,1196
|
|
2
|
+
api_client_syncstorage/__init__.py,sha256=TMIJrgGlinBK8e0cypc0XKhIC4ct8R_jWRmPMBP1N3A,800
|
|
3
|
+
api_client_syncstorage/api_client.py,sha256=gdOnIiftQhdPO_lcf00TSX3in-JCs-bwj6iS0wOXXxA,39233
|
|
4
|
+
api_client_syncstorage/configuration.py,sha256=bToukJ5xUvtBdmQmW7Gr08t2VPgmOqRqimQQoS43u1c,16352
|
|
5
|
+
api_client_syncstorage/exceptions.py,sha256=3Zyo7lsgd-CSojT0d0VCGzvw5ulCzmUI-rxMkn7WrkI,5068
|
|
6
|
+
api_client_syncstorage/model_utils.py,sha256=yg46dGZOh6Kkikc4QzwYrp2uu17wQQ8ZghFLN-D1_kE,82603
|
|
7
|
+
api_client_syncstorage/rest.py,sha256=qE5FWqE4GTY3ZYfyxcUyl2Kd80zMU5iWBmGfGnlb_MA,14406
|
|
8
|
+
api_client_syncstorage/api/__init__.py,sha256=Zt4IuzQj6ZZTwYv0ByWm6_r85lxI2c50JFC-sfbLVAI,229
|
|
9
|
+
api_client_syncstorage/api/completion_api.py,sha256=APWxrPI_socgRCwPf45tuIVZqFVUxyqvCYuesLDDLpg,10971
|
|
10
|
+
api_client_syncstorage/api/health_api.py,sha256=Ys0tH60sPTRjoIOqNcfvRzmc-zAg8PhAuNccYHFaO7E,5495
|
|
11
|
+
api_client_syncstorage/api/system_api.py,sha256=1TCm34iirvxWHhiltIPn9uAeVwXdt7Ysu7iYwiqrF-E,5488
|
|
12
|
+
api_client_syncstorage/api/test_results_api.py,sha256=TIkd7Qw0sJSX3nmb-UKld2fy8YH6r8Y8aMlblan2RGo,6634
|
|
13
|
+
api_client_syncstorage/api/workers_api.py,sha256=2GZaHh0SwaN8psnoXDigO0WKjquVTqpjL8Hm1q-OrWA,11434
|
|
14
|
+
api_client_syncstorage/apis/__init__.py,sha256=6U-f1CkuYJ4Lbw3zPLOic57M6rMfOogb_Fl7WKWvXOo,762
|
|
15
|
+
api_client_syncstorage/model/__init__.py,sha256=lNQhU3Bvbw-e5SeWD-O5bNV3osq2Oqrrdtgv66_zP2E,356
|
|
16
|
+
api_client_syncstorage/model/completion_response.py,sha256=pDLOFYaRCcae4lKn41946Dz55SCL5he56rf65khQ_oc,11649
|
|
17
|
+
api_client_syncstorage/model/health_status_response.py,sha256=5QdakodopMAkglgAClDlc1GrQ6YTP8UIX68nz36_PZM,11672
|
|
18
|
+
api_client_syncstorage/model/register_request.py,sha256=QypQd9875gu6vDIZnpbfxZtreFcGrxTYb_PPHtOtbVQ,12044
|
|
19
|
+
api_client_syncstorage/model/register_response.py,sha256=bPYwzcmd420iujmD8rPqQGwnzLF4L0SjvF5OCgbgAJk,12215
|
|
20
|
+
api_client_syncstorage/model/set_worker_status_request.py,sha256=PQ3zmajfxm4apHOBeW-XUUCLOPCBjSPCgFOmAdQPPOo,11834
|
|
21
|
+
api_client_syncstorage/model/set_worker_status_response.py,sha256=r1yLLLJHq7_eA5wi3fX0n_PC47hjUn5s2B1eCPBJZ2Q,11646
|
|
22
|
+
api_client_syncstorage/model/shutdown_response.py,sha256=vswOqfTGQUtwGCyVUOkG3NGQwigRoWCW-eiK5mZSB08,11625
|
|
23
|
+
api_client_syncstorage/model/test_result_cut_api_model.py,sha256=DU48FsOjf9Feey9uhx-XI9hTFdfZISTd0OIzSTy3gX0,12405
|
|
24
|
+
api_client_syncstorage/model/test_result_save_response.py,sha256=Q7J6WD6Yzx6TWYOszU-0Qb3IkKRXUYWmvFiRQ3zGzGk,11643
|
|
25
|
+
api_client_syncstorage/models/__init__.py,sha256=-Wa3--s7VgbP9o6_RY33g1lXm5J-1PSqLw7VcpPZsaY,1131
|
|
26
|
+
testit_python_commons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
+
testit_python_commons/app_properties.py,sha256=0o7G24OMdcyv7eZUAaZjyvuEp1z9QVVlnDMIra-wXQQ,9891
|
|
28
|
+
testit_python_commons/decorators.py,sha256=QI8qMjDincTRZmcZByP5y5wHsf_Fwd1oc67WuWAacCA,5988
|
|
29
|
+
testit_python_commons/dynamic_methods.py,sha256=WKHHA6Uj61tWXI3DphALYOi2BV6TnQKvQy0-Vt2JR2I,8816
|
|
30
|
+
testit_python_commons/step.py,sha256=CYUs_S8opmRg1tsTY1dVJOPzzt-HYR3_uLTBpYtHSfQ,3244
|
|
31
|
+
testit_python_commons/client/__init__.py,sha256=zSJhxDHzWWTFHJ0s8bqCaBtM5FK2GcPgZxaKdI8zf-o,165
|
|
32
|
+
testit_python_commons/client/api_client.py,sha256=9UWO-4l_nLwhvSjksMdgyB6W4dPaufgjAn-ScQauVbc,22082
|
|
33
|
+
testit_python_commons/client/client_configuration.py,sha256=1M-OrROU3Xj0YGsUX4venG-l6Ewr43LNvUcyFfY57ys,2776
|
|
34
|
+
testit_python_commons/client/converter.py,sha256=R_BYE2qjJIRAuV2bDzlWD3zLbhi2QDTfTRjGVG-vqLw,23966
|
|
35
|
+
testit_python_commons/client/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
36
|
+
testit_python_commons/client/helpers/bulk_autotest_helper.py,sha256=a3vwfdfzzvAnxY0s6o-WHkohKjNwogEAkmU1W81G-Bk,10805
|
|
37
|
+
testit_python_commons/client/helpers/threads_manager.py,sha256=TUJmvem5WMPWjZR-Hl0gzaiE49iRPkkCHNBBIsJbkcE,3507
|
|
38
|
+
testit_python_commons/client/models/__init__.py,sha256=4c3yNkadtebFF1eEZw7JmfwcLtOim2zHkwbZIHvCK0A,284
|
|
39
|
+
testit_python_commons/client/models/threads_for_create_and_result.py,sha256=AsasSnjECjBOgTryk6MqDp2AGHsQd1AfiwL7McAqYa4,1456
|
|
40
|
+
testit_python_commons/client/models/threads_for_update_and_result.py,sha256=IFPQ4YHjzj1eG6W_bSYMm6ic2oGThrliNtW_lKSIiAE,2127
|
|
41
|
+
testit_python_commons/configurations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
42
|
+
testit_python_commons/configurations/properties_names.py,sha256=DcMkFmJuyvRXylLjP3HgJTjiaXsNSJ0ZmQjKYgM1v00,2364
|
|
43
|
+
testit_python_commons/models/__init__.py,sha256=qtwpjoKOOaTCiRHiG0TlOWWQ26AFw7dumt4yV-u0LcE,90
|
|
44
|
+
testit_python_commons/models/adapter_mode.py,sha256=4U3IlRvjoDCe6WJ6AS4HaiP2nS9ntXIMOUmKGKcblWE,87
|
|
45
|
+
testit_python_commons/models/fixture.py,sha256=_KO-sWKHloHY1MFowp1anE3Dp5-ebdUeO8AAsLMNids,661
|
|
46
|
+
testit_python_commons/models/link.py,sha256=7TX7APxgRHMk6OJBGWrkcRs3GOiOcBKV1379PfOIl9k,1258
|
|
47
|
+
testit_python_commons/models/link_type.py,sha256=n5XaVXu4s2KQDp9vPaO10wRKSfm4NlAYyFFqKg9yIMM,211
|
|
48
|
+
testit_python_commons/models/outcome_type.py,sha256=WAAuPRTopmYPczd-Gd0IrE7SGDZOIZKFDwxkywVrTJM,111
|
|
49
|
+
testit_python_commons/models/status_type.py,sha256=lm4Hp02Z5Wo1mOaaMDjzBD1W0BE6gtwjBMPdhxuO_5U,152
|
|
50
|
+
testit_python_commons/models/step_result.py,sha256=T9BbcPB6BCz3ZkrWXy2Z2Guj2wecvKJX1oIrG-yJH4I,2209
|
|
51
|
+
testit_python_commons/models/test_result.py,sha256=z6OB3V_whCQDJvr4jQdgVanoJSoeKh8ZXgPAACkNrdw,7515
|
|
52
|
+
testit_python_commons/models/test_result_with_all_fixture_step_results_model.py,sha256=aI5nfJ_4EAhzZg7eIV1JhSf9i3EENiRiuQuqJvSUs3M,866
|
|
53
|
+
testit_python_commons/services/__init__.py,sha256=yVVmiCgMyike_8OoNPiHLNoMqKhCDlWYhvmGW0IRHJM,1197
|
|
54
|
+
testit_python_commons/services/adapter_manager.py,sha256=imAogzM7IVv1970lyz3sQXbPvxzVF6nWeKXu4NWVMKE,11435
|
|
55
|
+
testit_python_commons/services/adapter_manager_configuration.py,sha256=KyLhB_iNUQaVEurfLQ0RANIZfhJPB19c_WUkGpqf0xo,1619
|
|
56
|
+
testit_python_commons/services/fixture_manager.py,sha256=PCSLBEPa3yJVbCPsMt_Y5badWLAhKrJsZTaUesq7oNI,2006
|
|
57
|
+
testit_python_commons/services/fixture_storage.py,sha256=nAIW0p9lVvVWtiVdFNqaD7-gCBislY8DLrfdlh8OqFw,1205
|
|
58
|
+
testit_python_commons/services/logger.py,sha256=E4LmykJeEHPIzuRjJShAuo2WvObfBBwOgBjYnaQi4C4,810
|
|
59
|
+
testit_python_commons/services/plugin_manager.py,sha256=IIPJZSCJrOKQWfm0kmL9jWG9XbXB2mX-Zb4GNjuJcIA,3491
|
|
60
|
+
testit_python_commons/services/retry.py,sha256=bXyX3YmQbjDIQVi9OH0Q4xZm7zKUx0vLcDNV2dQya6k,2470
|
|
61
|
+
testit_python_commons/services/step_manager.py,sha256=pKTtEgpwOS_KJV_9kcBmKA8WRJkWvvdye_Y0ot0AKw0,1103
|
|
62
|
+
testit_python_commons/services/step_result_storage.py,sha256=4axfi2dnP3LABlEql9k6lydftOgDFV3c2cXN-1uEvXU,633
|
|
63
|
+
testit_python_commons/services/utils.py,sha256=5LBymIxEC1AKtGQz-S1-ipo3OYR-JzRUAC4WWFQylHc,4060
|
|
64
|
+
testit_python_commons/services/sync_storage/__init__.py,sha256=Qmjs6Xk5l-i5FFItk0-XPrlVK_HkDuX6FD8FID1iKu0,154
|
|
65
|
+
testit_python_commons/services/sync_storage/sync_storage_runner.py,sha256=93OXrGlGKik9359j8novwtG_hud8VL_Ksznts8sRdjE,20546
|
|
66
|
+
testit_python_commons/utils/__init__.py,sha256=CUHziylwvFAuKyUM1tCp13ik5Z2Mw-VK8n7c92aVy-I,78
|
|
67
|
+
testit_python_commons/utils/html_escape_utils.py,sha256=wx20lAVrMvhNiQ7bEu_f-SGqDBp85D-EBL2sgTmnJPY,8135
|
|
68
|
+
testit_python_commons-4.2.8.dist-info/METADATA,sha256=dn4GF2OOlJh4U9g4LCq9KgWnHfccCHZFjyBM5cRXcyg,1662
|
|
69
|
+
testit_python_commons-4.2.8.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
70
|
+
testit_python_commons-4.2.8.dist-info/top_level.txt,sha256=4uNwwrEWiX5vbjEzjHv6VhG67pQlpitKiuCjeSAwCkU,52
|
|
71
|
+
testit_python_commons-4.2.8.dist-info/RECORD,,
|