fix: close data integrity and hot-reload review findings

Backfill same-day snapshots for newly added or restored videos, preserve successful fan snapshots on resume, persist incomplete state before forced re-runs, and split collection completion from backup completion so failed backups can retry without API calls. Reject missing data payloads instead of treating them as empty folders, guard against mass-unfav on suspicious empty remote folders, rebuild Bilibili clients when credentials change, recompute primary folder ownership from active relations with per-folder fav_time, preserve original titles for dropped invalid videos, poll config every 60 seconds, and fix favsort verify interval and mixed resource types.
This commit is contained in:
kairee committed 2026-10-31 20:58:36 +08:00
1 parent 178ab64b18
commit 6a21b5d905
21 files changed
+895 -82

No files matched your search

+100
View File
@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""favsort run 参数校验与资源类型测试(不联网): uv run python tests/test_favsort_run.py"""
import os
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")
import favsort # noqa: E402
class Args:
media_id = 1
verify_every = 1
def config():
return {
"mid": 1,
"csrf": "csrf",
"cookie": "cookie",
"overrides": {},
"sleep": {
"min": 0, "max": 0,
"long_every_min": 1000, "long_every_max": 1000,
"long_min": 0, "long_max": 0,
"retry_wait": 0,
},
}
def test_zero_verify_every_rejected_before_writes():
calls = []
originals = (favsort.load_plan, favsort.fetch_ids, favsort.post_form)
favsort.load_plan = lambda media_id: {"target_order": [], "titles": {}}
favsort.fetch_ids = lambda cfg, media_id: calls.append("fetch") or []
favsort.post_form = lambda *args, **kwargs: calls.append("post") or {}
args = Args()
args.verify_every = 0
try:
code = favsort.cmd_run(config(), args)
finally:
favsort.load_plan, favsort.fetch_ids, favsort.post_form = originals
assert code == 2
assert calls == []
def test_sort_preserves_resource_types():
with tempfile.TemporaryDirectory() as tmp:
plan = {
"media_id": 1,
"target_order": [1, 2],
"titles": {"1": {"title": "音频"}, "2": {"title": "视频"}},
"types": {1: 2, 2: 12},
}
bodies = []
orders = [[2, 1]]
originals = (
favsort.load_plan,
favsort.fetch_ids,
favsort.post_form,
favsort.plan_path,
favsort.journal_path,
)
favsort.load_plan = lambda media_id: plan
favsort.plan_path = lambda media_id: str(Path(tmp) / "plan.json")
favsort.journal_path = lambda media_id: str(Path(tmp) / "journal.jsonl")
def fetch_ids(cfg, media_id):
order = orders.pop(0) if orders else [1, 2]
return [{"id": aid} for aid in order]
favsort.fetch_ids = fetch_ids
favsort.post_form = lambda cfg, url, body: bodies.append(body) or {"code": 0}
args = Args()
try:
code = favsort.cmd_run(config(), args)
finally:
(
favsort.load_plan,
favsort.fetch_ids,
favsort.post_form,
favsort.plan_path,
favsort.journal_path,
) = originals
assert code == 0
assert [body["sort"] for body in bodies] == ["1:2:2:12"]
def main():
test_zero_verify_every_rejected_before_writes()
test_sort_preserves_resource_types()
print("OK: favsort run 参数校验与资源类型测试全部通过")
if __name__ == "__main__":
main()