64 tests covering: auth/setup and the CSRF gate, the "owner adds their own data, anyone linked can mute it" permission pattern shared across calendar-select/tasks-source/whiteboard-source, migration correctness (fresh install, idempotent re-run, expected columns), battery estimate outlier rejection, calendar_feed's fetch/merge/partial-failure handling, webdav_client's fetch/list-directory, the whiteboard force-refresh throttle bypass and browse endpoint, and render-size invariants across calendar views/orientations. No DB/HTTP fixtures need Docker, Node, or a real Immich/CalDAV/WebDAV server -- a fresh temp SQLite file plus a couple of small local HTTP servers as test doubles cover it all. Table data is wiped and reseeded between tests rather than relying on SQLAlchemy's transaction-rollback isolation pattern, which needs a pysqlite event-listener workaround app/db.py's engine doesn't have and has no reason to gain just for tests. Wired into .gitea/workflows/server-docker-build.yml as its own job that build-and-push now depends on, so a failing suite blocks the image push rather than just running alongside it for show.
60 lines
2.3 KiB
Python
60 lines
2.3 KiB
Python
"""migration.py structural sanity: MIGRATIONS is well-formed, a fresh
|
|
install lands on the latest schema version with frame #1 + server
|
|
settings seeded, and re-running run_migrations() is a true no-op (it's
|
|
called unconditionally at every app.main import -- see main.py -- so it
|
|
has to tolerate being invoked against an already-current database)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from sqlalchemy import inspect, text
|
|
|
|
from app import db as db_module
|
|
from app.migration import MIGRATIONS, run_migrations
|
|
from app.models import Frame, ServerSettings
|
|
|
|
|
|
def test_migrations_list_is_sequential_and_unique():
|
|
versions = [v for v, _ in MIGRATIONS]
|
|
assert versions == sorted(versions)
|
|
assert len(versions) == len(set(versions))
|
|
assert versions == list(range(1, len(versions) + 1))
|
|
|
|
|
|
def test_fresh_install_lands_on_latest_version(db_session):
|
|
with db_module.engine.connect() as conn:
|
|
row = conn.execute(text("SELECT version FROM schema_version")).fetchone()
|
|
assert row is not None
|
|
assert row[0] == MIGRATIONS[-1][0]
|
|
|
|
|
|
def test_fresh_install_seeds_frame_one_and_server_settings(db_session):
|
|
frame = db_session.get(Frame, 1)
|
|
assert frame is not None
|
|
assert frame.name
|
|
|
|
settings = db_session.get(ServerSettings, 1)
|
|
assert settings is not None
|
|
|
|
|
|
def test_rerunning_migrations_is_a_no_op(db_session):
|
|
frame_count_before = len(db_session.query(Frame).all())
|
|
run_migrations()
|
|
run_migrations()
|
|
frame_count_after = len(db_session.query(Frame).all())
|
|
assert frame_count_before == frame_count_after == 1
|
|
|
|
|
|
def test_expected_columns_exist_on_current_schema():
|
|
"""A light spot-check, not exhaustive -- one column from a handful of
|
|
the more recent migrations, to catch an ALTER that silently didn't
|
|
apply (e.g. a typo'd table/column name in a migration function)."""
|
|
inspector = inspect(db_module.engine)
|
|
user_columns = {c["name"] for c in inspector.get_columns("users")}
|
|
frame_columns = {c["name"] for c in inspector.get_columns("frames")}
|
|
|
|
assert "webdav_base_url" in user_columns # migration 15
|
|
assert "webdav_username" in user_columns # migration 14
|
|
assert "calendar_caldav_url" in user_columns
|
|
assert "whiteboard_cached_image" in frame_columns # migration 14
|
|
assert "calendar_week_start_offset" in frame_columns
|