Files
alembic/tests/test_db.py
T
andrew e49d12a72b Add test suite (T1)
First automated coverage. 38 tests, run against the built image (which has the
app deps); tests/ and requirements-dev.txt are not baked into the runtime image.

- tests/test_dedup_gating.py: the safety-critical one. Drives dedup-library.sh's
  process_group with --apply + --only-paths and asserts ONLY the confirmed path
  is deleted and the higher-ranked FLAC is kept.
- tests/test_pipeline_runner.py: lock skip, use_lock=False bypass, lock release,
  success/failure/timeout recording.
- tests/test_db.py: schema version + idempotent migrations, FK-safe job-run
  pruning, stale-run reconciliation.
- tests/test_credential_service.py: conf-field substitution incl. newline-
  injection safety, secret/non-secret classification, cookie-expiry parsing.
- tests/test_playlist_service.py: cron round-trips, name validation, render.
- tests/test_diagnostics.py: /health + /setup checks.
- conftest.py points every path setting at throwaway temp dirs and a temp DB, so
  tests never touch a real library, /config, or the network. See tests/README.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:00:07 -06:00

62 lines
2.2 KiB
Python

import time
from sqlalchemy import text
from app import db as appdb
def test_init_db_sets_version_and_is_idempotent():
with appdb.engine.begin() as conn:
version = conn.execute(text("SELECT version FROM schema_version")).fetchone()[0]
assert version >= 3 # all shipped migrations applied
# Re-running must not raise (e.g. duplicate-column on a re-applied ALTER).
appdb.init_db()
appdb.init_db()
def test_prune_old_job_runs_fk_safe():
now = time.time()
old = now - 100 * 86400
with appdb.engine.begin() as conn:
conn.execute(
text(
"INSERT INTO job_runs (id, job_key, started_at, status, triggered_by) "
"VALUES (1,'old',:o,'success','schedule'),(2,'new',:n,'success','schedule')"
),
{"o": old, "n": now},
)
conn.execute(
text(
"INSERT INTO manual_imports (id, source_path, imported_by, imported_at, status, job_run_id) "
"VALUES (1,'x','me',:o,'done',1)"
),
{"o": old},
)
deleted = appdb.prune_old_job_runs(max_age_days=90)
assert deleted == 1
with appdb.engine.begin() as conn:
remaining = [r[0] for r in conn.execute(text("SELECT job_key FROM job_runs"))]
mi_ref = conn.execute(text("SELECT job_run_id FROM manual_imports WHERE id=1")).fetchone()[0]
mi_count = conn.execute(text("SELECT COUNT(*) FROM manual_imports")).fetchone()[0]
assert remaining == ["new"]
assert mi_ref is None # reference nulled, not cascade-deleted
assert mi_count == 1 # the import record itself is kept
def test_mark_interrupted_runs():
now = time.time()
with appdb.engine.begin() as conn:
conn.execute(
text(
"INSERT INTO job_runs (id, job_key, started_at, status, triggered_by) "
"VALUES (1,'stuck',:n,'running','schedule')"
),
{"n": now},
)
appdb.mark_interrupted_runs()
with appdb.engine.begin() as conn:
row = conn.execute(text("SELECT status, finished_at FROM job_runs WHERE id=1")).fetchone()
assert row[0] == "interrupted"
assert row[1] is not None