from __future__ import annotations import hashlib import json import tempfile import unittest from pathlib import Path from vpn_egressctl.errors import ValidationError from vpn_egressctl.installcheck import verify_install_state class InstallCheckTests(unittest.TestCase): def test_empty_install_root_is_allowed(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) verify_install_state(root / "policy.json", root / "state", root / "config.json") def test_legacy_policy_is_rejected_instead_of_migrated(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) raw = json.loads(Path("config/policy.json").read_text(encoding="utf-8")) raw["sing_box"]["required_version"] = "1.13.19" policy = root / "policy.json" policy.write_text(json.dumps(raw), encoding="utf-8") with self.assertRaisesRegex(ValidationError, "exactly 1.14.0"): verify_install_state(policy, root / "state", root / "config.json") def test_legacy_state_is_rejected(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) state_dir = root / "state" state_dir.mkdir() (state_dir / "state.json").write_text( json.dumps({"controller_version": "0.1.0"}), encoding="utf-8" ) with self.assertRaisesRegex(ValidationError, "another vpn-egressctl release"): verify_install_state(root / "policy.json", state_dir, root / "config.json") def test_unmanaged_existing_config_is_rejected(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) config = root / "config.json" config.write_text("{}\n", encoding="utf-8") with self.assertRaisesRegex(ValidationError, "unmanaged"): verify_install_state(root / "policy.json", root / "state", config) def test_orphaned_legacy_backup_is_rejected(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) state_dir = root / "state" state_dir.mkdir() (state_dir / "last-good.json").write_text("{}\n", encoding="utf-8") with self.assertRaisesRegex(ValidationError, "Orphaned"): verify_install_state(root / "policy.json", state_dir, root / "config.json") def test_same_release_state_and_config_are_allowed(self) -> None: with tempfile.TemporaryDirectory() as directory: root = Path(directory) config = root / "config.json" config.write_bytes(b"{}\n") state_dir = root / "state" state_dir.mkdir() (state_dir / "state.json").write_text( json.dumps( { "controller_version": "0.2.0", "sing_box_version": "1.14.0", "config_sha256": hashlib.sha256(b"{}\n").hexdigest(), } ), encoding="utf-8", ) verify_install_state(root / "policy.json", state_dir, config) if __name__ == "__main__": unittest.main()