All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m38s
CD Pipeline / build-and-deploy (push) Successful in 15m59s
CD Pipeline / post-deploy-checks (push) Successful in 3m3s
66 lines
2.6 KiB
Python
66 lines
2.6 KiB
Python
from __future__ import annotations
|
||
|
||
import re
|
||
from pathlib import Path
|
||
|
||
ROOT = Path(__file__).resolve().parents[3]
|
||
SCRIPT = ROOT / "scripts" / "backup" / "backup-awoooi.sh"
|
||
FREQUENT_SCRIPT = ROOT / "scripts" / "backup" / "backup-awoooi-frequent.sh"
|
||
|
||
|
||
def test_daily_backup_has_no_hardcoded_database_password() -> None:
|
||
text = SCRIPT.read_text(encoding="utf-8")
|
||
assignment = re.search(r"^AWOOOI_DB_PASS=(.+)$", text, re.MULTILINE)
|
||
|
||
assert assignment is not None
|
||
assert assignment.group(1) == '"${AWOOOI_DB_PASS:-}"'
|
||
assert "PGPASSWORD='${AWOOOI_DB_PASS}'" not in text
|
||
assert 'PGPASSFILE="$pgpass"' in text
|
||
assert "pg_dump --no-password" in text
|
||
|
||
|
||
def test_daily_backup_resolves_runtime_database_url_and_fails_closed() -> None:
|
||
text = SCRIPT.read_text(encoding="utf-8")
|
||
|
||
assert "resolve_database_url()" in text
|
||
assert "load_database_config()" in text
|
||
assert "AWOOOI_BACKUP_DATABASE_URL BACKUP_DATABASE_URL DATABASE_URL" in text
|
||
assert "無法解析 AWOOOI DATABASE_URL;拒絕使用舊硬編密碼" in text
|
||
assert "base64 -d" in text
|
||
assert 'printf \'%s\\n\' "${decoded}"' in text
|
||
|
||
|
||
def test_daily_and_frequent_backups_fail_closed_without_mutating_rls() -> None:
|
||
for script in (SCRIPT, FREQUENT_SCRIPT):
|
||
text = script.read_text(encoding="utf-8")
|
||
|
||
assert "logical_backup_preflight()" in text
|
||
assert "dump_database_fail_closed()" in text
|
||
assert "rolsuper" in text
|
||
assert "rolbypassrls" in text
|
||
assert "backup role must not be superuser or global BYPASSRLS" in text
|
||
assert "forced_rls_tables=${forced_rls_count}" in text
|
||
assert "physical/WAL backup with isolated restore verifier is required" in text
|
||
assert "return 78" in text
|
||
assert 'PGOPTIONS="-c statement_timeout=0 -c max_parallel_workers_per_gather=0"' in text
|
||
assert "ALTER TABLE" not in text
|
||
assert "NO FORCE" not in text
|
||
assert "collect_force_rls_sql" not in text
|
||
assert "apply_remote_sql" not in text
|
||
assert "restore_force_rls" not in text
|
||
|
||
|
||
def test_daily_backup_routes_every_database_through_fail_closed_dump() -> None:
|
||
text = SCRIPT.read_text(encoding="utf-8")
|
||
|
||
assert text.count("if dump_database_fail_closed \\") == 3
|
||
assert 'dump_database_fail_closed \\\n "awoooi_prod"' in text
|
||
assert 'dump_database_fail_closed \\\n "awoooi_dev"' in text
|
||
assert 'dump_database_fail_closed \\\n "k3s_datastore"' in text
|
||
|
||
|
||
def test_frequent_backup_routes_production_database_through_fail_closed_dump() -> None:
|
||
text = FREQUENT_SCRIPT.read_text(encoding="utf-8")
|
||
|
||
assert 'dump_database_fail_closed "awoooi_prod"' in text
|