orionis 0.406.0__py3-none-any.whl → 0.408.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/container/container.py +11 -9
- orionis/container/enums/lifetimes.py +2 -0
- orionis/container/validators/__init__.py +21 -0
- orionis/metadata/framework.py +1 -1
- orionis/services/asynchrony/contracts/coroutines.py +13 -5
- orionis/services/asynchrony/coroutines.py +33 -29
- orionis/services/asynchrony/exceptions/exception.py +9 -1
- orionis/services/environment/core/dot_env.py +46 -34
- orionis/services/environment/enums/__init__.py +0 -0
- orionis/services/environment/enums/cast_type.py +42 -0
- orionis/services/environment/serializer/__init__.py +0 -0
- orionis/services/environment/serializer/values.py +21 -0
- orionis/services/environment/validators/__init__.py +0 -0
- orionis/services/environment/validators/key_name.py +46 -0
- orionis/services/environment/validators/types.py +45 -0
- orionis/services/system/contracts/imports.py +38 -18
- orionis/services/system/contracts/workers.py +29 -12
- orionis/services/system/imports.py +65 -25
- orionis/services/system/runtime/imports.py +18 -9
- orionis/services/system/workers.py +49 -16
- orionis/test/output/dumper.py +1 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/METADATA +1 -1
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/RECORD +52 -45
- tests/container/context/test_manager.py +15 -5
- tests/container/context/test_scope.py +12 -4
- tests/container/entities/test_binding.py +130 -21
- tests/container/enums/test_lifetimes.py +52 -18
- tests/container/facades/test_facade.py +29 -12
- tests/container/providers/test_providers.py +17 -10
- tests/container/resolver/test_resolver.py +14 -7
- tests/container/test_container.py +226 -71
- tests/container/test_singleton.py +43 -24
- tests/container/test_thread_safety.py +28 -156
- tests/container/validators/test_implements.py +59 -13
- tests/container/validators/test_is_abstract_class.py +73 -25
- tests/container/validators/test_is_callable.py +55 -26
- tests/container/validators/test_is_concrete_class.py +80 -17
- tests/container/validators/test_is_instance.py +67 -22
- tests/container/validators/test_is_not_subclass.py +28 -95
- tests/container/validators/test_is_subclass.py +84 -21
- tests/container/validators/test_is_valid_alias.py +46 -12
- tests/container/validators/test_lifetime.py +45 -14
- tests/example/test_example.py +2 -2
- tests/metadata/test_metadata_framework.py +71 -6
- tests/metadata/test_metadata_package.py +55 -10
- tests/services/asynchrony/test_services_asynchrony_coroutine.py +52 -7
- tests/services/system/test_services_system_imports.py +119 -16
- tests/services/system/test_services_system_workers.py +71 -30
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/WHEEL +0 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/top_level.txt +0 -0
- {orionis-0.406.0.dist-info → orionis-0.408.0.dist-info}/zip-safe +0 -0
|
@@ -5,55 +5,64 @@ from orionis.container.container import Container
|
|
|
5
5
|
from orionis.test.cases.asynchronous import AsyncTestCase
|
|
6
6
|
|
|
7
7
|
class TestSingleton(AsyncTestCase):
|
|
8
|
-
"""Test suite for singleton pattern implementation."""
|
|
9
8
|
|
|
10
9
|
async def testSingletonBasicFunctionality(self) -> None:
|
|
11
10
|
"""
|
|
12
|
-
|
|
11
|
+
Tests the fundamental behavior of the singleton pattern for `Container` and `Orionis` classes.
|
|
13
12
|
|
|
14
|
-
This
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
This method verifies the following:
|
|
14
|
+
- Multiple instances of `Container` refer to the same object.
|
|
15
|
+
- Multiple instances of `Orionis` refer to the same object.
|
|
16
|
+
- The singleton instances of `Container` and `Orionis` are distinct from each other.
|
|
17
|
+
|
|
18
|
+
Returns
|
|
19
|
+
-------
|
|
20
|
+
None
|
|
21
|
+
This method does not return any value. Assertions are used to validate singleton behavior.
|
|
18
22
|
"""
|
|
19
|
-
# Create multiple instances
|
|
23
|
+
# Create multiple instances of Container and Orionis
|
|
20
24
|
container1 = Container()
|
|
21
25
|
container2 = Container()
|
|
22
26
|
orionis1 = Orionis()
|
|
23
27
|
orionis2 = Orionis()
|
|
24
28
|
|
|
25
|
-
#
|
|
29
|
+
# Assert that all Container instances are the same object
|
|
26
30
|
self.assertIs(container1, container2)
|
|
27
31
|
self.assertEqual(id(container1), id(container2))
|
|
28
32
|
|
|
29
|
-
#
|
|
33
|
+
# Assert that all Orionis instances are the same object
|
|
30
34
|
self.assertIs(orionis1, orionis2)
|
|
31
35
|
self.assertEqual(id(orionis1), id(orionis2))
|
|
32
36
|
|
|
33
|
-
#
|
|
37
|
+
# Assert that Container and Orionis are different singleton instances
|
|
34
38
|
self.assertIsNot(container1, orionis1)
|
|
35
39
|
|
|
36
40
|
async def testSingletonThreadingSafety(self) -> None:
|
|
37
41
|
"""
|
|
38
|
-
|
|
42
|
+
Validates the thread safety of the singleton pattern for `Container` and `Orionis` classes.
|
|
43
|
+
|
|
44
|
+
This method ensures that, even when multiple threads attempt to instantiate
|
|
45
|
+
`Container` and `Orionis` simultaneously, only one instance of each class is created.
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
None
|
|
50
|
+
This method does not return any value. Assertions are used to validate thread-safe singleton behavior.
|
|
42
51
|
"""
|
|
43
52
|
container_instances = []
|
|
44
53
|
orionis_instances = []
|
|
45
54
|
|
|
46
55
|
def create_container():
|
|
47
|
-
"""Create
|
|
48
|
-
time.sleep(0.01) #
|
|
56
|
+
"""Create and append a Container instance in a thread."""
|
|
57
|
+
time.sleep(0.01) # Increase chance of race condition
|
|
49
58
|
container_instances.append(Container())
|
|
50
59
|
|
|
51
60
|
def create_orionis():
|
|
52
|
-
"""Create
|
|
53
|
-
time.sleep(0.01) #
|
|
61
|
+
"""Create and append an Orionis instance in a thread."""
|
|
62
|
+
time.sleep(0.01) # Increase chance of race condition
|
|
54
63
|
orionis_instances.append(Orionis())
|
|
55
64
|
|
|
56
|
-
# Create
|
|
65
|
+
# Create threads for concurrent instantiation
|
|
57
66
|
threads = []
|
|
58
67
|
for i in range(10):
|
|
59
68
|
t1 = threading.Thread(target=create_container)
|
|
@@ -68,10 +77,11 @@ class TestSingleton(AsyncTestCase):
|
|
|
68
77
|
for t in threads:
|
|
69
78
|
t.join()
|
|
70
79
|
|
|
71
|
-
#
|
|
80
|
+
# Collect instance IDs for verification
|
|
72
81
|
container_ids = [id(c) for c in container_instances]
|
|
73
82
|
orionis_ids = [id(o) for o in orionis_instances]
|
|
74
83
|
|
|
84
|
+
# Assert that only one unique instance exists for each class
|
|
75
85
|
self.assertEqual(len(set(container_ids)), 1)
|
|
76
86
|
self.assertEqual(len(set(orionis_ids)), 1)
|
|
77
87
|
self.assertEqual(len(container_instances), 10)
|
|
@@ -79,20 +89,29 @@ class TestSingleton(AsyncTestCase):
|
|
|
79
89
|
|
|
80
90
|
async def testInheritanceSeparation(self) -> None:
|
|
81
91
|
"""
|
|
82
|
-
|
|
92
|
+
Ensures that singleton instances are maintained separately for `Container` and `Orionis` classes.
|
|
83
93
|
|
|
84
|
-
This
|
|
85
|
-
|
|
94
|
+
This method checks that:
|
|
95
|
+
- Each class maintains its own singleton instance.
|
|
96
|
+
- Data added to one singleton does not affect the other.
|
|
97
|
+
- Both classes correctly implement the singleton pattern independently.
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
None
|
|
102
|
+
This method does not return any value. Assertions are used to validate singleton separation.
|
|
86
103
|
"""
|
|
87
104
|
container = Container()
|
|
88
105
|
orionis = Orionis()
|
|
89
106
|
|
|
90
|
-
# Add
|
|
107
|
+
# Add a callable to the Container singleton
|
|
91
108
|
container.callable("test_container", lambda: "container_value")
|
|
92
109
|
|
|
93
|
-
#
|
|
110
|
+
# Verify that Container and Orionis are distinct singletons
|
|
94
111
|
self.assertEqual(type(container).__name__, "Container")
|
|
95
112
|
self.assertEqual(type(orionis).__name__, "Application")
|
|
96
113
|
self.assertIsNot(container, orionis)
|
|
114
|
+
|
|
115
|
+
# Check that the callable is bound only to Container
|
|
97
116
|
self.assertTrue(container.bound('test_container'))
|
|
98
117
|
self.assertFalse(orionis.bound('test_container'))
|
|
@@ -10,16 +10,34 @@ class TestThreadSafety(AsyncTestCase):
|
|
|
10
10
|
|
|
11
11
|
async def testStressSingleton(self) -> None:
|
|
12
12
|
"""
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
This
|
|
16
|
-
|
|
13
|
+
Stress test singleton behavior under extreme concurrent conditions.
|
|
14
|
+
|
|
15
|
+
This method creates a large number of threads that simultaneously attempt to
|
|
16
|
+
instantiate singleton objects (`Container` and `Orionis`). It verifies that
|
|
17
|
+
only one unique instance of each singleton exists, regardless of concurrent
|
|
18
|
+
access, and that the singleton instances are distinct from each other.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
None
|
|
23
|
+
|
|
24
|
+
Returns
|
|
25
|
+
-------
|
|
26
|
+
None
|
|
27
|
+
This method does not return any value. Assertions are used to validate
|
|
28
|
+
singleton behavior under stress.
|
|
29
|
+
|
|
30
|
+
Notes
|
|
31
|
+
-----
|
|
32
|
+
- Random delays are introduced to increase the likelihood of race conditions.
|
|
33
|
+
- ThreadPoolExecutor is used to simulate high concurrency.
|
|
34
|
+
- The test ensures that singleton integrity is maintained even under heavy load.
|
|
17
35
|
"""
|
|
18
36
|
container_instances = []
|
|
19
37
|
orionis_instances = []
|
|
20
38
|
|
|
21
39
|
def create_container_with_delay():
|
|
22
|
-
"""Create
|
|
40
|
+
"""Create a Container instance after a random delay to simulate real-world concurrency."""
|
|
23
41
|
# Random delay to increase chance of race conditions
|
|
24
42
|
time.sleep(random.uniform(0.001, 0.01))
|
|
25
43
|
container = Container()
|
|
@@ -27,16 +45,17 @@ class TestThreadSafety(AsyncTestCase):
|
|
|
27
45
|
return id(container)
|
|
28
46
|
|
|
29
47
|
def create_orionis_with_delay():
|
|
30
|
-
"""Create
|
|
48
|
+
"""Create an Orionis instance after a random delay to simulate real-world concurrency."""
|
|
31
49
|
# Random delay to increase chance of race conditions
|
|
32
50
|
time.sleep(random.uniform(0.001, 0.01))
|
|
33
51
|
orionis = Orionis()
|
|
34
52
|
orionis_instances.append(orionis)
|
|
35
53
|
return id(orionis)
|
|
36
54
|
|
|
37
|
-
#
|
|
55
|
+
# Number of concurrent threads to simulate
|
|
38
56
|
num_threads = 100
|
|
39
57
|
|
|
58
|
+
# Use ThreadPoolExecutor to run tasks concurrently
|
|
40
59
|
with ThreadPoolExecutor(max_workers=50) as executor:
|
|
41
60
|
# Submit container creation tasks
|
|
42
61
|
container_futures = [
|
|
@@ -50,11 +69,11 @@ class TestThreadSafety(AsyncTestCase):
|
|
|
50
69
|
for _ in range(num_threads)
|
|
51
70
|
]
|
|
52
71
|
|
|
53
|
-
# Wait for all tasks to complete
|
|
72
|
+
# Wait for all tasks to complete and collect instance IDs
|
|
54
73
|
container_ids = [future.result() for future in as_completed(container_futures)]
|
|
55
74
|
orionis_ids = [future.result() for future in as_completed(orionis_futures)]
|
|
56
75
|
|
|
57
|
-
# Verify all instances are the same
|
|
76
|
+
# Verify all instances are the same (singleton property)
|
|
58
77
|
unique_container_ids = set(container_ids)
|
|
59
78
|
unique_orionis_ids = set(orionis_ids)
|
|
60
79
|
|
|
@@ -68,150 +87,3 @@ class TestThreadSafety(AsyncTestCase):
|
|
|
68
87
|
orionis_id = list(unique_orionis_ids)[0] if unique_orionis_ids else None
|
|
69
88
|
|
|
70
89
|
self.assertNotEqual(container_id, orionis_id)
|
|
71
|
-
|
|
72
|
-
async def testRapidAccess(self) -> None:
|
|
73
|
-
"""
|
|
74
|
-
Test rapid concurrent access to existing singleton instances.
|
|
75
|
-
|
|
76
|
-
This test verifies that rapid concurrent access to already
|
|
77
|
-
created singleton instances maintains consistency.
|
|
78
|
-
"""
|
|
79
|
-
# Create initial instances
|
|
80
|
-
initial_container = Container()
|
|
81
|
-
initial_orionis = Orionis()
|
|
82
|
-
|
|
83
|
-
container_ids = []
|
|
84
|
-
orionis_ids = []
|
|
85
|
-
|
|
86
|
-
def rapid_container_access():
|
|
87
|
-
"""Rapidly access container singleton."""
|
|
88
|
-
for _ in range(100):
|
|
89
|
-
container = Container()
|
|
90
|
-
container_ids.append(id(container))
|
|
91
|
-
|
|
92
|
-
def rapid_orionis_access():
|
|
93
|
-
"""Rapidly access orionis singleton."""
|
|
94
|
-
for _ in range(100):
|
|
95
|
-
orionis = Orionis()
|
|
96
|
-
orionis_ids.append(id(orionis))
|
|
97
|
-
|
|
98
|
-
# Create threads for rapid access
|
|
99
|
-
threads = []
|
|
100
|
-
for _ in range(20):
|
|
101
|
-
t1 = threading.Thread(target=rapid_container_access)
|
|
102
|
-
t2 = threading.Thread(target=rapid_orionis_access)
|
|
103
|
-
threads.extend([t1, t2])
|
|
104
|
-
|
|
105
|
-
# Start all threads simultaneously
|
|
106
|
-
start_time = time.time()
|
|
107
|
-
for t in threads:
|
|
108
|
-
t.start()
|
|
109
|
-
|
|
110
|
-
# Wait for all threads to complete
|
|
111
|
-
for t in threads:
|
|
112
|
-
t.join()
|
|
113
|
-
|
|
114
|
-
end_time = time.time()
|
|
115
|
-
|
|
116
|
-
# Verify consistency
|
|
117
|
-
unique_container_ids = set(container_ids)
|
|
118
|
-
unique_orionis_ids = set(orionis_ids)
|
|
119
|
-
|
|
120
|
-
self.assertEqual(len(container_ids), 20 * 100) # 20 threads * 100 accesses each
|
|
121
|
-
self.assertEqual(len(unique_container_ids), 1)
|
|
122
|
-
self.assertEqual(len(orionis_ids), 20 * 100)
|
|
123
|
-
self.assertEqual(len(unique_orionis_ids), 1)
|
|
124
|
-
|
|
125
|
-
# Verify performance is reasonable (should complete quickly)
|
|
126
|
-
self.assertLess(end_time - start_time, 10.0) # Should complete in less than 10 seconds
|
|
127
|
-
|
|
128
|
-
async def testMixedOperations(self) -> None:
|
|
129
|
-
"""
|
|
130
|
-
Test mixed read/write operations on singletons.
|
|
131
|
-
|
|
132
|
-
This test verifies that concurrent read/write operations
|
|
133
|
-
maintain singleton consistency and data integrity.
|
|
134
|
-
"""
|
|
135
|
-
errors = []
|
|
136
|
-
|
|
137
|
-
def mixed_operations():
|
|
138
|
-
"""Perform mixed operations on containers."""
|
|
139
|
-
try:
|
|
140
|
-
# Get instances
|
|
141
|
-
container = Container()
|
|
142
|
-
orionis = Orionis()
|
|
143
|
-
|
|
144
|
-
# Perform some operations
|
|
145
|
-
test_key = f"test_func_{threading.current_thread().ident}"
|
|
146
|
-
container.callable(test_key, lambda: "test_value")
|
|
147
|
-
|
|
148
|
-
# Verify the same instance
|
|
149
|
-
container2 = Container()
|
|
150
|
-
orionis2 = Orionis()
|
|
151
|
-
|
|
152
|
-
if container is not container2:
|
|
153
|
-
errors.append("Container singleton violated")
|
|
154
|
-
|
|
155
|
-
if orionis is not orionis2:
|
|
156
|
-
errors.append("Orionis singleton violated")
|
|
157
|
-
|
|
158
|
-
# Check bindings consistency
|
|
159
|
-
if not container2.bound(test_key):
|
|
160
|
-
errors.append("Binding not consistent across instances")
|
|
161
|
-
|
|
162
|
-
except Exception as e:
|
|
163
|
-
errors.append(f"Exception in mixed operations: {e}")
|
|
164
|
-
|
|
165
|
-
# Run mixed operations in multiple threads
|
|
166
|
-
threads = []
|
|
167
|
-
for _ in range(50):
|
|
168
|
-
t = threading.Thread(target=mixed_operations)
|
|
169
|
-
threads.append(t)
|
|
170
|
-
|
|
171
|
-
for t in threads:
|
|
172
|
-
t.start()
|
|
173
|
-
|
|
174
|
-
for t in threads:
|
|
175
|
-
t.join()
|
|
176
|
-
|
|
177
|
-
# Assert no errors occurred
|
|
178
|
-
self.assertEqual(len(errors), 0, f"Errors found: {errors[:5]}")
|
|
179
|
-
|
|
180
|
-
async def testMemoryConsistency(self) -> None:
|
|
181
|
-
"""
|
|
182
|
-
Test memory consistency across threads.
|
|
183
|
-
|
|
184
|
-
This test verifies that changes made in one thread
|
|
185
|
-
are visible in other threads due to singleton behavior.
|
|
186
|
-
"""
|
|
187
|
-
results = []
|
|
188
|
-
|
|
189
|
-
def thread_a():
|
|
190
|
-
"""Thread A - modifies the container."""
|
|
191
|
-
container = Container()
|
|
192
|
-
container.callable("thread_a_marker", lambda: "from_thread_a")
|
|
193
|
-
results.append("A_completed")
|
|
194
|
-
|
|
195
|
-
def thread_b():
|
|
196
|
-
"""Thread B - reads from the container."""
|
|
197
|
-
# Small delay to ensure thread A runs first
|
|
198
|
-
time.sleep(0.01)
|
|
199
|
-
container = Container()
|
|
200
|
-
has_marker = container.bound("thread_a_marker")
|
|
201
|
-
results.append(f"B_sees_marker: {has_marker}")
|
|
202
|
-
|
|
203
|
-
t1 = threading.Thread(target=thread_a)
|
|
204
|
-
t2 = threading.Thread(target=thread_b)
|
|
205
|
-
|
|
206
|
-
t1.start()
|
|
207
|
-
t2.start()
|
|
208
|
-
|
|
209
|
-
t1.join()
|
|
210
|
-
t2.join()
|
|
211
|
-
|
|
212
|
-
# Verify that thread B saw the changes made by thread A
|
|
213
|
-
a_completed = "A_completed" in results
|
|
214
|
-
b_saw_marker = any("B_sees_marker: True" in r for r in results)
|
|
215
|
-
|
|
216
|
-
self.assertTrue(a_completed, "Thread A should have completed")
|
|
217
|
-
self.assertTrue(b_saw_marker, "Thread B should see Thread A's changes")
|
|
@@ -4,20 +4,25 @@ from orionis.container.validators.implements import ImplementsAbstractMethods
|
|
|
4
4
|
from orionis.container.exceptions.exception import OrionisContainerException
|
|
5
5
|
|
|
6
6
|
class TestImplementsAbstractMethods(AsyncTestCase):
|
|
7
|
-
"""
|
|
8
|
-
Test cases for the ImplementsAbstractMethods validator in orionis.container.validators.implements.
|
|
9
|
-
|
|
10
|
-
Notes
|
|
11
|
-
-----
|
|
12
|
-
This test suite validates the functionality of the ImplementsAbstractMethods validator
|
|
13
|
-
which ensures that concrete classes correctly implement abstract methods.
|
|
14
|
-
"""
|
|
15
7
|
|
|
16
8
|
async def asyncSetUp(self) -> None:
|
|
17
9
|
"""
|
|
18
|
-
Set up test fixtures.
|
|
10
|
+
Set up test fixtures for ImplementsAbstractMethods validator tests.
|
|
11
|
+
|
|
12
|
+
This method defines several abstract and concrete classes to be used in the test cases:
|
|
13
|
+
- An abstract base class with two abstract methods.
|
|
14
|
+
- A concrete class that correctly implements all abstract methods.
|
|
15
|
+
- A concrete class that does not implement all abstract methods.
|
|
16
|
+
- A non-abstract base class for negative test cases.
|
|
17
|
+
|
|
18
|
+
The defined classes are assigned to instance attributes for use in subsequent tests.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
None
|
|
23
|
+
This method does not return any value.
|
|
19
24
|
"""
|
|
20
|
-
# Define abstract
|
|
25
|
+
# Define an abstract base class with two abstract methods
|
|
21
26
|
class AbstractBase(ABC):
|
|
22
27
|
@abstractmethod
|
|
23
28
|
def abstract_method(self) -> None:
|
|
@@ -27,6 +32,7 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
27
32
|
def another_abstract_method(self) -> str:
|
|
28
33
|
pass
|
|
29
34
|
|
|
35
|
+
# Concrete class that implements all abstract methods
|
|
30
36
|
class ConcreteCorrect(AbstractBase):
|
|
31
37
|
def abstract_method(self) -> None:
|
|
32
38
|
pass
|
|
@@ -34,14 +40,17 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
34
40
|
def another_abstract_method(self) -> str:
|
|
35
41
|
return "implemented"
|
|
36
42
|
|
|
43
|
+
# Concrete class that does not implement all abstract methods
|
|
37
44
|
class ConcreteIncomplete(AbstractBase):
|
|
38
45
|
def abstract_method(self) -> None:
|
|
39
46
|
pass
|
|
40
47
|
|
|
48
|
+
# Non-abstract base class for negative test cases
|
|
41
49
|
class NonAbstractBase:
|
|
42
50
|
def regular_method(self) -> None:
|
|
43
51
|
pass
|
|
44
52
|
|
|
53
|
+
# Assign classes to instance attributes for use in tests
|
|
45
54
|
self.AbstractBase = AbstractBase
|
|
46
55
|
self.ConcreteCorrect = ConcreteCorrect
|
|
47
56
|
self.ConcreteIncomplete = ConcreteIncomplete
|
|
@@ -50,6 +59,11 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
50
59
|
async def testValidImplementation(self) -> None:
|
|
51
60
|
"""
|
|
52
61
|
Test that validation passes when all abstract methods are implemented.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
None
|
|
66
|
+
This method does not return any value.
|
|
53
67
|
"""
|
|
54
68
|
# Test with class
|
|
55
69
|
ImplementsAbstractMethods(
|
|
@@ -67,8 +81,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
67
81
|
async def testIncompleteImplementation(self) -> None:
|
|
68
82
|
"""
|
|
69
83
|
Test that validation fails when not all abstract methods are implemented.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
None
|
|
88
|
+
This method does not return any value.
|
|
70
89
|
"""
|
|
71
|
-
# Test with class
|
|
90
|
+
# Test with class missing an abstract method
|
|
72
91
|
with self.assertRaises(OrionisContainerException) as context:
|
|
73
92
|
ImplementsAbstractMethods(
|
|
74
93
|
abstract=self.AbstractBase,
|
|
@@ -78,7 +97,7 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
78
97
|
self.assertIn("does not implement the following abstract methods", str(context.exception))
|
|
79
98
|
self.assertIn("another_abstract_method", str(context.exception))
|
|
80
99
|
|
|
81
|
-
# Test with instance
|
|
100
|
+
# Test with instance missing an abstract method
|
|
82
101
|
with self.assertRaises(TypeError):
|
|
83
102
|
ImplementsAbstractMethods(
|
|
84
103
|
abstract=self.AbstractBase,
|
|
@@ -88,7 +107,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
88
107
|
async def testMissingAbstractClass(self) -> None:
|
|
89
108
|
"""
|
|
90
109
|
Test that validation fails when no abstract class is provided.
|
|
110
|
+
|
|
111
|
+
Returns
|
|
112
|
+
-------
|
|
113
|
+
None
|
|
114
|
+
This method does not return any value.
|
|
91
115
|
"""
|
|
116
|
+
# Should raise exception if abstract class is not provided
|
|
92
117
|
with self.assertRaises(OrionisContainerException) as context:
|
|
93
118
|
ImplementsAbstractMethods(
|
|
94
119
|
concrete=self.ConcreteCorrect
|
|
@@ -99,7 +124,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
99
124
|
async def testMissingConcreteImplementation(self) -> None:
|
|
100
125
|
"""
|
|
101
126
|
Test that validation fails when neither concrete class nor instance is provided.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
None
|
|
131
|
+
This method does not return any value.
|
|
102
132
|
"""
|
|
133
|
+
# Should raise exception if neither concrete nor instance is provided
|
|
103
134
|
with self.assertRaises(OrionisContainerException) as context:
|
|
104
135
|
ImplementsAbstractMethods(
|
|
105
136
|
abstract=self.AbstractBase
|
|
@@ -110,7 +141,13 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
110
141
|
async def testNonAbstractClass(self) -> None:
|
|
111
142
|
"""
|
|
112
143
|
Test that validation fails when the provided abstract class has no abstract methods.
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
None
|
|
148
|
+
This method does not return any value.
|
|
113
149
|
"""
|
|
150
|
+
# Should raise exception if abstract class does not define any abstract methods
|
|
114
151
|
with self.assertRaises(OrionisContainerException) as context:
|
|
115
152
|
ImplementsAbstractMethods(
|
|
116
153
|
abstract=self.NonAbstractBase,
|
|
@@ -122,13 +159,22 @@ class TestImplementsAbstractMethods(AsyncTestCase):
|
|
|
122
159
|
async def testRenamedAbstractMethods(self) -> None:
|
|
123
160
|
"""
|
|
124
161
|
Test handling of renamed abstract methods with class name prefixes.
|
|
162
|
+
|
|
163
|
+
This test verifies that the validator correctly handles cases where abstract methods
|
|
164
|
+
are renamed with class name prefixes in the concrete implementation.
|
|
165
|
+
|
|
166
|
+
Returns
|
|
167
|
+
-------
|
|
168
|
+
None
|
|
169
|
+
This method does not return any value.
|
|
125
170
|
"""
|
|
126
|
-
# Define
|
|
171
|
+
# Define an abstract class with a prefixed abstract method
|
|
127
172
|
class AbstractWithPrefix(ABC):
|
|
128
173
|
@abstractmethod
|
|
129
174
|
def _AbstractWithPrefix_method(self) -> None:
|
|
130
175
|
pass
|
|
131
176
|
|
|
177
|
+
# Concrete class with a similarly prefixed method
|
|
132
178
|
class ConcreteWithPrefix:
|
|
133
179
|
def _ConcreteWithPrefix_method(self) -> None:
|
|
134
180
|
pass
|