96 lines
3.7 KiB
Python
96 lines
3.7 KiB
Python
#!/usr/bin/env python3
|
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
"""Host tests for the data-only Phase-1.0AD activation contract."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from dataclasses import replace
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
def parse_args() -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--root", type=Path, required=True)
|
|
return parser.parse_args()
|
|
|
|
|
|
ARGS = parse_args()
|
|
sys.path.insert(0, str(ARGS.root / "tools"))
|
|
|
|
from phase10ad_activation_contract import ( # noqa: E402
|
|
ActivationContractError,
|
|
ActivationRecord,
|
|
PHASE,
|
|
validate_candidate,
|
|
validate_inactive,
|
|
validate_numeric_target,
|
|
)
|
|
|
|
|
|
def inactive() -> ActivationRecord:
|
|
return ActivationRecord(PHASE, False, None, None, None, None, None, None,
|
|
None, None, True, False, False, False, False,
|
|
False, False)
|
|
|
|
|
|
def candidate() -> ActivationRecord:
|
|
return ActivationRecord(
|
|
PHASE, True, "192.168.1.50", 2323, "CHIMERA_AD_001",
|
|
"2026-07-29T12:00:00Z", "2026-07-29T12:05:00Z", "a" * 64,
|
|
"b" * 64, "c" * 64, True, False, False, False, False, False, False)
|
|
|
|
|
|
class ActivationContractTests(unittest.TestCase):
|
|
def test_tracked_record_is_inactive(self) -> None:
|
|
validate_inactive(inactive())
|
|
|
|
def test_inactive_record_rejects_every_binding(self) -> None:
|
|
for field, value in (("target_address", "192.168.1.50"),
|
|
("target_port", 2323), ("run_id", "RUN_ID_001"),
|
|
("launcher_sha256", "a" * 64)):
|
|
with self.subTest(field=field), self.assertRaises(ActivationContractError):
|
|
validate_inactive(replace(inactive(), **{field: value}))
|
|
|
|
def test_private_canonical_ipv4(self) -> None:
|
|
for address in ("10.0.0.2", "172.16.1.2", "192.168.1.50"):
|
|
self.assertEqual(validate_numeric_target(address), address)
|
|
|
|
def test_rejects_names_public_and_special_addresses(self) -> None:
|
|
for address in ("ps5", "8.8.8.8", "127.0.0.1", "169.254.1.1",
|
|
"0.0.0.0", "192.168.001.050", "::1"):
|
|
with self.subTest(address=address), self.assertRaises(ActivationContractError):
|
|
validate_numeric_target(address)
|
|
|
|
def test_complete_candidate_is_data_valid(self) -> None:
|
|
validate_candidate(candidate())
|
|
|
|
def test_candidate_rejects_wrong_port(self) -> None:
|
|
with self.assertRaises(ActivationContractError):
|
|
validate_candidate(replace(candidate(), target_port=9021))
|
|
|
|
def test_candidate_rejects_long_or_reversed_window(self) -> None:
|
|
for end in ("2026-07-29T12:05:01Z", "2026-07-29T11:59:59Z"):
|
|
with self.subTest(end=end), self.assertRaises(ActivationContractError):
|
|
validate_candidate(replace(candidate(), expires_at=end))
|
|
|
|
def test_candidate_rejects_missing_or_bad_hashes(self) -> None:
|
|
for field, value in (("launcher_sha256", None),
|
|
("payload_sha256", "A" * 64),
|
|
("approval_sha256", "0" * 63)):
|
|
with self.subTest(field=field), self.assertRaises(ActivationContractError):
|
|
validate_candidate(replace(candidate(), **{field: value}))
|
|
|
|
def test_candidate_rejects_retry_and_effects(self) -> None:
|
|
for field in ("automatic_retry", "reconnect", "resume",
|
|
"device_write_authorized", "app_termination_authorized",
|
|
"system_remount_authorized"):
|
|
with self.subTest(field=field), self.assertRaises(ActivationContractError):
|
|
validate_candidate(replace(candidate(), **{field: True}))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(argv=[sys.argv[0]])
|