Archived
88 lines
3.0 KiB
Python
88 lines
3.0 KiB
Python
"""Tests for SQLite connection and transaction behavior."""
|
|
|
|
import sqlite3
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from recoll_modern.domain.errors import AppError
|
|
from recoll_modern.indexing.paths import PathPolicyError
|
|
from recoll_modern.storage.database import Database
|
|
|
|
|
|
@pytest.fixture
|
|
def database(tmp_path: Path) -> Database:
|
|
"""Provide an isolated database for each test."""
|
|
return Database(tmp_path / "recoll-modern.sqlite3")
|
|
|
|
|
|
def test_connect_configures_sqlite_policy(database: Database) -> None:
|
|
"""Fail if a connection stops enforcing the required SQLite safeguards."""
|
|
connection = database.connect()
|
|
try:
|
|
assert connection.row_factory is sqlite3.Row
|
|
assert connection.execute("PRAGMA foreign_keys").fetchone()[0] == 1
|
|
assert connection.execute("PRAGMA journal_mode").fetchone()[0] == "wal"
|
|
assert connection.execute("PRAGMA busy_timeout").fetchone()[0] == 5000
|
|
finally:
|
|
connection.close()
|
|
|
|
|
|
def test_transaction_commits_and_closes_connection(database: Database) -> None:
|
|
"""Fail if a successful transaction loses data or leaks its connection."""
|
|
database.migrate()
|
|
with database.transaction() as transaction_connection:
|
|
transaction_connection.execute(
|
|
"INSERT INTO users VALUES (?, ?, ?, ?, ?, ?)",
|
|
("u1", "Ada", "ada", "hash", "2026-09-18T00:00:00+00:00", None),
|
|
)
|
|
|
|
check = database.connect()
|
|
try:
|
|
assert check.execute("SELECT username FROM users").fetchone()[0] == "Ada"
|
|
finally:
|
|
check.close()
|
|
|
|
assert transaction_connection is not None
|
|
with pytest.raises(sqlite3.ProgrammingError):
|
|
transaction_connection.execute("SELECT 1")
|
|
|
|
|
|
def test_transaction_rolls_back_and_closes_connection(database: Database) -> None:
|
|
"""Fail if an exception leaves a partial write or an open connection behind."""
|
|
database.migrate()
|
|
transaction_connection: sqlite3.Connection | None = None
|
|
|
|
with pytest.raises(RuntimeError, match="abort"):
|
|
with database.transaction() as current_connection:
|
|
transaction_connection = current_connection
|
|
current_connection.execute(
|
|
"INSERT INTO users VALUES (?, ?, ?, ?, ?, ?)",
|
|
("u1", "Ada", "ada", "hash", "2026-09-18T00:00:00+00:00", None),
|
|
)
|
|
raise RuntimeError("abort")
|
|
|
|
check = database.connect()
|
|
try:
|
|
assert check.execute("SELECT COUNT(*) FROM users").fetchone()[0] == 0
|
|
finally:
|
|
check.close()
|
|
|
|
assert transaction_connection is not None
|
|
with pytest.raises(sqlite3.ProgrammingError):
|
|
transaction_connection.execute("SELECT 1")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"error",
|
|
[AppError("example", "Expected error", 422), PathPolicyError("invalid_source")],
|
|
)
|
|
def test_transaction_preserves_application_exceptions(
|
|
database: Database, error: Exception
|
|
) -> None:
|
|
database.migrate()
|
|
with pytest.raises(type(error)) as caught:
|
|
with database.transaction():
|
|
raise error
|
|
assert caught.value is error
|