| """A14 backup.py:暫存路徑 → 確認 tar.gz 產出且內容齊全。""" |
| from __future__ import annotations |
|
|
| import importlib.util |
| import tarfile |
| from pathlib import Path |
|
|
| SCRIPT = Path(__file__).resolve().parents[2] / "scripts" / "backup.py" |
|
|
|
|
| def _load_module(): |
| spec = importlib.util.spec_from_file_location("backup_script", SCRIPT) |
| assert spec and spec.loader |
| mod = importlib.util.module_from_spec(spec) |
| spec.loader.exec_module(mod) |
| return mod |
|
|
|
|
| def test_backup_produces_tar_with_db_and_vault(tmp_path, monkeypatch): |
| db = tmp_path / "kbv5.db" |
| db.write_bytes(b"SQLite format 3\x00") |
| vault = tmp_path / "vault" |
| (vault / "知識" / "筆記").mkdir(parents=True) |
| (vault / "知識" / "筆記" / "demo-1.md").write_text("# demo", encoding="utf-8") |
| history = tmp_path / "history" |
| history.mkdir() |
| (history / "v1.md").write_text("# v1", encoding="utf-8") |
|
|
| monkeypatch.setenv("KBV5_DB", str(db)) |
| monkeypatch.setenv("KBV5_VAULT_DIR", str(vault)) |
| monkeypatch.setenv("KBV5_HISTORY_DIR", str(history)) |
|
|
| out_dir = tmp_path / "out" |
| mod = _load_module() |
| rc = mod.main([str(out_dir)]) |
| assert rc == 0 |
| backups = list(out_dir.glob("kbv5-*.tar.gz")) |
| assert len(backups) == 1 |
| with tarfile.open(backups[0], "r:gz") as tar: |
| names = tar.getnames() |
| |
| assert any(n.endswith("kbv5.db") for n in names) |
| assert any("demo-1.md" in n for n in names) |
| assert any("v1.md" in n for n in names) |
|
|
|
|
| def test_backup_missing_all_returns_error(tmp_path, monkeypatch): |
| monkeypatch.setenv("KBV5_DB", str(tmp_path / "nope.db")) |
| monkeypatch.setenv("KBV5_VAULT_DIR", str(tmp_path / "nope_vault")) |
| monkeypatch.setenv("KBV5_HISTORY_DIR", str(tmp_path / "nope_history")) |
| mod = _load_module() |
| rc = mod.main([str(tmp_path / "out")]) |
| assert rc == 1 |
|
|
|
|
| def test_dockerfile_and_compose_exist(): |
| """A14 必要部署檔存在。""" |
| root = Path(__file__).resolve().parents[2] |
| for f in ("Dockerfile", "docker-compose.yml", ".env.example", ".dockerignore"): |
| assert (root / f).exists(), f"missing {f}" |
|
|
|
|
| def test_env_example_lists_critical_vars(): |
| """.env.example 必含關鍵 env 提示,避免新人漏設。""" |
| root = Path(__file__).resolve().parents[2] |
| text = (root / ".env.example").read_text(encoding="utf-8") |
| for var in ("JWT_SECRET", "KBV5_DB", "KBV5_VAULT_DIR", |
| "ALLOWED_EMAIL_DOMAINS", "ANTHROPIC_API_KEY", |
| "OPENAI_API_KEY", "GEMINI_API_KEY"): |
| assert var in text, f".env.example missing {var}" |
|
|