qase-python-commons 3.0.3__tar.gz → 3.1.0b2__tar.gz
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.
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/PKG-INFO +2 -2
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/pyproject.toml +2 -2
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/client/api_v1_client.py +19 -10
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/client/api_v2_client.py +13 -7
- qase_python_commons-3.1.0b2/src/qase/commons/config.py +219 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/api.py +15 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/batch.py +13 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/connection.py +24 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/framework.py +18 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/plan.py +8 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/qaseconfig.py +65 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/report.py +21 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/run.py +23 -0
- qase_python_commons-3.1.0b2/src/qase/commons/models/config/testops.py +31 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/core.py +30 -28
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/report.py +12 -10
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/testops.py +15 -14
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase_python_commons.egg-info/PKG-INFO +2 -2
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase_python_commons.egg-info/SOURCES.txt +9 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase_python_commons.egg-info/requires.txt +1 -1
- qase_python_commons-3.0.3/src/qase/commons/config.py +0 -66
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/README.md +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/setup.cfg +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/__init__.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/client/base_api_client.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/exceptions/reporter.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/loader.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/logger.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/__init__.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/attachment.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/basemodel.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/relation.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/result.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/run.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/runtime.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/step.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/suite.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/__init__.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/db.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/network.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/sleep.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/__init__.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/utils.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/validators/base.py +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase_python_commons.egg-info/dependency_links.txt +0 -0
- {qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase_python_commons.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qase-python-commons
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0b2
|
|
4
4
|
Summary: A library for Qase TestOps and Qase Report
|
|
5
5
|
Author-email: Qase Team <support@qase.io>
|
|
6
6
|
Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/master/qase-python-commons
|
|
@@ -22,7 +22,7 @@ Requires-Python: >=3.7
|
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
Requires-Dist: certifi>=2024.2.2
|
|
24
24
|
Requires-Dist: attrs>=23.2.0
|
|
25
|
-
Requires-Dist: qase-api-client~=1.0.
|
|
25
|
+
Requires-Dist: qase-api-client~=1.0.1
|
|
26
26
|
Requires-Dist: qase-api-v2-client~=1.0.0
|
|
27
27
|
Requires-Dist: more_itertools
|
|
28
28
|
Provides-Extra: testing
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qase-python-commons"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.1.0b2"
|
|
8
8
|
description = "A library for Qase TestOps and Qase Report"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{name = "Qase Team", email = "support@qase.io"}]
|
|
@@ -30,7 +30,7 @@ requires-python = ">=3.7"
|
|
|
30
30
|
dependencies = [
|
|
31
31
|
"certifi>=2024.2.2",
|
|
32
32
|
"attrs>=23.2.0",
|
|
33
|
-
"qase-api-client~=1.0.
|
|
33
|
+
"qase-api-client~=1.0.1",
|
|
34
34
|
"qase-api-v2-client~=1.0.0",
|
|
35
35
|
"more_itertools"
|
|
36
36
|
]
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/client/api_v1_client.py
RENAMED
|
@@ -4,30 +4,31 @@ import certifi
|
|
|
4
4
|
from qase.api_client_v1 import ApiClient, ProjectsApi, Project, EnvironmentsApi, RunsApi, AttachmentsApi, \
|
|
5
5
|
AttachmentGet, RunCreate, ResultsApi, ResultcreateBulk
|
|
6
6
|
from qase.api_client_v1.configuration import Configuration
|
|
7
|
-
from .. import
|
|
7
|
+
from .. import Logger
|
|
8
8
|
from .base_api_client import BaseApiClient
|
|
9
9
|
from ..exceptions.reporter import ReporterException
|
|
10
10
|
from ..models import Attachment, Result, Step
|
|
11
|
+
from ..models.config.qaseconfig import QaseConfig
|
|
11
12
|
from ..models.step import StepType
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class ApiV1Client(BaseApiClient):
|
|
15
|
-
def __init__(self, config:
|
|
16
|
+
def __init__(self, config: QaseConfig, logger: Logger):
|
|
16
17
|
self.logger = logger
|
|
17
18
|
self.config = config
|
|
18
19
|
|
|
19
20
|
try:
|
|
20
21
|
self.logger.log_debug("Preparing API client")
|
|
21
22
|
configuration = Configuration()
|
|
22
|
-
configuration.api_key['TokenAuth'] = self.config.
|
|
23
|
+
configuration.api_key['TokenAuth'] = self.config.testops.api.token
|
|
23
24
|
configuration.ssl_ca_cert = certifi.where()
|
|
24
|
-
host = self.config.
|
|
25
|
-
if
|
|
26
|
-
configuration.host = f'https://api-{host}/v1'
|
|
27
|
-
self.web = f'https://{host}'
|
|
28
|
-
else:
|
|
25
|
+
host = self.config.testops.api.host
|
|
26
|
+
if host == 'qase.io':
|
|
29
27
|
configuration.host = f'https://api.{host}/v1'
|
|
30
28
|
self.web = f'https://app.{host}'
|
|
29
|
+
else:
|
|
30
|
+
configuration.host = f'https://api-{host}/v1'
|
|
31
|
+
self.web = f'https://{host}'
|
|
31
32
|
|
|
32
33
|
self.client = ApiClient(configuration)
|
|
33
34
|
self.logger.log_debug("API client prepared")
|
|
@@ -165,8 +166,16 @@ class ApiV1Client(BaseApiClient):
|
|
|
165
166
|
if result.get_field('layer'):
|
|
166
167
|
case_data["layer"] = result.get_field('layer')
|
|
167
168
|
|
|
169
|
+
suite = None
|
|
168
170
|
if result.get_suite_title():
|
|
169
|
-
|
|
171
|
+
suite = "\t".join(result.get_suite_title().split("."))
|
|
172
|
+
|
|
173
|
+
root_suite = self.config.root_suite
|
|
174
|
+
if root_suite:
|
|
175
|
+
suite = f"{root_suite}\t{suite}"
|
|
176
|
+
|
|
177
|
+
if suite:
|
|
178
|
+
case_data["suite"] = suite
|
|
170
179
|
|
|
171
180
|
result_model = {
|
|
172
181
|
"status": result.execution.status,
|
|
@@ -176,7 +185,7 @@ class ApiV1Client(BaseApiClient):
|
|
|
176
185
|
"attachments": [attach.hash for attach in attached],
|
|
177
186
|
"steps": steps,
|
|
178
187
|
"param": result.params,
|
|
179
|
-
"defect": self.config.
|
|
188
|
+
"defect": self.config.testops.defect,
|
|
180
189
|
}
|
|
181
190
|
|
|
182
191
|
test_ops_id = result.get_testops_id()
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/client/api_v2_client.py
RENAMED
|
@@ -14,26 +14,29 @@ from qase.api_client_v2.models.result_step_status import ResultStepStatus
|
|
|
14
14
|
from qase.api_client_v2.models.result_steps_type import ResultStepsType
|
|
15
15
|
|
|
16
16
|
from .api_v1_client import ApiV1Client
|
|
17
|
-
from .. import
|
|
17
|
+
from .. import Logger
|
|
18
18
|
from ..exceptions.reporter import ReporterException
|
|
19
19
|
from ..models import Attachment, Result
|
|
20
|
+
from ..models.config.qaseconfig import QaseConfig
|
|
20
21
|
from ..models.step import StepType, Step
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class ApiV2Client(ApiV1Client):
|
|
24
|
-
def __init__(self, config:
|
|
25
|
+
def __init__(self, config: QaseConfig, logger: Logger):
|
|
25
26
|
ApiV1Client.__init__(self, config, logger)
|
|
26
27
|
|
|
27
28
|
try:
|
|
28
29
|
self.logger.log_debug("Preparing API V2 client")
|
|
29
30
|
configuration = Configuration()
|
|
30
|
-
configuration.api_key['TokenAuth'] = self.config.
|
|
31
|
+
configuration.api_key['TokenAuth'] = self.config.testops.api.token
|
|
31
32
|
configuration.ssl_ca_cert = certifi.where()
|
|
32
|
-
host = self.config.
|
|
33
|
-
if
|
|
34
|
-
configuration.host = f'https://api-{host}/v2'
|
|
35
|
-
else:
|
|
33
|
+
host = self.config.testops.api.host
|
|
34
|
+
if host == 'qase.io':
|
|
36
35
|
configuration.host = f'https://api.{host}/v2'
|
|
36
|
+
self.web = f'https://app.{host}'
|
|
37
|
+
else:
|
|
38
|
+
configuration.host = f'https://api-{host}/v2'
|
|
39
|
+
self.web = f'https://{host}'
|
|
37
40
|
|
|
38
41
|
self.client_v2 = ApiClient(configuration)
|
|
39
42
|
self.logger.log_debug("API V2 client prepared")
|
|
@@ -83,6 +86,9 @@ class ApiV2Client(ApiV1Client):
|
|
|
83
86
|
|
|
84
87
|
if result.get_suite_title():
|
|
85
88
|
data = []
|
|
89
|
+
root_suite = self.config.root_suite
|
|
90
|
+
if root_suite:
|
|
91
|
+
data.append(RelationSuiteItem(title=root_suite))
|
|
86
92
|
|
|
87
93
|
for suite in result.get_suite_title().split("."):
|
|
88
94
|
data.append(RelationSuiteItem(title=suite))
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
from .logger import Logger
|
|
4
|
+
from .models.config.qaseconfig import QaseConfig, Mode
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ConfigManager:
|
|
8
|
+
|
|
9
|
+
def __init__(self, config_file='./qase.config.json'):
|
|
10
|
+
self.logger = Logger()
|
|
11
|
+
self.__config_file = config_file
|
|
12
|
+
self.config = QaseConfig()
|
|
13
|
+
self.parse_bool = lambda d: d in ("y", "yes", "true", "1", 1, True)
|
|
14
|
+
|
|
15
|
+
self.__load_file_config()
|
|
16
|
+
self.__load_env_config()
|
|
17
|
+
|
|
18
|
+
def validate_config(self):
|
|
19
|
+
errors: list[str] = []
|
|
20
|
+
if self.config.mode is Mode.testops or self.config.fallback is Mode.testops:
|
|
21
|
+
if self.config.testops.api.token is None:
|
|
22
|
+
errors.append("Testops token is not set")
|
|
23
|
+
|
|
24
|
+
if self.config.testops.project is None:
|
|
25
|
+
errors.append("Testops project is not set")
|
|
26
|
+
|
|
27
|
+
if len(errors) > 0:
|
|
28
|
+
self.logger.log("Config validation failed", "error")
|
|
29
|
+
for error in errors:
|
|
30
|
+
self.logger.log(error, "error")
|
|
31
|
+
raise Exception("Config validation failed")
|
|
32
|
+
|
|
33
|
+
def __str__(self):
|
|
34
|
+
return json.dumps(self.config, indent=4, sort_keys=True)
|
|
35
|
+
|
|
36
|
+
def __load_file_config(self):
|
|
37
|
+
try:
|
|
38
|
+
if os.path.exists(self.__config_file):
|
|
39
|
+
with open(self.__config_file, "r") as file:
|
|
40
|
+
config = json.load(file)
|
|
41
|
+
|
|
42
|
+
if config.get("mode"):
|
|
43
|
+
self.config.set_mode(config.get("mode"))
|
|
44
|
+
|
|
45
|
+
if config.get("fallback"):
|
|
46
|
+
self.config.set_fallback(config.get("fallback"))
|
|
47
|
+
|
|
48
|
+
if config.get("environment"):
|
|
49
|
+
self.config.set_environment(config.get("environment"))
|
|
50
|
+
|
|
51
|
+
if config.get("rootSuite"):
|
|
52
|
+
self.config.set_root_suite(config.get("rootSuite"))
|
|
53
|
+
|
|
54
|
+
if config.get("profilers"):
|
|
55
|
+
self.config.set_profilers(config.get("profilers"))
|
|
56
|
+
|
|
57
|
+
if config.get("debug"):
|
|
58
|
+
self.config.set_debug(
|
|
59
|
+
self.parse_bool(config.get("debug"))
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
if config.get("executionPlan"):
|
|
63
|
+
execution_plan = config.get("executionPlan")
|
|
64
|
+
if execution_plan.get("path"):
|
|
65
|
+
self.config.execution_plan.set_path(execution_plan.get("path"))
|
|
66
|
+
|
|
67
|
+
if config.get("testops"):
|
|
68
|
+
testops = config.get("testops")
|
|
69
|
+
|
|
70
|
+
if testops.get("api"):
|
|
71
|
+
api = testops.get("api")
|
|
72
|
+
|
|
73
|
+
if api.get("host"):
|
|
74
|
+
self.config.testops.api.set_host(api.get("host"))
|
|
75
|
+
|
|
76
|
+
if api.get("token"):
|
|
77
|
+
self.config.testops.api.set_token(api.get("token"))
|
|
78
|
+
|
|
79
|
+
if testops.get("project"):
|
|
80
|
+
self.config.testops.set_project(testops.get("project"))
|
|
81
|
+
|
|
82
|
+
if testops.get("defect"):
|
|
83
|
+
self.config.testops.set_defect(
|
|
84
|
+
self.parse_bool(testops.get("defect"))
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if testops.get("useV2"):
|
|
88
|
+
self.config.testops.set_use_v2(
|
|
89
|
+
self.parse_bool(testops.get("useV2"))
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if testops.get("plan"):
|
|
93
|
+
plan = testops.get("plan")
|
|
94
|
+
|
|
95
|
+
if plan.get("id"):
|
|
96
|
+
self.config.testops.plan.set_id(plan.get("id"))
|
|
97
|
+
|
|
98
|
+
if testops.get("run"):
|
|
99
|
+
run = testops.get("run")
|
|
100
|
+
|
|
101
|
+
if run.get("id"):
|
|
102
|
+
self.config.testops.run.set_id(run.get("id"))
|
|
103
|
+
|
|
104
|
+
if run.get("title"):
|
|
105
|
+
self.config.testops.run.set_title(run.get("title"))
|
|
106
|
+
|
|
107
|
+
if run.get("description"):
|
|
108
|
+
self.config.testops.run.set_description(run.get("description"))
|
|
109
|
+
|
|
110
|
+
if run.get("complete"):
|
|
111
|
+
self.config.testops.run.set_complete(
|
|
112
|
+
self.parse_bool(run.get("complete"))
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if testops.get("batch"):
|
|
116
|
+
batch = testops.get("batch")
|
|
117
|
+
|
|
118
|
+
if batch.get("size"):
|
|
119
|
+
self.config.testops.batch.set_size(batch.get("size"))
|
|
120
|
+
|
|
121
|
+
if config.get("report"):
|
|
122
|
+
report = config.get("report")
|
|
123
|
+
|
|
124
|
+
if report.get("driver"):
|
|
125
|
+
self.config.report.set_driver(report.get("driver"))
|
|
126
|
+
|
|
127
|
+
if report.get("connection"):
|
|
128
|
+
connection = report.get("connection")
|
|
129
|
+
|
|
130
|
+
if connection.get("path"):
|
|
131
|
+
self.config.report.connection.set_path(connection.get("path"))
|
|
132
|
+
|
|
133
|
+
if connection.get("format"):
|
|
134
|
+
self.config.report.connection.set_format(
|
|
135
|
+
connection.get("format")
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if config.get("framework"):
|
|
139
|
+
framework = config.get("framework")
|
|
140
|
+
|
|
141
|
+
if framework.get("pytest"):
|
|
142
|
+
pytest = framework.get("pytest")
|
|
143
|
+
|
|
144
|
+
if pytest.get("captureLogs"):
|
|
145
|
+
self.config.framework.pytest.set_capture_logs(
|
|
146
|
+
self.parse_bool(pytest.get("captureLogs"))
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
except Exception as e:
|
|
150
|
+
self.logger.log("Failed to load config from file", "error")
|
|
151
|
+
|
|
152
|
+
def __load_env_config(self):
|
|
153
|
+
try:
|
|
154
|
+
for key, value in os.environ.items():
|
|
155
|
+
if key == 'QASE_MODE':
|
|
156
|
+
self.config.set_mode(value)
|
|
157
|
+
|
|
158
|
+
if key == 'QASE_FALLBACK':
|
|
159
|
+
self.config.set_fallback(value)
|
|
160
|
+
|
|
161
|
+
if key == 'QASE_ENVIRONMENT':
|
|
162
|
+
self.config.set_environment(value)
|
|
163
|
+
|
|
164
|
+
if key == 'QASE_ROOT_SUITE':
|
|
165
|
+
self.config.set_root_suite(value)
|
|
166
|
+
|
|
167
|
+
if key == 'QASE_PROFILERS':
|
|
168
|
+
self.config.set_profilers(value.split(','))
|
|
169
|
+
|
|
170
|
+
if key == 'QASE_DEBUG':
|
|
171
|
+
self.config.set_debug(self.parse_bool(value))
|
|
172
|
+
|
|
173
|
+
if key == 'QASE_EXECUTION_PLAN_PATH':
|
|
174
|
+
self.config.execution_plan.set_path(value)
|
|
175
|
+
|
|
176
|
+
if key == 'QASE_TESTOPS_API_HOST':
|
|
177
|
+
self.config.testops.api.set_host(value)
|
|
178
|
+
|
|
179
|
+
if key == 'QASE_TESTOPS_API_TOKEN':
|
|
180
|
+
self.config.testops.api.set_token(value)
|
|
181
|
+
|
|
182
|
+
if key == 'QASE_TESTOPS_PROJECT':
|
|
183
|
+
self.config.testops.set_project(value)
|
|
184
|
+
|
|
185
|
+
if key == 'QASE_TESTOPS_DEFECT':
|
|
186
|
+
self.config.testops.set_defect(self.parse_bool(value))
|
|
187
|
+
|
|
188
|
+
if key == 'QASE_TESTOPS_PLAN_ID':
|
|
189
|
+
self.config.testops.plan.set_id(value)
|
|
190
|
+
|
|
191
|
+
if key == 'QASE_TESTOPS_RUN_ID':
|
|
192
|
+
self.config.testops.run.set_id(value)
|
|
193
|
+
|
|
194
|
+
if key == 'QASE_TESTOPS_RUN_TITLE':
|
|
195
|
+
self.config.testops.run.set_title(value)
|
|
196
|
+
|
|
197
|
+
if key == 'QASE_TESTOPS_RUN_DESCRIPTION':
|
|
198
|
+
self.config.testops.run.set_description(value)
|
|
199
|
+
|
|
200
|
+
if key == 'QASE_TESTOPS_RUN_COMPLETE':
|
|
201
|
+
self.config.testops.run.set_complete(self.parse_bool(value))
|
|
202
|
+
|
|
203
|
+
if key == 'QASE_TESTOPS_BATCH_SIZE':
|
|
204
|
+
self.config.testops.batch.set_size(value)
|
|
205
|
+
|
|
206
|
+
if key == 'QASE_REPORT_DRIVER':
|
|
207
|
+
self.config.report.set_driver(value)
|
|
208
|
+
|
|
209
|
+
if key == 'QASE_REPORT_CONNECTION_PATH':
|
|
210
|
+
self.config.report.connection.set_path(value)
|
|
211
|
+
|
|
212
|
+
if key == 'QASE_REPORT_CONNECTION_FORMAT':
|
|
213
|
+
self.config.report.connection.set_format(value)
|
|
214
|
+
|
|
215
|
+
if key == 'QASE_PYTEST_CAPTURE_LOGS':
|
|
216
|
+
self.config.framework.pytest.set_capture_logs(self.parse_bool(value))
|
|
217
|
+
|
|
218
|
+
except Exception as e:
|
|
219
|
+
self.logger.log("Failed to load config from env vars {e}", "error")
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from ..basemodel import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ApiConfig(BaseModel):
|
|
5
|
+
token: str = None
|
|
6
|
+
host: str = None
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.host = "qase.io"
|
|
10
|
+
|
|
11
|
+
def set_token(self, token: str):
|
|
12
|
+
self.token = token
|
|
13
|
+
|
|
14
|
+
def set_host(self, host: str):
|
|
15
|
+
self.host = host
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ..basemodel import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BatchConfig(BaseModel):
|
|
5
|
+
size: int = None
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.size = 200
|
|
9
|
+
|
|
10
|
+
def set_size(self, size: int):
|
|
11
|
+
if size > 2000 or size == 0:
|
|
12
|
+
raise ValueError("Batch size should be greater than 0 and less than 2000")
|
|
13
|
+
self.size = size
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from ..basemodel import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Format(Enum):
|
|
7
|
+
json = "json"
|
|
8
|
+
jsonp = "jsonp"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ConnectionConfig(BaseModel):
|
|
12
|
+
path: str = None
|
|
13
|
+
format: Format = None
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.format = Format.json
|
|
17
|
+
self.path = "./build/qase-report"
|
|
18
|
+
|
|
19
|
+
def set_path(self, path: str):
|
|
20
|
+
self.path = path
|
|
21
|
+
|
|
22
|
+
def set_format(self, format: str):
|
|
23
|
+
if any(format == e.value for e in Format.__members__.values()):
|
|
24
|
+
self.format = Format[format]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from ..basemodel import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PytestConfig(BaseModel):
|
|
5
|
+
capture_logs: bool = None
|
|
6
|
+
|
|
7
|
+
def __init__(self):
|
|
8
|
+
self.capture_logs = False
|
|
9
|
+
|
|
10
|
+
def set_capture_logs(self, capture_logs: bool):
|
|
11
|
+
self.capture_logs = capture_logs
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Framework(BaseModel):
|
|
15
|
+
pytest: PytestConfig = None
|
|
16
|
+
|
|
17
|
+
def __init__(self):
|
|
18
|
+
self.pytest = PytestConfig()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from .framework import Framework
|
|
4
|
+
from .report import ReportConfig
|
|
5
|
+
from .testops import TestopsConfig
|
|
6
|
+
from ..basemodel import BaseModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Mode(Enum):
|
|
10
|
+
testops = "testops"
|
|
11
|
+
report = "report"
|
|
12
|
+
off = "off"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ExecutionPlan(BaseModel):
|
|
16
|
+
path: str = None
|
|
17
|
+
|
|
18
|
+
def __init__(self):
|
|
19
|
+
self.path = "./build/qase-execution-plan.json"
|
|
20
|
+
|
|
21
|
+
def set_path(self, path: str):
|
|
22
|
+
self.path = path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class QaseConfig(BaseModel):
|
|
26
|
+
mode: Mode = None
|
|
27
|
+
fallback: Mode = None
|
|
28
|
+
environment: str = None
|
|
29
|
+
root_suite: str = None
|
|
30
|
+
debug: bool = None
|
|
31
|
+
execution_plan: ExecutionPlan = None
|
|
32
|
+
testops: TestopsConfig = None
|
|
33
|
+
report: ReportConfig = None
|
|
34
|
+
profilers: list = None
|
|
35
|
+
framework: Framework = None
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.mode = Mode.off
|
|
39
|
+
self.fallback = Mode.off
|
|
40
|
+
self.debug = False
|
|
41
|
+
self.testops = TestopsConfig()
|
|
42
|
+
self.report = ReportConfig()
|
|
43
|
+
self.execution_plan = ExecutionPlan()
|
|
44
|
+
self.framework = Framework()
|
|
45
|
+
self.profilers = []
|
|
46
|
+
|
|
47
|
+
def set_mode(self, mode: str):
|
|
48
|
+
if any(mode == e.value for e in Mode.__members__.values()):
|
|
49
|
+
self.mode = Mode[mode]
|
|
50
|
+
|
|
51
|
+
def set_fallback(self, fallback: str):
|
|
52
|
+
if any(fallback == e.value for e in Mode.__members__.values()):
|
|
53
|
+
self.fallback = Mode[fallback]
|
|
54
|
+
|
|
55
|
+
def set_environment(self, environment: str):
|
|
56
|
+
self.environment = environment
|
|
57
|
+
|
|
58
|
+
def set_profilers(self, profilers: list):
|
|
59
|
+
self.profilers = profilers
|
|
60
|
+
|
|
61
|
+
def set_root_suite(self, root_suite: str):
|
|
62
|
+
self.root_suite = root_suite
|
|
63
|
+
|
|
64
|
+
def set_debug(self, debug: bool):
|
|
65
|
+
self.debug = debug
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from .connection import ConnectionConfig
|
|
4
|
+
from ..basemodel import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Driver(Enum):
|
|
8
|
+
local = "local"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ReportConfig(BaseModel):
|
|
12
|
+
driver: Driver = None
|
|
13
|
+
connection: ConnectionConfig = None
|
|
14
|
+
|
|
15
|
+
def __init__(self):
|
|
16
|
+
self.driver = Driver.local
|
|
17
|
+
self.connection = ConnectionConfig()
|
|
18
|
+
|
|
19
|
+
def set_driver(self, driver: str):
|
|
20
|
+
if any(driver == e.value for e in Driver.__members__.values()):
|
|
21
|
+
self.driver = Driver[driver]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from ..basemodel import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RunConfig(BaseModel):
|
|
5
|
+
title: str = None
|
|
6
|
+
description: str = None
|
|
7
|
+
complete: bool = None
|
|
8
|
+
id: int = None
|
|
9
|
+
|
|
10
|
+
def __init__(self):
|
|
11
|
+
self.complete = True
|
|
12
|
+
|
|
13
|
+
def set_title(self, title: str):
|
|
14
|
+
self.title = title
|
|
15
|
+
|
|
16
|
+
def set_description(self, description: str):
|
|
17
|
+
self.description = description
|
|
18
|
+
|
|
19
|
+
def set_complete(self, complete: bool):
|
|
20
|
+
self.complete = complete
|
|
21
|
+
|
|
22
|
+
def set_id(self, id: int):
|
|
23
|
+
self.id = id
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from .api import ApiConfig
|
|
2
|
+
from .batch import BatchConfig
|
|
3
|
+
from .plan import PlanConfig
|
|
4
|
+
from .run import RunConfig
|
|
5
|
+
from ..basemodel import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestopsConfig(BaseModel):
|
|
9
|
+
project: str = None
|
|
10
|
+
defect: bool = None
|
|
11
|
+
api: ApiConfig = None
|
|
12
|
+
run: RunConfig = None
|
|
13
|
+
plan: PlanConfig = None
|
|
14
|
+
batch: BatchConfig = None
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
self.api = ApiConfig()
|
|
18
|
+
self.run = RunConfig()
|
|
19
|
+
self.batch = BatchConfig()
|
|
20
|
+
self.plan = PlanConfig()
|
|
21
|
+
self.defect = False
|
|
22
|
+
self.use_v2 = False
|
|
23
|
+
|
|
24
|
+
def set_project(self, project: str):
|
|
25
|
+
self.project = project
|
|
26
|
+
|
|
27
|
+
def set_defect(self, defect: bool):
|
|
28
|
+
self.defect = defect
|
|
29
|
+
|
|
30
|
+
def set_use_v2(self, use_v2: bool):
|
|
31
|
+
self.use_v2 = use_v2
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/core.py
RENAMED
|
@@ -2,13 +2,14 @@ import os
|
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
4
|
|
|
5
|
-
from ..config import ConfigManager
|
|
5
|
+
from ..config import ConfigManager
|
|
6
6
|
from ..logger import Logger
|
|
7
7
|
|
|
8
8
|
from .report import QaseReport
|
|
9
9
|
from .testops import QaseTestOps
|
|
10
10
|
|
|
11
11
|
from ..models import Result, Attachment, Runtime
|
|
12
|
+
from ..models.config.qaseconfig import Mode
|
|
12
13
|
from typing import Union
|
|
13
14
|
|
|
14
15
|
"""
|
|
@@ -18,31 +19,32 @@ from typing import Union
|
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
class QaseCoreReporter:
|
|
21
|
-
def __init__(self, config:
|
|
22
|
-
|
|
23
|
-
self.
|
|
22
|
+
def __init__(self, config: ConfigManager):
|
|
23
|
+
config.validate_config()
|
|
24
|
+
self.config = config.config
|
|
25
|
+
self.logger = Logger(self.config.debug)
|
|
24
26
|
self.execution_plan = None
|
|
25
27
|
self.profilers = []
|
|
26
28
|
self.overhead = 0
|
|
27
29
|
|
|
28
|
-
self._selective_execution_setup()
|
|
30
|
+
# self._selective_execution_setup()
|
|
29
31
|
self.fallback = self._fallback_setup()
|
|
30
32
|
|
|
31
33
|
self.logger.log_debug(f"Config: {self.config}")
|
|
32
34
|
|
|
33
35
|
# Reading reporter mode from config file
|
|
34
|
-
mode = config.
|
|
36
|
+
mode = self.config.mode
|
|
35
37
|
|
|
36
|
-
if mode ==
|
|
38
|
+
if mode == Mode.testops:
|
|
37
39
|
try:
|
|
38
40
|
self._load_testops_plan()
|
|
39
|
-
self.reporter = QaseTestOps(config=config, logger=self.logger)
|
|
41
|
+
self.reporter = QaseTestOps(config=self.config, logger=self.logger)
|
|
40
42
|
except Exception as e:
|
|
41
43
|
self.logger.log('Failed to initialize TestOps reporter. Using fallback.', 'info')
|
|
42
44
|
self.logger.log(e, 'error')
|
|
43
45
|
self.reporter = self.fallback
|
|
44
|
-
elif mode ==
|
|
45
|
-
self.reporter = QaseReport(config=config, logger=self.logger)
|
|
46
|
+
elif mode == Mode.report:
|
|
47
|
+
self.reporter = QaseReport(config=self.config, logger=self.logger)
|
|
46
48
|
else:
|
|
47
49
|
self.reporter = None
|
|
48
50
|
|
|
@@ -103,14 +105,14 @@ class QaseCoreReporter:
|
|
|
103
105
|
self._run_fallback()
|
|
104
106
|
|
|
105
107
|
def setup_profilers(self, runtime: Runtime) -> None:
|
|
106
|
-
profilers = self.config.
|
|
108
|
+
profilers = self.config.profilers
|
|
107
109
|
|
|
108
110
|
for profiler in profilers:
|
|
109
111
|
if profiler == "network":
|
|
110
112
|
# Lazy import
|
|
111
113
|
from ..profilers import NetworkProfilerSingleton
|
|
112
114
|
NetworkProfilerSingleton.init(runtime=runtime,
|
|
113
|
-
skip_domain=self.config.
|
|
115
|
+
skip_domain=self.config.testops.api.host)
|
|
114
116
|
self.profilers.append(NetworkProfilerSingleton.get_instance())
|
|
115
117
|
if profiler == "sleep":
|
|
116
118
|
from ..profilers import SleepProfiler
|
|
@@ -166,33 +168,33 @@ class QaseCoreReporter:
|
|
|
166
168
|
|
|
167
169
|
def _load_testops_plan(self) -> None:
|
|
168
170
|
try:
|
|
169
|
-
if self.config.
|
|
171
|
+
if self.config.testops.plan.id is not None:
|
|
170
172
|
from .. import TestOpsPlanLoader
|
|
171
173
|
|
|
172
174
|
# Load test plan data from Qase TestOps
|
|
173
175
|
loader = TestOpsPlanLoader(
|
|
174
|
-
api_token=self.config.
|
|
175
|
-
host=self.config.
|
|
176
|
+
api_token=self.config.testops.api.token,
|
|
177
|
+
host=self.config.testops.api.host
|
|
176
178
|
)
|
|
177
|
-
self.execution_plan = loader.load(self.config.
|
|
178
|
-
int(self.config.
|
|
179
|
+
self.execution_plan = loader.load(self.config.testops.project,
|
|
180
|
+
int(self.config.testops.plan.id))
|
|
179
181
|
except Exception as e:
|
|
180
182
|
self.logger.log('Failed to load test plan from Qase TestOps', 'info')
|
|
181
183
|
self.logger.log(e, 'error')
|
|
182
184
|
|
|
183
185
|
# TODO: won't work, need to fix
|
|
184
|
-
def _selective_execution_setup(self) -> list:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
186
|
+
# def _selective_execution_setup(self) -> list:
|
|
187
|
+
# # Load execution plan from file
|
|
188
|
+
# path = self.config.execution_plan.path
|
|
189
|
+
# if not self.config.execution_plan.path and path and os.path.isfile(self.config.execution_plan.path):
|
|
190
|
+
# with open(self.config.execution_plan.path) as f:
|
|
191
|
+
# return json.load(f)
|
|
192
|
+
#
|
|
193
|
+
# # Load execution plan from command line or env variable
|
|
194
|
+
# if self.config.self.config.execution_plan.path:
|
|
195
|
+
# return [int(n) for n in str(self.config.self.config.execution_plan.path.split(","))]
|
|
194
196
|
|
|
195
197
|
def _fallback_setup(self) -> Union[QaseReport, None]:
|
|
196
|
-
if self.config.
|
|
198
|
+
if self.config.fallback == Mode.report:
|
|
197
199
|
return QaseReport(config=self.config, logger=self.logger)
|
|
198
200
|
return None
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/report.py
RENAMED
|
@@ -4,13 +4,15 @@ import shutil
|
|
|
4
4
|
import json
|
|
5
5
|
import re
|
|
6
6
|
from ..models import Result, Run, Attachment
|
|
7
|
-
from .. import QaseUtils,
|
|
7
|
+
from .. import QaseUtils, Logger
|
|
8
|
+
from ..models.config.connection import Format
|
|
9
|
+
from ..models.config.qaseconfig import QaseConfig
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class QaseReport:
|
|
11
13
|
def __init__(
|
|
12
14
|
self,
|
|
13
|
-
config:
|
|
15
|
+
config: QaseConfig,
|
|
14
16
|
logger: Logger
|
|
15
17
|
):
|
|
16
18
|
self.duration = 0
|
|
@@ -19,13 +21,13 @@ class QaseReport:
|
|
|
19
21
|
self.config = config
|
|
20
22
|
self.logger = logger
|
|
21
23
|
|
|
22
|
-
self.report_path = self.config.
|
|
23
|
-
self.format = self.config.
|
|
24
|
+
self.report_path = self.config.report.connection.path
|
|
25
|
+
self.format = self.config.report.connection.format
|
|
24
26
|
|
|
25
27
|
self.start_time = None
|
|
26
28
|
self.end_time = None
|
|
27
29
|
self.run_id = None
|
|
28
|
-
self.environment = self.config.
|
|
30
|
+
self.environment = self.config.environment
|
|
29
31
|
|
|
30
32
|
def start_run(self):
|
|
31
33
|
self._check_report_path()
|
|
@@ -118,17 +120,17 @@ class QaseReport:
|
|
|
118
120
|
|
|
119
121
|
# Saves a model to a file
|
|
120
122
|
def _store_object(self, object, path, filename):
|
|
121
|
-
data = object.
|
|
122
|
-
if self.format ==
|
|
123
|
+
data = object.__str__()
|
|
124
|
+
if self.format == Format.jsonp:
|
|
123
125
|
data = f"qaseJsonp({data});"
|
|
124
|
-
with open(f"{path}/{filename}.{self.format}", 'w', encoding='utf-8') as f:
|
|
126
|
+
with open(f"{path}/{filename}.{self.format.value}", 'w', encoding='utf-8') as f:
|
|
125
127
|
f.write(data)
|
|
126
128
|
|
|
127
129
|
def _read_object(self, source):
|
|
128
130
|
data = source.read()
|
|
129
|
-
if self.format ==
|
|
131
|
+
if self.format == Format.json:
|
|
130
132
|
return json.loads(data)
|
|
131
|
-
elif self.format ==
|
|
133
|
+
elif self.format == Format.jsonp:
|
|
132
134
|
jsonp_pattern = r'\w+\(\s*({[\s\S]*})\s*\);'
|
|
133
135
|
match = re.search(jsonp_pattern, data)
|
|
134
136
|
if match:
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/testops.py
RENAMED
|
@@ -2,52 +2,53 @@ import threading
|
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import List
|
|
5
|
-
from .. import
|
|
5
|
+
from .. import Logger, ReporterException
|
|
6
6
|
from ..client.api_v1_client import ApiV1Client
|
|
7
7
|
from ..client.base_api_client import BaseApiClient
|
|
8
8
|
from ..models import Result
|
|
9
|
+
from ..models.config.qaseconfig import QaseConfig
|
|
9
10
|
|
|
10
11
|
DEFAULT_BATCH_SIZE = 200
|
|
12
|
+
DEFAULT_THREAD_COUNT = 4
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class QaseTestOps:
|
|
14
16
|
|
|
15
|
-
def __init__(self, config:
|
|
17
|
+
def __init__(self, config: QaseConfig, logger: Logger) -> None:
|
|
16
18
|
self.config = config
|
|
17
19
|
self.logger = logger
|
|
18
20
|
|
|
19
21
|
self.client = self._prepare_client()
|
|
20
22
|
|
|
21
|
-
run_id = self.config.
|
|
22
|
-
plan_id = self.config.
|
|
23
|
+
run_id = self.config.testops.run.id
|
|
24
|
+
plan_id = self.config.testops.plan.id
|
|
23
25
|
|
|
24
|
-
self.project_code = self.config.
|
|
26
|
+
self.project_code = self.config.testops.project
|
|
25
27
|
self.run_id = int(run_id) if run_id else run_id
|
|
26
28
|
self.plan_id = int(plan_id) if plan_id else plan_id
|
|
27
|
-
self.defect = self.config.
|
|
28
|
-
self.complete_after_run = self.config.
|
|
29
|
+
self.defect = self.config.testops.defect
|
|
30
|
+
self.complete_after_run = self.config.testops.run.complete
|
|
29
31
|
self.environment = None
|
|
30
32
|
|
|
31
|
-
self.batch_size = min(2000, max(1, int(self.config.
|
|
32
|
-
self.send_semaphore = threading.Semaphore(
|
|
33
|
-
self.config.get('testops.batch.threads', 4)) # Semaphore to limit concurrent sends
|
|
33
|
+
self.batch_size = min(2000, max(1, int(self.config.testops.batch.size or DEFAULT_BATCH_SIZE)))
|
|
34
|
+
self.send_semaphore = threading.Semaphore(DEFAULT_THREAD_COUNT) # Semaphore to limit concurrent sends
|
|
34
35
|
self.lock = threading.Lock()
|
|
35
36
|
self.count_running_threads = 0
|
|
36
37
|
|
|
37
|
-
environment = self.config.
|
|
38
|
+
environment = self.config.environment
|
|
38
39
|
if environment:
|
|
39
40
|
if isinstance(environment, int) or (isinstance(environment, str) and environment.isnumeric()):
|
|
40
41
|
self.environment = environment
|
|
41
42
|
elif isinstance(environment, str):
|
|
42
43
|
self.environment = self.client.get_environment(environment, self.project_code)
|
|
43
44
|
|
|
44
|
-
run_title = self.config.
|
|
45
|
+
run_title = self.config.testops.run.title
|
|
45
46
|
if run_title and run_title != '':
|
|
46
47
|
self.run_title = run_title
|
|
47
48
|
else:
|
|
48
49
|
self.run_title = "Automated Run {}".format(str(datetime.now()))
|
|
49
50
|
|
|
50
|
-
run_description = self.config.
|
|
51
|
+
run_description = self.config.testops.run.description
|
|
51
52
|
if run_description and run_description != '':
|
|
52
53
|
self.run_description = run_description
|
|
53
54
|
else:
|
|
@@ -66,7 +67,7 @@ class QaseTestOps:
|
|
|
66
67
|
self.client.get_project(self.project_code)
|
|
67
68
|
|
|
68
69
|
def _prepare_client(self) -> BaseApiClient:
|
|
69
|
-
if self.config.
|
|
70
|
+
if self.config.testops.use_v2:
|
|
70
71
|
from ..client.api_v2_client import ApiV2Client
|
|
71
72
|
return ApiV2Client(self.config, self.logger)
|
|
72
73
|
return ApiV1Client(self.config, self.logger)
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase_python_commons.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: qase-python-commons
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.1.0b2
|
|
4
4
|
Summary: A library for Qase TestOps and Qase Report
|
|
5
5
|
Author-email: Qase Team <support@qase.io>
|
|
6
6
|
Project-URL: Homepage, https://github.com/qase-tms/qase-python/tree/master/qase-python-commons
|
|
@@ -22,7 +22,7 @@ Requires-Python: >=3.7
|
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
23
23
|
Requires-Dist: certifi>=2024.2.2
|
|
24
24
|
Requires-Dist: attrs>=23.2.0
|
|
25
|
-
Requires-Dist: qase-api-client~=1.0.
|
|
25
|
+
Requires-Dist: qase-api-client~=1.0.1
|
|
26
26
|
Requires-Dist: qase-api-v2-client~=1.0.0
|
|
27
27
|
Requires-Dist: more_itertools
|
|
28
28
|
Provides-Extra: testing
|
|
@@ -18,6 +18,15 @@ src/qase/commons/models/run.py
|
|
|
18
18
|
src/qase/commons/models/runtime.py
|
|
19
19
|
src/qase/commons/models/step.py
|
|
20
20
|
src/qase/commons/models/suite.py
|
|
21
|
+
src/qase/commons/models/config/api.py
|
|
22
|
+
src/qase/commons/models/config/batch.py
|
|
23
|
+
src/qase/commons/models/config/connection.py
|
|
24
|
+
src/qase/commons/models/config/framework.py
|
|
25
|
+
src/qase/commons/models/config/plan.py
|
|
26
|
+
src/qase/commons/models/config/qaseconfig.py
|
|
27
|
+
src/qase/commons/models/config/report.py
|
|
28
|
+
src/qase/commons/models/config/run.py
|
|
29
|
+
src/qase/commons/models/config/testops.py
|
|
21
30
|
src/qase/commons/profilers/__init__.py
|
|
22
31
|
src/qase/commons/profilers/db.py
|
|
23
32
|
src/qase/commons/profilers/network.py
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import json
|
|
3
|
-
from .logger import Logger
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class ConfigManager:
|
|
7
|
-
|
|
8
|
-
def __init__(self, config_file='./qase.config.json', env_vars_prefix='QASE_'):
|
|
9
|
-
self.logger = Logger()
|
|
10
|
-
self.config = {}
|
|
11
|
-
self.parseBool = lambda d: d in ("y", "yes", "true", "1", 1, True)
|
|
12
|
-
|
|
13
|
-
try:
|
|
14
|
-
if os.path.exists(config_file):
|
|
15
|
-
with open(config_file, "r") as file:
|
|
16
|
-
def transform_keys(obj):
|
|
17
|
-
return {k.lower(): v for k, v in obj.items()}
|
|
18
|
-
self.config = json.load(file, object_hook=transform_keys)
|
|
19
|
-
except Exception as e:
|
|
20
|
-
self.logger.log("Failed to load config from file", "error")
|
|
21
|
-
|
|
22
|
-
# Load from env vars
|
|
23
|
-
try:
|
|
24
|
-
for key, value in os.environ.items():
|
|
25
|
-
if key.startswith(env_vars_prefix):
|
|
26
|
-
self._set_config(key[len(env_vars_prefix):].lower().replace('_', '.'), value)
|
|
27
|
-
except Exception as e:
|
|
28
|
-
self.logger.log("Failed to load config from env vars {e}", "error")
|
|
29
|
-
|
|
30
|
-
def get(self, key, default=None, value_type=None):
|
|
31
|
-
# Use _get_config method to get the value. If None, return default.
|
|
32
|
-
value = self._get_config(key)
|
|
33
|
-
if value is not None and value_type is not None and value_type == bool:
|
|
34
|
-
return self.parseBool(value)
|
|
35
|
-
return value or default
|
|
36
|
-
|
|
37
|
-
def validate_config(self):
|
|
38
|
-
if self.validator:
|
|
39
|
-
self.validator.validate(self.config)
|
|
40
|
-
|
|
41
|
-
def set(self, key, value):
|
|
42
|
-
self._set_config(key, value)
|
|
43
|
-
|
|
44
|
-
def _get_keys(self, config, prefix=""):
|
|
45
|
-
for key, value in config.items():
|
|
46
|
-
if isinstance(value, dict):
|
|
47
|
-
yield from self._get_keys(value, f"{prefix}{key}.")
|
|
48
|
-
else:
|
|
49
|
-
yield f"{prefix}{key}"
|
|
50
|
-
|
|
51
|
-
def _set_config(self, key, value, delimiter="."):
|
|
52
|
-
keys = key.split(delimiter)
|
|
53
|
-
config = self.config
|
|
54
|
-
for key in keys[:-1]:
|
|
55
|
-
config = config.setdefault(key, {})
|
|
56
|
-
config[keys[-1]] = value
|
|
57
|
-
|
|
58
|
-
def _get_config(self, key):
|
|
59
|
-
keys = key.split(".")
|
|
60
|
-
config = self.config
|
|
61
|
-
for key in keys[:-1]:
|
|
62
|
-
config = config.get(key, {})
|
|
63
|
-
return config.get(keys[-1], None)
|
|
64
|
-
|
|
65
|
-
def __str__(self):
|
|
66
|
-
return json.dumps(self.config, indent=4, sort_keys=True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/client/base_api_client.py
RENAMED
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/exceptions/reporter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/__init__.py
RENAMED
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/attachment.py
RENAMED
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/basemodel.py
RENAMED
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/relation.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/models/runtime.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/network.py
RENAMED
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/profilers/sleep.py
RENAMED
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/reporters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{qase_python_commons-3.0.3 → qase_python_commons-3.1.0b2}/src/qase/commons/validators/base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|