orionis 0.591.0__py3-none-any.whl → 0.592.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.
@@ -1,44 +1,59 @@
1
- import json
2
- import sqlite3
3
1
  from pathlib import Path
4
2
  from typing import Dict, List, Optional, Tuple
5
- from orionis.test.exceptions import OrionisTestPersistenceError, OrionisTestValueError
3
+ import json
4
+ import sqlite3
6
5
  from orionis.test.contracts.logs import ITestLogs
6
+ from orionis.test.exceptions import OrionisTestPersistenceError, OrionisTestValueError
7
7
 
8
8
  class TestLogs(ITestLogs):
9
9
 
10
10
  def __init__(
11
11
  self,
12
- storage_path: str
12
+ storage_path: str | Path,
13
+ db_name: str = 'tests.sqlite',
14
+ table_name: str = 'reports'
13
15
  ) -> None:
14
16
  """
15
- Initialize the TestLogs instance, setting up the SQLite database path and connection.
17
+ Initialize a TestLogs instance, configuring the SQLite database location and connection.
16
18
 
17
19
  Parameters
18
20
  ----------
19
- storage_path : str
20
- Directory path where the SQLite database file ('tests.sqlite') will be stored. The directory
21
- will be created if it does not exist.
21
+ storage_path : str or Path
22
+ Directory path where the SQLite database file will be stored. If the directory does not exist,
23
+ it will be created automatically.
24
+ db_name : str, optional
25
+ Name of the SQLite database file. Defaults to 'tests.sqlite'.
26
+ table_name : str, optional
27
+ Name of the table used to store test reports. Defaults to 'reports'.
22
28
 
23
29
  Returns
24
30
  -------
25
31
  None
32
+ This method does not return any value.
33
+
34
+ Notes
35
+ -----
36
+ The database connection is not established during initialization; it is set to None and will be
37
+ created when needed. The database path is resolved to an absolute path.
26
38
  """
27
- # Set the database file and table names
28
- self.__db_name = 'tests.sqlite'
29
- self.__table_name = 'reports'
30
39
 
31
- # Create the full path to the database file
32
- db_path = Path(storage_path)
40
+ # Store the database file and table names as private attributes
41
+ self.__db_name = db_name
42
+ self.__table_name = table_name
43
+
44
+ # Convert storage_path to Path object if it is a string
45
+ db_path = Path(storage_path) if isinstance(storage_path, str) else storage_path
46
+
47
+ # Append the database file name to the directory path
33
48
  db_path = db_path / self.__db_name
34
49
 
35
- # Ensure the parent directory exists
50
+ # Ensure the parent directory for the database exists
36
51
  db_path.parent.mkdir(parents=True, exist_ok=True)
37
52
 
38
- # Store the resolved absolute path to the database
53
+ # Store the absolute path to the database file
39
54
  self.__db_path = db_path.resolve()
40
55
 
41
- # Initialize the database connection as None
56
+ # Initialize the database connection attribute to None
42
57
  self._conn: Optional[sqlite3.Connection] = None
43
58
 
44
59
  def __connect(
@@ -47,21 +62,33 @@ class TestLogs(ITestLogs):
47
62
  """
48
63
  Establish a connection to the SQLite database if not already connected.
49
64
 
65
+ Parameters
66
+ ----------
67
+ None
68
+
69
+ Returns
70
+ -------
71
+ None
72
+ This method does not return any value. It sets up the database connection as a side effect.
73
+
50
74
  Raises
51
75
  ------
52
76
  OrionisTestPersistenceError
53
77
  If a database connection error occurs.
54
78
 
55
- Returns
56
- -------
57
- None
79
+ Notes
80
+ -----
81
+ This method initializes the SQLite connection only if it is not already established.
82
+ It configures the connection for improved concurrency using WAL mode and sets the synchronous
83
+ mode to NORMAL for better performance. The connection is stored in the `_conn` attribute.
58
84
  """
85
+
59
86
  # Only connect if there is no existing connection
60
87
  if self._conn is None:
61
88
 
62
89
  try:
63
90
 
64
- # Attempt to establish a new SQLite connection
91
+ # Attempt to establish a new SQLite connection with custom settings
65
92
  self._conn = sqlite3.connect(
66
93
  database=str(self.__db_path),
67
94
  timeout=5.0,
@@ -70,40 +97,54 @@ class TestLogs(ITestLogs):
70
97
  autocommit=True
71
98
  )
72
99
 
73
- # Hability to use WAL mode for better concurrency
100
+ # Enable Write-Ahead Logging for better concurrency
74
101
  self._conn.execute("PRAGMA journal_mode=WAL;")
102
+
103
+ # Set synchronous mode to NORMAL for performance
75
104
  self._conn.execute("PRAGMA synchronous=NORMAL;")
76
105
 
77
106
  except (sqlite3.Error, Exception) as e:
78
107
 
79
108
  # Raise a custom exception if connection fails
80
- raise OrionisTestPersistenceError(f"Database connection error: {e}")
109
+ raise OrionisTestPersistenceError(
110
+ f"Failed to connect to SQLite database at '{self.__db_path}': {e}"
111
+ )
81
112
 
82
113
  def __createTableIfNotExists(
83
114
  self
84
115
  ) -> bool:
85
116
  """
86
- Ensure the reports table exists in the SQLite database.
117
+ Ensures that the reports table exists in the SQLite database, creating it if necessary.
118
+
119
+ Parameters
120
+ ----------
121
+ None
87
122
 
88
123
  Returns
89
124
  -------
90
125
  bool
91
- True if the table was created or already exists.
126
+ Returns True if the table was created or already exists.
92
127
 
93
128
  Raises
94
129
  ------
95
130
  OrionisTestPersistenceError
96
- If table creation fails due to a database error.
131
+ Raised if table creation fails due to a database error.
132
+
133
+ Notes
134
+ -----
135
+ This method establishes a connection to the SQLite database and attempts to create the reports
136
+ table with the required schema if it does not already exist. The schema includes fields for
137
+ storing the report as JSON, test statistics, and a timestamp. The method commits the transaction
138
+ if successful, rolls back on error, and always closes the connection at the end.
97
139
  """
98
140
  # Establish a connection to the database
99
141
  self.__connect()
100
142
 
101
143
  try:
102
-
103
144
  # Create a cursor to execute SQL commands
104
145
  cursor = self._conn.cursor()
105
146
 
106
- # Create the table with the required schema if it does not exist
147
+ # Create the reports table with the required schema if it does not exist
107
148
  cursor.execute(f'''
108
149
  CREATE TABLE IF NOT EXISTS {self.__table_name} (
109
150
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -128,16 +169,18 @@ class TestLogs(ITestLogs):
128
169
  except sqlite3.Error as e:
129
170
 
130
171
  # Roll back the transaction if an error occurs
131
- if self._conn:
172
+ if isinstance(self._conn, sqlite3.Connection):
132
173
  self._conn.rollback()
133
174
 
134
175
  # Raise a custom exception with the error details
135
- raise OrionisTestPersistenceError(f"Failed to create table: {e}")
176
+ raise OrionisTestPersistenceError(
177
+ f"Failed to create or verify table '{self.__table_name}' in database '{self.__db_name}' at '{self.__db_path}': {e}"
178
+ )
136
179
 
137
180
  finally:
138
181
 
139
- # Close the database connection
140
- if self._conn:
182
+ # Always close the database connection after the operation
183
+ if isinstance(self._conn, sqlite3.Connection):
141
184
  self.__close()
142
185
  self._conn = None
143
186
 
@@ -146,43 +189,58 @@ class TestLogs(ITestLogs):
146
189
  report: Dict
147
190
  ) -> bool:
148
191
  """
149
- Insert a test report into the reports table.
192
+ Inserts a test report into the reports table in the SQLite database.
150
193
 
151
194
  Parameters
152
195
  ----------
153
196
  report : dict
154
- Dictionary containing the report data. Must include keys:
197
+ Dictionary containing the report data. Must include the following keys:
155
198
  'total_tests', 'passed', 'failed', 'errors', 'skipped', 'total_time', 'success_rate', 'timestamp'.
199
+ The entire report will be serialized and stored in the 'json' column.
156
200
 
157
201
  Returns
158
202
  -------
159
203
  bool
160
- True if the report was successfully inserted.
204
+ Returns True if the report was successfully inserted into the database.
161
205
 
162
206
  Raises
163
207
  ------
164
208
  OrionisTestPersistenceError
165
- If there is an error inserting the report into the database.
209
+ If an error occurs while inserting the report into the database.
166
210
  OrionisTestValueError
167
- If required fields are missing from the report.
211
+ If any required fields are missing from the report.
212
+
213
+ Notes
214
+ -----
215
+ This method validates the presence of all required fields in the report dictionary (except 'json', which is
216
+ handled by serializing the entire report). The report is inserted as a new row in the reports table, with the
217
+ full dictionary stored as a JSON string in the 'json' column and individual fields mapped to their respective
218
+ columns. The database connection is managed internally and closed after the operation.
168
219
  """
169
- # List of required fields for the report
220
+
221
+ # List of required fields for the report (excluding 'json', which is handled separately)
170
222
  fields = [
171
223
  "json", "total_tests", "passed", "failed", "errors",
172
224
  "skipped", "total_time", "success_rate", "timestamp"
173
225
  ]
174
226
 
175
- # Check for missing required fields (excluding "json" which is handled separately)
227
+ # Check for missing required fields in the report dictionary
176
228
  missing = []
177
229
  for key in fields:
178
230
  if key not in report and key != "json":
179
231
  missing.append(key)
232
+
233
+ # If any required fields are missing, raise an exception
180
234
  if missing:
181
- raise OrionisTestValueError(f"Missing report fields: {missing}")
235
+ raise OrionisTestValueError(
236
+ f"The report is missing the following required fields: {', '.join(missing)}"
237
+ )
182
238
 
183
239
  # Establish a connection to the database
184
240
  self.__connect()
241
+
185
242
  try:
243
+
186
244
  # Prepare the SQL query to insert the report data
187
245
  query = f'''
188
246
  INSERT INTO {self.__table_name} (
@@ -191,10 +249,10 @@ class TestLogs(ITestLogs):
191
249
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
192
250
  '''
193
251
 
194
- # Execute the insert query with the report data, serializing the entire report as JSON
252
+ # Create a cursor for executing the SQL statement
195
253
  cursor = self._conn.cursor()
196
254
 
197
- # Ensure the 'json' field is serialized to JSON format
255
+ # Insert the report data, serializing the entire report as JSON for the 'json' column
198
256
  cursor.execute(query, (
199
257
  json.dumps(report),
200
258
  report["total_tests"],
@@ -207,23 +265,27 @@ class TestLogs(ITestLogs):
207
265
  report["timestamp"]
208
266
  ))
209
267
 
210
- # Commit the transaction to save the new report
268
+ # Commit the transaction to persist the new report
211
269
  self._conn.commit()
212
270
 
213
- # Return True indicating the report was successfully inserted
271
+ # Return True to indicate successful insertion
214
272
  return True
215
273
 
216
274
  except sqlite3.Error as e:
217
275
 
218
276
  # Roll back the transaction if an error occurs during insertion
219
- if self._conn:
277
+ if isinstance(self._conn, sqlite3.Connection):
220
278
  self._conn.rollback()
221
- raise OrionisTestPersistenceError(f"Failed to insert report: {e}")
279
+
280
+ # Raise a custom exception with the error details
281
+ raise OrionisTestPersistenceError(
282
+ f"Failed to insert report into table '{self.__table_name}' in database '{self.__db_name}' at '{self.__db_path}': {e}"
283
+ )
222
284
 
223
285
  finally:
224
286
 
225
287
  # Ensure the database connection is closed after the operation
226
- if self._conn:
288
+ if isinstance(self._conn, sqlite3.Connection):
227
289
  self.__close()
228
290
  self._conn = None
229
291
 
@@ -233,19 +295,20 @@ class TestLogs(ITestLogs):
233
295
  last: Optional[int] = None
234
296
  ) -> List[Tuple]:
235
297
  """
236
- Retrieve a specified number of report records from the database.
298
+ Retrieve a specified number of report records from the database, either the earliest or latest entries.
237
299
 
238
300
  Parameters
239
301
  ----------
240
302
  first : int or None, optional
241
- Number of earliest reports to retrieve, ordered by ascending ID.
303
+ The number of earliest reports to retrieve, ordered by ascending ID. Must be a positive integer.
242
304
  last : int or None, optional
243
- Number of latest reports to retrieve, ordered by descending ID.
305
+ The number of latest reports to retrieve, ordered by descending ID. Must be a positive integer.
244
306
 
245
307
  Returns
246
308
  -------
247
- list of tuple
248
- List of tuples representing report records.
309
+ List[Tuple]
310
+ A list of tuples, where each tuple represents a row from the reports table. Each tuple contains:
311
+ (id, json, total_tests, passed, failed, errors, skipped, total_time, success_rate, timestamp).
249
312
 
250
313
  Raises
251
314
  ------
@@ -253,24 +316,31 @@ class TestLogs(ITestLogs):
253
316
  If both 'first' and 'last' are specified, or if either is not a positive integer.
254
317
  OrionisTestPersistenceError
255
318
  If there is an error retrieving reports from the database.
319
+
320
+ Notes
321
+ -----
322
+ Only one of 'first' or 'last' can be specified at a time. If neither is provided, no records are returned.
323
+ The method ensures proper connection management and closes the database connection after retrieval.
256
324
  """
325
+
257
326
  # Ensure that only one of 'first' or 'last' is specified
258
327
  if first is not None and last is not None:
259
328
  raise OrionisTestValueError(
260
- "Cannot specify both 'first' and 'last' parameters. Use one or the other."
329
+ "You cannot specify both 'first' and 'last' parameters at the same time. Please provide only one."
261
330
  )
262
331
 
263
332
  # Validate 'first' parameter if provided
264
333
  if first is not None:
265
334
  if not isinstance(first, int) or first <= 0:
266
- raise OrionisTestValueError("'first' must be an integer greater than 0.")
335
+ raise OrionisTestValueError("'first' must be a positive integer greater than zero.")
267
336
 
268
337
  # Validate 'last' parameter if provided
269
338
  if last is not None:
270
339
  if not isinstance(last, int) or last <= 0:
271
- raise OrionisTestValueError("'last' must be an integer greater than 0.")
340
+ raise OrionisTestValueError("'last' must be a positive integer greater than zero.")
272
341
 
273
342
  # Determine the order and quantity of records to retrieve
343
+ # If 'last' is specified, order by descending ID; otherwise, ascending
274
344
  order = 'DESC' if last is not None else 'ASC'
275
345
  quantity = first if first is not None else last
276
346
 
@@ -286,21 +356,23 @@ class TestLogs(ITestLogs):
286
356
  query = f"SELECT * FROM {self.__table_name} ORDER BY id {order} LIMIT ?"
287
357
  cursor.execute(query, (quantity,))
288
358
 
289
- # Fetch all matching records
359
+ # Fetch all matching records from the database
290
360
  results = cursor.fetchall()
291
361
 
292
- # Return the list of report records
362
+ # Return the list of report records as tuples
293
363
  return results
294
364
 
295
365
  except sqlite3.Error as e:
296
366
 
297
367
  # Raise a custom exception if retrieval fails
298
- raise OrionisTestPersistenceError(f"Failed to retrieve reports from '{self.__db_name}': {e}")
368
+ raise OrionisTestPersistenceError(
369
+ f"An error occurred while retrieving reports from table '{self.__table_name}' in database '{self.__db_name}' at '{self.__db_path}': {e}"
370
+ )
299
371
 
300
372
  finally:
301
373
 
302
374
  # Ensure the database connection is closed after the operation
303
- if self._conn:
375
+ if isinstance(self._conn, sqlite3.Connection):
304
376
  self.__close()
305
377
  self._conn = None
306
378
 
@@ -308,42 +380,56 @@ class TestLogs(ITestLogs):
308
380
  self
309
381
  ) -> bool:
310
382
  """
311
- Drop the reports table from the SQLite database.
383
+ Drops the reports table from the SQLite database, effectively clearing all stored test history.
384
+
385
+ Parameters
386
+ ----------
387
+ None
312
388
 
313
389
  Returns
314
390
  -------
315
391
  bool
316
- True if the table was successfully dropped or did not exist.
392
+ Returns True if the reports table was successfully dropped or did not exist. If the operation
393
+ completes without raising an exception, the database is considered reset.
317
394
 
318
395
  Raises
319
396
  ------
320
397
  OrionisTestPersistenceError
321
398
  If an SQLite error occurs while attempting to drop the table.
399
+
400
+ Notes
401
+ -----
402
+ This method establishes a connection to the SQLite database and attempts to drop the reports table
403
+ specified by `self.__table_name`. If the table does not exist, the operation completes silently.
404
+ The database connection is closed after the operation, regardless of success or failure.
322
405
  """
406
+
323
407
  # Establish a connection to the database
324
408
  self.__connect()
325
409
 
326
410
  try:
327
411
 
328
- # Create a cursor and execute the DROP TABLE statement
412
+ # Create a cursor and execute the DROP TABLE statement to remove the reports table
329
413
  cursor = self._conn.cursor()
330
414
  cursor.execute(f'DROP TABLE IF EXISTS {self.__table_name}')
331
415
 
332
416
  # Commit the transaction to apply the changes
333
417
  self._conn.commit()
334
418
 
335
- # Return True to indicate the reset was successful
419
+ # Return True to indicate the reset was successful or the table did not exist
336
420
  return True
337
421
 
338
422
  except sqlite3.Error as e:
339
423
 
340
424
  # Raise a custom exception if the reset fails
341
- raise OrionisTestPersistenceError(f"Failed to reset database: {e}")
425
+ raise OrionisTestPersistenceError(
426
+ f"Failed to reset the reports table '{self.__table_name}' in database '{self.__db_name}' at '{self.__db_path}': {e}"
427
+ )
342
428
 
343
429
  finally:
344
430
 
345
431
  # Ensure the database connection is closed after the operation
346
- if self._conn:
432
+ if isinstance(self._conn, sqlite3.Connection):
347
433
  self.__close()
348
434
  self._conn = None
349
435
 
@@ -353,13 +439,30 @@ class TestLogs(ITestLogs):
353
439
  """
354
440
  Close the active SQLite database connection if it exists.
355
441
 
442
+ This method safely closes the current SQLite database connection if it is open.
443
+ It ensures that resources are released and the connection attribute is reset to None.
444
+ This is important for preventing resource leaks and maintaining proper connection management
445
+ within the TestLogs class.
446
+
356
447
  Returns
357
448
  -------
358
449
  None
450
+ This method does not return any value. The side effect is that the database connection
451
+ is closed and the internal connection attribute is set to None.
452
+
453
+ Notes
454
+ -----
455
+ This method should be called after database operations to ensure the connection is properly closed.
456
+ It checks if the `_conn` attribute is an active `sqlite3.Connection` before attempting to close it.
359
457
  """
360
- # If a database connection exists, close it and set the connection attribute to None
361
- if self._conn:
458
+
459
+ # Check if there is an active SQLite connection before closing
460
+ if isinstance(self._conn, sqlite3.Connection):
461
+
462
+ # Close the database connection to release resources
362
463
  self._conn.close()
464
+
465
+ # Reset the connection attribute to None
363
466
  self._conn = None
364
467
 
365
468
  def create(
@@ -367,48 +470,73 @@ class TestLogs(ITestLogs):
367
470
  report: Dict
368
471
  ) -> bool:
369
472
  """
370
- Insert a new test report into the database after ensuring the reports table exists.
473
+ Inserts a new test report into the database after ensuring the reports table exists.
371
474
 
372
475
  Parameters
373
476
  ----------
374
477
  report : dict
375
- Dictionary containing the test report data. Must include all required fields.
478
+ Dictionary containing the test report data. Must include all required fields:
479
+ 'total_tests', 'passed', 'failed', 'errors', 'skipped', 'total_time', 'success_rate', 'timestamp'.
480
+ The entire report will be serialized and stored in the 'json' column.
376
481
 
377
482
  Returns
378
483
  -------
379
484
  bool
380
- True if the report was successfully inserted.
485
+ True if the report was successfully inserted into the database; otherwise, raises an exception.
381
486
 
382
487
  Raises
383
488
  ------
384
489
  OrionisTestPersistenceError
385
- If the operation fails due to database errors.
490
+ If a database error occurs during table creation or report insertion.
386
491
  OrionisTestValueError
387
- If required fields are missing from the report.
492
+ If required fields are missing from the report dictionary.
493
+
494
+ Notes
495
+ -----
496
+ This method first ensures that the reports table exists in the database. It then validates and inserts
497
+ the provided report dictionary as a new row, storing the full report as a JSON string and mapping individual
498
+ fields to their respective columns. The database connection is managed internally and closed after the operation.
388
499
  """
389
- # Ensure the reports table exists before inserting the report
500
+
501
+ # Ensure the reports table exists before attempting to insert the report
390
502
  self.__createTableIfNotExists()
391
503
 
392
- # Insert the report into the database and return the result
504
+ # Insert the report into the database and return True if successful
393
505
  return self.__insertReport(report)
394
506
 
395
507
  def reset(
396
508
  self
397
509
  ) -> bool:
398
510
  """
399
- Drop the reports table from the SQLite database, clearing all test history records.
511
+ Drops the reports table from the SQLite database, effectively clearing all stored test history.
512
+
513
+ This method attempts to remove the reports table specified by `self.__table_name` from the database.
514
+ If the table does not exist, the operation completes without error. The database connection is managed
515
+ internally and closed after the operation.
516
+
517
+ Parameters
518
+ ----------
519
+ None
400
520
 
401
521
  Returns
402
522
  -------
403
523
  bool
404
- True if the reports table was successfully dropped or did not exist.
524
+ Returns True if the reports table was successfully dropped or did not exist. If the operation
525
+ completes without raising an exception, the database is considered reset.
405
526
 
406
527
  Raises
407
528
  ------
408
529
  OrionisTestPersistenceError
409
- If the operation fails due to a database error.
530
+ If an SQLite error occurs while attempting to drop the table.
531
+
532
+ Notes
533
+ -----
534
+ This method is useful for clearing all test report history from the database, such as during test
535
+ environment resets or cleanup operations.
410
536
  """
411
- # Attempt to drop the reports table and reset the database
537
+
538
+ # Attempt to drop the reports table and reset the database.
539
+ # Returns True if successful or if the table did not exist.
412
540
  return self.__resetDatabase()
413
541
 
414
542
  def get(
@@ -417,19 +545,23 @@ class TestLogs(ITestLogs):
417
545
  last: Optional[int] = None
418
546
  ) -> List[Tuple]:
419
547
  """
420
- Retrieve test reports from the database.
548
+ Retrieve a specified number of test report records from the database.
421
549
 
422
550
  Parameters
423
551
  ----------
424
552
  first : int or None, optional
425
- Number of earliest reports to retrieve, ordered by ascending ID.
553
+ The number of earliest reports to retrieve, ordered by ascending ID. Must be a positive integer.
554
+ If specified, returns the oldest reports.
426
555
  last : int or None, optional
427
- Number of latest reports to retrieve, ordered by descending ID.
556
+ The number of latest reports to retrieve, ordered by descending ID. Must be a positive integer.
557
+ If specified, returns the most recent reports.
428
558
 
429
559
  Returns
430
560
  -------
431
- list of tuple
432
- List of tuples representing report records.
561
+ List[Tuple]
562
+ A list of tuples, where each tuple represents a row from the reports table:
563
+ (id, json, total_tests, passed, failed, errors, skipped, total_time, success_rate, timestamp).
564
+ If neither `first` nor `last` is provided, an empty list is returned.
433
565
 
434
566
  Raises
435
567
  ------
@@ -437,6 +569,14 @@ class TestLogs(ITestLogs):
437
569
  If both 'first' and 'last' are specified, or if either is not a positive integer.
438
570
  OrionisTestPersistenceError
439
571
  If there is an error retrieving reports from the database.
572
+
573
+ Notes
574
+ -----
575
+ Only one of `first` or `last` can be specified at a time. The method delegates the retrieval
576
+ logic to the internal `__getReports` method, which handles database connection management and
577
+ query execution.
440
578
  """
441
- # Delegate the retrieval logic to the internal __getReports method
579
+
580
+ # Delegate the retrieval logic to the internal __getReports method.
581
+ # This ensures proper validation and database access.
442
582
  return self.__getReports(first, last)