File size: 2,648 Bytes
3b1ab70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""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")     # 假 SQLite header
    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()
    # 三項都應在 tar 內(檔名透過 arcname 處理)
    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}"