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,297 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
|
|
3
|
+
from testit_python_commons.models.link import Link
|
|
4
|
+
from testit_python_commons.models.step_result import StepResult
|
|
5
|
+
from testit_python_commons.services.logger import adapter_logger
|
|
6
|
+
from testit_python_commons.utils import HtmlEscapeUtils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestResult:
|
|
10
|
+
__external_id: str = None
|
|
11
|
+
__autotest_name: str = None
|
|
12
|
+
__outcome: str = None
|
|
13
|
+
__status_type: str = None
|
|
14
|
+
__title: str = None
|
|
15
|
+
__description: str = None
|
|
16
|
+
__duration: int = None
|
|
17
|
+
__started_on: str = None
|
|
18
|
+
__completed_on: str = None
|
|
19
|
+
__namespace: str = None
|
|
20
|
+
__classname: str = None
|
|
21
|
+
__message: str = None
|
|
22
|
+
__traces: str = None
|
|
23
|
+
__step_results: List[StepResult] = []
|
|
24
|
+
__setup_results: List[StepResult] = []
|
|
25
|
+
__teardown_results: List[StepResult] = []
|
|
26
|
+
__links: List[Link] = []
|
|
27
|
+
__result_links: List[Link] = []
|
|
28
|
+
__attachments: List[str] = []
|
|
29
|
+
__labels: List[str] = []
|
|
30
|
+
__tags: List[str] = []
|
|
31
|
+
__work_item_ids: List[str] = []
|
|
32
|
+
__parameters: dict = {}
|
|
33
|
+
__properties: dict = {}
|
|
34
|
+
__automatic_creation_test_cases: bool = False
|
|
35
|
+
__external_key: str = None
|
|
36
|
+
|
|
37
|
+
@adapter_logger
|
|
38
|
+
def set_external_id(self, external_id: str):
|
|
39
|
+
# don't use escaping for externalId
|
|
40
|
+
# self.__external_id = HtmlEscapeUtils.escape_html_tags(external_id)
|
|
41
|
+
self.__external_id = external_id
|
|
42
|
+
|
|
43
|
+
return self
|
|
44
|
+
|
|
45
|
+
@adapter_logger
|
|
46
|
+
def get_external_id(self) -> str:
|
|
47
|
+
return self.__external_id
|
|
48
|
+
|
|
49
|
+
@adapter_logger
|
|
50
|
+
def set_autotest_name(self, autotest_name: str):
|
|
51
|
+
self.__autotest_name = HtmlEscapeUtils.escape_html_tags(autotest_name)
|
|
52
|
+
|
|
53
|
+
return self
|
|
54
|
+
|
|
55
|
+
@adapter_logger
|
|
56
|
+
def get_autotest_name(self) -> str:
|
|
57
|
+
return self.__autotest_name
|
|
58
|
+
|
|
59
|
+
@adapter_logger
|
|
60
|
+
def set_outcome(self, outcome: str):
|
|
61
|
+
self.__outcome = HtmlEscapeUtils.escape_html_tags(outcome)
|
|
62
|
+
|
|
63
|
+
return self
|
|
64
|
+
|
|
65
|
+
@adapter_logger
|
|
66
|
+
def get_outcome(self) -> str:
|
|
67
|
+
return self.__outcome
|
|
68
|
+
|
|
69
|
+
@adapter_logger
|
|
70
|
+
def set_status_type(self, status_type: str):
|
|
71
|
+
self.__status_type = HtmlEscapeUtils.escape_html_tags(status_type)
|
|
72
|
+
|
|
73
|
+
return self
|
|
74
|
+
|
|
75
|
+
@adapter_logger
|
|
76
|
+
def get_status_type(self) -> str:
|
|
77
|
+
return self.__status_type
|
|
78
|
+
|
|
79
|
+
@adapter_logger
|
|
80
|
+
def set_title(self, title: str):
|
|
81
|
+
self.__title = HtmlEscapeUtils.escape_html_tags(title)
|
|
82
|
+
|
|
83
|
+
return self
|
|
84
|
+
|
|
85
|
+
@adapter_logger
|
|
86
|
+
def get_title(self) -> str:
|
|
87
|
+
return self.__title
|
|
88
|
+
|
|
89
|
+
@adapter_logger
|
|
90
|
+
def set_description(self, description: str):
|
|
91
|
+
self.__description = HtmlEscapeUtils.escape_html_tags(description)
|
|
92
|
+
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
@adapter_logger
|
|
96
|
+
def get_description(self) -> str:
|
|
97
|
+
return self.__description
|
|
98
|
+
|
|
99
|
+
@adapter_logger
|
|
100
|
+
def set_duration(self, duration: int):
|
|
101
|
+
self.__duration = duration
|
|
102
|
+
|
|
103
|
+
return self
|
|
104
|
+
|
|
105
|
+
@adapter_logger
|
|
106
|
+
def get_duration(self) -> int:
|
|
107
|
+
return self.__duration
|
|
108
|
+
|
|
109
|
+
@adapter_logger
|
|
110
|
+
def set_started_on(self, started_on: str):
|
|
111
|
+
self.__started_on = started_on
|
|
112
|
+
|
|
113
|
+
return self
|
|
114
|
+
|
|
115
|
+
@adapter_logger
|
|
116
|
+
def get_started_on(self) -> str:
|
|
117
|
+
return self.__started_on
|
|
118
|
+
|
|
119
|
+
@adapter_logger
|
|
120
|
+
def set_completed_on(self, completed_on: str):
|
|
121
|
+
self.__completed_on = completed_on
|
|
122
|
+
|
|
123
|
+
return self
|
|
124
|
+
|
|
125
|
+
@adapter_logger
|
|
126
|
+
def get_completed_on(self) -> str:
|
|
127
|
+
return self.__completed_on
|
|
128
|
+
|
|
129
|
+
@adapter_logger
|
|
130
|
+
def set_namespace(self, namespace: str):
|
|
131
|
+
self.__namespace = HtmlEscapeUtils.escape_html_tags(namespace)
|
|
132
|
+
|
|
133
|
+
return self
|
|
134
|
+
|
|
135
|
+
@adapter_logger
|
|
136
|
+
def get_namespace(self) -> str:
|
|
137
|
+
return self.__namespace
|
|
138
|
+
|
|
139
|
+
@adapter_logger
|
|
140
|
+
def set_classname(self, classname: str):
|
|
141
|
+
self.__classname = HtmlEscapeUtils.escape_html_tags(classname)
|
|
142
|
+
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
@adapter_logger
|
|
146
|
+
def get_classname(self) -> str:
|
|
147
|
+
return self.__classname
|
|
148
|
+
|
|
149
|
+
@adapter_logger
|
|
150
|
+
def set_message(self, message: str):
|
|
151
|
+
self.__message = HtmlEscapeUtils.escape_html_tags(message)
|
|
152
|
+
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
@adapter_logger
|
|
156
|
+
def get_message(self) -> str:
|
|
157
|
+
return self.__message
|
|
158
|
+
|
|
159
|
+
@adapter_logger
|
|
160
|
+
def set_traces(self, traces: str):
|
|
161
|
+
self.__traces = HtmlEscapeUtils.escape_html_tags(traces)
|
|
162
|
+
|
|
163
|
+
return self
|
|
164
|
+
|
|
165
|
+
@adapter_logger
|
|
166
|
+
def get_traces(self) -> str:
|
|
167
|
+
return self.__traces
|
|
168
|
+
|
|
169
|
+
@adapter_logger
|
|
170
|
+
def set_step_results(self, step_results: list):
|
|
171
|
+
self.__step_results = step_results
|
|
172
|
+
|
|
173
|
+
return self
|
|
174
|
+
|
|
175
|
+
@adapter_logger
|
|
176
|
+
def get_step_results(self) -> List[StepResult]:
|
|
177
|
+
return self.__step_results
|
|
178
|
+
|
|
179
|
+
@adapter_logger
|
|
180
|
+
def set_setup_results(self, setup_results: list):
|
|
181
|
+
self.__setup_results = setup_results
|
|
182
|
+
|
|
183
|
+
return self
|
|
184
|
+
|
|
185
|
+
@adapter_logger
|
|
186
|
+
def get_setup_results(self) -> List[StepResult]:
|
|
187
|
+
return self.__setup_results
|
|
188
|
+
|
|
189
|
+
@adapter_logger
|
|
190
|
+
def set_teardown_results(self, teardown_results: list):
|
|
191
|
+
self.__teardown_results = teardown_results
|
|
192
|
+
|
|
193
|
+
return self
|
|
194
|
+
|
|
195
|
+
@adapter_logger
|
|
196
|
+
def get_teardown_results(self) -> List[StepResult]:
|
|
197
|
+
return self.__teardown_results
|
|
198
|
+
|
|
199
|
+
@adapter_logger
|
|
200
|
+
def set_links(self, links: list):
|
|
201
|
+
self.__links = links
|
|
202
|
+
|
|
203
|
+
return self
|
|
204
|
+
|
|
205
|
+
@adapter_logger
|
|
206
|
+
def get_links(self) -> list:
|
|
207
|
+
return self.__links
|
|
208
|
+
|
|
209
|
+
@adapter_logger
|
|
210
|
+
def set_result_links(self, result_links: list):
|
|
211
|
+
self.__result_links = result_links
|
|
212
|
+
|
|
213
|
+
return self
|
|
214
|
+
|
|
215
|
+
@adapter_logger
|
|
216
|
+
def get_result_links(self) -> list:
|
|
217
|
+
return self.__result_links
|
|
218
|
+
|
|
219
|
+
@adapter_logger
|
|
220
|
+
def set_attachments(self, attachments: list):
|
|
221
|
+
self.__attachments = attachments
|
|
222
|
+
|
|
223
|
+
return self
|
|
224
|
+
|
|
225
|
+
@adapter_logger
|
|
226
|
+
def get_attachments(self) -> list:
|
|
227
|
+
return self.__attachments
|
|
228
|
+
|
|
229
|
+
@adapter_logger
|
|
230
|
+
def set_labels(self, labels: list):
|
|
231
|
+
self.__labels = HtmlEscapeUtils.escape_html_in_object(labels)
|
|
232
|
+
|
|
233
|
+
return self
|
|
234
|
+
|
|
235
|
+
@adapter_logger
|
|
236
|
+
def get_labels(self) -> list:
|
|
237
|
+
return self.__labels
|
|
238
|
+
|
|
239
|
+
@adapter_logger
|
|
240
|
+
def set_tags(self, tags: list):
|
|
241
|
+
self.__tags = HtmlEscapeUtils.escape_html_in_object(tags)
|
|
242
|
+
|
|
243
|
+
return self
|
|
244
|
+
|
|
245
|
+
@adapter_logger
|
|
246
|
+
def get_tags(self) -> list:
|
|
247
|
+
return self.__tags
|
|
248
|
+
|
|
249
|
+
@adapter_logger
|
|
250
|
+
def set_work_item_ids(self, work_item_ids: list):
|
|
251
|
+
self.__work_item_ids = work_item_ids
|
|
252
|
+
|
|
253
|
+
return self
|
|
254
|
+
|
|
255
|
+
@adapter_logger
|
|
256
|
+
def get_work_item_ids(self) -> list:
|
|
257
|
+
return self.__work_item_ids
|
|
258
|
+
|
|
259
|
+
@adapter_logger
|
|
260
|
+
def set_parameters(self, parameters: dict):
|
|
261
|
+
self.__parameters = HtmlEscapeUtils.escape_html_in_object(parameters)
|
|
262
|
+
|
|
263
|
+
return self
|
|
264
|
+
|
|
265
|
+
@adapter_logger
|
|
266
|
+
def get_parameters(self) -> dict:
|
|
267
|
+
return self.__parameters
|
|
268
|
+
|
|
269
|
+
@adapter_logger
|
|
270
|
+
def set_properties(self, properties: dict):
|
|
271
|
+
self.__properties = HtmlEscapeUtils.escape_html_in_object(properties)
|
|
272
|
+
|
|
273
|
+
return self
|
|
274
|
+
|
|
275
|
+
@adapter_logger
|
|
276
|
+
def get_properties(self) -> dict:
|
|
277
|
+
return self.__properties
|
|
278
|
+
|
|
279
|
+
@adapter_logger
|
|
280
|
+
def set_automatic_creation_test_cases(self, automatic_creation_test_cases: bool):
|
|
281
|
+
self.__automatic_creation_test_cases = automatic_creation_test_cases
|
|
282
|
+
|
|
283
|
+
return self
|
|
284
|
+
|
|
285
|
+
@adapter_logger
|
|
286
|
+
def get_automatic_creation_test_cases(self) -> bool:
|
|
287
|
+
return self.__automatic_creation_test_cases
|
|
288
|
+
|
|
289
|
+
@adapter_logger
|
|
290
|
+
def set_external_key(self, external_key: str):
|
|
291
|
+
self.__external_key = external_key
|
|
292
|
+
|
|
293
|
+
return self
|
|
294
|
+
|
|
295
|
+
@adapter_logger
|
|
296
|
+
def get_external_key(self) -> str:
|
|
297
|
+
return self.__external_key
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from testit_python_commons.models.step_result import StepResult
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class TestResultWithAllFixtureStepResults:
|
|
6
|
+
def __init__(self, test_result_id: str):
|
|
7
|
+
self.__test_result_id = test_result_id
|
|
8
|
+
self.__setup_results = []
|
|
9
|
+
self.__teardown_results = []
|
|
10
|
+
|
|
11
|
+
def get_test_result_id(self) -> str:
|
|
12
|
+
return self.__test_result_id
|
|
13
|
+
|
|
14
|
+
def set_setup_results(self, setup_results: List[StepResult]):
|
|
15
|
+
self.__setup_results += setup_results
|
|
16
|
+
|
|
17
|
+
return self
|
|
18
|
+
|
|
19
|
+
def get_setup_results(self) -> List[StepResult]:
|
|
20
|
+
return self.__setup_results
|
|
21
|
+
|
|
22
|
+
def set_teardown_results(self, teardown_results: List[StepResult]):
|
|
23
|
+
self.__teardown_results = teardown_results + self.__teardown_results
|
|
24
|
+
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
def get_teardown_results(self) -> List[StepResult]:
|
|
28
|
+
return self.__teardown_results
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from pluggy import HookimplMarker
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"AdapterManager",
|
|
5
|
+
"TmsPluginManager",
|
|
6
|
+
"FixtureManager",
|
|
7
|
+
"StepManager",
|
|
8
|
+
"Utils",
|
|
9
|
+
"SyncStorageRunner",
|
|
10
|
+
"hookimpl",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
hookimpl = HookimplMarker("testit")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def __getattr__(name):
|
|
17
|
+
if name == "AdapterManager":
|
|
18
|
+
from testit_python_commons.services.adapter_manager import AdapterManager
|
|
19
|
+
|
|
20
|
+
return AdapterManager
|
|
21
|
+
if name == "TmsPluginManager":
|
|
22
|
+
from testit_python_commons.services.plugin_manager import TmsPluginManager
|
|
23
|
+
|
|
24
|
+
return TmsPluginManager
|
|
25
|
+
if name == "FixtureManager":
|
|
26
|
+
from testit_python_commons.services.fixture_manager import FixtureManager
|
|
27
|
+
|
|
28
|
+
return FixtureManager
|
|
29
|
+
if name == "StepManager":
|
|
30
|
+
from testit_python_commons.services.step_manager import StepManager
|
|
31
|
+
|
|
32
|
+
return StepManager
|
|
33
|
+
if name == "Utils":
|
|
34
|
+
from testit_python_commons.services.utils import Utils
|
|
35
|
+
|
|
36
|
+
return Utils
|
|
37
|
+
if name == "SyncStorageRunner":
|
|
38
|
+
from testit_python_commons.services.sync_storage import SyncStorageRunner
|
|
39
|
+
|
|
40
|
+
return SyncStorageRunner
|
|
41
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import uuid
|
|
4
|
+
|
|
5
|
+
from testit_python_commons.client.api_client import ApiClientWorker
|
|
6
|
+
from testit_python_commons.client.client_configuration import ClientConfiguration
|
|
7
|
+
from testit_python_commons.models.adapter_mode import AdapterMode
|
|
8
|
+
from testit_python_commons.models.test_result import TestResult
|
|
9
|
+
from testit_python_commons.services.adapter_manager_configuration import (
|
|
10
|
+
AdapterManagerConfiguration,
|
|
11
|
+
)
|
|
12
|
+
from testit_python_commons.services.fixture_manager import FixtureManager
|
|
13
|
+
from testit_python_commons.services.logger import adapter_logger
|
|
14
|
+
from testit_python_commons.services.utils import Utils
|
|
15
|
+
|
|
16
|
+
from testit_python_commons.services.sync_storage.sync_storage_runner import (
|
|
17
|
+
SyncStorageRunner,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
SYNC_STORAGE_AVAILABLE = True
|
|
21
|
+
IN_PROGRESS_LITERAL = "InProgress"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AdapterManager:
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
adapter_configuration: AdapterManagerConfiguration,
|
|
28
|
+
client_configuration: ClientConfiguration,
|
|
29
|
+
fixture_manager: FixtureManager,
|
|
30
|
+
):
|
|
31
|
+
|
|
32
|
+
self.__config = adapter_configuration
|
|
33
|
+
self.__client_config = client_configuration
|
|
34
|
+
self.__api_client = ApiClientWorker(client_configuration)
|
|
35
|
+
self.__fixture_manager = fixture_manager
|
|
36
|
+
self.__test_result_map = {}
|
|
37
|
+
self.__test_results = []
|
|
38
|
+
|
|
39
|
+
# Sync Storage integration
|
|
40
|
+
self.__sync_storage_runner = None
|
|
41
|
+
legacy_workflow_enabled = client_configuration.is_legacy_workflow() is True
|
|
42
|
+
# Initialize Sync Storage if available and enabled
|
|
43
|
+
if SYNC_STORAGE_AVAILABLE and not legacy_workflow_enabled:
|
|
44
|
+
self.__sync_storage_runner = self._initialize_sync_storage(
|
|
45
|
+
client_configuration
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _initialize_sync_storage(self, client_configuration: ClientConfiguration):
|
|
50
|
+
"""Initialize Sync Storage runner if enabled."""
|
|
51
|
+
if client_configuration.is_legacy_workflow():
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
test_run_id = self.__config.get_test_run_id()
|
|
55
|
+
if not test_run_id and self.__config.get_mode() == AdapterMode.NEW_TEST_RUN:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
if not test_run_id:
|
|
59
|
+
logging.warning('Sync Storage requires test run ID; skipping initialization')
|
|
60
|
+
return None
|
|
61
|
+
|
|
62
|
+
return self._start_sync_storage(test_run_id, client_configuration)
|
|
63
|
+
|
|
64
|
+
def _start_sync_storage(
|
|
65
|
+
self,
|
|
66
|
+
test_run_id: str,
|
|
67
|
+
client_configuration: ClientConfiguration):
|
|
68
|
+
try:
|
|
69
|
+
if not SYNC_STORAGE_AVAILABLE:
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
sync_storage_runner = SyncStorageRunner(
|
|
73
|
+
test_run_id=test_run_id,
|
|
74
|
+
port=client_configuration.get_sync_storage_port(),
|
|
75
|
+
base_url=client_configuration.get_url(),
|
|
76
|
+
private_token=client_configuration.get_private_token(),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
if sync_storage_runner.start():
|
|
80
|
+
logging.info("Sync Storage started successfully")
|
|
81
|
+
return sync_storage_runner
|
|
82
|
+
|
|
83
|
+
logging.warning("Failed to start Sync Storage")
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logging.warning(f"Failed to initialize SyncStorage: {e}", exc_info=True)
|
|
86
|
+
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
@adapter_logger
|
|
90
|
+
def set_test_run_id(self, test_run_id: str) -> None:
|
|
91
|
+
|
|
92
|
+
self.__config.set_test_run_id(test_run_id)
|
|
93
|
+
|
|
94
|
+
self.__api_client.set_test_run_id(test_run_id)
|
|
95
|
+
|
|
96
|
+
if self.__sync_storage_runner:
|
|
97
|
+
self.__sync_storage_runner.test_run_id = test_run_id
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
if SYNC_STORAGE_AVAILABLE and not self.__client_config.is_legacy_workflow():
|
|
101
|
+
self.__sync_storage_runner = self._start_sync_storage(
|
|
102
|
+
test_run_id, self.__client_config)
|
|
103
|
+
|
|
104
|
+
@adapter_logger
|
|
105
|
+
def get_test_run_id(self) -> str:
|
|
106
|
+
if self.__config.get_mode() != AdapterMode.NEW_TEST_RUN:
|
|
107
|
+
test_run_id = self.__config.get_test_run_id()
|
|
108
|
+
|
|
109
|
+
if test_run_id:
|
|
110
|
+
self.__update_test_run_name(test_run_id)
|
|
111
|
+
|
|
112
|
+
return test_run_id or ""
|
|
113
|
+
|
|
114
|
+
result = self.__config.get_test_run_id()
|
|
115
|
+
if result is not None:
|
|
116
|
+
return result
|
|
117
|
+
|
|
118
|
+
# create if not exists
|
|
119
|
+
test_run_name = self.__config.get_test_run_name()
|
|
120
|
+
return self.__api_client.create_test_run(test_run_name if test_run_name else "")
|
|
121
|
+
|
|
122
|
+
@adapter_logger
|
|
123
|
+
def __update_test_run_name(self, test_run_id: str) -> None:
|
|
124
|
+
test_run_name = self.__config.get_test_run_name()
|
|
125
|
+
|
|
126
|
+
if not test_run_name:
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
test_run = self.__api_client.get_test_run(test_run_id)
|
|
130
|
+
|
|
131
|
+
if test_run_name == test_run.name:
|
|
132
|
+
return
|
|
133
|
+
|
|
134
|
+
test_run.name = test_run_name
|
|
135
|
+
|
|
136
|
+
self.__api_client.update_test_run(test_run)
|
|
137
|
+
|
|
138
|
+
@adapter_logger
|
|
139
|
+
def get_autotests_for_launch(self):
|
|
140
|
+
if self.__config.get_mode() == AdapterMode.USE_FILTER:
|
|
141
|
+
return self.__api_client.get_external_ids_for_test_run_id()
|
|
142
|
+
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
@adapter_logger
|
|
146
|
+
def write_test(self, test_result: TestResult) -> None:
|
|
147
|
+
if self.__config.should_import_realtime():
|
|
148
|
+
self.__write_test_realtime(test_result)
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
# for realtime false
|
|
152
|
+
# if
|
|
153
|
+
logging.debug("Is already in progress: " + str(self.__is_already_in_progress()))
|
|
154
|
+
# Handle Sync Storage integration if available
|
|
155
|
+
# Check if current worker is master and no test is in progress
|
|
156
|
+
if self.__is_active_syncstorage_instance() and self.__is_master_and_no_in_progress():
|
|
157
|
+
logging.debug(f"Outcome: {test_result.get_outcome()}")
|
|
158
|
+
is_ok = self.on_master_no_already_in_progress(test_result)
|
|
159
|
+
if is_ok:
|
|
160
|
+
return
|
|
161
|
+
# else continue normal processing
|
|
162
|
+
|
|
163
|
+
self.__test_results.append(test_result)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@adapter_logger
|
|
167
|
+
def on_master_no_already_in_progress(self, test_result: TestResult) -> bool:
|
|
168
|
+
# Convert TestResult to TestResultCutApiModel
|
|
169
|
+
|
|
170
|
+
project_id = self.__client_config.get_project_id()
|
|
171
|
+
tr_cut_api_model = SyncStorageRunner.test_result_to_test_result_cut_api_model(
|
|
172
|
+
test_result, project_id
|
|
173
|
+
)
|
|
174
|
+
logging.debug("Set as in progress status_code, auto_test_external_id: "
|
|
175
|
+
+ tr_cut_api_model.status_code + " " + tr_cut_api_model.auto_test_external_id)
|
|
176
|
+
logging.debug("Started_on: " + str(test_result.get_started_on()))
|
|
177
|
+
|
|
178
|
+
# Send test result to Sync Storage
|
|
179
|
+
success = self.__sync_storage_runner.send_in_progress_test_result(
|
|
180
|
+
tr_cut_api_model
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if not success:
|
|
184
|
+
return False
|
|
185
|
+
|
|
186
|
+
# Set the in-progress flag in the runner
|
|
187
|
+
self.__sync_storage_runner.set_is_already_in_progress(True)
|
|
188
|
+
|
|
189
|
+
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)
|
|
193
|
+
self._write_test_realtime_internal(test_result)
|
|
194
|
+
return True
|
|
195
|
+
except Exception as e:
|
|
196
|
+
logging.warning(
|
|
197
|
+
f"Error in Sync Storage handling, falling back to normal processing: {e}"
|
|
198
|
+
)
|
|
199
|
+
# Reset in-progress flag and continue with normal processing
|
|
200
|
+
self.__sync_storage_runner.set_is_already_in_progress(False)
|
|
201
|
+
return False
|
|
202
|
+
|
|
203
|
+
@adapter_logger
|
|
204
|
+
def __write_test_realtime(self, test_result: TestResult) -> None:
|
|
205
|
+
|
|
206
|
+
logging.debug("Is already in progress: " + str(self.__is_already_in_progress()))
|
|
207
|
+
|
|
208
|
+
# Handle Sync Storage integration if available
|
|
209
|
+
if self.__is_active_syncstorage_instance() and self.__is_master_and_no_in_progress():
|
|
210
|
+
# Check if current worker is master and no test is in progress
|
|
211
|
+
is_ok = self.on_master_no_already_in_progress(test_result)
|
|
212
|
+
if is_ok:
|
|
213
|
+
return
|
|
214
|
+
# else continue normal processing
|
|
215
|
+
|
|
216
|
+
# Normal processing (also fallback for Sync Storage errors)
|
|
217
|
+
self._write_test_realtime_internal(test_result)
|
|
218
|
+
|
|
219
|
+
def _write_test_realtime_internal(self, test_result: TestResult) -> None:
|
|
220
|
+
"""Internal method for writing test results to maintain clean separation."""
|
|
221
|
+
test_result.set_automatic_creation_test_cases(
|
|
222
|
+
self.__config.should_automatic_creation_test_cases()
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
ext_id = test_result.get_external_id()
|
|
226
|
+
test_result_id = self.__api_client.write_test(test_result)
|
|
227
|
+
if ext_id is None or test_result_id is None:
|
|
228
|
+
logging.warning("test_result got empty external_id or test_result_id")
|
|
229
|
+
logging.warning(test_result)
|
|
230
|
+
return
|
|
231
|
+
|
|
232
|
+
self.__test_result_map[ext_id] = test_result_id
|
|
233
|
+
|
|
234
|
+
@adapter_logger
|
|
235
|
+
def write_tests(self) -> None:
|
|
236
|
+
if self.__config.should_import_realtime():
|
|
237
|
+
self.__load_setup_and_teardown_step_results()
|
|
238
|
+
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
self.__write_tests_after_all()
|
|
242
|
+
|
|
243
|
+
@adapter_logger
|
|
244
|
+
def __load_setup_and_teardown_step_results(self) -> None:
|
|
245
|
+
fixtures = self.__fixture_manager.get_all_items()
|
|
246
|
+
if not fixtures:
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
self.__api_client.update_test_results(fixtures, self.__test_result_map)
|
|
250
|
+
|
|
251
|
+
@adapter_logger
|
|
252
|
+
def __write_tests_after_all(self) -> None:
|
|
253
|
+
|
|
254
|
+
fixtures = self.__fixture_manager.get_all_items()
|
|
255
|
+
|
|
256
|
+
# Ensure this option is propagated for each buffered test result in bulk mode.
|
|
257
|
+
should_create_work_item = self.__config.should_automatic_creation_test_cases()
|
|
258
|
+
for test_result in self.__test_results:
|
|
259
|
+
test_result.set_automatic_creation_test_cases(should_create_work_item)
|
|
260
|
+
|
|
261
|
+
self.__api_client.write_tests(self.__test_results, fixtures)
|
|
262
|
+
|
|
263
|
+
@adapter_logger
|
|
264
|
+
def load_attachments(self, attach_paths):
|
|
265
|
+
return self.__api_client.load_attachments(attach_paths)
|
|
266
|
+
|
|
267
|
+
@adapter_logger
|
|
268
|
+
def create_attachment(self, body, name: str):
|
|
269
|
+
if name is None:
|
|
270
|
+
name = str(uuid.uuid4()) + "-attachment.txt"
|
|
271
|
+
|
|
272
|
+
path = os.path.join(os.path.abspath(""), name)
|
|
273
|
+
|
|
274
|
+
with open(path, "wb") as attached_file:
|
|
275
|
+
attached_file.write(Utils.convert_body_of_attachment(body))
|
|
276
|
+
|
|
277
|
+
attachment_id = self.__api_client.load_attachments((path,))
|
|
278
|
+
|
|
279
|
+
os.remove(path)
|
|
280
|
+
|
|
281
|
+
return attachment_id
|
|
282
|
+
|
|
283
|
+
@adapter_logger
|
|
284
|
+
def on_block_completed(self):
|
|
285
|
+
self.set_worker_status("completed")
|
|
286
|
+
|
|
287
|
+
@adapter_logger
|
|
288
|
+
def on_running_started(self):
|
|
289
|
+
self.set_worker_status("in_progress")
|
|
290
|
+
|
|
291
|
+
@adapter_logger
|
|
292
|
+
def set_worker_status(self, status: str):
|
|
293
|
+
logging.debug(f"Set worker_status to {status}")
|
|
294
|
+
if not self.__is_active_syncstorage_instance():
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
self.__sync_storage_runner.set_worker_status(status)
|
|
298
|
+
|
|
299
|
+
def is_realtime(self):
|
|
300
|
+
return self.__config.should_import_realtime()
|
|
301
|
+
|
|
302
|
+
def __is_active_syncstorage_instance(self):
|
|
303
|
+
return (self.__sync_storage_runner
|
|
304
|
+
and self.__sync_storage_runner.is_running_status()
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
def __is_master_and_no_in_progress(self) -> bool:
|
|
308
|
+
return (
|
|
309
|
+
self.__sync_storage_runner is not None
|
|
310
|
+
and self.__sync_storage_runner.is_master_worker()
|
|
311
|
+
and not self.__sync_storage_runner.is_already_in_progress_flag()
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
def __is_already_in_progress(self) -> bool:
|
|
315
|
+
return (
|
|
316
|
+
self.__sync_storage_runner is not None
|
|
317
|
+
and self.__sync_storage_runner.is_already_in_progress_flag()
|
|
318
|
+
)
|