"""备份恢复的核心保护:七域真实往返与拒绝覆盖非空目标。 个人工具的最小安全网——全部作品资产在一个 PG 库里,备份必须能真恢复, 恢复不得覆盖任何已有内容。其余切换/篡改/权限变体用例已按简化决策删除。 """ from __future__ import annotations import json from decimal import Decimal from pathlib import Path from types import SimpleNamespace import pytest from muse.共享.调用身份 import 用途 from muse.基础设施.备份恢复 import 创建备份, 备份恢复错误, 恢复备份, 核对备份 from muse.基础设施.数据库.连接 import 数据库工厂 from muse.资源加载 import 加载清单 from muse.配置 import 应用配置 pytestmark = pytest.mark.数据库 def _资源身份() -> str: return 加载清单()["构建身份"] def _应用配置( 工厂: 数据库工厂, raw: Path, drafts: Path, ) -> 应用配置: return 应用配置( 工厂.引用, _资源身份(), 运行用途=用途.维护, 原文暂存=str(raw), 探索草稿=str(drafts), ) def _准备文件(tmp_path: Path, 前缀: str) -> tuple[Path, Path]: raw = tmp_path / f"{前缀}-raw" drafts = tmp_path / f"{前缀}-drafts" raw.mkdir() drafts.mkdir() (raw / "供应方原文.bin").write_bytes(b"\x00\xffraw-evidence\x80") (drafts / "第一章 草稿.txt").write_text("风从旧城来。\n", encoding="utf-8") return raw, drafts def _建立数据库探针(工厂: 数据库工厂) -> None: with 工厂.连接() as 连, 连.transaction(): 连.execute("DROP TABLE IF EXISTS public.w29_child, public.w29_parent CASCADE") 连.execute( """ CREATE TABLE public.w29_parent ( id text PRIMARY KEY, dynamic_payload jsonb NOT NULL, raw_bytes bytea NOT NULL ); CREATE TABLE public.w29_child ( id text PRIMARY KEY, parent_id text NOT NULL REFERENCES public.w29_parent(id), audit_note text NOT NULL ); GRANT SELECT, INSERT, UPDATE, DELETE ON public.w29_parent, public.w29_child TO muse_app; GRANT SELECT ON public.w29_parent, public.w29_child TO muse_eval; """ ) 连.execute( "INSERT INTO public.w29_parent VALUES (%s, %s, %s)", ( "parent-1", json.dumps({"扩展字段": {"层级": [1, "二", {"保留": True}]}}), b"\x00\xff\x10" + "原文-bytea".encode(), ), ) 连.execute( "INSERT INTO public.w29_child VALUES (%s, %s, %s)", ("child-1", "parent-1", "审计理由保留"), ) def _建立类型探针(维护工厂: 数据库工厂) -> None: """decimal 精度、timestamptz、嵌套 jsonb 与序列推进,供确定性往返用。""" with 维护工厂.连接() as 连, 连.transaction(): 连.execute("DROP TABLE IF EXISTS public.w29_seq_probe, public.w29_typed CASCADE") 连.execute( """ CREATE TABLE public.w29_typed ( id integer PRIMARY KEY, at timestamptz NOT NULL, score numeric(10,4) NOT NULL, payload jsonb NOT NULL ); CREATE TABLE public.w29_seq_probe ( id serial PRIMARY KEY, note text NOT NULL ); """ ) 连.execute( "INSERT INTO public.w29_typed VALUES (%s,%s,%s,%s)", ( 10, "2026-07-21T08:00:00+00", Decimal("1.2300"), json.dumps({"扩展": {"层级": [1, "二"]}}), ), ) 连.execute( "INSERT INTO public.w29_typed VALUES (%s,%s,%s,%s)", (2, "2026-07-20T00:00:00+00", Decimal("0.10"), json.dumps({"序": 2})), ) @pytest.mark.case_id( "TC-0f7c45927fbe", environment="隔离 PostgreSQL 与临时目录", when="备份、校验、恢复。", contract="docs/系统架构/新版设计/迁移与验收.md", ) def test_备份单快照七域往返且同内容重放一致__0f7c45( 应用测试库: dict, 新空目标库: SimpleNamespace, tmp_path: Path ) -> None: """数据库与文件进入同一份校验清单;关闭连接后从磁盘复验一致。""" 来源工厂 = 应用测试库[用途.维护] _建立类型探针(来源工厂) raw, drafts = _准备文件(tmp_path, "domains") 备份目录 = tmp_path / "domains-backup" 清单 = 创建备份(_应用配置(来源工厂, raw, drafts), 备份目录) 复验 = 核对备份(备份目录) assert 复验 == 清单 assert set(清单["database"]) == { "archive", "size", "sha256", "source_fingerprint", "fingerprint_scope", "server_version_num", "migrations", "summary", } assert 清单["database"]["migrations"] and 清单["file_archive"]["file_count"] == 2 assert 清单["file_archive"]["roots"] == ["drafts", "raw"] 目标raw, 目标drafts = tmp_path / "domains-target-raw", tmp_path / "domains-target-drafts" 恢复备份(备份目录, _应用配置(新空目标库.工厂, 目标raw, 目标drafts), 清单["backup_id"]) with 新空目标库.工厂.连接() as 连: assert 连.execute("SELECT count(*) FROM public.w29_typed").fetchone() == (2,) @pytest.mark.case_id( "NC-w29-29b004", environment="隔离PG", given="显式隔离配置与合成数据", when="向来源库或已有内容目标发起恢复", then=["拒绝来源库与已有内容目标且不覆盖文件"], contract="docs/系统架构/新版设计/迁移与验收.md#备份与恢复入口", ) def test_拒绝来源库与已有内容目标且不覆盖文件__29b004( 应用测试库: dict, 新空目标库: SimpleNamespace, tmp_path: Path ) -> None: 来源工厂 = 应用测试库[用途.维护] _建立数据库探针(来源工厂) raw, drafts = _准备文件(tmp_path, "guard") 备份目录 = tmp_path / "guard-backup" 清单 = 创建备份(_应用配置(来源工厂, raw, drafts), 备份目录) 来源目标配置 = _应用配置( 来源工厂, tmp_path / "source-target-raw", tmp_path / "source-target-drafts" ) with pytest.raises(备份恢复错误, match="来源数据库"): 恢复备份(备份目录, 来源目标配置, 清单["backup_id"]) with 新空目标库.工厂.连接() as 连, 连.transaction(): 连.execute("CREATE TABLE public.keep_me (value text NOT NULL)") 连.execute("INSERT INTO public.keep_me VALUES ('保留')") 目标raw = tmp_path / "occupied-target-raw" 目标drafts = tmp_path / "occupied-target-drafts" with pytest.raises(备份恢复错误, match="全新空库"): 恢复备份( 备份目录, _应用配置(新空目标库.工厂, 目标raw, 目标drafts), 清单["backup_id"], ) with 新空目标库.工厂.连接() as 连: assert 连.execute("SELECT value FROM public.keep_me").fetchone() == ("保留",) assert not 目标raw.exists() and not 目标drafts.exists()