An interval override was a row of its own table beside the one store every other admin knob uses; it is now the `job_interval_<job>` sysconfig key (migration 0110 copies them over and drops the table). The Scheduler page stays its one editor: the Configuration page and the setup wizard neither list nor write those keys, since a write there would skip the reschedule. Each ConfigSpec now states `applies` (required, no default). Every key is `live`, held by a scan that finds no read at import time; a `restart` key is refused until the page can say so. A second scan requires a reader for every key on the Configuration page — `sponsorblock_default` had none since it was added, so it leaves the registry, the translations and the store.
57 lines
2.1 KiB
Python
57 lines
2.1 KiB
Python
"""Migration 0110's data step, DB-lane: an admin's interval override moves from `scheduler_settings`
|
|
into sysconfig and back, a row for a job that no longer exists is not carried over, and the unread
|
|
SponsorBlock default leaves the store.
|
|
|
|
The schema half is checked by test_migrations.py; a value lost on the way is invisible there.
|
|
"""
|
|
|
|
from alembic import command
|
|
from alembic.config import Config
|
|
from sqlalchemy import text
|
|
|
|
from app import scheduler
|
|
from app.db import engine
|
|
|
|
|
|
def _config(conn) -> dict[str, str]:
|
|
return dict(conn.execute(text("SELECT key, value FROM system_config")).tuples().all())
|
|
|
|
|
|
def test_an_interval_override_moves_into_sysconfig_and_back(db):
|
|
cfg = Config("alembic.ini")
|
|
command.downgrade(cfg, "0109_job_runs")
|
|
try:
|
|
with engine.begin() as conn:
|
|
conn.execute(
|
|
text(
|
|
"INSERT INTO scheduler_settings (job_id, interval_minutes)"
|
|
" VALUES ('rss_poll', 25), ('log_gc', 90), ('retired_job', 5)"
|
|
)
|
|
)
|
|
conn.execute(
|
|
text(
|
|
"INSERT INTO system_config (key, value, encrypted) VALUES"
|
|
" ('sponsorblock_default', 'true', false), ('quota_daily_budget', '5000', false)"
|
|
)
|
|
)
|
|
finally:
|
|
command.upgrade(cfg, "head")
|
|
|
|
with engine.connect() as conn:
|
|
assert _config(conn) == {
|
|
"job_interval_rss_poll": "25",
|
|
"job_interval_log_gc": "90",
|
|
"quota_daily_budget": "5000",
|
|
}
|
|
assert conn.execute(text("SELECT to_regclass('scheduler_settings')")).scalar() is None
|
|
assert scheduler.load_intervals(db)["rss_poll"] == 25
|
|
|
|
command.downgrade(cfg, "0109_job_runs")
|
|
try:
|
|
with engine.connect() as conn:
|
|
back = conn.execute(text("SELECT job_id, interval_minutes FROM scheduler_settings"))
|
|
assert dict(back.tuples().all()) == {"rss_poll": 25, "log_gc": 90}
|
|
assert _config(conn) == {"quota_daily_budget": "5000"}
|
|
finally:
|
|
command.upgrade(cfg, "head")
|