from __future__ import annotations import sqlite3 import zipfile import pytest from tools.backup import create_backup from tools.restore import restore_backup def test_backup_restore_round_trip_and_rejects_tampering(tmp_path) -> None: database = tmp_path / "source.db" with sqlite3.connect(database) as connection: connection.execute("CREATE TABLE sample (value TEXT)") connection.execute("INSERT INTO sample VALUES ('preserved')") skills = tmp_path / "skills" skills.mkdir() skills.joinpath("private.md").write_text("private", encoding="utf-8") environment = tmp_path / "source.env" environment.write_text("APP_ENCRYPTION_KEY=hidden\n", encoding="utf-8") archive = tmp_path / "xiaobai-2026-07-30.zip" create_backup(database, archive, private_skills=skills, environment_file=environment) restored = tmp_path / "restored.db" restored_skills = tmp_path / "restored-skills" restore_backup(archive, restored, private_skills=restored_skills) with sqlite3.connect(restored) as connection: assert connection.execute("SELECT value FROM sample").fetchone()[0] == "preserved" assert restored_skills.joinpath("private.md").read_text(encoding="utf-8") == "private" tampered = tmp_path / "tampered.zip" with zipfile.ZipFile(archive) as source, zipfile.ZipFile(tampered, "w") as destination: for item in source.infolist(): content = source.read(item.filename) replacement = b"broken" if item.filename == "database.sqlite3" else content destination.writestr(item, replacement) with pytest.raises(ValueError, match="checksum mismatch"): restore_backup(tampered, tmp_path / "rejected.db") def test_restore_refuses_implicit_overwrite(tmp_path) -> None: database = tmp_path / "source.db" with sqlite3.connect(database) as connection: connection.execute("CREATE TABLE sample (value TEXT)") archive = tmp_path / "backup.zip" create_backup(database, archive) destination = tmp_path / "existing.db" destination.write_bytes(b"keep") with pytest.raises(FileExistsError): restore_backup(archive, destination) assert destination.read_bytes() == b"keep"