orionis 0.287.0__py3-none-any.whl → 0.288.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.
Files changed (72) hide show
  1. orionis/metadata/framework.py +1 -1
  2. orionis/services/environment/contracts/types.py +70 -0
  3. orionis/services/environment/dot_env.py +11 -13
  4. orionis/services/environment/{type_hint.py → types.py} +64 -45
  5. orionis/services/paths/exceptions/not_found_exceptions.py +15 -12
  6. orionis/services/paths/exceptions/path_value_exceptions.py +13 -10
  7. orionis/services/standard/contracts/std.py +1 -1
  8. orionis/services/standard/exceptions/std_value_exception.py +23 -0
  9. orionis/services/standard/std.py +2 -2
  10. orionis/services/system/contracts/imports.py +27 -7
  11. orionis/services/system/contracts/workers.py +8 -3
  12. orionis/services/system/imports.py +31 -12
  13. orionis/services/system/runtime_imports.py +33 -23
  14. orionis/services/system/workers.py +6 -8
  15. orionis/services/wrapper/dicts/dot_dict.py +82 -41
  16. orionis/unittesting.py +12 -12
  17. {orionis-0.287.0.dist-info → orionis-0.288.0.dist-info}/METADATA +1 -1
  18. {orionis-0.287.0.dist-info → orionis-0.288.0.dist-info}/RECORD +70 -66
  19. tests/foundation/config/app/{test_app.py → test_foundation_config_app.py} +1 -1
  20. tests/foundation/config/auth/{test_auth.py → test_foundation_config_auth.py} +1 -1
  21. tests/foundation/config/cache/{test_cache.py → test_foundation_config_cache.py} +1 -1
  22. tests/foundation/config/cache/{test_cache_file.py → test_foundation_config_cache_file.py} +1 -1
  23. tests/foundation/config/cache/{test_cache_stores.py → test_foundation_config_cache_stores.py} +1 -1
  24. tests/foundation/config/cors/{test_cors.py → test_foundation_config_cors.py} +1 -1
  25. tests/foundation/config/database/{test_database.py → test_foundation_config_database.py} +1 -1
  26. tests/foundation/config/database/{test_database_connections.py → test_foundation_config_database_connections.py} +1 -1
  27. tests/foundation/config/database/{test_database_mysql.py → test_foundation_config_database_mysql.py} +1 -1
  28. tests/foundation/config/database/{test_database_oracle.py → test_foundation_config_database_oracle.py} +1 -1
  29. tests/foundation/config/database/{test_database_pgsql.py → test_foundation_config_database_pgsql.py} +1 -1
  30. tests/foundation/config/database/{test_database_sqlite.py → test_foundation_config_database_sqlite.py} +1 -1
  31. tests/foundation/config/exceptions/{test_exceptions_integrity.py → test_foundation_config_exceptions.py} +1 -1
  32. tests/foundation/config/filesystems/{test_filesystems.py → test_foundation_config_filesystems.py} +1 -1
  33. tests/foundation/config/filesystems/{test_filesystems_aws.py → test_foundation_config_filesystems_aws.py} +1 -1
  34. tests/foundation/config/filesystems/{test_filesystems_disks.py → test_foundation_config_filesystems_disks.py} +1 -1
  35. tests/foundation/config/filesystems/{test_filesystems_local.py → test_foundation_config_filesystems_local.py} +1 -1
  36. tests/foundation/config/filesystems/{test_filesystems_public.py → test_foundation_config_filesystems_public.py} +1 -1
  37. tests/foundation/config/logging/{test_logging.py → test_foundation_config_logging.py} +1 -1
  38. tests/foundation/config/logging/{test_logging_channels.py → test_foundation_config_logging_channels.py} +1 -1
  39. tests/foundation/config/logging/{test_logging_chunked.py → test_foundation_config_logging_chunked.py} +1 -1
  40. tests/foundation/config/logging/{test_logging_daily.py → test_foundation_config_logging_daily.py} +1 -1
  41. tests/foundation/config/logging/{test_logging_hourly.py → test_foundation_config_logging_hourly.py} +1 -1
  42. tests/foundation/config/logging/{test_logging_monthly.py → test_foundation_config_logging_monthly.py} +1 -1
  43. tests/foundation/config/logging/{test_logging_stack.py → test_foundation_config_logging_stack.py} +1 -1
  44. tests/foundation/config/logging/{test_logging_weekly.py → test_foundation_config_logging_weekly.py} +1 -1
  45. tests/foundation/config/mail/{test_mail.py → test_foundation_config_mail.py} +1 -1
  46. tests/foundation/config/mail/{test_mail_file.py → test_foundation_config_mail_file.py} +1 -1
  47. tests/foundation/config/mail/{test_mail_mailers.py → test_foundation_config_mail_mailers.py} +1 -1
  48. tests/foundation/config/mail/{test_mail_smtp.py → test_foundation_config_mail_smtp.py} +1 -1
  49. tests/foundation/config/queue/{test_queue.py → test_foundation_config_queue.py} +1 -1
  50. tests/foundation/config/queue/{test_queue_brokers.py → test_foundation_config_queue_brokers.py} +1 -1
  51. tests/foundation/config/queue/{test_queue_database.py → test_foundation_config_queue_database.py} +1 -1
  52. tests/foundation/config/root/{test_root_paths.py → test_foundation_config_root_paths.py} +1 -1
  53. tests/foundation/config/session/{test_session.py → test_foundation_config_session.py} +1 -1
  54. tests/foundation/config/startup/{test_config_startup.py → test_foundation_config_startup.py} +14 -14
  55. tests/foundation/config/testing/{test_testing.py → test_foundation_config_testing.py} +1 -1
  56. tests/patterns/singleton/{test_singleton.py → test_patterns_singleton.py} +1 -1
  57. tests/services/asynchrony/{test_async_io.py → test_services_asynchrony_coroutine.py} +1 -1
  58. tests/services/environment/test_services_environment.py +93 -0
  59. tests/services/path/{test_resolver.py → test_services_resolver.py} +51 -12
  60. tests/services/standard/{test_std.py → test_services_std.py} +45 -22
  61. tests/services/system/__init__.py +0 -0
  62. tests/services/system/test_services_system_imports.py +101 -0
  63. tests/services/system/test_services_system_workers.py +89 -0
  64. tests/services/wrapper/{test_wrapper_doc_dict.py → test_services_wrapper_docdict.py} +28 -16
  65. tests/testing/test_testing_result.py +1 -1
  66. tests/testing/test_testing_unit.py +16 -16
  67. orionis/services/standard/exceptions/path_value_exceptions.py +0 -28
  68. tests/services/environment/test_env.py +0 -154
  69. {orionis-0.287.0.dist-info → orionis-0.288.0.dist-info}/WHEEL +0 -0
  70. {orionis-0.287.0.dist-info → orionis-0.288.0.dist-info}/licenses/LICENCE +0 -0
  71. {orionis-0.287.0.dist-info → orionis-0.288.0.dist-info}/top_level.txt +0 -0
  72. {orionis-0.287.0.dist-info → orionis-0.288.0.dist-info}/zip-safe +0 -0
@@ -1,9 +1,9 @@
1
1
  from dataclasses import is_dataclass
2
2
  from orionis.foundation.config.exceptions.integrity import OrionisIntegrityException
3
3
  from orionis.foundation.config.startup import Configuration
4
- from orionis.unittesting import TestCase, UnittestMock
4
+ from orionis.unittesting import TestCase, Mock
5
5
 
6
- class TestConfiguration(TestCase):
6
+ class TestFoundationConfigStartup(TestCase):
7
7
  """
8
8
  Test suite for the Configuration dataclass.
9
9
 
@@ -98,18 +98,18 @@ class TestConfiguration(TestCase):
98
98
 
99
99
  # Test each section with wrong type
100
100
  sections = [
101
- ('paths', UnittestMock()),
102
- ('app', UnittestMock()),
103
- ('auth', UnittestMock()),
104
- ('cache', UnittestMock()),
105
- ('cors', UnittestMock()),
106
- ('database', UnittestMock()),
107
- ('filesystems', UnittestMock()),
108
- ('logging', UnittestMock()),
109
- ('mail', UnittestMock()),
110
- ('queue', UnittestMock()),
111
- ('session', UnittestMock()),
112
- ('testing', UnittestMock())
101
+ ('paths', Mock()),
102
+ ('app', Mock()),
103
+ ('auth', Mock()),
104
+ ('cache', Mock()),
105
+ ('cors', Mock()),
106
+ ('database', Mock()),
107
+ ('filesystems', Mock()),
108
+ ('logging', Mock()),
109
+ ('mail', Mock()),
110
+ ('queue', Mock()),
111
+ ('session', Mock()),
112
+ ('testing', Mock())
113
113
  ]
114
114
 
115
115
  for section_name, wrong_value in sections:
@@ -3,7 +3,7 @@ from orionis.foundation.config.exceptions.integrity import OrionisIntegrityExcep
3
3
  from orionis.test.enums.test_mode import ExecutionMode
4
4
  from orionis.unittesting import TestCase
5
5
 
6
- class TestTestingConfig(TestCase):
6
+ class TestFoundationConfigTesting(TestCase):
7
7
  """
8
8
  Test suite for the Testing configuration entity.
9
9
 
@@ -1,7 +1,7 @@
1
1
  from orionis.patterns.singleton.meta_class import Singleton
2
2
  from orionis.test.cases.test_case import TestCase
3
3
 
4
- class TestsAsyncCoroutine(TestCase):
4
+ class TestPatternsSingleton(TestCase):
5
5
  """
6
6
  Test cases for the Singleton metaclass.
7
7
 
@@ -3,7 +3,7 @@ from orionis.services.asynchrony.coroutines import Coroutine
3
3
  from orionis.services.asynchrony.exceptions.coroutine_exception import OrionisCoroutineException
4
4
  from orionis.unittesting import TestCase
5
5
 
6
- class TestsAsyncIO(TestCase):
6
+ class TestServicesAsynchronyCoroutine(TestCase):
7
7
 
8
8
  async def testExecuteWithActiveEventLoop(self):
9
9
  """
@@ -0,0 +1,93 @@
1
+ from orionis.services.environment.env import Env
2
+ from orionis.unittesting import TestCase
3
+
4
+ class TestServicesEnvironment(TestCase):
5
+
6
+ async def testSetAndGetConstants(self):
7
+ """
8
+ Test storing and retrieving framework metadata constants using Env.set and Env.get.
9
+
10
+ Imports several metadata constants from the `orionis.metadata.framework` module, sets each constant
11
+ in the Env storage using `Env.set`, and verifies that the operation succeeds. Then retrieves each
12
+ constant using `Env.get` and asserts that the retrieved value matches the original constant.
13
+
14
+ Ensures
15
+ -------
16
+ - `Env.set` returns True for each constant.
17
+ - `Env.get` returns the correct value for each constant.
18
+ """
19
+ from orionis.metadata.framework import (
20
+ NAME, VERSION, AUTHOR, AUTHOR_EMAIL, DESCRIPTION,
21
+ SKELETON, FRAMEWORK, DOCS, API, PYTHON_REQUIRES
22
+ )
23
+ constants = {
24
+ "NAME": NAME,
25
+ "VERSION": VERSION,
26
+ "AUTHOR": AUTHOR,
27
+ "AUTHOR_EMAIL": AUTHOR_EMAIL,
28
+ "DESCRIPTION": DESCRIPTION,
29
+ "SKELETON": SKELETON,
30
+ "FRAMEWORK": FRAMEWORK,
31
+ "DOCS": DOCS,
32
+ "API": API,
33
+ "PYTHON_REQUIRES": PYTHON_REQUIRES
34
+ }
35
+ for key, value in constants.items():
36
+ result = Env.set(key, value)
37
+ self.assertTrue(result)
38
+ for key, value in constants.items():
39
+ retrieved = Env.get(key)
40
+ self.assertEqual(retrieved, value)
41
+
42
+ async def testGetNonExistentKey(self):
43
+ """
44
+ Test that Env.get returns None for a non-existent environment key.
45
+
46
+ Ensures
47
+ -------
48
+ - `Env.get` returns None when the key does not exist.
49
+ """
50
+ self.assertIsNone(Env.get("NON_EXISTENT_KEY"))
51
+
52
+ async def testTypeHints(self):
53
+ """
54
+ Test that Env.set and Env.get correctly handle and preserve Python type hints.
55
+
56
+ Sets environment variables with various data types (int, float, bool, str, list, dict, tuple, set)
57
+ using the `Env.set` method, specifying the type as a string. Then retrieves each variable using
58
+ `Env.get` and asserts that the returned value is of the expected Python type.
59
+
60
+ Ensures
61
+ -------
62
+ - The returned value from `Env.get` matches the expected Python type for each variable.
63
+ """
64
+
65
+ # Set environment variables with type hints
66
+ Env.set("TEST_INT", 42, 'int')
67
+ Env.set("TEST_FLOAT", 3.14, 'float')
68
+ Env.set("TEST_BOOL", True, 'bool')
69
+ Env.set("TEST_STR", "Hello, World!", 'str')
70
+ Env.set("TEST_LIST", [1, 2, 3], 'list')
71
+ Env.set("TEST_DICT", {"key": "value"}, 'dict')
72
+ Env.set("TEST_TUPLE", (1,2,3), 'tuple')
73
+ Env.set("TEST_SET", {1, 2, 3}, 'set')
74
+
75
+ # Retrieve and check types
76
+ self.assertIsInstance(Env.get("TEST_INT"), int)
77
+ self.assertIsInstance(Env.get("TEST_FLOAT"), float)
78
+ self.assertIsInstance(Env.get("TEST_BOOL"), bool)
79
+ self.assertIsInstance(Env.get("TEST_STR"), str)
80
+ self.assertIsInstance(Env.get("TEST_LIST"), list)
81
+ self.assertIsInstance(Env.get("TEST_DICT"), dict)
82
+ self.assertIsInstance(Env.get("TEST_TUPLE"), tuple)
83
+ self.assertIsInstance(Env.get("TEST_SET"), set)
84
+
85
+ # Clean up environment variables after test
86
+ Env.unset("TEST_INT")
87
+ Env.unset("TEST_FLOAT")
88
+ Env.unset("TEST_BOOL")
89
+ Env.unset("TEST_STR")
90
+ Env.unset("TEST_LIST")
91
+ Env.unset("TEST_DICT")
92
+ Env.unset("TEST_TUPLE")
93
+ Env.unset("TEST_SET")
@@ -1,27 +1,42 @@
1
- import tempfile
2
1
  import os
2
+ import tempfile
3
3
  from pathlib import Path
4
+ from orionis.services.paths.exceptions.not_found_exceptions import OrionisFileNotFoundException
4
5
  from orionis.services.paths.resolver import Resolver
5
6
  from orionis.unittesting import TestCase
6
7
 
7
- class TestsResolver(TestCase):
8
- """
9
- Unit tests for the Resolver class, which resolves file and directory paths relative to a base directory.
10
- """
8
+ class TestServicesResolver(TestCase):
11
9
 
12
- async def test_file_not_found(self):
10
+ async def testFileNotFound(self):
13
11
  """
14
- Test that resolving a non-existent file path raises FileNotFoundError.
12
+ Test that resolving a non-existent file path raises OrionisFileNotFoundException.
13
+
14
+ Returns
15
+ -------
16
+ None
17
+
18
+ Raises
19
+ ------
20
+ OrionisFileNotFoundException
21
+ If the file does not exist.
15
22
  """
16
23
  with tempfile.TemporaryDirectory() as tmpdir:
17
24
  resolver = Resolver(tmpdir)
18
25
  non_existent = "does_not_exist.txt"
19
- with self.assertRaises(FileNotFoundError):
26
+ with self.assertRaises(OrionisFileNotFoundException):
20
27
  resolver.relativePath(non_existent)
21
28
 
22
- async def test_valid_file_path(self):
29
+ async def testValidFilePath(self):
23
30
  """
24
31
  Test that resolving a valid file path returns the correct absolute path.
32
+
33
+ Returns
34
+ -------
35
+ None
36
+
37
+ Asserts
38
+ -------
39
+ The resolved path ends with the file name and is absolute.
25
40
  """
26
41
  with tempfile.TemporaryDirectory() as tmpdir:
27
42
  # Create a temporary file inside the temp directory
@@ -34,9 +49,17 @@ class TestsResolver(TestCase):
34
49
  # The resolved path should be absolute
35
50
  self.assertTrue(os.path.isabs(resolved))
36
51
 
37
- async def test_valid_directory_path(self):
52
+ async def testValidDirectoryPath(self):
38
53
  """
39
54
  Test that resolving a valid directory path returns the correct absolute path.
55
+
56
+ Returns
57
+ -------
58
+ None
59
+
60
+ Asserts
61
+ -------
62
+ The resolved path ends with the directory name and is absolute.
40
63
  """
41
64
  with tempfile.TemporaryDirectory() as tmpdir:
42
65
  # Create a subdirectory inside the temp directory
@@ -47,9 +70,17 @@ class TestsResolver(TestCase):
47
70
  self.assertTrue(resolved.endswith("subdir"))
48
71
  self.assertTrue(os.path.isabs(resolved))
49
72
 
50
- async def test_other_base_path(self):
73
+ async def testOtherBasePath(self):
51
74
  """
52
75
  Test that providing a different base path to Resolver works as expected.
76
+
77
+ Returns
78
+ -------
79
+ None
80
+
81
+ Asserts
82
+ -------
83
+ The resolved path ends with the file name and is absolute.
53
84
  """
54
85
  with tempfile.TemporaryDirectory() as tmpdir:
55
86
  # Create a file in a subdirectory
@@ -62,9 +93,17 @@ class TestsResolver(TestCase):
62
93
  self.assertTrue(resolved.endswith("file.txt"))
63
94
  self.assertTrue(os.path.isabs(resolved))
64
95
 
65
- async def test_equal_output_string(self):
96
+ async def testEqualOutputString(self):
66
97
  """
67
98
  Test that the string representation of the resolved path matches the output of toString().
99
+
100
+ Returns
101
+ -------
102
+ None
103
+
104
+ Asserts
105
+ -------
106
+ The string representation of the resolved path matches the output of toString().
68
107
  """
69
108
  with tempfile.TemporaryDirectory() as tmpdir:
70
109
  file_path = Path(tmpdir) / "file.txt"
@@ -1,14 +1,16 @@
1
-
1
+ from orionis.services.standard.exceptions.std_value_exception import OrionisStdValueException
2
2
  from orionis.services.standard.std import StdClass
3
3
  from orionis.unittesting import TestCase
4
4
 
5
- class TestStdClass(TestCase):
5
+ class TestServicesStd(TestCase):
6
6
 
7
7
  async def testInitializationAndAccess(self):
8
8
  """
9
9
  Test the initialization of StdClass and access to its attributes.
10
10
 
11
- This test verifies that an instance of StdClass can be created with the given
11
+ Notes
12
+ -----
13
+ Verifies that an instance of StdClass can be created with the given
12
14
  first name, last name, and age, and that these attributes can be accessed
13
15
  correctly after initialization.
14
16
  """
@@ -24,7 +26,9 @@ class TestStdClass(TestCase):
24
26
  """
25
27
  Test that the toDict method of StdClass returns a dictionary with the correct data.
26
28
 
27
- This test creates an instance of StdClass with specific attributes and verifies
29
+ Notes
30
+ -----
31
+ Creates an instance of StdClass with specific attributes and verifies
28
32
  that calling toDict() returns a dictionary containing those attributes and their values.
29
33
  """
30
34
  obj = StdClass(a=1, b=2)
@@ -35,7 +39,9 @@ class TestStdClass(TestCase):
35
39
  """
36
40
  Test that the `update` method of `StdClass` correctly sets multiple attributes.
37
41
 
38
- This test creates an instance of `StdClass`, updates its attributes using the `update` method,
42
+ Notes
43
+ -----
44
+ Creates an instance of `StdClass`, updates its attributes using the `update` method,
39
45
  and asserts that the attributes `foo` and `number` are set to the expected values.
40
46
  """
41
47
  obj = StdClass()
@@ -47,27 +53,36 @@ class TestStdClass(TestCase):
47
53
  """
48
54
  Test that updating a reserved attribute (such as '__init__') on a StdClass instance
49
55
  raises a ValueError exception.
56
+
57
+ Notes
58
+ -----
59
+ Ensures that updating a reserved attribute raises an OrionisStdValueException.
50
60
  """
51
61
  obj = StdClass()
52
- with self.assertRaises(ValueError):
62
+ with self.assertRaises(OrionisStdValueException):
53
63
  obj.update(__init__='bad')
54
64
 
55
65
  async def testUpdateConflictingAttributeRaisesError(self):
56
66
  """
57
67
  Test that updating an object with a conflicting attribute name ('toDict') raises a ValueError.
58
68
 
59
- This test ensures that attempting to update the StdClass instance with a keyword argument
69
+ Notes
70
+ -----
71
+ Ensures that attempting to update the StdClass instance with a keyword argument
60
72
  that conflicts with an existing method or reserved attribute ('toDict') correctly triggers
61
- a ValueError, enforcing attribute safety.
73
+ an OrionisStdValueException, enforcing attribute safety.
62
74
  """
63
75
  obj = StdClass()
64
- with self.assertRaises(ValueError):
76
+ with self.assertRaises(OrionisStdValueException):
65
77
  obj.update(toDict='oops')
66
78
 
67
79
  async def testRemoveExistingAttributes(self):
68
80
  """
69
- Tests that the `remove` method of `StdClass` successfully removes an existing attribute ('x') from the object,
70
- while leaving other attributes ('y') intact.
81
+ Test that the `remove` method of `StdClass` successfully removes an existing attribute.
82
+
83
+ Notes
84
+ -----
85
+ Removes attribute 'x' from the object and checks that 'y' remains.
71
86
  """
72
87
  obj = StdClass(x=1, y=2)
73
88
  obj.remove('x')
@@ -78,7 +93,9 @@ class TestStdClass(TestCase):
78
93
  """
79
94
  Test that attempting to remove a non-existing attribute from a StdClass instance raises an AttributeError.
80
95
 
81
- This test verifies that the `remove` method of `StdClass` correctly raises an AttributeError
96
+ Notes
97
+ -----
98
+ Verifies that the `remove` method of `StdClass` raises an AttributeError
82
99
  when called with the name of an attribute that does not exist on the object.
83
100
  """
84
101
  obj = StdClass()
@@ -89,20 +106,23 @@ class TestStdClass(TestCase):
89
106
  """
90
107
  Test that StdClass.from_dict creates an instance equivalent to the original data.
91
108
 
92
- This test verifies that when a dictionary is passed to StdClass.from_dict,
109
+ Notes
110
+ -----
111
+ Verifies that when a dictionary is passed to StdClass.from_dict,
93
112
  the resulting object's toDict() method returns a dictionary equal to the original input.
94
113
  """
95
114
  data = {'a': 10, 'b': 20}
96
- obj = StdClass.from_dict(data)
115
+ obj = StdClass.fromDict(data)
97
116
  self.assertEqual(obj.toDict(), data)
98
117
 
99
118
  async def testReprAndStr(self):
100
119
  """
101
120
  Test that the __repr__ and __str__ methods of StdClass include the class name and the value of 'x' respectively.
102
121
 
103
- This test verifies:
104
- - The string representation produced by repr(obj) contains the class name 'StdClass'.
105
- - The string representation produced by str(obj) contains the key-value pair "'x': 5".
122
+ Notes
123
+ -----
124
+ - Checks that repr(obj) contains the class name 'StdClass'.
125
+ - Checks that str(obj) contains the key-value pair "'x': 5".
106
126
  """
107
127
  obj = StdClass(x=5)
108
128
  self.assertIn("StdClass", repr(obj))
@@ -110,15 +130,18 @@ class TestStdClass(TestCase):
110
130
 
111
131
  async def testEquality(self):
112
132
  """
113
- Tests the equality and inequality operations for StdClass instances.
133
+ Test the equality and inequality operations for StdClass instances.
114
134
 
115
- This test creates three instances of StdClass:
135
+ Notes
136
+ -----
137
+ Creates three instances of StdClass:
116
138
  - 'a' and 'b' with identical attributes (x=1, y=2), which should be considered equal.
117
139
  - 'c' with a different attribute (x=3), which should not be equal to 'a'.
118
140
 
119
- Assertions:
120
- - Verifies that 'a' and 'b' are equal.
121
- - Verifies that 'a' and 'c' are not equal.
141
+ Asserts
142
+ -------
143
+ - 'a' and 'b' are equal.
144
+ - 'a' and 'c' are not equal.
122
145
  """
123
146
  a = StdClass(x=1, y=2)
124
147
  b = StdClass(x=1, y=2)
File without changes
@@ -0,0 +1,101 @@
1
+ from orionis.services.system.imports import Imports
2
+ from orionis.unittesting import TestCase
3
+ import sys
4
+ import types
5
+
6
+ class TestServicesSystemImports(TestCase):
7
+
8
+ def testImportModule(self) -> None:
9
+ """
10
+ Test that Imports can be instantiated and collected.
11
+
12
+ Returns
13
+ -------
14
+ None
15
+ """
16
+ imports = Imports()
17
+ imports.collect()
18
+ self.assertIsInstance(imports, Imports)
19
+
20
+ def testCollectPopulatesImports(self):
21
+ """
22
+ Test that collect() populates the imports list with modules.
23
+
24
+ Returns
25
+ -------
26
+ None
27
+ """
28
+ dummy_mod = types.ModuleType("dummy_mod")
29
+ dummy_mod.__file__ = __file__
30
+ def dummy_func(): pass
31
+ dummy_mod.dummy_func = dummy_func
32
+ dummy_func.__module__ = "dummy_mod"
33
+ sys.modules["dummy_mod"] = dummy_mod
34
+
35
+ imports = Imports()
36
+ imports.collect()
37
+ found = any(imp["name"] == "dummy_mod" for imp in imports.imports)
38
+ self.assertTrue(found)
39
+
40
+ # Cleanup
41
+ del sys.modules["dummy_mod"]
42
+
43
+ def testCollectExcludesStdlibAndSpecialModules(self):
44
+ """
45
+ Test that collect() excludes standard library and special modules.
46
+
47
+ Returns
48
+ -------
49
+ None
50
+ """
51
+ imports = Imports()
52
+ imports.collect()
53
+ names = [imp["name"] for imp in imports.imports]
54
+ self.assertNotIn("__main__", names)
55
+ self.assertFalse(any(n.startswith("_distutils") for n in names))
56
+
57
+ def testClearEmptiesImports(self):
58
+ """
59
+ Test that clear() empties the imports list.
60
+
61
+ Returns
62
+ -------
63
+ None
64
+ """
65
+ imports = Imports()
66
+ imports.imports = [{"name": "test", "file": "test.py", "symbols": ["a"]}]
67
+ imports.clear()
68
+ self.assertEqual(imports.imports, [])
69
+
70
+ def testCollectHandlesModulesWithoutFile(self):
71
+ """
72
+ Test that collect() handles modules without a __file__ attribute.
73
+
74
+ Returns
75
+ -------
76
+ None
77
+ """
78
+ mod = types.ModuleType("mod_without_file")
79
+ sys.modules["mod_without_file"] = mod
80
+ imports = Imports()
81
+ imports.collect()
82
+ names = [imp["name"] for imp in imports.imports]
83
+ self.assertNotIn("mod_without_file", names)
84
+ del sys.modules["mod_without_file"]
85
+
86
+ def testCollectSkipsBinaryExtensions(self):
87
+ """
88
+ Test that collect() skips binary extension modules.
89
+
90
+ Returns
91
+ -------
92
+ None
93
+ """
94
+ mod = types.ModuleType("bin_mod")
95
+ mod.__file__ = "bin_mod.pyd"
96
+ sys.modules["bin_mod"] = mod
97
+ imports = Imports()
98
+ imports.collect()
99
+ names = [imp["name"] for imp in imports.imports]
100
+ self.assertNotIn("bin_mod", names)
101
+ del sys.modules["bin_mod"]
@@ -0,0 +1,89 @@
1
+ from orionis.services.system.workers import Workers
2
+ from orionis.unittesting import TestCase, patch
3
+
4
+ class TestServicesSystemWorkers(TestCase):
5
+ """
6
+ Unit tests for the Workers class.
7
+
8
+ This test suite verifies the correct calculation of the number of workers
9
+ based on available CPU and RAM resources.
10
+
11
+ Methods
12
+ -------
13
+ testCalculateCpuLimited()
14
+ Test when the number of workers is limited by CPU count.
15
+ testCalculateRamLimited()
16
+ Test when the number of workers is limited by available RAM.
17
+ testCalculateExactFit()
18
+ Test when CPU and RAM allow for the same number of workers.
19
+ testCalculateLowRam()
20
+ Test when low RAM restricts the number of workers to less than CPU count.
21
+ """
22
+
23
+ @patch('multiprocessing.cpu_count', return_value=8)
24
+ @patch('psutil.virtual_memory')
25
+ def testCalculateCpuLimited(self, mockVm, mockCpuCount):
26
+ """
27
+ Test when the number of workers is limited by CPU count.
28
+
29
+ Simulates 8 CPUs and 16 GB RAM, with ram_per_worker=1.
30
+ RAM allows 16 workers, but CPU only allows 8.
31
+
32
+ Returns
33
+ -------
34
+ None
35
+ """
36
+ mockVm.return_value.total = 16 * 1024 ** 3
37
+ workers = Workers(ram_per_worker=1)
38
+ self.assertEqual(workers.calculate(), 8)
39
+
40
+ @patch('multiprocessing.cpu_count', return_value=32)
41
+ @patch('psutil.virtual_memory')
42
+ def testCalculateRamLimited(self, mockVm, mockCpuCount):
43
+ """
44
+ Test when the number of workers is limited by available RAM.
45
+
46
+ Simulates 32 CPUs and 4 GB RAM, with ram_per_worker=1.
47
+ RAM allows only 4 workers.
48
+
49
+ Returns
50
+ -------
51
+ None
52
+ """
53
+ mockVm.return_value.total = 4 * 1024 ** 3
54
+ workers = Workers(ram_per_worker=1)
55
+ self.assertEqual(workers.calculate(), 4)
56
+
57
+ @patch('multiprocessing.cpu_count', return_value=4)
58
+ @patch('psutil.virtual_memory')
59
+ def testCalculateExactFit(self, mockVm, mockCpuCount):
60
+ """
61
+ Test when CPU and RAM allow for the same number of workers.
62
+
63
+ Simulates 4 CPUs and 2 GB RAM, with ram_per_worker=0.5.
64
+ RAM allows 4 workers.
65
+
66
+ Returns
67
+ -------
68
+ None
69
+ """
70
+ mockVm.return_value.total = 2 * 1024 ** 3
71
+ workers = Workers(ram_per_worker=0.5)
72
+ self.assertEqual(workers.calculate(), 4)
73
+
74
+ @patch('multiprocessing.cpu_count', return_value=2)
75
+ @patch('psutil.virtual_memory')
76
+ def testCalculateLowRam(self, mockVm, mockCpuCount):
77
+ """
78
+ Test when low RAM restricts the number of workers to less than CPU count.
79
+
80
+ Simulates 2 CPUs and 0.7 GB RAM, with ram_per_worker=0.5.
81
+ RAM allows only 1 worker.
82
+
83
+ Returns
84
+ -------
85
+ None
86
+ """
87
+ mockVm.return_value.total = 0.7 * 1024 ** 3
88
+ workers = Workers(ram_per_worker=0.5)
89
+ self.assertEqual(workers.calculate(), 1)