feat: handle missing, valid, and corrupt databases on startup (#22)

This commit is contained in:
Joshua King
2026-09-14 08:32:39 -04:00
parent 12bb4d58b4
commit c5cebc9f49
6 changed files with 566 additions and 9 deletions
+31 -2
View File
@@ -101,12 +101,20 @@ def test_executor_helper_creates_directory(tmp_path) -> None:
def test_no_sqlite3_import_outside_db_module() -> None:
"""sqlite3 is banned package-wide except db.py, the connection wrapper."""
"""sqlite3 is banned package-wide except two justified modules.
db.py is the connection wrapper (all statement execution lives
there). __init__.py imports sqlite3 ONLY for its exception types
to classify corruption on startup (done-condition: corrupt file ->
ConfigEntryNotReady); it never opens a connection or executes
SQL — that is asserted separately below.
"""
pkg_dir = Path(store.__file__).parent
py_files = list(pkg_dir.glob("*.py"))
assert py_files
allowed = {"db.py", "__init__.py"}
for py_file in py_files:
if py_file.name == "db.py":
if py_file.name in allowed:
continue
tree = ast.parse(py_file.read_text(encoding="utf-8"), filename=str(py_file))
for node in ast.walk(tree):
@@ -121,6 +129,27 @@ def test_no_sqlite3_import_outside_db_module() -> None:
)
def test_init_module_never_opens_or_executes_sqlite() -> None:
"""The __init__.py sqlite3 exemption is exception-typing only: no
sqlite3.connect, no .execute, no cursor usage may appear there.
"""
import re
from pathlib import Path
init_path = (
Path(store.__file__).parent / "__init__.py"
).read_text(encoding="utf-8")
usage_pattern = re.compile(
r"sqlite3\s*\.\s*(connect|Cursor|Row|register|complete_statement)"
r"|\bconn(ection)?\s*\.\s*execute\b",
re.IGNORECASE,
)
assert usage_pattern.search(init_path) is None, (
"__init__.py must use sqlite3 exception types only, never open "
"connections or execute SQL"
)
def test_no_file_creation_calls_in_store() -> None:
source = Path(store.__file__).read_text(encoding="utf-8")
tree = ast.parse(source, filename=str(store.__file__))