feat(p1): add daily snapshots and fan tracking
Implement P1: seven-metric view snapshots, relation/stat fan snapshots, failure placeholders, resume by daily completion markers, Cookie login warnings, daily SQLite backups, Shanghai daily scheduling, and CLI snapshot/auth-check commands. Real read-only run captured 795/795 videos successfully over 1124s with a verified daily backup.
This commit is contained in:
1 parent
ee289bfa56
commit
5c2ab7b187
20 files changed
+1680
-54
No files matched your search
@@ -0,0 +1,78 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""每日备份与保留期测试: uv run python tests/test_maintenance.py"""
|
||||
|
||||
import os
|
||||
import sqlite3
|
||||
import sys
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
|
||||
from reaction_board.database import Database # noqa: E402
|
||||
from reaction_board.services.maintenance import ( # noqa: E402
|
||||
backup_daily,
|
||||
daily_backup_path,
|
||||
prune_daily_backups,
|
||||
)
|
||||
|
||||
|
||||
def test_daily_backup_and_restore():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
db = Database(Path(tmp) / "app.db")
|
||||
db.initialize()
|
||||
try:
|
||||
db.write(lambda c: c.execute("INSERT INTO kv(k, v) VALUES ('x', '42')"))
|
||||
target = backup_daily(db, Path(tmp) / "backup", "2026-10-31")
|
||||
assert target == daily_backup_path(Path(tmp) / "backup", "2026-10-31")
|
||||
assert target.name == "app-20261031.db"
|
||||
assert target.exists()
|
||||
|
||||
check = sqlite3.connect(target)
|
||||
try:
|
||||
assert check.execute("SELECT v FROM kv WHERE k='x'").fetchone()[0] == "42"
|
||||
finally:
|
||||
check.close()
|
||||
|
||||
# 覆盖同日备份也安全
|
||||
db.write(lambda c: c.execute("UPDATE kv SET v='43' WHERE k='x'"))
|
||||
backup_daily(db, Path(tmp) / "backup", "2026-10-31")
|
||||
check = sqlite3.connect(target)
|
||||
try:
|
||||
assert check.execute("SELECT v FROM kv WHERE k='x'").fetchone()[0] == "43"
|
||||
finally:
|
||||
check.close()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
def test_prune_daily_backups_only():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
backup_dir = Path(tmp)
|
||||
old_daily = backup_dir / "app-20260101.db"
|
||||
fresh_daily = backup_dir / "app-20261031.db"
|
||||
old_migration = backup_dir / "app-pre-v1-20260101.db"
|
||||
other = backup_dir / "keep.txt"
|
||||
for path in (old_daily, fresh_daily, old_migration, other):
|
||||
path.write_bytes(b"x")
|
||||
now = 1_800_000_000
|
||||
old_time = now - 20 * 86400
|
||||
for path in (old_daily, old_migration):
|
||||
os.utime(path, (old_time, old_time))
|
||||
os.utime(fresh_daily, (now, now))
|
||||
removed = prune_daily_backups(backup_dir, keep_days=14, now=now)
|
||||
assert removed == [old_daily]
|
||||
assert fresh_daily.exists()
|
||||
assert old_migration.exists() # 迁移备份不属于日备份清理范围
|
||||
assert other.exists()
|
||||
|
||||
|
||||
def main():
|
||||
test_daily_backup_and_restore()
|
||||
test_prune_daily_backups_only()
|
||||
print("OK: 每日备份与保留期测试全部通过")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user