orionis 0.405.0__py3-none-any.whl → 0.407.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 (175) hide show
  1. orionis/console/base/command.py +57 -50
  2. orionis/console/base/contracts/command.py +68 -0
  3. orionis/console/dynamic/contracts/progress_bar.py +3 -3
  4. orionis/console/dynamic/progress_bar.py +8 -8
  5. orionis/console/output/console.py +8 -2
  6. orionis/console/output/contracts/console.py +1 -1
  7. orionis/container/container.py +2 -2
  8. orionis/container/context/scope.py +4 -1
  9. orionis/container/contracts/service_provider.py +2 -2
  10. orionis/container/entities/binding.py +31 -44
  11. orionis/container/enums/lifetimes.py +22 -1
  12. orionis/container/facades/facade.py +1 -2
  13. orionis/container/providers/service_provider.py +2 -2
  14. orionis/foundation/application.py +542 -248
  15. orionis/foundation/config/app/entities/app.py +107 -90
  16. orionis/foundation/config/auth/entities/auth.py +4 -33
  17. orionis/foundation/config/cache/entities/cache.py +18 -41
  18. orionis/foundation/config/cache/entities/file.py +8 -35
  19. orionis/foundation/config/cache/entities/stores.py +17 -38
  20. orionis/foundation/config/cors/entities/cors.py +41 -54
  21. orionis/foundation/config/database/entities/connections.py +40 -56
  22. orionis/foundation/config/database/entities/database.py +11 -38
  23. orionis/foundation/config/database/entities/mysql.py +48 -76
  24. orionis/foundation/config/database/entities/oracle.py +30 -57
  25. orionis/foundation/config/database/entities/pgsql.py +45 -61
  26. orionis/foundation/config/database/entities/sqlite.py +26 -53
  27. orionis/foundation/config/filesystems/entitites/aws.py +28 -49
  28. orionis/foundation/config/filesystems/entitites/disks.py +27 -47
  29. orionis/foundation/config/filesystems/entitites/filesystems.py +15 -37
  30. orionis/foundation/config/filesystems/entitites/local.py +9 -35
  31. orionis/foundation/config/filesystems/entitites/public.py +14 -41
  32. orionis/foundation/config/logging/entities/channels.py +56 -86
  33. orionis/foundation/config/logging/entities/chunked.py +9 -9
  34. orionis/foundation/config/logging/entities/daily.py +8 -8
  35. orionis/foundation/config/logging/entities/hourly.py +6 -6
  36. orionis/foundation/config/logging/entities/logging.py +12 -18
  37. orionis/foundation/config/logging/entities/monthly.py +7 -7
  38. orionis/foundation/config/logging/entities/stack.py +5 -5
  39. orionis/foundation/config/logging/entities/weekly.py +6 -6
  40. orionis/foundation/config/mail/entities/file.py +9 -36
  41. orionis/foundation/config/mail/entities/mail.py +22 -40
  42. orionis/foundation/config/mail/entities/mailers.py +29 -44
  43. orionis/foundation/config/mail/entities/smtp.py +47 -48
  44. orionis/foundation/config/queue/entities/brokers.py +19 -41
  45. orionis/foundation/config/queue/entities/database.py +24 -46
  46. orionis/foundation/config/queue/entities/queue.py +28 -40
  47. orionis/foundation/config/roots/paths.py +272 -468
  48. orionis/foundation/config/session/entities/session.py +23 -53
  49. orionis/foundation/config/startup.py +165 -135
  50. orionis/foundation/config/testing/entities/testing.py +137 -122
  51. orionis/foundation/config/testing/enums/__init__.py +6 -2
  52. orionis/foundation/config/testing/enums/drivers.py +16 -0
  53. orionis/foundation/config/testing/enums/verbosity.py +18 -0
  54. orionis/foundation/contracts/application.py +152 -362
  55. orionis/foundation/providers/console_provider.py +24 -2
  56. orionis/foundation/providers/dumper_provider.py +24 -2
  57. orionis/foundation/providers/logger_provider.py +24 -2
  58. orionis/foundation/providers/path_resolver_provider.py +25 -2
  59. orionis/foundation/providers/progress_bar_provider.py +24 -2
  60. orionis/foundation/providers/testing_provider.py +39 -0
  61. orionis/foundation/providers/workers_provider.py +24 -2
  62. orionis/metadata/framework.py +1 -1
  63. orionis/services/asynchrony/contracts/coroutines.py +13 -5
  64. orionis/services/asynchrony/coroutines.py +33 -29
  65. orionis/services/asynchrony/exceptions/exception.py +9 -1
  66. orionis/services/environment/core/dot_env.py +46 -34
  67. orionis/services/environment/enums/__init__.py +0 -0
  68. orionis/services/environment/enums/cast_type.py +42 -0
  69. orionis/services/environment/helpers/functions.py +1 -2
  70. orionis/services/environment/key/__init__.py +0 -0
  71. orionis/services/environment/key/key_generator.py +37 -0
  72. orionis/services/environment/serializer/__init__.py +0 -0
  73. orionis/services/environment/serializer/values.py +21 -0
  74. orionis/services/environment/validators/__init__.py +0 -0
  75. orionis/services/environment/validators/key_name.py +46 -0
  76. orionis/services/environment/validators/types.py +45 -0
  77. orionis/services/system/contracts/imports.py +38 -18
  78. orionis/services/system/contracts/workers.py +29 -12
  79. orionis/services/system/imports.py +65 -25
  80. orionis/services/system/runtime/imports.py +18 -9
  81. orionis/services/system/workers.py +49 -16
  82. orionis/support/entities/__init__.py +0 -0
  83. orionis/support/entities/base.py +104 -0
  84. orionis/support/facades/testing.py +15 -0
  85. orionis/support/facades/workers.py +1 -1
  86. orionis/test/cases/asynchronous.py +0 -11
  87. orionis/test/cases/synchronous.py +0 -9
  88. orionis/test/contracts/dumper.py +11 -4
  89. orionis/test/contracts/kernel.py +5 -110
  90. orionis/test/contracts/logs.py +27 -65
  91. orionis/test/contracts/printer.py +16 -128
  92. orionis/test/contracts/test_result.py +100 -0
  93. orionis/test/contracts/unit_test.py +87 -150
  94. orionis/test/core/unit_test.py +608 -554
  95. orionis/test/entities/result.py +22 -2
  96. orionis/test/enums/__init__.py +0 -2
  97. orionis/test/enums/status.py +14 -9
  98. orionis/test/exceptions/config.py +9 -1
  99. orionis/test/exceptions/failure.py +34 -11
  100. orionis/test/exceptions/persistence.py +10 -2
  101. orionis/test/exceptions/runtime.py +9 -1
  102. orionis/test/exceptions/value.py +13 -1
  103. orionis/test/kernel.py +87 -289
  104. orionis/test/output/dumper.py +83 -18
  105. orionis/test/output/printer.py +399 -156
  106. orionis/test/records/logs.py +203 -82
  107. orionis/test/validators/__init__.py +33 -0
  108. orionis/test/validators/base_path.py +45 -0
  109. orionis/test/validators/execution_mode.py +45 -0
  110. orionis/test/validators/fail_fast.py +37 -0
  111. orionis/test/validators/folder_path.py +34 -0
  112. orionis/test/validators/module_name.py +31 -0
  113. orionis/test/validators/name_pattern.py +40 -0
  114. orionis/test/validators/pattern.py +36 -0
  115. orionis/test/validators/persistent.py +42 -0
  116. orionis/test/validators/persistent_driver.py +43 -0
  117. orionis/test/validators/print_result.py +37 -0
  118. orionis/test/validators/tags.py +37 -0
  119. orionis/test/validators/throw_exception.py +39 -0
  120. orionis/test/validators/verbosity.py +37 -0
  121. orionis/test/validators/web_report.py +35 -0
  122. orionis/test/validators/workers.py +31 -0
  123. orionis/test/view/render.py +48 -54
  124. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/METADATA +1 -1
  125. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/RECORD +170 -112
  126. tests/container/__init__.py +0 -0
  127. tests/container/context/__init__.py +0 -0
  128. tests/container/context/test_manager.py +27 -0
  129. tests/container/context/test_scope.py +23 -0
  130. tests/container/entities/__init__.py +0 -0
  131. tests/container/entities/test_binding.py +133 -0
  132. tests/container/enums/__init__.py +0 -0
  133. tests/container/enums/test_lifetimes.py +63 -0
  134. tests/container/facades/__init__.py +0 -0
  135. tests/container/facades/test_facade.py +61 -0
  136. tests/container/mocks/__init__.py +0 -0
  137. tests/container/mocks/mock_complex_classes.py +482 -0
  138. tests/container/mocks/mock_simple_classes.py +32 -0
  139. tests/container/providers/__init__.py +0 -0
  140. tests/container/providers/test_providers.py +48 -0
  141. tests/container/resolver/__init__.py +0 -0
  142. tests/container/resolver/test_resolver.py +55 -0
  143. tests/container/test_container.py +254 -0
  144. tests/container/test_singleton.py +98 -0
  145. tests/container/test_thread_safety.py +217 -0
  146. tests/container/validators/__init__.py +0 -0
  147. tests/container/validators/test_implements.py +140 -0
  148. tests/container/validators/test_is_abstract_class.py +99 -0
  149. tests/container/validators/test_is_callable.py +73 -0
  150. tests/container/validators/test_is_concrete_class.py +97 -0
  151. tests/container/validators/test_is_instance.py +105 -0
  152. tests/container/validators/test_is_not_subclass.py +117 -0
  153. tests/container/validators/test_is_subclass.py +115 -0
  154. tests/container/validators/test_is_valid_alias.py +113 -0
  155. tests/container/validators/test_lifetime.py +75 -0
  156. tests/example/test_example.py +2 -2
  157. tests/foundation/config/testing/test_foundation_config_testing.py +1 -1
  158. tests/metadata/test_metadata_framework.py +89 -24
  159. tests/metadata/test_metadata_package.py +55 -10
  160. tests/services/asynchrony/test_services_asynchrony_coroutine.py +52 -7
  161. tests/services/system/test_services_system_imports.py +119 -16
  162. tests/services/system/test_services_system_workers.py +71 -30
  163. tests/testing/test_testing_result.py +117 -117
  164. tests/testing/test_testing_unit.py +209 -209
  165. orionis/foundation/config/base.py +0 -112
  166. orionis/test/arguments/parser.py +0 -187
  167. orionis/test/contracts/parser.py +0 -43
  168. orionis/test/entities/arguments.py +0 -38
  169. orionis/test/enums/execution_mode.py +0 -16
  170. /orionis/{test/arguments → console/base/contracts}/__init__.py +0 -0
  171. /orionis/foundation/config/testing/enums/{test_mode.py → mode.py} +0 -0
  172. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/WHEEL +0 -0
  173. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/licenses/LICENCE +0 -0
  174. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/top_level.txt +0 -0
  175. {orionis-0.405.0.dist-info → orionis-0.407.0.dist-info}/zip-safe +0 -0
@@ -1,9 +1,7 @@
1
1
  import json
2
- import re
3
2
  import sqlite3
4
3
  from pathlib import Path
5
4
  from typing import Dict, List, Optional, Tuple
6
- from orionis.services.environment.env import Env
7
5
  from orionis.test.exceptions import OrionisTestPersistenceError, OrionisTestValueError
8
6
  from orionis.test.contracts.logs import ITestLogs
9
7
 
@@ -11,64 +9,42 @@ class TestLogs(ITestLogs):
11
9
 
12
10
  def __init__(
13
11
  self,
14
- storage_path: Optional[str] = None,
15
- db_name: Optional[str] = 'tests.sqlite',
16
- table_name: Optional[str] = 'reports',
12
+ storage_path: str
17
13
  ) -> None:
18
14
  """
19
- Initialize the history storage for test logs.
15
+ Initialize a new instance of the TestLogs class, configuring the SQLite database path and connection.
16
+
17
+ This constructor sets up the database file and table names, ensures the storage directory exists,
18
+ and prepares the absolute path for the SQLite database file. The database connection is initialized
19
+ as None and will be established when needed.
20
20
 
21
21
  Parameters
22
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.
23
+ storage_path : str
24
+ The directory path where the SQLite database file ('tests.sqlite') will be stored. If the directory
25
+ does not exist, it will be created automatically.
32
26
 
33
- Raises
34
- ------
35
- OrionisTestValueError
36
- If db_name or table_name do not meet the required format.
27
+ Returns
28
+ -------
29
+ None
30
+ This method does not return a value.
37
31
  """
38
32
 
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
33
+ # Set the database file and table names
34
+ self.__db_name = 'tests.sqlite'
35
+ self.__table_name = 'reports'
36
+
37
+ # Create the full path to the database file
38
+ db_path = Path(storage_path)
39
+ db_path = db_path / self.__db_name
40
+
41
+ # Ensure the parent directory exists
65
42
  db_path.parent.mkdir(parents=True, exist_ok=True)
66
43
 
67
- # Store path in environment
68
- Env.set("TEST_DB_PATH", str(db_path), 'path')
69
- self.__db_path = db_path
44
+ # Store the resolved absolute path to the database
45
+ self.__db_path = db_path.resolve()
70
46
 
71
- # Create a connection to the database, initially set to None
47
+ # Initialize the database connection as None
72
48
  self._conn: Optional[sqlite3.Connection] = None
73
49
 
74
50
  def __connect(
@@ -77,28 +53,56 @@ class TestLogs(ITestLogs):
77
53
  """
78
54
  Establishes a connection to the SQLite database if not already connected.
79
55
 
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.
56
+ This method checks if a database connection is already established. If not, it attempts to create a new
57
+ SQLite connection using the absolute path specified during initialization. If the connection attempt fails,
58
+ it raises an OrionisTestPersistenceError with the error details.
82
59
 
83
60
  Raises
84
61
  ------
85
62
  OrionisTestPersistenceError
86
63
  If a database connection error occurs.
64
+
65
+ Returns
66
+ -------
67
+ None
68
+ This method does not return a value. It sets the self._conn attribute to an active SQLite connection
69
+ if successful, or raises an exception if the connection fails.
87
70
  """
71
+
72
+ # Only connect if there is no existing connection
88
73
  if self._conn is None:
74
+
89
75
  try:
90
- self._conn = sqlite3.connect(str(self.__db_path))
76
+
77
+ # Attempt to establish a new SQLite connection
78
+ self._conn = sqlite3.connect(
79
+ database=str(self.__db_path),
80
+ timeout=5.0,
81
+ isolation_level=None,
82
+ check_same_thread=False,
83
+ autocommit=True
84
+ )
85
+
86
+ # Hability to use WAL mode for better concurrency
87
+ self._conn.execute("PRAGMA journal_mode=WAL;")
88
+ self._conn.execute("PRAGMA synchronous=NORMAL;")
89
+
91
90
  except (sqlite3.Error, Exception) as e:
91
+
92
+ # Raise a custom exception if connection fails
92
93
  raise OrionisTestPersistenceError(f"Database connection error: {e}")
93
94
 
94
95
  def __createTableIfNotExists(
95
96
  self
96
97
  ) -> bool:
97
98
  """
98
- Ensures that the test history table exists in the database.
99
+ Ensures the existence of the test history table in the SQLite database.
99
100
 
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.
101
+ This method establishes a connection to the database and attempts to create the table
102
+ specified by `self.__table_name` with the required schema if it does not already exist.
103
+ The table includes columns for the report JSON, test statistics, and a timestamp.
104
+ If a database error occurs during table creation, the transaction is rolled back and
105
+ an OrionisTestPersistenceError is raised.
102
106
 
103
107
  Raises
104
108
  ------
@@ -108,12 +112,19 @@ class TestLogs(ITestLogs):
108
112
  Returns
109
113
  -------
110
114
  bool
111
- True if the table was created successfully or already exists, False otherwise.
115
+ Returns True if the table was created successfully or already exists.
116
+ Returns False only if an unexpected error occurs (which will typically raise an exception).
112
117
  """
113
118
 
119
+ # Establish a connection to the database
114
120
  self.__connect()
121
+
115
122
  try:
123
+
124
+ # Create a cursor to execute SQL commands
116
125
  cursor = self._conn.cursor()
126
+
127
+ # Create the table with the required schema if it does not exist
117
128
  cursor.execute(f'''
118
129
  CREATE TABLE IF NOT EXISTS {self.__table_name} (
119
130
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -128,13 +139,25 @@ class TestLogs(ITestLogs):
128
139
  timestamp TEXT
129
140
  )
130
141
  ''')
142
+
143
+ # Commit the transaction to save changes
131
144
  self._conn.commit()
145
+
146
+ # Return True indicating the table exists or was created successfully
132
147
  return True
148
+
133
149
  except sqlite3.Error as e:
150
+
151
+ # Roll back the transaction if an error occurs
134
152
  if self._conn:
135
153
  self._conn.rollback()
154
+
155
+ # Raise a custom exception with the error details
136
156
  raise OrionisTestPersistenceError(f"Failed to create table: {e}")
157
+
137
158
  finally:
159
+
160
+ # Close the database connection
138
161
  if self._conn:
139
162
  self.__close()
140
163
  self._conn = None
@@ -146,10 +169,14 @@ class TestLogs(ITestLogs):
146
169
  """
147
170
  Inserts a test report into the history database table.
148
171
 
172
+ This method validates the provided report dictionary to ensure all required fields are present,
173
+ serializes the report as JSON, and inserts it into the database table. If any required field is missing,
174
+ or if a database error occurs during insertion, an appropriate exception is raised.
175
+
149
176
  Parameters
150
177
  ----------
151
178
  report : Dict
152
- A dictionary containing the report data. Must include the following keys:
179
+ A dictionary containing the report data. The dictionary must include the following keys:
153
180
  - total_tests
154
181
  - passed
155
182
  - failed
@@ -169,16 +196,17 @@ class TestLogs(ITestLogs):
169
196
  Returns
170
197
  -------
171
198
  bool
172
- True if the report was successfully inserted, False otherwise.
199
+ Returns True if the report was successfully inserted into the database.
200
+ Returns False only if an unexpected error occurs (which will typically raise an exception).
173
201
  """
174
202
 
175
- # Required fields in the report
203
+ # List of required fields for the report
176
204
  fields = [
177
205
  "json", "total_tests", "passed", "failed", "errors",
178
206
  "skipped", "total_time", "success_rate", "timestamp"
179
207
  ]
180
208
 
181
- # Validate report structure
209
+ # Check for missing required fields (excluding "json" which is handled separately)
182
210
  missing = []
183
211
  for key in fields:
184
212
  if key not in report and key != "json":
@@ -186,11 +214,10 @@ class TestLogs(ITestLogs):
186
214
  if missing:
187
215
  raise OrionisTestValueError(f"Missing report fields: {missing}")
188
216
 
189
- # Insert the report into the database
217
+ # Establish a connection to the database
190
218
  self.__connect()
191
219
  try:
192
-
193
- # Query to insert the report into the table
220
+ # Prepare the SQL query to insert the report data
194
221
  query = f'''
195
222
  INSERT INTO {self.__table_name} (
196
223
  json, total_tests, passed, failed, errors,
@@ -198,8 +225,10 @@ class TestLogs(ITestLogs):
198
225
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
199
226
  '''
200
227
 
201
- # Execute the insert query with the report data
228
+ # Execute the insert query with the report data, serializing the entire report as JSON
202
229
  cursor = self._conn.cursor()
230
+
231
+ # Ensure the 'json' field is serialized to JSON format
203
232
  cursor.execute(query, (
204
233
  json.dumps(report),
205
234
  report["total_tests"],
@@ -211,13 +240,23 @@ class TestLogs(ITestLogs):
211
240
  report["success_rate"],
212
241
  report["timestamp"]
213
242
  ))
243
+
244
+ # Commit the transaction to save the new report
214
245
  self._conn.commit()
246
+
247
+ # Return True indicating the report was successfully inserted
215
248
  return True
249
+
216
250
  except sqlite3.Error as e:
251
+
252
+ # Roll back the transaction if an error occurs during insertion
217
253
  if self._conn:
218
254
  self._conn.rollback()
219
255
  raise OrionisTestPersistenceError(f"Failed to insert report: {e}")
256
+
220
257
  finally:
258
+
259
+ # Ensure the database connection is closed after the operation
221
260
  if self._conn:
222
261
  self.__close()
223
262
  self._conn = None
@@ -230,6 +269,11 @@ class TestLogs(ITestLogs):
230
269
  """
231
270
  Retrieves a specified number of report records from the database, ordered by their ID.
232
271
 
272
+ This method allows fetching either the earliest or latest test reports from the database,
273
+ depending on the parameters provided. If `first` is specified, it retrieves the earliest
274
+ reports in ascending order by ID. If `last` is specified, it retrieves the latest reports
275
+ in descending order by ID. Only one of `first` or `last` can be provided at a time.
276
+
233
277
  Parameters
234
278
  ----------
235
279
  first : Optional[int], default=None
@@ -240,7 +284,9 @@ class TestLogs(ITestLogs):
240
284
  Returns
241
285
  -------
242
286
  List[Tuple]
243
- A list of tuples representing the report records.
287
+ A list of tuples, where each tuple represents a report record retrieved from the database.
288
+ Each tuple contains all columns from the reports table, including the serialized JSON report
289
+ and associated statistics.
244
290
 
245
291
  Raises
246
292
  ------
@@ -250,31 +296,52 @@ class TestLogs(ITestLogs):
250
296
  If there is an error retrieving reports from the database.
251
297
  """
252
298
 
253
- # Validate parameters
299
+ # Ensure that only one of 'first' or 'last' is specified
254
300
  if first is not None and last is not None:
255
301
  raise OrionisTestValueError(
256
302
  "Cannot specify both 'first' and 'last' parameters. Use one or the other."
257
303
  )
304
+
305
+ # Validate 'first' parameter if provided
258
306
  if first is not None:
259
307
  if not isinstance(first, int) or first <= 0:
260
308
  raise OrionisTestValueError("'first' must be an integer greater than 0.")
309
+
310
+ # Validate 'last' parameter if provided
261
311
  if last is not None:
262
312
  if not isinstance(last, int) or last <= 0:
263
313
  raise OrionisTestValueError("'last' must be an integer greater than 0.")
264
314
 
315
+ # Determine the order and quantity of records to retrieve
265
316
  order = 'DESC' if last is not None else 'ASC'
266
317
  quantity = first if first is not None else last
267
318
 
319
+ # Establish a connection to the database
268
320
  self.__connect()
321
+
269
322
  try:
323
+
324
+ # Create a cursor to execute SQL commands
270
325
  cursor = self._conn.cursor()
326
+
327
+ # Prepare the SQL query to select the desired reports
271
328
  query = f"SELECT * FROM {self.__table_name} ORDER BY id {order} LIMIT ?"
272
329
  cursor.execute(query, (quantity,))
330
+
331
+ # Fetch all matching records
273
332
  results = cursor.fetchall()
333
+
334
+ # Return the list of report records
274
335
  return results
336
+
275
337
  except sqlite3.Error as e:
338
+
339
+ # Raise a custom exception if retrieval fails
276
340
  raise OrionisTestPersistenceError(f"Failed to retrieve reports from '{self.__db_name}': {e}")
341
+
277
342
  finally:
343
+
344
+ # Ensure the database connection is closed after the operation
278
345
  if self._conn:
279
346
  self.__close()
280
347
  self._conn = None
@@ -283,32 +350,47 @@ class TestLogs(ITestLogs):
283
350
  self
284
351
  ) -> bool:
285
352
  """
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.
353
+ Drops the reports table from the SQLite database, effectively resetting the test history.
354
+
355
+ This method establishes a connection to the database and attempts to drop the table specified
356
+ by `self.__table_name` if it exists. After dropping the table, it commits the changes and closes
357
+ the connection. If an error occurs during the operation, an OrionisTestPersistenceError is raised.
291
358
 
292
359
  Raises
293
360
  ------
294
361
  OrionisTestPersistenceError
295
- If the database reset operation fails due to an SQLite error.
362
+ If an SQLite error occurs while attempting to drop the table.
296
363
 
297
364
  Returns
298
365
  -------
299
366
  bool
300
- True if the database was successfully reset, False otherwise.
367
+ Returns True if the table was successfully dropped or did not exist.
368
+ Returns False only if an unexpected error occurs (which will typically raise an exception).
301
369
  """
302
370
 
371
+ # Establish a connection to the database
303
372
  self.__connect()
373
+
304
374
  try:
375
+
376
+ # Create a cursor and execute the DROP TABLE statement
305
377
  cursor = self._conn.cursor()
306
378
  cursor.execute(f'DROP TABLE IF EXISTS {self.__table_name}')
379
+
380
+ # Commit the transaction to apply the changes
307
381
  self._conn.commit()
382
+
383
+ # Return True to indicate the reset was successful
308
384
  return True
385
+
309
386
  except sqlite3.Error as e:
387
+
388
+ # Raise a custom exception if the reset fails
310
389
  raise OrionisTestPersistenceError(f"Failed to reset database: {e}")
390
+
311
391
  finally:
392
+
393
+ # Ensure the database connection is closed after the operation
312
394
  if self._conn:
313
395
  self.__close()
314
396
  self._conn = None
@@ -317,14 +399,19 @@ class TestLogs(ITestLogs):
317
399
  self
318
400
  ) -> None:
319
401
  """
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.
402
+ Closes the active SQLite database connection if it exists.
403
+
404
+ This method checks whether a database connection is currently open. If so, it closes the connection
405
+ to release any associated resources and sets the connection attribute to None to indicate that
406
+ there is no active connection.
322
407
 
323
408
  Returns
324
409
  -------
325
410
  None
411
+ This method does not return a value. It ensures that the database connection is properly closed.
326
412
  """
327
413
 
414
+ # If a database connection exists, close it and set the connection attribute to None
328
415
  if self._conn:
329
416
  self._conn.close()
330
417
  self._conn = None
@@ -334,32 +421,50 @@ class TestLogs(ITestLogs):
334
421
  report: Dict
335
422
  ) -> bool:
336
423
  """
337
- Create a new test report in the history database.
424
+ Inserts a new test report into the history database after ensuring the reports table exists.
425
+
426
+ This method first checks for the existence of the reports table in the SQLite database,
427
+ creating it if necessary. It then attempts to insert the provided report dictionary into
428
+ the table. The report must contain all required fields as defined by the schema.
338
429
 
339
430
  Parameters
340
431
  ----------
341
432
  report : Dict
342
- A dictionary containing the test report data.
433
+ A dictionary containing the test report data. The dictionary must include all required
434
+ fields such as total_tests, passed, failed, errors, skipped, total_time, success_rate, and timestamp.
343
435
 
344
436
  Returns
345
437
  -------
346
438
  bool
347
- True if the report was successfully created, False otherwise.
439
+ Returns True if the report was successfully inserted into the database.
440
+ Raises an exception if the operation fails due to missing fields or database errors.
348
441
  """
442
+
443
+ # Ensure the reports table exists before inserting the report
349
444
  self.__createTableIfNotExists()
445
+
446
+ # Insert the report into the database and return the result
350
447
  return self.__insertReport(report)
351
448
 
352
449
  def reset(
353
450
  self
354
451
  ) -> bool:
355
452
  """
356
- Reset the history database by dropping the existing table.
453
+ Drops the reports table from the SQLite database, effectively clearing all test history records.
454
+
455
+ This method establishes a connection to the database and attempts to drop the table specified
456
+ by `self.__table_name` if it exists. This operation removes all stored test reports, resetting
457
+ the database to an empty state. If the table does not exist, the method completes without error.
458
+ If an error occurs during the operation, an OrionisTestPersistenceError is raised.
357
459
 
358
460
  Returns
359
461
  -------
360
462
  bool
361
- True if the database was successfully reset, False otherwise.
463
+ Returns True if the reports table was successfully dropped or did not exist.
464
+ Raises an exception if the operation fails due to a database error.
362
465
  """
466
+
467
+ # Attempt to drop the reports table and reset the database
363
468
  return self.__resetDatabase()
364
469
 
365
470
  def get(
@@ -368,7 +473,12 @@ class TestLogs(ITestLogs):
368
473
  last: Optional[int] = None
369
474
  ) -> List[Tuple]:
370
475
  """
371
- Retrieve test reports from the history database.
476
+ Retrieves test reports from the history database based on the specified parameters.
477
+
478
+ This method allows fetching either the earliest or latest test reports from the database.
479
+ If `first` is provided, it retrieves the earliest reports in ascending order by ID.
480
+ If `last` is provided, it retrieves the latest reports in descending order by ID.
481
+ Only one of `first` or `last` can be specified at a time; providing both will result in an error.
372
482
 
373
483
  Parameters
374
484
  ----------
@@ -380,6 +490,17 @@ class TestLogs(ITestLogs):
380
490
  Returns
381
491
  -------
382
492
  List[Tuple]
383
- A list of tuples representing the retrieved reports.
493
+ A list of tuples, where each tuple represents a report record retrieved from the database.
494
+ Each tuple contains all columns from the reports table, including the serialized JSON report
495
+ and associated statistics.
496
+
497
+ Raises
498
+ ------
499
+ OrionisTestValueError
500
+ If both 'first' and 'last' are specified, or if either is not a positive integer.
501
+ OrionisTestPersistenceError
502
+ If there is an error retrieving reports from the database.
384
503
  """
504
+
505
+ # Delegate the retrieval logic to the internal __getReports method
385
506
  return self.__getReports(first, last)
@@ -0,0 +1,33 @@
1
+ from .base_path import ValidBasePath
2
+ from .execution_mode import ValidExecutionMode
3
+ from .fail_fast import ValidFailFast
4
+ from .folder_path import ValidFolderPath
5
+ from .module_name import ValidModuleName
6
+ from .name_pattern import ValidNamePattern
7
+ from .pattern import ValidPattern
8
+ from .persistent_driver import ValidPersistentDriver
9
+ from .persistent import ValidPersistent
10
+ from .print_result import ValidPrintResult
11
+ from .tags import ValidTags
12
+ from .throw_exception import ValidThrowException
13
+ from .verbosity import ValidVerbosity
14
+ from .web_report import ValidWebReport
15
+ from .workers import ValidWorkers
16
+
17
+ __all__ = [
18
+ 'ValidBasePath',
19
+ 'ValidExecutionMode',
20
+ 'ValidFailFast',
21
+ 'ValidFolderPath',
22
+ 'ValidModuleName',
23
+ 'ValidNamePattern',
24
+ 'ValidPattern',
25
+ 'ValidPersistentDriver',
26
+ 'ValidPersistent',
27
+ 'ValidPrintResult',
28
+ 'ValidTags',
29
+ 'ValidThrowException',
30
+ 'ValidVerbosity',
31
+ 'ValidWebReport',
32
+ 'ValidWorkers'
33
+ ]
@@ -0,0 +1,45 @@
1
+ from pathlib import Path
2
+ from orionis.test.exceptions import OrionisTestValueError
3
+
4
+ class __ValidBasePath:
5
+
6
+ def __call__(self, base_path) -> Path:
7
+ """
8
+ Callable class to validate and normalize a base path.
9
+ This validator ensures that the provided `base_path` is either a non-empty string or a `Path` object.
10
+ If valid, it returns a normalized `Path` object. Otherwise, it raises an `OrionisTestValueError`.
11
+
12
+ Parameters
13
+ ----------
14
+ base_path : str or Path
15
+ The base path to validate. Must be a non-empty string or a `Path` object.
16
+
17
+ Returns
18
+ -------
19
+ Path
20
+ A normalized `Path` object corresponding to the provided base path.
21
+ If `base_path` is not a non-empty string or a valid `Path` object.
22
+ """
23
+
24
+ if isinstance(base_path, str):
25
+ base_path = base_path.strip()
26
+ if not base_path:
27
+ raise OrionisTestValueError(
28
+ "Invalid base_path: Expected a non-empty string or Path."
29
+ )
30
+ return Path(base_path)
31
+
32
+ elif isinstance(base_path, Path):
33
+ if not str(base_path).strip():
34
+ raise OrionisTestValueError(
35
+ "Invalid base_path: Path cannot be empty."
36
+ )
37
+ return base_path
38
+
39
+ else:
40
+ raise OrionisTestValueError(
41
+ f"Invalid base_path: Expected a non-empty string or Path, got '{str(base_path)}' ({type(base_path).__name__})."
42
+ )
43
+
44
+ # Exported singleton instance
45
+ ValidBasePath = __ValidBasePath()
@@ -0,0 +1,45 @@
1
+ from orionis.foundation.config.testing.enums.mode import ExecutionMode
2
+ from orionis.test.exceptions import OrionisTestValueError
3
+
4
+ class __ValidExecutionMode:
5
+
6
+ def __call__(self, execution_mode: str | ExecutionMode) -> str:
7
+ """
8
+ Validates that the provided execution_mode is either a valid string representation
9
+ of an ExecutionMode enum member or an instance of the ExecutionMode enum.
10
+
11
+ Parameters
12
+ ----------
13
+ execution_mode : str or ExecutionMode
14
+ The execution mode to validate. Can be a string (case-insensitive) matching
15
+ an ExecutionMode enum member, or an ExecutionMode enum instance.
16
+
17
+ Returns
18
+ -------
19
+ str
20
+ The string value of the validated ExecutionMode.
21
+
22
+ Raises
23
+ ------
24
+ OrionisTestValueError
25
+ If execution_mode is not a string or ExecutionMode enum, or if the string
26
+ does not correspond to a valid ExecutionMode member.
27
+ """
28
+
29
+ if not isinstance(execution_mode, (str, ExecutionMode)):
30
+ raise OrionisTestValueError(
31
+ f"Invalid execution_mode: Expected a string or ExecutionMode enum, got '{execution_mode}' ({type(execution_mode).__name__})."
32
+ )
33
+
34
+ if isinstance(execution_mode, ExecutionMode):
35
+ return execution_mode.value
36
+
37
+ elif isinstance(execution_mode, str):
38
+ if execution_mode.upper() not in ExecutionMode.__members__:
39
+ raise OrionisTestValueError(
40
+ f"Invalid execution_mode: '{execution_mode}' is not a valid ExecutionMode."
41
+ )
42
+ return ExecutionMode[execution_mode.upper()].value
43
+
44
+ # Exported singleton instance
45
+ ValidExecutionMode = __ValidExecutionMode()