import os import stat import tempfile import unittest from contextlib import redirect_stderr from io import StringIO from pathlib import Path from tools.qmt_shadow_plan import ( _load_existing_account_hash_key, _load_or_create_account_hash_key, build_parser, ) class QmtShadowToolTests(unittest.TestCase): def test_account_hash_key_is_created_once_with_private_permissions(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "private" / "account-hash.key" first = _load_or_create_account_hash_key(path) second = _load_or_create_account_hash_key(path) self.assertEqual(first, second) self.assertEqual(len(first), 32) if os.name != "nt": self.assertEqual( stat.S_IMODE(path.stat().st_mode), 0o600, ) def test_short_account_hash_key_is_rejected(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "account-hash.key" path.write_bytes(b"too-short") path.chmod(0o600) with self.assertRaisesRegex(ValueError, "at least 32 bytes"): _load_or_create_account_hash_key(path) def test_verify_key_loader_never_creates_missing_key(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "missing-account-hash.key" with self.assertRaisesRegex( ValueError, "does not exist" ): _load_existing_account_hash_key(path) self.assertFalse(path.exists()) def test_existing_parent_permissions_are_not_changed(self): if os.name == "nt": self.skipTest("POSIX permission invariant") with tempfile.TemporaryDirectory() as directory: parent = Path(directory) / "shared-parent" parent.mkdir(mode=0o755) parent.chmod(0o755) _load_or_create_account_hash_key(parent / "account-hash.key") self.assertEqual( stat.S_IMODE(parent.stat().st_mode), 0o755, ) def test_cli_rejects_widened_safety_thresholds(self): base = [ "plan", "--decision", "decision-manifest.json", "--output", "shadow-output", ] for option, value in ( ("--max-query-duration-seconds", "10.0001"), ("--market-value-tolerance", "1.0001"), ): with self.subTest(option=option), redirect_stderr(StringIO()): with self.assertRaises(SystemExit): build_parser().parse_args(base + [option, value]) def test_cli_accepts_only_equal_or_stricter_thresholds(self): parsed = build_parser().parse_args( [ "plan", "--decision", "decision-manifest.json", "--output", "shadow-output", "--max-query-duration-seconds", "5", "--market-value-tolerance", "0.5", ] ) self.assertEqual(parsed.max_query_duration_seconds, 5.0) self.assertEqual(parsed.market_value_tolerance, 0.5) def test_cli_verify_requires_original_decision(self): with redirect_stderr(StringIO()): with self.assertRaises(SystemExit): build_parser().parse_args( ["verify", "--manifest", "shadow-manifest.json"] ) if __name__ == "__main__": unittest.main()