orionis 0.383.0__py3-none-any.whl → 0.385.0__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.
- orionis/metadata/framework.py +1 -1
- {orionis-0.383.0.dist-info → orionis-0.385.0.dist-info}/METADATA +1 -1
- {orionis-0.383.0.dist-info → orionis-0.385.0.dist-info}/RECORD +8 -9
- tests/example/test_example.py +720 -18
- orionis/test/record/history.py +0 -385
- {orionis-0.383.0.dist-info → orionis-0.385.0.dist-info}/WHEEL +0 -0
- {orionis-0.383.0.dist-info → orionis-0.385.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.383.0.dist-info → orionis-0.385.0.dist-info}/top_level.txt +0 -0
- {orionis-0.383.0.dist-info → orionis-0.385.0.dist-info}/zip-safe +0 -0
orionis/metadata/framework.py
CHANGED
|
@@ -247,7 +247,7 @@ orionis/foundation/providers/path_resolver_provider.py,sha256=rXvaVc5sSqmDgRzWJo
|
|
|
247
247
|
orionis/foundation/providers/progress_bar_provider.py,sha256=75Jr4iEgUOUGl8Di1DioeP5_HRQlR-1lVzPmS96sWjA,737
|
|
248
248
|
orionis/foundation/providers/workers_provider.py,sha256=WWlji3C69_-Y0c42aZDbR_bmcE_qZEh2SaA_cNkCivI,702
|
|
249
249
|
orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
250
|
-
orionis/metadata/framework.py,sha256=
|
|
250
|
+
orionis/metadata/framework.py,sha256=yPevn6fPgNAn7N2vN-FlVyYTvJZ-_piFlZ8RB_V5pDo,4960
|
|
251
251
|
orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
|
|
252
252
|
orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
253
253
|
orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -380,15 +380,14 @@ orionis/test/exceptions/value.py,sha256=r3tVWTE3gNp7of2gXk71NN-VYoAlOpB0kulw0LOJ
|
|
|
380
380
|
orionis/test/output/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
381
381
|
orionis/test/output/dumper.py,sha256=5_SrMWTlmdqDbPA6ggnhTrI2gaUMr6aA0xTNYpOLFMk,4202
|
|
382
382
|
orionis/test/output/printer.py,sha256=gHDa2Q_q2ZnWM7j_JpCnrjzfKQrT-lMCcnVp7-2fUyU,17625
|
|
383
|
-
orionis/test/record/history.py,sha256=EOQcloMVdhlNl2lU9igQz8H4b-OtKtiwh2pgr_QZWOI,13186
|
|
384
383
|
orionis/test/records/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
385
384
|
orionis/test/records/logs.py,sha256=EOQcloMVdhlNl2lU9igQz8H4b-OtKtiwh2pgr_QZWOI,13186
|
|
386
385
|
orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
387
386
|
orionis/test/view/render.py,sha256=zd7xDvVfmQ2HxZamDTzL2-z2PpyL99EaolbbM7wTah4,5014
|
|
388
|
-
orionis-0.
|
|
387
|
+
orionis-0.385.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
|
|
389
388
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
390
389
|
tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
391
|
-
tests/example/test_example.py,sha256
|
|
390
|
+
tests/example/test_example.py,sha256=8G7kp74PZZ0Tdnw8WkheZ7lvZVFpdx_9ShOZBN9GEF0,25582
|
|
392
391
|
tests/foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
393
392
|
tests/foundation/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
394
393
|
tests/foundation/config/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -486,8 +485,8 @@ tests/support/wrapper/test_services_wrapper_docdict.py,sha256=nTNrvJkMSPx_aopEQ9
|
|
|
486
485
|
tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
487
486
|
tests/testing/test_testing_result.py,sha256=fnH7hjumNSErAFGITJgq2LHxSzvPF2tdtmHL9kyAv-Y,4409
|
|
488
487
|
tests/testing/test_testing_unit.py,sha256=d3CRGo6608fMzYcZKIKapjx_af2aigqWiKSiuK9euIY,7600
|
|
489
|
-
orionis-0.
|
|
490
|
-
orionis-0.
|
|
491
|
-
orionis-0.
|
|
492
|
-
orionis-0.
|
|
493
|
-
orionis-0.
|
|
488
|
+
orionis-0.385.0.dist-info/METADATA,sha256=nphEHmIaOM7ba4ts0gUfRjzTQZr4B0Id_TV_2oN2FH8,4772
|
|
489
|
+
orionis-0.385.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
490
|
+
orionis-0.385.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
|
|
491
|
+
orionis-0.385.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
|
492
|
+
orionis-0.385.0.dist-info/RECORD,,
|
tests/example/test_example.py
CHANGED
|
@@ -1,33 +1,735 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Orionis Framework Test Examples
|
|
3
|
+
===============================
|
|
4
|
+
|
|
5
|
+
This module contains comprehensive test examples demonstrating the capabilities
|
|
6
|
+
of the Orionis testing framework, including both synchronous and asynchronous
|
|
7
|
+
testing patterns with dependency injection.
|
|
8
|
+
|
|
9
|
+
Examples
|
|
10
|
+
--------
|
|
11
|
+
Run synchronous tests:
|
|
12
|
+
>>> from tests.example.test_example import TestSynchronousExample
|
|
13
|
+
>>> test = TestSynchronousExample()
|
|
14
|
+
>>> test.setUp()
|
|
15
|
+
>>> test.testBasicAssertions()
|
|
16
|
+
|
|
17
|
+
Run asynchronous tests:
|
|
18
|
+
>>> from tests.example.test_example import TestAsynchronousExample
|
|
19
|
+
>>> test = TestAsynchronousExample()
|
|
20
|
+
>>> await test.asyncSetUp()
|
|
21
|
+
>>> await test.testAsyncBasicOperations()
|
|
22
|
+
|
|
23
|
+
Notes
|
|
24
|
+
-----
|
|
25
|
+
These examples showcase:
|
|
26
|
+
- Dependency injection patterns
|
|
27
|
+
- Path resolution services
|
|
28
|
+
- Container integration
|
|
29
|
+
- Error handling strategies
|
|
30
|
+
- Data validation techniques
|
|
31
|
+
- Concurrent operations
|
|
32
|
+
- Async/await patterns
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import asyncio
|
|
36
|
+
import time
|
|
37
|
+
from typing import Dict, List, Any
|
|
38
|
+
from orionis.foundation.application import Application
|
|
1
39
|
from orionis.services.paths.contracts.resolver import IResolver
|
|
40
|
+
from orionis.test.cases.asynchronous import AsyncTestCase
|
|
2
41
|
from orionis.test.cases.synchronous import SyncTestCase
|
|
3
42
|
|
|
4
|
-
class
|
|
43
|
+
class TestSynchronousExample(SyncTestCase):
|
|
44
|
+
"""
|
|
45
|
+
Synchronous test example demonstrating Orionis framework capabilities.
|
|
46
|
+
|
|
47
|
+
This class showcases various testing patterns including dependency injection,
|
|
48
|
+
path resolution, container usage, and error handling in a synchronous context.
|
|
49
|
+
The tests demonstrate best practices for writing maintainable and reliable
|
|
50
|
+
test cases within the Orionis framework.
|
|
51
|
+
|
|
52
|
+
Attributes
|
|
53
|
+
----------
|
|
54
|
+
test_data : Dict[str, Any]
|
|
55
|
+
Test data dictionary containing sample files and expected values
|
|
56
|
+
for use across multiple test methods.
|
|
57
|
+
|
|
58
|
+
Methods
|
|
59
|
+
-------
|
|
60
|
+
setUp()
|
|
61
|
+
Initialize test environment before each test method execution.
|
|
62
|
+
tearDown()
|
|
63
|
+
Clean up resources after each test method completion.
|
|
64
|
+
testBasicAssertions()
|
|
65
|
+
Validate basic assertion functionality and patterns.
|
|
66
|
+
testPathResolution(paths)
|
|
67
|
+
Test path resolution service functionality with dependency injection.
|
|
68
|
+
testContainerIntegration(container)
|
|
69
|
+
Validate container dependency injection capabilities.
|
|
70
|
+
testErrorHandling()
|
|
71
|
+
Test error handling and exception management patterns.
|
|
72
|
+
testDataValidation()
|
|
73
|
+
Validate data validation and complex assertion patterns.
|
|
74
|
+
|
|
75
|
+
Examples
|
|
76
|
+
--------
|
|
77
|
+
Basic usage:
|
|
78
|
+
>>> test = TestSynchronousExample()
|
|
79
|
+
>>> test.setUp()
|
|
80
|
+
>>> test.testBasicAssertions()
|
|
81
|
+
>>> test.tearDown()
|
|
82
|
+
|
|
83
|
+
With dependency injection:
|
|
84
|
+
>>> test = TestSynchronousExample()
|
|
85
|
+
>>> test.setUp()
|
|
86
|
+
>>> # Path resolver will be injected automatically
|
|
87
|
+
>>> test.testPathResolution(resolver_instance)
|
|
88
|
+
>>> test.tearDown()
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def setUp(self) -> None:
|
|
92
|
+
"""
|
|
93
|
+
Set up test environment before each test method.
|
|
94
|
+
|
|
95
|
+
Initializes test data dictionary with sample files and expected values
|
|
96
|
+
that will be used across multiple test methods. This method is called
|
|
97
|
+
automatically before each test method execution.
|
|
98
|
+
|
|
99
|
+
Notes
|
|
100
|
+
-----
|
|
101
|
+
The test_data dictionary contains:
|
|
102
|
+
- sample_file: Path to the current test file for path resolution tests
|
|
103
|
+
- expected_values: List of integers used in assertion validation tests
|
|
104
|
+
"""
|
|
105
|
+
self.test_data: Dict[str, Any] = {
|
|
106
|
+
"sample_file": "tests/example/test_example.py",
|
|
107
|
+
"expected_values": [1, 2, 3, 4, 5]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
def tearDown(self) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Clean up resources after each test method completion.
|
|
113
|
+
|
|
114
|
+
Resets the test_data attribute to None to ensure clean state
|
|
115
|
+
between test method executions and prevent memory leaks.
|
|
116
|
+
"""
|
|
117
|
+
self.test_data = None
|
|
118
|
+
|
|
119
|
+
def testBasicAssertions(self) -> None:
|
|
120
|
+
"""
|
|
121
|
+
Test basic assertion functionality and patterns.
|
|
122
|
+
|
|
123
|
+
Validates the fundamental assertion methods provided by the testing
|
|
124
|
+
framework, including equality checks, boolean assertions, and
|
|
125
|
+
container membership validation.
|
|
126
|
+
|
|
127
|
+
Tests
|
|
128
|
+
-----
|
|
129
|
+
- Equality assertions (assertEqual)
|
|
130
|
+
- Boolean assertions (assertTrue, assertFalse)
|
|
131
|
+
- Container membership (assertIn, assertNotIn)
|
|
132
|
+
|
|
133
|
+
Raises
|
|
134
|
+
------
|
|
135
|
+
AssertionError
|
|
136
|
+
If any of the basic assertions fail, indicating a problem
|
|
137
|
+
with the testing framework's assertion mechanisms.
|
|
138
|
+
"""
|
|
139
|
+
# Test equality assertions
|
|
140
|
+
self.assertEqual(2, 2, "Basic equality check failed")
|
|
141
|
+
self.assertEqual(3, 3, "Second equality check failed")
|
|
142
|
+
|
|
143
|
+
# Test boolean assertions
|
|
144
|
+
self.assertTrue(True, "Boolean true assertion failed")
|
|
145
|
+
self.assertFalse(False, "Boolean false assertion failed")
|
|
146
|
+
|
|
147
|
+
# Test container assertions
|
|
148
|
+
self.assertIn(
|
|
149
|
+
3,
|
|
150
|
+
self.test_data["expected_values"],
|
|
151
|
+
"Value not found in container"
|
|
152
|
+
)
|
|
153
|
+
self.assertNotIn(
|
|
154
|
+
10,
|
|
155
|
+
self.test_data["expected_values"],
|
|
156
|
+
"Unexpected value found in container"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
def testPathResolution(self, paths: IResolver) -> None:
|
|
160
|
+
"""
|
|
161
|
+
Test path resolution service functionality with dependency injection.
|
|
162
|
+
|
|
163
|
+
Validates the path resolution service by testing relative path creation
|
|
164
|
+
and string conversion operations. This method demonstrates how dependency
|
|
165
|
+
injection works within the Orionis testing framework.
|
|
166
|
+
|
|
167
|
+
Parameters
|
|
168
|
+
----------
|
|
169
|
+
paths : IResolver
|
|
170
|
+
Injected path resolver service instance for testing path operations.
|
|
171
|
+
This parameter is automatically injected by the testing framework
|
|
172
|
+
based on the type annotation.
|
|
173
|
+
|
|
174
|
+
Tests
|
|
175
|
+
-----
|
|
176
|
+
- Relative path creation from string path
|
|
177
|
+
- Path string conversion and format validation
|
|
178
|
+
- Path ending validation
|
|
179
|
+
- Path content validation
|
|
180
|
+
|
|
181
|
+
Raises
|
|
182
|
+
------
|
|
183
|
+
AssertionError
|
|
184
|
+
If path resolution fails or returns unexpected results.
|
|
185
|
+
"""
|
|
186
|
+
# Test relative path resolution
|
|
187
|
+
relative_path = paths.relativePath(self.test_data["sample_file"])
|
|
188
|
+
path_string = relative_path.toString()
|
|
189
|
+
|
|
190
|
+
# Verify path resolution results
|
|
191
|
+
self.assertTrue(
|
|
192
|
+
path_string.endswith("test_example.py"),
|
|
193
|
+
"Path should end with test_example.py"
|
|
194
|
+
)
|
|
195
|
+
self.assertIn(
|
|
196
|
+
"tests\\example\\test_example.py",
|
|
197
|
+
path_string,
|
|
198
|
+
"Path should contain expected directory structure"
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def testContainerIntegration(self, container: Application) -> None:
|
|
202
|
+
"""
|
|
203
|
+
Test container dependency injection functionality.
|
|
204
|
+
|
|
205
|
+
Validates the container's ability to resolve services and manage
|
|
206
|
+
dependencies. This method demonstrates the dependency injection
|
|
207
|
+
capabilities of the Orionis application container.
|
|
208
|
+
|
|
209
|
+
Parameters
|
|
210
|
+
----------
|
|
211
|
+
container : Application
|
|
212
|
+
Injected application container instance for testing dependency
|
|
213
|
+
injection capabilities. The container manages service resolution
|
|
214
|
+
and dependency lifecycle.
|
|
215
|
+
|
|
216
|
+
Tests
|
|
217
|
+
-----
|
|
218
|
+
- Container instance validation
|
|
219
|
+
- Service resolution from container
|
|
220
|
+
- Service functionality validation through container
|
|
221
|
+
- Dependency lifecycle management
|
|
222
|
+
|
|
223
|
+
Raises
|
|
224
|
+
------
|
|
225
|
+
AssertionError
|
|
226
|
+
If container operations fail or services cannot be resolved.
|
|
227
|
+
"""
|
|
228
|
+
# Test container instance validation
|
|
229
|
+
self.assertIsNotNone(container, "Container instance should not be None")
|
|
230
|
+
|
|
231
|
+
# Test service resolution from container
|
|
232
|
+
path_resolver: IResolver = container.make(IResolver)
|
|
233
|
+
self.assertIsNotNone(
|
|
234
|
+
path_resolver,
|
|
235
|
+
"Service resolution should return valid instance"
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Test service functionality through container
|
|
239
|
+
test_path = path_resolver.relativePath("README.md")
|
|
240
|
+
self.assertIsNotNone(
|
|
241
|
+
test_path,
|
|
242
|
+
"Service method execution should return valid result"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def testErrorHandling(self) -> None:
|
|
246
|
+
"""
|
|
247
|
+
Test error handling and exception management patterns.
|
|
248
|
+
|
|
249
|
+
Validates the framework's ability to handle expected exceptions
|
|
250
|
+
and provides examples of proper exception testing patterns.
|
|
251
|
+
This method demonstrates both basic exception catching and
|
|
252
|
+
regex-based exception message validation.
|
|
253
|
+
|
|
254
|
+
Tests
|
|
255
|
+
-----
|
|
256
|
+
- Basic exception assertion with assertRaises
|
|
257
|
+
- Exception message pattern matching with assertRaisesRegex
|
|
258
|
+
- Proper exception type validation
|
|
259
|
+
- Exception context management
|
|
260
|
+
|
|
261
|
+
Raises
|
|
262
|
+
------
|
|
263
|
+
AssertionError
|
|
264
|
+
If expected exceptions are not raised or have incorrect types.
|
|
265
|
+
"""
|
|
266
|
+
# Test basic exception assertion
|
|
267
|
+
with self.assertRaises(ValueError):
|
|
268
|
+
raise ValueError("Expected test exception")
|
|
269
|
+
|
|
270
|
+
# Test exception message pattern matching
|
|
271
|
+
with self.assertRaisesRegex(RuntimeError, r"test.*pattern"):
|
|
272
|
+
raise RuntimeError("test error pattern match")
|
|
273
|
+
|
|
274
|
+
def testDataValidation(self) -> None:
|
|
275
|
+
"""
|
|
276
|
+
Test data validation and complex assertion patterns.
|
|
277
|
+
|
|
278
|
+
Validates complex data structures and demonstrates advanced assertion
|
|
279
|
+
techniques including list comparisons, dictionary operations, and
|
|
280
|
+
length validation. This method showcases best practices for testing
|
|
281
|
+
data integrity and structure validation.
|
|
282
|
+
|
|
283
|
+
Tests
|
|
284
|
+
-----
|
|
285
|
+
- List length validation
|
|
286
|
+
- List content comparison with assertListEqual
|
|
287
|
+
- Dictionary key existence validation
|
|
288
|
+
- Dictionary value validation
|
|
289
|
+
- Complex data structure assertions
|
|
290
|
+
|
|
291
|
+
Raises
|
|
292
|
+
------
|
|
293
|
+
AssertionError
|
|
294
|
+
If data validation fails or structures don't match expectations.
|
|
295
|
+
"""
|
|
296
|
+
# Test list operations and validation
|
|
297
|
+
test_list = [1, 2, 3, 4, 5]
|
|
298
|
+
self.assertEqual(
|
|
299
|
+
len(test_list),
|
|
300
|
+
5,
|
|
301
|
+
"List length should match expected value"
|
|
302
|
+
)
|
|
303
|
+
self.assertListEqual(
|
|
304
|
+
test_list,
|
|
305
|
+
self.test_data["expected_values"],
|
|
306
|
+
"List content should match expected values"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
# Test dictionary operations and validation
|
|
310
|
+
test_dict = {"key1": "value1", "key2": "value2"}
|
|
311
|
+
self.assertIn(
|
|
312
|
+
"key1",
|
|
313
|
+
test_dict,
|
|
314
|
+
"Dictionary should contain expected key"
|
|
315
|
+
)
|
|
316
|
+
self.assertEqual(
|
|
317
|
+
test_dict["key1"],
|
|
318
|
+
"value1",
|
|
319
|
+
"Dictionary value should match expected value"
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
class TestAsynchronousExample(AsyncTestCase):
|
|
323
|
+
"""
|
|
324
|
+
Asynchronous test example demonstrating async capabilities in Orionis framework.
|
|
325
|
+
|
|
326
|
+
This class showcases asynchronous testing patterns including async dependency
|
|
327
|
+
injection, concurrent operations, timing validation, and async error handling.
|
|
328
|
+
The tests demonstrate best practices for writing async test cases that are
|
|
329
|
+
both performant and reliable.
|
|
330
|
+
|
|
331
|
+
Attributes
|
|
332
|
+
----------
|
|
333
|
+
async_data : Dict[str, Any]
|
|
334
|
+
Asynchronous test data dictionary containing timing parameters,
|
|
335
|
+
task configuration, and expected results for async operations.
|
|
336
|
+
|
|
337
|
+
Methods
|
|
338
|
+
-------
|
|
339
|
+
asyncSetUp()
|
|
340
|
+
Initialize async test environment before each test method.
|
|
341
|
+
asyncTearDown()
|
|
342
|
+
Clean up async resources after each test method completion.
|
|
343
|
+
testAsyncBasicOperations()
|
|
344
|
+
Test basic async operations including timing and sleep validation.
|
|
345
|
+
testAsyncPathResolution(paths)
|
|
346
|
+
Test async path resolution with dependency injection.
|
|
347
|
+
testConcurrentOperations()
|
|
348
|
+
Test concurrent async operations and task management.
|
|
349
|
+
testAsyncErrorHandling()
|
|
350
|
+
Test async error handling and timeout management.
|
|
351
|
+
testAsyncContainerIntegration(container)
|
|
352
|
+
Test async container dependency injection functionality.
|
|
353
|
+
testAsyncDataProcessing()
|
|
354
|
+
Test async data processing and validation patterns.
|
|
355
|
+
|
|
356
|
+
Examples
|
|
357
|
+
--------
|
|
358
|
+
Basic async usage:
|
|
359
|
+
>>> test = TestAsynchronousExample()
|
|
360
|
+
>>> await test.asyncSetUp()
|
|
361
|
+
>>> await test.testAsyncBasicOperations()
|
|
362
|
+
>>> await test.asyncTearDown()
|
|
5
363
|
|
|
6
|
-
|
|
364
|
+
Concurrent operations:
|
|
365
|
+
>>> test = TestAsynchronousExample()
|
|
366
|
+
>>> await test.asyncSetUp()
|
|
367
|
+
>>> await test.testConcurrentOperations()
|
|
368
|
+
>>> await test.asyncTearDown()
|
|
369
|
+
"""
|
|
370
|
+
|
|
371
|
+
async def asyncSetUp(self) -> None:
|
|
372
|
+
"""
|
|
373
|
+
Set up async test environment before each test method.
|
|
374
|
+
|
|
375
|
+
Initializes async test data dictionary with timing parameters,
|
|
376
|
+
concurrent task configuration, and expected results for async
|
|
377
|
+
operations. This method is called automatically before each
|
|
378
|
+
async test method execution.
|
|
379
|
+
|
|
380
|
+
Notes
|
|
381
|
+
-----
|
|
382
|
+
The async_data dictionary contains:
|
|
383
|
+
- delay_time: Standard delay time for async operations testing
|
|
384
|
+
- concurrent_tasks: Number of concurrent tasks for testing
|
|
385
|
+
- expected_results: Expected results from concurrent operations
|
|
386
|
+
"""
|
|
387
|
+
self.async_data: Dict[str, Any] = {
|
|
388
|
+
"delay_time": 0.1,
|
|
389
|
+
"concurrent_tasks": 3,
|
|
390
|
+
"expected_results": ["result1", "result2", "result3"]
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async def asyncTearDown(self) -> None:
|
|
394
|
+
"""
|
|
395
|
+
Clean up async resources after each test method completion.
|
|
396
|
+
|
|
397
|
+
Resets the async_data attribute to None to ensure clean state
|
|
398
|
+
between async test method executions and prevent memory leaks.
|
|
399
|
+
"""
|
|
400
|
+
self.async_data = None
|
|
401
|
+
|
|
402
|
+
async def testAsyncBasicOperations(self) -> None:
|
|
403
|
+
"""
|
|
404
|
+
Test basic async operations including timing and sleep validation.
|
|
405
|
+
|
|
406
|
+
Validates the framework's ability to handle async operations
|
|
407
|
+
correctly, including timing precision and sleep duration validation.
|
|
408
|
+
This method demonstrates proper async timing testing patterns.
|
|
409
|
+
|
|
410
|
+
Tests
|
|
411
|
+
-----
|
|
412
|
+
- Async sleep duration validation
|
|
413
|
+
- Timing precision testing
|
|
414
|
+
- Async operation timing boundaries
|
|
415
|
+
- Time measurement accuracy
|
|
416
|
+
|
|
417
|
+
Raises
|
|
418
|
+
------
|
|
419
|
+
AssertionError
|
|
420
|
+
If async timing operations don't meet expected constraints.
|
|
421
|
+
"""
|
|
422
|
+
# Test async sleep and timing precision
|
|
423
|
+
start_time = time.time()
|
|
424
|
+
await asyncio.sleep(self.async_data["delay_time"])
|
|
425
|
+
end_time = time.time()
|
|
426
|
+
|
|
427
|
+
elapsed = end_time - start_time
|
|
428
|
+
self.assertGreaterEqual(
|
|
429
|
+
elapsed,
|
|
430
|
+
self.async_data["delay_time"],
|
|
431
|
+
"Async sleep duration should meet minimum time requirement"
|
|
432
|
+
)
|
|
433
|
+
self.assertLess(
|
|
434
|
+
elapsed,
|
|
435
|
+
self.async_data["delay_time"] + 0.05,
|
|
436
|
+
"Async sleep duration should not exceed maximum time tolerance"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
async def testAsyncPathResolution(self, paths: IResolver) -> None:
|
|
7
440
|
"""
|
|
8
|
-
|
|
441
|
+
Test async path resolution service functionality with dependency injection.
|
|
9
442
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
443
|
+
Validates async path resolution operations by simulating async I/O
|
|
444
|
+
operations and testing path resolution in an asynchronous context.
|
|
445
|
+
This method demonstrates async dependency injection patterns.
|
|
13
446
|
|
|
14
447
|
Parameters
|
|
15
448
|
----------
|
|
16
449
|
paths : IResolver
|
|
17
|
-
|
|
450
|
+
Injected path resolver service instance for async path operations.
|
|
451
|
+
This parameter is automatically injected by the async testing framework.
|
|
452
|
+
|
|
453
|
+
Tests
|
|
454
|
+
-----
|
|
455
|
+
- Async path resolution with simulated I/O delay
|
|
456
|
+
- Path string conversion in async context
|
|
457
|
+
- Path validation in async operations
|
|
458
|
+
- Async service method execution
|
|
459
|
+
|
|
460
|
+
Raises
|
|
461
|
+
------
|
|
462
|
+
AssertionError
|
|
463
|
+
If async path resolution fails or returns unexpected results.
|
|
464
|
+
"""
|
|
465
|
+
async def resolve_path_async(path_name: str) -> str:
|
|
466
|
+
"""
|
|
467
|
+
Simulate async path resolution with I/O delay.
|
|
468
|
+
|
|
469
|
+
Parameters
|
|
470
|
+
----------
|
|
471
|
+
path_name : str
|
|
472
|
+
Path name to resolve asynchronously.
|
|
473
|
+
|
|
474
|
+
Returns
|
|
475
|
+
-------
|
|
476
|
+
str
|
|
477
|
+
Resolved path as string.
|
|
478
|
+
"""
|
|
479
|
+
await asyncio.sleep(0.01) # Simulate async I/O operation
|
|
480
|
+
return paths.relativePath(path_name).toString()
|
|
481
|
+
|
|
482
|
+
# Test async path resolution
|
|
483
|
+
resolved_path = await resolve_path_async("tests/example/test_example.py")
|
|
484
|
+
self.assertTrue(
|
|
485
|
+
resolved_path.endswith("test_example.py"),
|
|
486
|
+
"Async path resolution should return correct file ending"
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
async def testConcurrentOperations(self) -> None:
|
|
490
|
+
"""
|
|
491
|
+
Test concurrent async operations and task management.
|
|
492
|
+
|
|
493
|
+
Validates the framework's ability to handle multiple concurrent
|
|
494
|
+
async operations correctly, including task creation, execution,
|
|
495
|
+
and result aggregation. This method demonstrates proper concurrent
|
|
496
|
+
async testing patterns.
|
|
497
|
+
|
|
498
|
+
Tests
|
|
499
|
+
-----
|
|
500
|
+
- Concurrent task creation and execution
|
|
501
|
+
- Task result aggregation with asyncio.gather
|
|
502
|
+
- Concurrent operation result validation
|
|
503
|
+
- Task count and result verification
|
|
504
|
+
|
|
505
|
+
Raises
|
|
506
|
+
------
|
|
507
|
+
AssertionError
|
|
508
|
+
If concurrent operations fail or results don't match expectations.
|
|
509
|
+
"""
|
|
510
|
+
async def async_task(task_id: int) -> str:
|
|
511
|
+
"""
|
|
512
|
+
Simulate async task with unique result.
|
|
513
|
+
|
|
514
|
+
Parameters
|
|
515
|
+
----------
|
|
516
|
+
task_id : int
|
|
517
|
+
Unique identifier for the async task.
|
|
518
|
+
|
|
519
|
+
Returns
|
|
520
|
+
-------
|
|
521
|
+
str
|
|
522
|
+
Task result string with task ID.
|
|
523
|
+
"""
|
|
524
|
+
await asyncio.sleep(0.05)
|
|
525
|
+
return f"result{task_id}"
|
|
526
|
+
|
|
527
|
+
# Create concurrent tasks
|
|
528
|
+
tasks = [
|
|
529
|
+
async_task(i)
|
|
530
|
+
for i in range(1, self.async_data["concurrent_tasks"] + 1)
|
|
531
|
+
]
|
|
532
|
+
|
|
533
|
+
# Execute tasks concurrently
|
|
534
|
+
results = await asyncio.gather(*tasks)
|
|
535
|
+
|
|
536
|
+
# Verify concurrent operation results
|
|
537
|
+
self.assertEqual(
|
|
538
|
+
len(results),
|
|
539
|
+
self.async_data["concurrent_tasks"],
|
|
540
|
+
"Concurrent task count should match expected value"
|
|
541
|
+
)
|
|
542
|
+
self.assertListEqual(
|
|
543
|
+
results,
|
|
544
|
+
self.async_data["expected_results"],
|
|
545
|
+
"Concurrent task results should match expected values"
|
|
546
|
+
)
|
|
18
547
|
|
|
19
|
-
|
|
20
|
-
-------
|
|
21
|
-
None
|
|
22
|
-
This test method doesn't return anything.
|
|
548
|
+
async def testAsyncErrorHandling(self) -> None:
|
|
23
549
|
"""
|
|
550
|
+
Test async error handling and timeout management.
|
|
551
|
+
|
|
552
|
+
Validates the framework's ability to handle async exceptions
|
|
553
|
+
and timeout scenarios correctly. This method demonstrates proper
|
|
554
|
+
async error handling patterns including exception catching and
|
|
555
|
+
timeout management.
|
|
556
|
+
|
|
557
|
+
Tests
|
|
558
|
+
-----
|
|
559
|
+
- Async exception assertion with assertRaises
|
|
560
|
+
- Async timeout handling with asyncio.wait_for
|
|
561
|
+
- Async exception type validation
|
|
562
|
+
- Async context manager exception handling
|
|
563
|
+
|
|
564
|
+
Raises
|
|
565
|
+
------
|
|
566
|
+
AssertionError
|
|
567
|
+
If async error handling doesn't work as expected.
|
|
568
|
+
"""
|
|
569
|
+
async def failing_async_function() -> None:
|
|
570
|
+
"""
|
|
571
|
+
Simulate async function that raises an exception.
|
|
572
|
+
|
|
573
|
+
Raises
|
|
574
|
+
------
|
|
575
|
+
ValueError
|
|
576
|
+
Always raises ValueError for testing purposes.
|
|
577
|
+
"""
|
|
578
|
+
await asyncio.sleep(0.01)
|
|
579
|
+
raise ValueError("Async test exception")
|
|
580
|
+
|
|
581
|
+
# Test async exception assertion
|
|
582
|
+
with self.assertRaises(ValueError):
|
|
583
|
+
await failing_async_function()
|
|
584
|
+
|
|
585
|
+
async def slow_async_function() -> str:
|
|
586
|
+
"""
|
|
587
|
+
Simulate slow async function for timeout testing.
|
|
588
|
+
|
|
589
|
+
Returns
|
|
590
|
+
-------
|
|
591
|
+
str
|
|
592
|
+
Result string after long delay.
|
|
593
|
+
"""
|
|
594
|
+
await asyncio.sleep(1.0)
|
|
595
|
+
return "slow result"
|
|
596
|
+
|
|
597
|
+
# Test async timeout handling
|
|
598
|
+
with self.assertRaises(asyncio.TimeoutError):
|
|
599
|
+
await asyncio.wait_for(slow_async_function(), timeout=0.1)
|
|
600
|
+
|
|
601
|
+
async def testAsyncContainerIntegration(self, container: Application) -> None:
|
|
602
|
+
"""
|
|
603
|
+
Test async container dependency injection functionality.
|
|
604
|
+
|
|
605
|
+
Validates the container's ability to resolve services in async
|
|
606
|
+
contexts and manage async dependencies. This method demonstrates
|
|
607
|
+
async dependency injection patterns and service resolution.
|
|
608
|
+
|
|
609
|
+
Parameters
|
|
610
|
+
----------
|
|
611
|
+
container : Application
|
|
612
|
+
Injected application container instance for testing async
|
|
613
|
+
dependency injection capabilities.
|
|
614
|
+
|
|
615
|
+
Tests
|
|
616
|
+
-----
|
|
617
|
+
- Async service resolution from container
|
|
618
|
+
- Async service method execution
|
|
619
|
+
- Async dependency lifecycle management
|
|
620
|
+
- Async service functionality validation
|
|
621
|
+
|
|
622
|
+
Raises
|
|
623
|
+
------
|
|
624
|
+
AssertionError
|
|
625
|
+
If async container operations fail or services cannot be resolved.
|
|
626
|
+
"""
|
|
627
|
+
async def resolve_service_async() -> IResolver:
|
|
628
|
+
"""
|
|
629
|
+
Simulate async service resolution.
|
|
630
|
+
|
|
631
|
+
Returns
|
|
632
|
+
-------
|
|
633
|
+
IResolver
|
|
634
|
+
Resolved path resolver service instance.
|
|
635
|
+
"""
|
|
636
|
+
await asyncio.sleep(0.01)
|
|
637
|
+
return container.make(IResolver)
|
|
638
|
+
|
|
639
|
+
# Test async service resolution
|
|
640
|
+
path_resolver = await resolve_service_async()
|
|
641
|
+
self.assertIsNotNone(
|
|
642
|
+
path_resolver,
|
|
643
|
+
"Async service resolution should return valid instance"
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
async def use_service_async() -> str:
|
|
647
|
+
"""
|
|
648
|
+
Simulate async service method execution.
|
|
649
|
+
|
|
650
|
+
Returns
|
|
651
|
+
-------
|
|
652
|
+
str
|
|
653
|
+
Result from async service method call.
|
|
654
|
+
"""
|
|
655
|
+
await asyncio.sleep(0.01)
|
|
656
|
+
return path_resolver.relativePath("README.md").toString()
|
|
657
|
+
|
|
658
|
+
# Test async service method execution
|
|
659
|
+
result = await use_service_async()
|
|
660
|
+
self.assertTrue(
|
|
661
|
+
result.endswith("README.md"),
|
|
662
|
+
"Async service method execution should return correct result"
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
async def testAsyncDataProcessing(self) -> None:
|
|
666
|
+
"""
|
|
667
|
+
Test async data processing and validation patterns.
|
|
668
|
+
|
|
669
|
+
Validates async data transformation, processing, and validation
|
|
670
|
+
operations. This method demonstrates proper async data handling
|
|
671
|
+
patterns and validation techniques.
|
|
672
|
+
|
|
673
|
+
Tests
|
|
674
|
+
-----
|
|
675
|
+
- Async data transformation operations
|
|
676
|
+
- Async data validation with type checking
|
|
677
|
+
- Async list processing and comparison
|
|
678
|
+
- Async data integrity validation
|
|
679
|
+
|
|
680
|
+
Raises
|
|
681
|
+
------
|
|
682
|
+
AssertionError
|
|
683
|
+
If async data processing fails or results don't match expectations.
|
|
684
|
+
"""
|
|
685
|
+
async def process_data_async(data: List[int]) -> List[int]:
|
|
686
|
+
"""
|
|
687
|
+
Simulate async data processing with transformation.
|
|
688
|
+
|
|
689
|
+
Parameters
|
|
690
|
+
----------
|
|
691
|
+
data : List[int]
|
|
692
|
+
Input data list for processing.
|
|
693
|
+
|
|
694
|
+
Returns
|
|
695
|
+
-------
|
|
696
|
+
List[int]
|
|
697
|
+
Processed data list with transformed values.
|
|
698
|
+
"""
|
|
699
|
+
await asyncio.sleep(0.01)
|
|
700
|
+
return [item * 2 for item in data]
|
|
701
|
+
|
|
702
|
+
# Test async data transformation
|
|
703
|
+
input_data = [1, 2, 3, 4, 5]
|
|
704
|
+
processed_data = await process_data_async(input_data)
|
|
705
|
+
expected_data = [2, 4, 6, 8, 10]
|
|
706
|
+
|
|
707
|
+
self.assertListEqual(
|
|
708
|
+
processed_data,
|
|
709
|
+
expected_data,
|
|
710
|
+
"Async data processing should transform values correctly"
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
async def validate_data_async(data: List[int]) -> bool:
|
|
714
|
+
"""
|
|
715
|
+
Simulate async data validation.
|
|
24
716
|
|
|
25
|
-
|
|
26
|
-
|
|
717
|
+
Parameters
|
|
718
|
+
----------
|
|
719
|
+
data : List[int]
|
|
720
|
+
Data list to validate.
|
|
27
721
|
|
|
28
|
-
|
|
29
|
-
|
|
722
|
+
Returns
|
|
723
|
+
-------
|
|
724
|
+
bool
|
|
725
|
+
True if all items are integers, False otherwise.
|
|
726
|
+
"""
|
|
727
|
+
await asyncio.sleep(0.01)
|
|
728
|
+
return all(isinstance(item, int) for item in data)
|
|
30
729
|
|
|
31
|
-
#
|
|
32
|
-
|
|
33
|
-
self.assertTrue(
|
|
730
|
+
# Test async data validation
|
|
731
|
+
is_valid = await validate_data_async(processed_data)
|
|
732
|
+
self.assertTrue(
|
|
733
|
+
is_valid,
|
|
734
|
+
"Async data validation should confirm data integrity"
|
|
735
|
+
)
|
orionis/test/record/history.py
DELETED
|
@@ -1,385 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import re
|
|
3
|
-
import sqlite3
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import Dict, List, Optional, Tuple
|
|
6
|
-
from orionis.services.environment.env import Env
|
|
7
|
-
from orionis.test.exceptions import OrionisTestPersistenceError, OrionisTestValueError
|
|
8
|
-
from orionis.test.contracts.logs import ITestLogs
|
|
9
|
-
|
|
10
|
-
class TestLogs(ITestLogs):
|
|
11
|
-
|
|
12
|
-
def __init__(
|
|
13
|
-
self,
|
|
14
|
-
storage_path: Optional[str] = None,
|
|
15
|
-
db_name: Optional[str] = 'tests.sqlite',
|
|
16
|
-
table_name: Optional[str] = 'reports',
|
|
17
|
-
) -> None:
|
|
18
|
-
"""
|
|
19
|
-
Initialize the history storage for test logs.
|
|
20
|
-
|
|
21
|
-
Parameters
|
|
22
|
-
----------
|
|
23
|
-
storage_path : Optional[str], default=None
|
|
24
|
-
Directory path where the database file will be stored. If not provided,
|
|
25
|
-
the path is determined from the TEST_DB_PATH environment variable or
|
|
26
|
-
defaults to 'orionis/test/logs/storage' in the current working directory.
|
|
27
|
-
db_name : Optional[str], default='tests.sqlite'
|
|
28
|
-
Name of the SQLite database file. Must be alphanumeric or underscore and
|
|
29
|
-
end with '.sqlite'.
|
|
30
|
-
table_name : Optional[str], default='reports'
|
|
31
|
-
Name of the table to use in the database. Must be alphanumeric or underscore.
|
|
32
|
-
|
|
33
|
-
Raises
|
|
34
|
-
------
|
|
35
|
-
OrionisTestValueError
|
|
36
|
-
If db_name or table_name do not meet the required format.
|
|
37
|
-
"""
|
|
38
|
-
|
|
39
|
-
# Validate db_name: only alphanumeric and underscores, must end with .sqlite
|
|
40
|
-
if not isinstance(db_name, str) or not re.fullmatch(r'[a-zA-Z0-9_]+\.sqlite', db_name):
|
|
41
|
-
raise OrionisTestValueError("Database name must be alphanumeric/underscore and end with '.sqlite'.")
|
|
42
|
-
self.__db_name = db_name
|
|
43
|
-
|
|
44
|
-
# Validate table_name: only alphanumeric and underscores
|
|
45
|
-
if not isinstance(table_name, str) or not re.fullmatch(r'[a-zA-Z0-9_]+', table_name):
|
|
46
|
-
raise OrionisTestValueError("Table name must be alphanumeric/underscore only.")
|
|
47
|
-
self.__table_name = table_name
|
|
48
|
-
|
|
49
|
-
# Determine database path
|
|
50
|
-
db_path = None
|
|
51
|
-
if storage_path:
|
|
52
|
-
db_path = Path(storage_path).expanduser().resolve()
|
|
53
|
-
if db_path.is_dir():
|
|
54
|
-
db_path = db_path / self.__db_name
|
|
55
|
-
else:
|
|
56
|
-
env_path = Env.get("TEST_DB_PATH", None)
|
|
57
|
-
if env_path:
|
|
58
|
-
db_path = Path(env_path).expanduser().resolve()
|
|
59
|
-
if db_path.is_dir():
|
|
60
|
-
db_path = db_path / self.__db_name
|
|
61
|
-
else:
|
|
62
|
-
db_path = Path.cwd() / 'storage/framework/testing' / self.__db_name
|
|
63
|
-
|
|
64
|
-
# Ensure parent directory exists
|
|
65
|
-
db_path.parent.mkdir(parents=True, exist_ok=True)
|
|
66
|
-
|
|
67
|
-
# Store path in environment
|
|
68
|
-
Env.set("TEST_DB_PATH", str(db_path), 'path')
|
|
69
|
-
self.__db_path = db_path
|
|
70
|
-
|
|
71
|
-
# Create a connection to the database, initially set to None
|
|
72
|
-
self._conn: Optional[sqlite3.Connection] = None
|
|
73
|
-
|
|
74
|
-
def __connect(
|
|
75
|
-
self
|
|
76
|
-
) -> None:
|
|
77
|
-
"""
|
|
78
|
-
Establishes a connection to the SQLite database if not already connected.
|
|
79
|
-
|
|
80
|
-
Attempts to create a new SQLite connection using the provided database path.
|
|
81
|
-
If the connection fails, raises an OrionisTestPersistenceError with the error details.
|
|
82
|
-
|
|
83
|
-
Raises
|
|
84
|
-
------
|
|
85
|
-
OrionisTestPersistenceError
|
|
86
|
-
If a database connection error occurs.
|
|
87
|
-
"""
|
|
88
|
-
if self._conn is None:
|
|
89
|
-
try:
|
|
90
|
-
self._conn = sqlite3.connect(str(self.__db_path))
|
|
91
|
-
except (sqlite3.Error, Exception) as e:
|
|
92
|
-
raise OrionisTestPersistenceError(f"Database connection error: {e}")
|
|
93
|
-
|
|
94
|
-
def __createTableIfNotExists(
|
|
95
|
-
self
|
|
96
|
-
) -> bool:
|
|
97
|
-
"""
|
|
98
|
-
Ensures that the test history table exists in the database.
|
|
99
|
-
|
|
100
|
-
Connects to the database and creates the table with the required schema if it does not already exist.
|
|
101
|
-
Handles any SQLite errors by rolling back the transaction and raising a custom exception.
|
|
102
|
-
|
|
103
|
-
Raises
|
|
104
|
-
------
|
|
105
|
-
OrionisTestPersistenceError
|
|
106
|
-
If the table creation fails due to a database error.
|
|
107
|
-
|
|
108
|
-
Returns
|
|
109
|
-
-------
|
|
110
|
-
bool
|
|
111
|
-
True if the table was created successfully or already exists, False otherwise.
|
|
112
|
-
"""
|
|
113
|
-
|
|
114
|
-
self.__connect()
|
|
115
|
-
try:
|
|
116
|
-
cursor = self._conn.cursor()
|
|
117
|
-
cursor.execute(f'''
|
|
118
|
-
CREATE TABLE IF NOT EXISTS {self.__table_name} (
|
|
119
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
120
|
-
json TEXT NOT NULL,
|
|
121
|
-
total_tests INTEGER,
|
|
122
|
-
passed INTEGER,
|
|
123
|
-
failed INTEGER,
|
|
124
|
-
errors INTEGER,
|
|
125
|
-
skipped INTEGER,
|
|
126
|
-
total_time REAL,
|
|
127
|
-
success_rate REAL,
|
|
128
|
-
timestamp TEXT
|
|
129
|
-
)
|
|
130
|
-
''')
|
|
131
|
-
self._conn.commit()
|
|
132
|
-
return True
|
|
133
|
-
except sqlite3.Error as e:
|
|
134
|
-
if self._conn:
|
|
135
|
-
self._conn.rollback()
|
|
136
|
-
raise OrionisTestPersistenceError(f"Failed to create table: {e}")
|
|
137
|
-
finally:
|
|
138
|
-
if self._conn:
|
|
139
|
-
self.__close()
|
|
140
|
-
self._conn = None
|
|
141
|
-
|
|
142
|
-
def __insertReport(
|
|
143
|
-
self,
|
|
144
|
-
report: Dict
|
|
145
|
-
) -> bool:
|
|
146
|
-
"""
|
|
147
|
-
Inserts a test report into the history database table.
|
|
148
|
-
|
|
149
|
-
Parameters
|
|
150
|
-
----------
|
|
151
|
-
report : Dict
|
|
152
|
-
A dictionary containing the report data. Must include the following keys:
|
|
153
|
-
- total_tests
|
|
154
|
-
- passed
|
|
155
|
-
- failed
|
|
156
|
-
- errors
|
|
157
|
-
- skipped
|
|
158
|
-
- total_time
|
|
159
|
-
- success_rate
|
|
160
|
-
- timestamp
|
|
161
|
-
|
|
162
|
-
Raises
|
|
163
|
-
------
|
|
164
|
-
OrionisTestPersistenceError
|
|
165
|
-
If there is an error inserting the report into the database.
|
|
166
|
-
OrionisTestValueError
|
|
167
|
-
If required fields are missing from the report.
|
|
168
|
-
|
|
169
|
-
Returns
|
|
170
|
-
-------
|
|
171
|
-
bool
|
|
172
|
-
True if the report was successfully inserted, False otherwise.
|
|
173
|
-
"""
|
|
174
|
-
|
|
175
|
-
# Required fields in the report
|
|
176
|
-
fields = [
|
|
177
|
-
"json", "total_tests", "passed", "failed", "errors",
|
|
178
|
-
"skipped", "total_time", "success_rate", "timestamp"
|
|
179
|
-
]
|
|
180
|
-
|
|
181
|
-
# Validate report structure
|
|
182
|
-
missing = []
|
|
183
|
-
for key in fields:
|
|
184
|
-
if key not in report and key != "json":
|
|
185
|
-
missing.append(key)
|
|
186
|
-
if missing:
|
|
187
|
-
raise OrionisTestValueError(f"Missing report fields: {missing}")
|
|
188
|
-
|
|
189
|
-
# Insert the report into the database
|
|
190
|
-
self.__connect()
|
|
191
|
-
try:
|
|
192
|
-
|
|
193
|
-
# Query to insert the report into the table
|
|
194
|
-
query = f'''
|
|
195
|
-
INSERT INTO {self.__table_name} (
|
|
196
|
-
json, total_tests, passed, failed, errors,
|
|
197
|
-
skipped, total_time, success_rate, timestamp
|
|
198
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
199
|
-
'''
|
|
200
|
-
|
|
201
|
-
# Execute the insert query with the report data
|
|
202
|
-
cursor = self._conn.cursor()
|
|
203
|
-
cursor.execute(query, (
|
|
204
|
-
json.dumps(report),
|
|
205
|
-
report["total_tests"],
|
|
206
|
-
report["passed"],
|
|
207
|
-
report["failed"],
|
|
208
|
-
report["errors"],
|
|
209
|
-
report["skipped"],
|
|
210
|
-
report["total_time"],
|
|
211
|
-
report["success_rate"],
|
|
212
|
-
report["timestamp"]
|
|
213
|
-
))
|
|
214
|
-
self._conn.commit()
|
|
215
|
-
return True
|
|
216
|
-
except sqlite3.Error as e:
|
|
217
|
-
if self._conn:
|
|
218
|
-
self._conn.rollback()
|
|
219
|
-
raise OrionisTestPersistenceError(f"Failed to insert report: {e}")
|
|
220
|
-
finally:
|
|
221
|
-
if self._conn:
|
|
222
|
-
self.__close()
|
|
223
|
-
self._conn = None
|
|
224
|
-
|
|
225
|
-
def __getReports(
|
|
226
|
-
self,
|
|
227
|
-
first: Optional[int] = None,
|
|
228
|
-
last: Optional[int] = None
|
|
229
|
-
) -> List[Tuple]:
|
|
230
|
-
"""
|
|
231
|
-
Retrieves a specified number of report records from the database, ordered by their ID.
|
|
232
|
-
|
|
233
|
-
Parameters
|
|
234
|
-
----------
|
|
235
|
-
first : Optional[int], default=None
|
|
236
|
-
The number of earliest reports to retrieve, ordered ascending by ID.
|
|
237
|
-
last : Optional[int], default=None
|
|
238
|
-
The number of latest reports to retrieve, ordered descending by ID.
|
|
239
|
-
|
|
240
|
-
Returns
|
|
241
|
-
-------
|
|
242
|
-
List[Tuple]
|
|
243
|
-
A list of tuples representing the report records.
|
|
244
|
-
|
|
245
|
-
Raises
|
|
246
|
-
------
|
|
247
|
-
OrionisTestValueError
|
|
248
|
-
If both 'first' and 'last' are specified, or if either is not a positive integer.
|
|
249
|
-
OrionisTestPersistenceError
|
|
250
|
-
If there is an error retrieving reports from the database.
|
|
251
|
-
"""
|
|
252
|
-
|
|
253
|
-
# Validate parameters
|
|
254
|
-
if first is not None and last is not None:
|
|
255
|
-
raise OrionisTestValueError(
|
|
256
|
-
"Cannot specify both 'first' and 'last' parameters. Use one or the other."
|
|
257
|
-
)
|
|
258
|
-
if first is not None:
|
|
259
|
-
if not isinstance(first, int) or first <= 0:
|
|
260
|
-
raise OrionisTestValueError("'first' must be an integer greater than 0.")
|
|
261
|
-
if last is not None:
|
|
262
|
-
if not isinstance(last, int) or last <= 0:
|
|
263
|
-
raise OrionisTestValueError("'last' must be an integer greater than 0.")
|
|
264
|
-
|
|
265
|
-
order = 'DESC' if last is not None else 'ASC'
|
|
266
|
-
quantity = first if first is not None else last
|
|
267
|
-
|
|
268
|
-
self.__connect()
|
|
269
|
-
try:
|
|
270
|
-
cursor = self._conn.cursor()
|
|
271
|
-
query = f"SELECT * FROM {self.__table_name} ORDER BY id {order} LIMIT ?"
|
|
272
|
-
cursor.execute(query, (quantity,))
|
|
273
|
-
results = cursor.fetchall()
|
|
274
|
-
return results
|
|
275
|
-
except sqlite3.Error as e:
|
|
276
|
-
raise OrionisTestPersistenceError(f"Failed to retrieve reports from '{self.__db_name}': {e}")
|
|
277
|
-
finally:
|
|
278
|
-
if self._conn:
|
|
279
|
-
self.__close()
|
|
280
|
-
self._conn = None
|
|
281
|
-
|
|
282
|
-
def __resetDatabase(
|
|
283
|
-
self
|
|
284
|
-
) -> bool:
|
|
285
|
-
"""
|
|
286
|
-
Resets the database by dropping the existing table.
|
|
287
|
-
This method connects to the database, drops the table specified by
|
|
288
|
-
`self.__table_name` if it exists, commits the changes, and then closes
|
|
289
|
-
the connection. If an error occurs during the process, an
|
|
290
|
-
OrionisTestPersistenceError is raised.
|
|
291
|
-
|
|
292
|
-
Raises
|
|
293
|
-
------
|
|
294
|
-
OrionisTestPersistenceError
|
|
295
|
-
If the database reset operation fails due to an SQLite error.
|
|
296
|
-
|
|
297
|
-
Returns
|
|
298
|
-
-------
|
|
299
|
-
bool
|
|
300
|
-
True if the database was successfully reset, False otherwise.
|
|
301
|
-
"""
|
|
302
|
-
|
|
303
|
-
self.__connect()
|
|
304
|
-
try:
|
|
305
|
-
cursor = self._conn.cursor()
|
|
306
|
-
cursor.execute(f'DROP TABLE IF EXISTS {self.__table_name}')
|
|
307
|
-
self._conn.commit()
|
|
308
|
-
return True
|
|
309
|
-
except sqlite3.Error as e:
|
|
310
|
-
raise OrionisTestPersistenceError(f"Failed to reset database: {e}")
|
|
311
|
-
finally:
|
|
312
|
-
if self._conn:
|
|
313
|
-
self.__close()
|
|
314
|
-
self._conn = None
|
|
315
|
-
|
|
316
|
-
def __close(
|
|
317
|
-
self
|
|
318
|
-
) -> None:
|
|
319
|
-
"""
|
|
320
|
-
Closes the current database connection.
|
|
321
|
-
This method checks if a database connection exists. If so, it closes the connection and sets the connection attribute to None.
|
|
322
|
-
|
|
323
|
-
Returns
|
|
324
|
-
-------
|
|
325
|
-
None
|
|
326
|
-
"""
|
|
327
|
-
|
|
328
|
-
if self._conn:
|
|
329
|
-
self._conn.close()
|
|
330
|
-
self._conn = None
|
|
331
|
-
|
|
332
|
-
def create(
|
|
333
|
-
self,
|
|
334
|
-
report: Dict
|
|
335
|
-
) -> bool:
|
|
336
|
-
"""
|
|
337
|
-
Create a new test report in the history database.
|
|
338
|
-
|
|
339
|
-
Parameters
|
|
340
|
-
----------
|
|
341
|
-
report : Dict
|
|
342
|
-
A dictionary containing the test report data.
|
|
343
|
-
|
|
344
|
-
Returns
|
|
345
|
-
-------
|
|
346
|
-
bool
|
|
347
|
-
True if the report was successfully created, False otherwise.
|
|
348
|
-
"""
|
|
349
|
-
self.__createTableIfNotExists()
|
|
350
|
-
return self.__insertReport(report)
|
|
351
|
-
|
|
352
|
-
def reset(
|
|
353
|
-
self
|
|
354
|
-
) -> bool:
|
|
355
|
-
"""
|
|
356
|
-
Reset the history database by dropping the existing table.
|
|
357
|
-
|
|
358
|
-
Returns
|
|
359
|
-
-------
|
|
360
|
-
bool
|
|
361
|
-
True if the database was successfully reset, False otherwise.
|
|
362
|
-
"""
|
|
363
|
-
return self.__resetDatabase()
|
|
364
|
-
|
|
365
|
-
def get(
|
|
366
|
-
self,
|
|
367
|
-
first: Optional[int] = None,
|
|
368
|
-
last: Optional[int] = None
|
|
369
|
-
) -> List[Tuple]:
|
|
370
|
-
"""
|
|
371
|
-
Retrieve test reports from the history database.
|
|
372
|
-
|
|
373
|
-
Parameters
|
|
374
|
-
----------
|
|
375
|
-
first : Optional[int], default=None
|
|
376
|
-
The number of earliest reports to retrieve, ordered ascending by ID.
|
|
377
|
-
last : Optional[int], default=None
|
|
378
|
-
The number of latest reports to retrieve, ordered descending by ID.
|
|
379
|
-
|
|
380
|
-
Returns
|
|
381
|
-
-------
|
|
382
|
-
List[Tuple]
|
|
383
|
-
A list of tuples representing the retrieved reports.
|
|
384
|
-
"""
|
|
385
|
-
return self.__getReports(first, last)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|