qase-python-commons 3.0.2b2__tar.gz → 3.0.2b3__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.
Files changed (36) hide show
  1. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/PKG-INFO +2 -1
  2. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/pyproject.toml +2 -1
  3. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/client/api_v1_client.py +1 -1
  4. qase_python_commons-3.0.2b3/src/qase/commons/client/api_v2_client.py +148 -0
  5. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/client/base_api_client.py +3 -3
  6. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/config.py +3 -1
  7. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/reporters/testops.py +3 -0
  8. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase_python_commons.egg-info/PKG-INFO +2 -1
  9. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase_python_commons.egg-info/SOURCES.txt +1 -0
  10. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase_python_commons.egg-info/requires.txt +1 -0
  11. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/README.md +0 -0
  12. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/setup.cfg +0 -0
  13. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/__init__.py +0 -0
  14. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/exceptions/reporter.py +0 -0
  15. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/loader.py +0 -0
  16. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/logger.py +0 -0
  17. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/__init__.py +0 -0
  18. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/attachment.py +0 -0
  19. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/basemodel.py +0 -0
  20. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/relation.py +0 -0
  21. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/result.py +0 -0
  22. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/run.py +0 -0
  23. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/runtime.py +0 -0
  24. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/step.py +0 -0
  25. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/models/suite.py +0 -0
  26. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/profilers/__init__.py +0 -0
  27. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/profilers/db.py +0 -0
  28. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/profilers/network.py +0 -0
  29. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/profilers/sleep.py +0 -0
  30. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/reporters/__init__.py +0 -0
  31. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/reporters/core.py +0 -0
  32. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/reporters/report.py +0 -0
  33. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/utils.py +0 -0
  34. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase/commons/validators/base.py +0 -0
  35. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/src/qase_python_commons.egg-info/dependency_links.txt +0 -0
  36. {qase_python_commons-3.0.2b2 → qase_python_commons-3.0.2b3}/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.0.2b2
3
+ Version: 3.0.2b3
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
@@ -23,6 +23,7 @@ Description-Content-Type: text/markdown
23
23
  Requires-Dist: certifi>=2024.2.2
24
24
  Requires-Dist: attrs>=23.2.0
25
25
  Requires-Dist: qase-api-client~=1.0.0b2
26
+ Requires-Dist: qase-api-v2-client~=1.0.0b2
26
27
  Requires-Dist: more_itertools
27
28
  Provides-Extra: testing
28
29
  Requires-Dist: pytest; 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.0.2b2"
7
+ version = "3.0.2b3"
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"}]
@@ -31,6 +31,7 @@ dependencies = [
31
31
  "certifi>=2024.2.2",
32
32
  "attrs>=23.2.0",
33
33
  "qase-api-client~=1.0.0b2",
34
+ "qase-api-v2-client~=1.0.0b2",
34
35
  "more_itertools"
35
36
  ]
36
37
 
@@ -120,7 +120,7 @@ class ApiV1Client(BaseApiClient):
120
120
  return True
121
121
  return False
122
122
 
123
- def send_results(self, project_code: str, run_id: int, results: []) -> None:
123
+ def send_results(self, project_code: str, run_id: str, results: []) -> None:
124
124
  api_results = ResultsApi(self.client)
125
125
  results_to_send = [self._prepare_result(project_code, result) for result in results]
126
126
  self.logger.log_debug(f"Sending results for run {run_id}: {results_to_send}")
@@ -0,0 +1,148 @@
1
+ from typing import Dict
2
+
3
+ import certifi
4
+ from qase.api_client_v2 import ResultsApi
5
+ from qase.api_client_v2.api_client import ApiClient
6
+ from qase.api_client_v2.configuration import Configuration
7
+ from qase.api_client_v2.models.create_results_request_v2 import CreateResultsRequestV2
8
+ from qase.api_client_v2.models.relation_suite import RelationSuite
9
+ from qase.api_client_v2.models.relation_suite_item import RelationSuiteItem
10
+ from qase.api_client_v2.models.result_create import ResultCreate
11
+ from qase.api_client_v2.models.result_execution import ResultExecution
12
+ from qase.api_client_v2.models.result_relations import ResultRelations
13
+ from qase.api_client_v2.models.result_step_status import ResultStepStatus
14
+ from qase.api_client_v2.models.result_steps_type import ResultStepsType
15
+
16
+ from .api_v1_client import ApiV1Client
17
+ from .. import ConfigManager, Logger
18
+ from ..exceptions.reporter import ReporterException
19
+ from ..models import Attachment, Result
20
+ from ..models.step import StepType, Step
21
+
22
+
23
+ class ApiV2Client(ApiV1Client):
24
+ def __init__(self, config: ConfigManager, logger: Logger):
25
+ ApiV1Client.__init__(self, config, logger)
26
+
27
+ try:
28
+ self.logger.log_debug("Preparing API V2 client")
29
+ configuration = Configuration()
30
+ configuration.api_key['TokenAuth'] = self.config.get('testops.api.token')
31
+ configuration.ssl_ca_cert = certifi.where()
32
+ host = self.config.get('testops.api.host', 'qase.io')
33
+ if self.config.get('testops.api.enterprise', False, bool):
34
+ configuration.host = f'https://api-{host}/v2'
35
+ else:
36
+ configuration.host = f'https://api.{host}/v2'
37
+
38
+ self.client_v2 = ApiClient(configuration)
39
+ self.logger.log_debug("API V2 client prepared")
40
+ except Exception as e:
41
+ self.logger.log(f"Error at preparing API V2 client: {e}", "error")
42
+ raise ReporterException(e)
43
+
44
+ def send_results(self, project_code: str, run_id: str, results: []) -> None:
45
+ api_results = ResultsApi(self.client_v2)
46
+ results_to_send = [self._prepare_result(project_code, result) for result in results]
47
+ self.logger.log_debug(f"Sending results for run {run_id}: {results_to_send}")
48
+ api_results.create_results_v2(project_code, run_id,
49
+ create_results_request_v2=CreateResultsRequestV2(results=results_to_send))
50
+ self.logger.log_debug(f"Results for run {run_id} sent successfully")
51
+
52
+ def _prepare_result(self, project_code: str, result: Result) -> ResultCreate:
53
+ attached = []
54
+ if result.attachments:
55
+ for attachment in result.attachments:
56
+ attached.extend(self._upload_attachment(project_code, attachment))
57
+
58
+ steps = []
59
+ for step in result.steps:
60
+ prepared = self._prepare_step(project_code, step)
61
+ steps.append(prepared)
62
+
63
+ for key, param in result.params.items():
64
+ # Hack to match old TestOps API
65
+ if param == "":
66
+ result.params[key] = "empty"
67
+
68
+ result_model_v2 = ResultCreate(
69
+ title=result.get_title(),
70
+ signature=result.signature,
71
+ testops_id=result.get_testops_id(),
72
+ execution=ResultExecution(start_time=result.execution.start_time, end_time=result.execution.end_time,
73
+ status=result.execution.status, duration=result.execution.duration,
74
+ stacktrace=result.execution.stacktrace, thread=result.execution.thread),
75
+ fields=result.fields,
76
+ attachments=[attach.hash for attach in attached],
77
+ steps=steps,
78
+ step_type=ResultStepsType.CLASSIC,
79
+ params=result.params,
80
+ muted=False,
81
+ message=result.message,
82
+ )
83
+
84
+ if result.get_suite_title():
85
+ data = []
86
+
87
+ for suite in result.get_suite_title().split("."):
88
+ data.append(RelationSuiteItem(title=suite))
89
+
90
+ result_model_v2.relations = ResultRelations(suite=RelationSuite(data=data))
91
+
92
+ self.logger.log_debug(f"Prepared result: {result_model_v2.to_json()}")
93
+
94
+ return result_model_v2
95
+
96
+ def _prepare_step(self, project_code: str, step: Step) -> Dict:
97
+ prepared_children = []
98
+
99
+ try:
100
+ prepared_step = {'execution': {}, 'data': {}, 'steps': []}
101
+ prepared_step['execution']['status'] = ResultStepStatus(step.execution.status)
102
+ prepared_step['execution']['duration'] = step.execution.duration
103
+
104
+ if step.step_type == StepType.TEXT:
105
+ prepared_step['data']['action'] = step.data.action
106
+ if step.data.expected_result:
107
+ prepared_step['data']['expected_result'] = step.data.expected_result
108
+
109
+ if step.step_type == StepType.REQUEST:
110
+ prepared_step['data']['action'] = step.data.request_method + " " + step.data.request_url
111
+
112
+ if step.data.request_body:
113
+ step.attachments.append(
114
+ Attachment(file_name='request_body.txt', content=step.data.request_body, mime_type='text/plain',
115
+ temporary=True))
116
+ if step.data.request_headers:
117
+ step.attachments.append(
118
+ Attachment(file_name='request_headers.txt', content=step.data.request_headers,
119
+ mime_type='text/plain', temporary=True))
120
+ if step.data.response_body:
121
+ step.attachments.append(Attachment(file_name='response_body.txt', content=step.data.response_body,
122
+ mime_type='text/plain', temporary=True))
123
+ if step.data.response_headers:
124
+ step.attachments.append(
125
+ Attachment(file_name='response_headers.txt', content=step.data.response_headers,
126
+ mime_type='text/plain', temporary=True))
127
+
128
+ if step.step_type == StepType.GHERKIN:
129
+ prepared_step['data']['action'] = step.data.keyword
130
+
131
+ if step.step_type == StepType.SLEEP:
132
+ prepared_step['data']['action'] = f"Sleep for {step.data.duration} seconds"
133
+
134
+ if step.attachments:
135
+ uploaded_attachments = []
136
+ for file in step.attachments:
137
+ uploaded_attachments.extend(self._upload_attachment(project_code, file))
138
+
139
+ prepared_step['execution']['attachments'] = [attach.hash for attach in uploaded_attachments]
140
+
141
+ if step.steps:
142
+ for substep in step.steps:
143
+ prepared_children.append(self._prepare_step(project_code, substep))
144
+ prepared_step['steps'] = prepared_children
145
+ return prepared_step
146
+ except Exception as e:
147
+ self.logger.log(f"Error at preparing step: {e}", "error")
148
+ raise ReporterException(e)
@@ -53,7 +53,7 @@ class BaseApiClient(abc.ABC):
53
53
 
54
54
  @abc.abstractmethod
55
55
  def create_test_run(self, project_code: str, title: str, description: str, plan_id=None,
56
- environment_id=None) -> int:
56
+ environment_id=None) -> str:
57
57
  """
58
58
  Create a test run in Qase TestOps
59
59
 
@@ -67,7 +67,7 @@ class BaseApiClient(abc.ABC):
67
67
  pass
68
68
 
69
69
  @abc.abstractmethod
70
- def check_test_run(self, project_code: str, run_id: int) -> bool:
70
+ def check_test_run(self, project_code: str, run_id: str) -> bool:
71
71
  """
72
72
  Check if test run exists in Qase TestOps
73
73
  :param project_code: project code
@@ -77,7 +77,7 @@ class BaseApiClient(abc.ABC):
77
77
  pass
78
78
 
79
79
  @abc.abstractmethod
80
- def send_results(self, project_code: str, run_id: int, results: []) -> None:
80
+ def send_results(self, project_code: str, run_id: str, results: []) -> None:
81
81
  """
82
82
  Send test results to Qase TestOps
83
83
  :param project_code: project code
@@ -13,7 +13,9 @@ class ConfigManager:
13
13
  try:
14
14
  if os.path.exists(config_file):
15
15
  with open(config_file, "r") as file:
16
- self.config = json.load(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)
17
19
  except Exception as e:
18
20
  self.logger.log("Failed to load config from file", "error")
19
21
 
@@ -65,6 +65,9 @@ class QaseTestOps:
65
65
  self.client.get_project(self.project_code)
66
66
 
67
67
  def _prepare_client(self) -> BaseApiClient:
68
+ if self.config.get('testops.usev2', False, bool):
69
+ from ..client.api_v2_client import ApiV2Client
70
+ return ApiV2Client(self.config, self.logger)
68
71
  return ApiV1Client(self.config, self.logger)
69
72
 
70
73
  def _send_results_threaded(self, results):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qase-python-commons
3
- Version: 3.0.2b2
3
+ Version: 3.0.2b3
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
@@ -23,6 +23,7 @@ Description-Content-Type: text/markdown
23
23
  Requires-Dist: certifi>=2024.2.2
24
24
  Requires-Dist: attrs>=23.2.0
25
25
  Requires-Dist: qase-api-client~=1.0.0b2
26
+ Requires-Dist: qase-api-v2-client~=1.0.0b2
26
27
  Requires-Dist: more_itertools
27
28
  Provides-Extra: testing
28
29
  Requires-Dist: pytest; extra == "testing"
@@ -6,6 +6,7 @@ src/qase/commons/loader.py
6
6
  src/qase/commons/logger.py
7
7
  src/qase/commons/utils.py
8
8
  src/qase/commons/client/api_v1_client.py
9
+ src/qase/commons/client/api_v2_client.py
9
10
  src/qase/commons/client/base_api_client.py
10
11
  src/qase/commons/exceptions/reporter.py
11
12
  src/qase/commons/models/__init__.py
@@ -1,6 +1,7 @@
1
1
  certifi>=2024.2.2
2
2
  attrs>=23.2.0
3
3
  qase-api-client~=1.0.0b2
4
+ qase-api-v2-client~=1.0.0b2
4
5
  more_itertools
5
6
 
6
7
  [testing]