orionis 0.404.0__py3-none-any.whl → 0.406.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/console/base/command.py +57 -50
- orionis/console/base/contracts/command.py +68 -0
- orionis/console/dynamic/contracts/progress_bar.py +3 -3
- orionis/console/dynamic/progress_bar.py +8 -8
- orionis/console/output/console.py +8 -2
- orionis/console/output/contracts/console.py +1 -1
- orionis/container/container.py +2 -2
- orionis/container/context/scope.py +4 -1
- orionis/container/contracts/service_provider.py +2 -2
- orionis/container/entities/binding.py +31 -44
- orionis/container/enums/lifetimes.py +22 -1
- orionis/container/facades/facade.py +1 -2
- orionis/container/providers/service_provider.py +2 -2
- orionis/foundation/application.py +542 -248
- orionis/foundation/config/app/entities/app.py +107 -90
- orionis/foundation/config/auth/entities/auth.py +4 -33
- orionis/foundation/config/cache/entities/cache.py +18 -41
- orionis/foundation/config/cache/entities/file.py +8 -35
- orionis/foundation/config/cache/entities/stores.py +17 -38
- orionis/foundation/config/cors/entities/cors.py +41 -54
- orionis/foundation/config/database/entities/connections.py +40 -56
- orionis/foundation/config/database/entities/database.py +11 -38
- orionis/foundation/config/database/entities/mysql.py +48 -76
- orionis/foundation/config/database/entities/oracle.py +30 -57
- orionis/foundation/config/database/entities/pgsql.py +45 -61
- orionis/foundation/config/database/entities/sqlite.py +26 -53
- orionis/foundation/config/filesystems/entitites/aws.py +28 -49
- orionis/foundation/config/filesystems/entitites/disks.py +27 -47
- orionis/foundation/config/filesystems/entitites/filesystems.py +15 -37
- orionis/foundation/config/filesystems/entitites/local.py +9 -35
- orionis/foundation/config/filesystems/entitites/public.py +14 -41
- orionis/foundation/config/logging/entities/channels.py +56 -86
- orionis/foundation/config/logging/entities/chunked.py +18 -10
- orionis/foundation/config/logging/entities/daily.py +17 -9
- orionis/foundation/config/logging/entities/hourly.py +15 -7
- orionis/foundation/config/logging/entities/logging.py +12 -18
- orionis/foundation/config/logging/entities/monthly.py +16 -8
- orionis/foundation/config/logging/entities/stack.py +15 -7
- orionis/foundation/config/logging/entities/weekly.py +15 -7
- orionis/foundation/config/logging/validators/path.py +6 -0
- orionis/foundation/config/mail/entities/file.py +9 -36
- orionis/foundation/config/mail/entities/mail.py +22 -40
- orionis/foundation/config/mail/entities/mailers.py +29 -44
- orionis/foundation/config/mail/entities/smtp.py +47 -48
- orionis/foundation/config/queue/entities/brokers.py +19 -41
- orionis/foundation/config/queue/entities/database.py +24 -46
- orionis/foundation/config/queue/entities/queue.py +28 -40
- orionis/foundation/config/roots/paths.py +272 -468
- orionis/foundation/config/session/entities/session.py +23 -53
- orionis/foundation/config/startup.py +165 -135
- orionis/foundation/config/testing/entities/testing.py +137 -122
- orionis/foundation/config/testing/enums/__init__.py +6 -2
- orionis/foundation/config/testing/enums/drivers.py +16 -0
- orionis/foundation/config/testing/enums/verbosity.py +18 -0
- orionis/foundation/contracts/application.py +152 -362
- orionis/foundation/providers/console_provider.py +24 -2
- orionis/foundation/providers/dumper_provider.py +24 -2
- orionis/foundation/providers/logger_provider.py +24 -2
- orionis/foundation/providers/path_resolver_provider.py +25 -2
- orionis/foundation/providers/progress_bar_provider.py +24 -2
- orionis/foundation/providers/testing_provider.py +39 -0
- orionis/foundation/providers/workers_provider.py +24 -2
- orionis/metadata/framework.py +1 -1
- orionis/services/environment/helpers/functions.py +1 -2
- orionis/services/environment/key/__init__.py +0 -0
- orionis/services/environment/key/key_generator.py +37 -0
- orionis/services/log/handlers/filename.py +64 -0
- orionis/services/log/handlers/size_rotating.py +9 -40
- orionis/services/log/handlers/timed_rotating.py +9 -41
- orionis/services/log/log_service.py +9 -52
- orionis/support/entities/__init__.py +0 -0
- orionis/support/entities/base.py +104 -0
- orionis/support/facades/testing.py +15 -0
- orionis/support/facades/workers.py +1 -1
- orionis/test/cases/asynchronous.py +0 -11
- orionis/test/cases/synchronous.py +0 -9
- orionis/test/contracts/dumper.py +11 -4
- orionis/test/contracts/kernel.py +5 -110
- orionis/test/contracts/logs.py +27 -65
- orionis/test/contracts/printer.py +16 -128
- orionis/test/contracts/test_result.py +100 -0
- orionis/test/contracts/unit_test.py +87 -150
- orionis/test/core/unit_test.py +608 -554
- orionis/test/entities/result.py +22 -2
- orionis/test/enums/__init__.py +0 -2
- orionis/test/enums/status.py +14 -9
- orionis/test/exceptions/config.py +9 -1
- orionis/test/exceptions/failure.py +34 -11
- orionis/test/exceptions/persistence.py +10 -2
- orionis/test/exceptions/runtime.py +9 -1
- orionis/test/exceptions/value.py +13 -1
- orionis/test/kernel.py +87 -289
- orionis/test/output/dumper.py +82 -18
- orionis/test/output/printer.py +399 -156
- orionis/test/records/logs.py +203 -82
- orionis/test/validators/__init__.py +33 -0
- orionis/test/validators/base_path.py +45 -0
- orionis/test/validators/execution_mode.py +45 -0
- orionis/test/validators/fail_fast.py +37 -0
- orionis/test/validators/folder_path.py +34 -0
- orionis/test/validators/module_name.py +31 -0
- orionis/test/validators/name_pattern.py +40 -0
- orionis/test/validators/pattern.py +36 -0
- orionis/test/validators/persistent.py +42 -0
- orionis/test/validators/persistent_driver.py +43 -0
- orionis/test/validators/print_result.py +37 -0
- orionis/test/validators/tags.py +37 -0
- orionis/test/validators/throw_exception.py +39 -0
- orionis/test/validators/verbosity.py +37 -0
- orionis/test/validators/web_report.py +35 -0
- orionis/test/validators/workers.py +31 -0
- orionis/test/view/render.py +48 -54
- {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/METADATA +1 -1
- {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/RECORD +160 -108
- tests/container/__init__.py +0 -0
- tests/container/context/__init__.py +0 -0
- tests/container/context/test_manager.py +27 -0
- tests/container/context/test_scope.py +23 -0
- tests/container/entities/__init__.py +0 -0
- tests/container/entities/test_binding.py +133 -0
- tests/container/enums/__init__.py +0 -0
- tests/container/enums/test_lifetimes.py +63 -0
- tests/container/facades/__init__.py +0 -0
- tests/container/facades/test_facade.py +61 -0
- tests/container/mocks/__init__.py +0 -0
- tests/container/mocks/mock_complex_classes.py +482 -0
- tests/container/mocks/mock_simple_classes.py +32 -0
- tests/container/providers/__init__.py +0 -0
- tests/container/providers/test_providers.py +48 -0
- tests/container/resolver/__init__.py +0 -0
- tests/container/resolver/test_resolver.py +55 -0
- tests/container/test_container.py +254 -0
- tests/container/test_singleton.py +98 -0
- tests/container/test_thread_safety.py +217 -0
- tests/container/validators/__init__.py +0 -0
- tests/container/validators/test_implements.py +140 -0
- tests/container/validators/test_is_abstract_class.py +99 -0
- tests/container/validators/test_is_callable.py +73 -0
- tests/container/validators/test_is_concrete_class.py +97 -0
- tests/container/validators/test_is_instance.py +105 -0
- tests/container/validators/test_is_not_subclass.py +117 -0
- tests/container/validators/test_is_subclass.py +115 -0
- tests/container/validators/test_is_valid_alias.py +113 -0
- tests/container/validators/test_lifetime.py +75 -0
- tests/foundation/config/logging/test_foundation_config_logging_chunked.py +12 -34
- tests/foundation/config/logging/test_foundation_config_logging_daily.py +11 -11
- tests/foundation/config/logging/test_foundation_config_logging_hourly.py +7 -8
- tests/foundation/config/logging/test_foundation_config_logging_monthly.py +7 -10
- tests/foundation/config/logging/test_foundation_config_logging_stack.py +6 -11
- tests/foundation/config/logging/test_foundation_config_logging_weekly.py +6 -5
- tests/foundation/config/testing/test_foundation_config_testing.py +1 -1
- tests/metadata/test_metadata_framework.py +18 -18
- tests/testing/test_testing_result.py +117 -117
- tests/testing/test_testing_unit.py +209 -209
- orionis/foundation/config/base.py +0 -112
- orionis/test/arguments/parser.py +0 -187
- orionis/test/contracts/parser.py +0 -43
- orionis/test/entities/arguments.py +0 -38
- orionis/test/enums/execution_mode.py +0 -16
- /orionis/{test/arguments → console/base/contracts}/__init__.py +0 -0
- /orionis/foundation/config/testing/enums/{test_mode.py → mode.py} +0 -0
- {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/WHEEL +0 -0
- {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/top_level.txt +0 -0
- {orionis-0.404.0.dist-info → orionis-0.406.0.dist-info}/zip-safe +0 -0
orionis/test/records/logs.py
CHANGED
|
@@ -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:
|
|
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
|
|
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 :
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
None
|
|
30
|
+
This method does not return a value.
|
|
37
31
|
"""
|
|
38
32
|
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
68
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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
|
|
99
|
+
Ensures the existence of the test history table in the SQLite database.
|
|
99
100
|
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
|
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
|
|
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
|
|
321
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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()
|