orionis 0.372.0__py3-none-any.whl → 0.373.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 (98) hide show
  1. orionis/__init__.py +4 -2
  2. orionis/container/resolver/resolver.py +3 -3
  3. orionis/foundation/application.py +25 -0
  4. orionis/metadata/framework.py +1 -1
  5. orionis/services/introspection/dependencies/entities/callable_dependencies.py +6 -6
  6. orionis/services/introspection/dependencies/entities/class_dependencies.py +7 -7
  7. orionis/services/introspection/dependencies/entities/{resolved_dependencies.py → known_dependencies.py} +1 -1
  8. orionis/services/introspection/dependencies/entities/method_dependencies.py +6 -6
  9. orionis/services/introspection/dependencies/reflection.py +4 -4
  10. orionis/test/arguments/parser.py +197 -0
  11. orionis/test/cases/asynchronous.py +145 -0
  12. orionis/test/cases/synchronous.py +135 -0
  13. orionis/test/contracts/kernel.py +99 -0
  14. orionis/test/contracts/logs.py +125 -0
  15. orionis/test/contracts/parser.py +37 -0
  16. orionis/test/contracts/render.py +30 -0
  17. orionis/test/contracts/unit_test.py +21 -0
  18. orionis/test/core/unit_test.py +104 -23
  19. orionis/test/entities/arguments.py +38 -0
  20. orionis/test/kernel.py +276 -0
  21. orionis/test/output/dumper.py +3 -4
  22. orionis/test/output/printer.py +22 -0
  23. orionis/test/{logs → record}/history.py +25 -9
  24. orionis/test/records/__init__.py +0 -0
  25. orionis/test/records/logs.py +385 -0
  26. orionis/test/view/render.py +8 -5
  27. {orionis-0.372.0.dist-info → orionis-0.373.0.dist-info}/METADATA +1 -1
  28. {orionis-0.372.0.dist-info → orionis-0.373.0.dist-info}/RECORD +92 -87
  29. tests/example/test_example.py +4 -3
  30. tests/foundation/config/app/test_foundation_config_app.py +2 -2
  31. tests/foundation/config/auth/test_foundation_config_auth.py +2 -2
  32. tests/foundation/config/cache/test_foundation_config_cache.py +2 -2
  33. tests/foundation/config/cache/test_foundation_config_cache_file.py +2 -2
  34. tests/foundation/config/cache/test_foundation_config_cache_stores.py +2 -2
  35. tests/foundation/config/cors/test_foundation_config_cors.py +2 -2
  36. tests/foundation/config/database/test_foundation_config_database.py +2 -2
  37. tests/foundation/config/database/test_foundation_config_database_connections.py +2 -2
  38. tests/foundation/config/database/test_foundation_config_database_mysql.py +2 -2
  39. tests/foundation/config/database/test_foundation_config_database_oracle.py +2 -2
  40. tests/foundation/config/database/test_foundation_config_database_pgsql.py +2 -2
  41. tests/foundation/config/database/test_foundation_config_database_sqlite.py +2 -2
  42. tests/foundation/config/filesystems/test_foundation_config_filesystems.py +2 -2
  43. tests/foundation/config/filesystems/test_foundation_config_filesystems_aws.py +2 -2
  44. tests/foundation/config/filesystems/test_foundation_config_filesystems_disks.py +2 -2
  45. tests/foundation/config/filesystems/test_foundation_config_filesystems_local.py +2 -2
  46. tests/foundation/config/filesystems/test_foundation_config_filesystems_public.py +2 -2
  47. tests/foundation/config/logging/test_foundation_config_logging.py +2 -2
  48. tests/foundation/config/logging/test_foundation_config_logging_channels.py +2 -2
  49. tests/foundation/config/logging/test_foundation_config_logging_chunked.py +2 -2
  50. tests/foundation/config/logging/test_foundation_config_logging_daily.py +2 -2
  51. tests/foundation/config/logging/test_foundation_config_logging_hourly.py +2 -2
  52. tests/foundation/config/logging/test_foundation_config_logging_monthly.py +2 -2
  53. tests/foundation/config/logging/test_foundation_config_logging_stack.py +2 -2
  54. tests/foundation/config/logging/test_foundation_config_logging_weekly.py +2 -2
  55. tests/foundation/config/mail/test_foundation_config_mail.py +2 -2
  56. tests/foundation/config/mail/test_foundation_config_mail_file.py +2 -2
  57. tests/foundation/config/mail/test_foundation_config_mail_mailers.py +2 -2
  58. tests/foundation/config/mail/test_foundation_config_mail_smtp.py +2 -2
  59. tests/foundation/config/queue/test_foundation_config_queue.py +2 -5
  60. tests/foundation/config/queue/test_foundation_config_queue_brokers.py +2 -2
  61. tests/foundation/config/queue/test_foundation_config_queue_database.py +3 -2
  62. tests/foundation/config/root/test_foundation_config_root_paths.py +3 -3
  63. tests/foundation/config/session/test_foundation_config_session.py +4 -3
  64. tests/foundation/config/startup/test_foundation_config_startup.py +4 -3
  65. tests/foundation/config/testing/test_foundation_config_testing.py +3 -3
  66. tests/foundation/exceptions/test_foundation_config_exceptions.py +3 -3
  67. tests/metadata/test_metadata_framework.py +2 -2
  68. tests/metadata/test_metadata_package.py +3 -2
  69. tests/services/asynchrony/test_services_asynchrony_coroutine.py +2 -2
  70. tests/services/environment/test_services_environment.py +2 -2
  71. tests/services/inspection/dependencies/test_reflect_dependencies.py +22 -22
  72. tests/services/inspection/reflection/test_reflection_abstract.py +2 -2
  73. tests/services/inspection/reflection/test_reflection_callable.py +2 -2
  74. tests/services/inspection/reflection/test_reflection_concrete.py +2 -2
  75. tests/services/inspection/reflection/test_reflection_instance.py +2 -2
  76. tests/services/inspection/reflection/test_reflection_module.py +2 -2
  77. tests/services/inspection/test_reflection.py +2 -2
  78. tests/services/path/test_services_resolver.py +2 -2
  79. tests/services/system/test_services_system_imports.py +2 -2
  80. tests/services/system/test_services_system_workers.py +3 -2
  81. tests/support/parsers/test_services_parser_exceptions.py +2 -2
  82. tests/support/patterns/singleton/test_patterns_singleton.py +2 -2
  83. tests/support/standard/test_services_std.py +2 -2
  84. tests/support/wrapper/test_services_wrapper_docdict.py +2 -2
  85. tests/testing/test_testing_result.py +4 -6
  86. tests/testing/test_testing_unit.py +9 -10
  87. orionis/test/cases/test_async.py +0 -55
  88. orionis/test/cases/test_case.py +0 -42
  89. orionis/test/cases/test_sync.py +0 -33
  90. orionis/test/contracts/history.py +0 -54
  91. orionis/test/test_suite.py +0 -142
  92. orionis/unittesting.py +0 -64
  93. /orionis/test/{logs → arguments}/__init__.py +0 -0
  94. /orionis/test/entities/{test_result.py → result.py} +0 -0
  95. {orionis-0.372.0.dist-info → orionis-0.373.0.dist-info}/WHEEL +0 -0
  96. {orionis-0.372.0.dist-info → orionis-0.373.0.dist-info}/licenses/LICENCE +0 -0
  97. {orionis-0.372.0.dist-info → orionis-0.373.0.dist-info}/top_level.txt +0 -0
  98. {orionis-0.372.0.dist-info → orionis-0.373.0.dist-info}/zip-safe +0 -0
orionis/test/kernel.py ADDED
@@ -0,0 +1,276 @@
1
+ import gc
2
+ import os
3
+ import re
4
+ from os import walk
5
+ from orionis.foundation.config.testing.entities.testing import Testing as Configuration
6
+ from orionis.foundation.contracts.application import IApplication
7
+ from orionis.test.contracts.kernel import ITestKernel
8
+ from orionis.test.core.unit_test import UnitTest
9
+ from orionis.test.exceptions import OrionisTestConfigException
10
+
11
+ class TestKernel(ITestKernel):
12
+ """
13
+ Core test kernel for the Orionis testing framework.
14
+
15
+ This class provides the main interface for discovering, configuring, and executing
16
+ test suites within the Orionis framework. It handles test configuration validation,
17
+ test discovery across multiple directories, and orchestrates the execution of
18
+ discovered tests.
19
+
20
+ Parameters
21
+ ----------
22
+ app : IApplication
23
+ The Orionis application instance that provides the testing context.
24
+
25
+ Attributes
26
+ ----------
27
+ __app : IApplication
28
+ Private reference to the application instance.
29
+ __config : Configuration
30
+ Private reference to the testing configuration.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ app: IApplication
36
+ ) -> None:
37
+ """
38
+ Initialize the Orionis test kernel.
39
+
40
+ Parameters
41
+ ----------
42
+ app : IApplication
43
+ The application instance that implements the IApplication interface.
44
+ This provides the context and services needed for test execution.
45
+
46
+ Raises
47
+ ------
48
+ ValueError
49
+ If the provided app is None or not an instance of IApplication.
50
+ """
51
+ # Validate application instance
52
+ if app is None or not isinstance(app, IApplication):
53
+ raise ValueError("The provided application is not a valid instance of IApplication.")
54
+
55
+ # Set the application instance
56
+ self.__app = app
57
+
58
+ def __checkConfiguration(
59
+ self,
60
+ config: Configuration = None,
61
+ **kwargs
62
+ ) -> Configuration:
63
+ """
64
+ Validate and initialize the testing configuration.
65
+
66
+ This method validates the provided configuration or creates a new one from
67
+ keyword arguments. It ensures that the configuration is properly set up
68
+ before test execution begins.
69
+
70
+ Parameters
71
+ ----------
72
+ config : Configuration, optional
73
+ A pre-configured Testing configuration instance. If None, attempts to
74
+ create one from kwargs.
75
+ **kwargs : dict
76
+ Keyword arguments to create a Configuration instance if config is None.
77
+ Must match the Configuration class constructor parameters.
78
+
79
+ Returns
80
+ -------
81
+ bool
82
+ True if configuration validation succeeds.
83
+
84
+ Raises
85
+ ------
86
+ OrionisTestConfigException
87
+ If the configuration is invalid or required fields are missing.
88
+ The exception message includes details about required fields and their types.
89
+ """
90
+ # Check if config is None and kwargs are provided
91
+ if config is None:
92
+ try:
93
+ # Attempt to create a Configuration instance with provided keyword arguments
94
+ config = Configuration(**kwargs)
95
+ except TypeError:
96
+ # If a TypeError occurs, it indicates that the provided arguments do not match the Configuration class
97
+ required_fields = []
98
+ for field in Configuration().getFields():
99
+ required_fields.append(f"{field.get('name')} = (Type: {field.get('type')}, Default: {field.get('default')})")
100
+
101
+ # Raise an exception with a detailed message about the required fields
102
+ raise OrionisTestConfigException(f"The provided configuration is not valid. Please ensure it is an instance of the Configuration class or provide valid keyword arguments. \n{str('\n').join(required_fields)}]")
103
+
104
+ # Assign the configuration to the instance variable
105
+ return config or Configuration()
106
+
107
+ def handle(
108
+ self,
109
+ config: Configuration = None,
110
+ **kwargs
111
+ ) -> UnitTest:
112
+ """
113
+ Execute the complete test discovery and execution pipeline.
114
+
115
+ This is the main entry point for running tests. It validates the configuration,
116
+ discovers test files based on specified patterns and paths, configures the
117
+ test suite, and executes all discovered tests.
118
+
119
+ Parameters
120
+ ----------
121
+ config : Configuration, optional
122
+ A pre-configured Testing configuration instance. If None, attempts to
123
+ create one from kwargs.
124
+ **kwargs : dict
125
+ Keyword arguments to create a Configuration instance if config is None.
126
+ Common parameters include:
127
+ - base_path : str, base directory for test discovery
128
+ - folder_path : str or list, specific folders to search
129
+ - pattern : str, file pattern for test discovery
130
+ - verbosity : int, output verbosity level
131
+ - execution_mode : str, test execution mode
132
+ - max_workers : int, maximum number of worker threads
133
+ - fail_fast : bool, stop on first failure
134
+
135
+ Returns
136
+ -------
137
+ UnitTest
138
+ The configured and executed test suite instance containing all results.
139
+
140
+ Raises
141
+ ------
142
+ OrionisTestConfigException
143
+ If the configuration validation fails.
144
+ """
145
+ # Validate and set configuration
146
+ config = self.__checkConfiguration(config, **kwargs)
147
+
148
+ # Initialize the test suite
149
+ tests = UnitTest()
150
+
151
+ # Assign the application instance to the test suite
152
+ tests.setApplication(self.__app)
153
+
154
+ # Configure the test suite with validated configuration values
155
+ tests.configure(
156
+ verbosity=config.verbosity,
157
+ execution_mode=config.execution_mode,
158
+ max_workers=config.max_workers,
159
+ fail_fast=config.fail_fast,
160
+ print_result=config.print_result,
161
+ throw_exception=config.throw_exception,
162
+ persistent=config.persistent,
163
+ persistent_driver=config.persistent_driver,
164
+ web_report=config.web_report
165
+ )
166
+
167
+ # Extract configuration values for test discovery
168
+ base_path = config.base_path
169
+ folder_path = config.folder_path
170
+ pattern = config.pattern
171
+
172
+ def list_matching_folders(custom_path: str, pattern: str):
173
+ """
174
+ Discover folders containing files that match the specified pattern.
175
+
176
+ This helper function walks through the directory tree starting from
177
+ custom_path and identifies folders that contain files matching the
178
+ given pattern.
179
+
180
+ Parameters
181
+ ----------
182
+ custom_path : str
183
+ The root path to start the search from.
184
+ pattern : str
185
+ The file pattern to match (supports wildcards * and ?).
186
+
187
+ Returns
188
+ -------
189
+ list of str
190
+ List of relative folder paths containing matching files.
191
+ """
192
+ matched_folders = []
193
+ for root, _, files in walk(custom_path):
194
+ for file in files:
195
+ if re.fullmatch(pattern.replace('*', '.*').replace('?', '.'), file):
196
+ relative_path = root.replace(base_path, '').replace('\\', '/').lstrip('/')
197
+ if relative_path not in matched_folders:
198
+ matched_folders.append(relative_path)
199
+ return matched_folders
200
+
201
+ # Discover test folders based on configuration
202
+ discovered_folders = []
203
+ if folder_path == '*':
204
+ # Search all folders under base_path
205
+ discovered_folders.extend(list_matching_folders(base_path, pattern))
206
+ elif isinstance(folder_path, list):
207
+ # Search specific folders provided in the list
208
+ for custom_path in folder_path:
209
+ discovered_folders.extend(list_matching_folders(f"{base_path}/{custom_path}", pattern))
210
+ else:
211
+ # Search single specified folder
212
+ discovered_folders.extend(list_matching_folders(folder_path, pattern))
213
+
214
+ # Add discovered folders to the test suite for execution
215
+ for folder in discovered_folders:
216
+ tests.discoverTestsInFolder(
217
+ folder_path=folder,
218
+ base_path=base_path,
219
+ pattern=pattern,
220
+ test_name_pattern=config.test_name_pattern if config.test_name_pattern else None,
221
+ tags=config.tags if config.tags else None
222
+ )
223
+
224
+ # Execute the test suite and return the results
225
+ tests.run()
226
+
227
+ # Return the test suite instance containing all results
228
+ return tests
229
+
230
+ def exit(
231
+ self,
232
+ code: int = 0
233
+ ) -> None:
234
+ """
235
+ Terminate the test execution process and free associated resources.
236
+
237
+ This method performs a clean shutdown of the test kernel by explicitly
238
+ triggering garbage collection to release memory resources and then
239
+ terminating the process with the provided exit code. It ensures that any
240
+ remaining file handles, threads, or other resources are properly released.
241
+
242
+ Parameters
243
+ ----------
244
+ code : int
245
+ The exit code to return to the operating system. Should be 0 for
246
+ successful execution or a non-zero value to indicate an error.
247
+
248
+ Returns
249
+ -------
250
+ None
251
+ This method does not return as it terminates the process.
252
+
253
+ Raises
254
+ ------
255
+ ValueError
256
+ If the provided code is not a valid integer or outside the allowed range.
257
+
258
+ Notes
259
+ -----
260
+ Using os._exit() bypasses normal Python cleanup mechanisms and
261
+ immediately terminates the process. This can be necessary when
262
+ normal sys.exit() would be caught by exception handlers.
263
+ """
264
+ # Validate the exit code
265
+ if not isinstance(code, int):
266
+ raise ValueError("Exit code must be an integer")
267
+
268
+ # Check if the code is within the allowed range (typically 0-255)
269
+ if code < 0 or code > 255:
270
+ raise ValueError("Exit code must be between 0 and 255")
271
+
272
+ # Force garbage collection to free memory
273
+ gc.collect()
274
+
275
+ # Terminate the process immediately without running cleanup handlers
276
+ os._exit(code)
@@ -45,10 +45,9 @@ class TestDumper(ITestDumper):
45
45
  try:
46
46
  if value is None:
47
47
  return False
48
- from orionis.test.cases.test_async import AsyncTestCase
49
- from orionis.test.cases.test_case import TestCase
50
- from orionis.test.cases.test_sync import SyncTestCase
51
- return isinstance(value, (AsyncTestCase, TestCase, SyncTestCase))
48
+ from orionis.test.cases.asynchronous import AsyncTestCase
49
+ from orionis.test.cases.synchronous import SyncTestCase
50
+ return isinstance(value, (AsyncTestCase, SyncTestCase))
52
51
  except Exception:
53
52
  return False
54
53
 
@@ -16,6 +16,28 @@ class TestPrinter(ITestPrinter):
16
16
  def __init__(
17
17
  self
18
18
  ) -> None:
19
+ """
20
+ Initialize the test output printer.
21
+
22
+ This initializes a Rich Console for output rendering, setting up panel
23
+ parameters and debug keywords for test result display.
24
+
25
+ Parameters
26
+ ----------
27
+ None
28
+
29
+ Returns
30
+ -------
31
+ None
32
+
33
+ Notes
34
+ -----
35
+ Sets up the following attributes:
36
+ - __rich_console: Rich Console instance for formatted terminal output
37
+ - __panel_title: Title string for the output panel
38
+ - __panel_width: Width of the output panel (75% of console width)
39
+ - __debbug_keywords: List of keywords for identifying debug calls
40
+ """
19
41
  self.__rich_console = Console()
20
42
  self.__panel_title: str = "🧪 Orionis Framework - Component Test Suite"
21
43
  self.__panel_width: int = int(self.__rich_console.width * 0.75)
@@ -5,9 +5,9 @@ from pathlib import Path
5
5
  from typing import Dict, List, Optional, Tuple
6
6
  from orionis.services.environment.env import Env
7
7
  from orionis.test.exceptions import OrionisTestPersistenceError, OrionisTestValueError
8
- from orionis.test.contracts.history import ITestHistory
8
+ from orionis.test.contracts.logs import ITestLogs
9
9
 
10
- class TestHistory(ITestHistory):
10
+ class TestLogs(ITestLogs):
11
11
 
12
12
  def __init__(
13
13
  self,
@@ -71,7 +71,9 @@ class TestHistory(ITestHistory):
71
71
  # Create a connection to the database, initially set to None
72
72
  self._conn: Optional[sqlite3.Connection] = None
73
73
 
74
- def __connect(self) -> None:
74
+ def __connect(
75
+ self
76
+ ) -> None:
75
77
  """
76
78
  Establishes a connection to the SQLite database if not already connected.
77
79
 
@@ -89,7 +91,9 @@ class TestHistory(ITestHistory):
89
91
  except (sqlite3.Error, Exception) as e:
90
92
  raise OrionisTestPersistenceError(f"Database connection error: {e}")
91
93
 
92
- def __createTableIfNotExists(self) -> bool:
94
+ def __createTableIfNotExists(
95
+ self
96
+ ) -> bool:
93
97
  """
94
98
  Ensures that the test history table exists in the database.
95
99
 
@@ -135,7 +139,10 @@ class TestHistory(ITestHistory):
135
139
  self.__close()
136
140
  self._conn = None
137
141
 
138
- def __insertReport(self, report: Dict) -> bool:
142
+ def __insertReport(
143
+ self,
144
+ report: Dict
145
+ ) -> bool:
139
146
  """
140
147
  Inserts a test report into the history database table.
141
148
 
@@ -272,7 +279,9 @@ class TestHistory(ITestHistory):
272
279
  self.__close()
273
280
  self._conn = None
274
281
 
275
- def __resetDatabase(self) -> bool:
282
+ def __resetDatabase(
283
+ self
284
+ ) -> bool:
276
285
  """
277
286
  Resets the database by dropping the existing table.
278
287
  This method connects to the database, drops the table specified by
@@ -304,7 +313,9 @@ class TestHistory(ITestHistory):
304
313
  self.__close()
305
314
  self._conn = None
306
315
 
307
- def __close(self) -> None:
316
+ def __close(
317
+ self
318
+ ) -> None:
308
319
  """
309
320
  Closes the current database connection.
310
321
  This method checks if a database connection exists. If so, it closes the connection and sets the connection attribute to None.
@@ -318,7 +329,10 @@ class TestHistory(ITestHistory):
318
329
  self._conn.close()
319
330
  self._conn = None
320
331
 
321
- def create(self, report: Dict) -> bool:
332
+ def create(
333
+ self,
334
+ report: Dict
335
+ ) -> bool:
322
336
  """
323
337
  Create a new test report in the history database.
324
338
 
@@ -335,7 +349,9 @@ class TestHistory(ITestHistory):
335
349
  self.__createTableIfNotExists()
336
350
  return self.__insertReport(report)
337
351
 
338
- def reset(self) -> bool:
352
+ def reset(
353
+ self
354
+ ) -> bool:
339
355
  """
340
356
  Reset the history database by dropping the existing table.
341
357
 
File without changes