#!/usr/bin/env python3 import importlib.util import json from pathlib import Path import tempfile import unittest SCRIPT = Path(__file__).with_name("public_mirror_agent.py") ROOT = SCRIPT.parents[2] SPEC = importlib.util.spec_from_file_location("public_mirror_agent", SCRIPT) M = importlib.util.module_from_spec(SPEC) SPEC.loader.exec_module(M) def contribution(**overrides): value = { "contribution_id":"LA-SKILL-CONTRIB-20260908-001", "module_id":"SKILL-PUBLIC-TEST-001", "name":"事实闭环", "version":"1.0.0", "contributor_human_id":"TCS-GL-TEST∞", "contributor_persona_id":"PER-TEST-001", "summary":"核验事实", "usage":"输入来源并运行检查", "capabilities":["FACT_CHECK"], "provenance":{"source_kind":"PERSONA_SKILL_DERIVATION","source_id":"PRIVATE-SNAPSHOT-HASH-ONLY"}, "persona_share_decision":"SHARE", "contains_private_human_data":True, "human_data_consent":"GRANTED", "public_payload":{"instructions":"不要读取 /Users/private/memory.json;联系 a@example.com", "input_schema":{"source":"string"}, "private_memory":"never"} } value.update(overrides) return value class MirrorAgentTest(unittest.TestCase): def test_architecture_mirror_allowlists_and_has_no_private_path(self): with tempfile.TemporaryDirectory() as temp: result = M.sync_architecture(ROOT, Path(temp)) self.assertEqual(result["outcome"], "PASS") body = (Path(temp) / "architecture/CURRENT.json").read_text() self.assertNotIn("/Volumes/", body) self.assertNotIn("continuity-memory", body) def test_no_persona_decision_no_candidate(self): with tempfile.TemporaryDirectory() as temp: with self.assertRaisesRegex(M.MirrorError, "PERSONA_EXPLICIT"): M.prepare_contribution(contribution(persona_share_decision="DO_NOT_SHARE"), Path(temp)) def test_private_human_data_requires_human_consent(self): with tempfile.TemporaryDirectory() as temp: with self.assertRaisesRegex(M.MirrorError, "HUMAN_DATA_CONSENT"): M.prepare_contribution(contribution(human_data_consent="NOT_GRANTED"), Path(temp)) def test_sanitized_candidate_is_only_quarantined(self): with tempfile.TemporaryDirectory() as temp: state = Path(temp) item = M.prepare_contribution(contribution(), state) body = json.dumps(item, ensure_ascii=False) self.assertNotIn("/Users/private", body) self.assertNotIn("a@example.com", body) self.assertFalse((state / "catalog/CURRENT.json").exists()) def test_both_independent_reviews_required_and_hash_bound(self): with tempfile.TemporaryDirectory() as temp: state = Path(temp) item = M.prepare_contribution(contribution(), state) base = {"module_id":item["module_id"],"contribution_id":item["contribution_id"],"candidate_sha256":item["candidate_sha256"],"decision":"ACCEPT"} with self.assertRaisesRegex(M.MirrorError, "MOTHER_ACCEPT"): M.review({**base,"reviewer_id":"TEAM-1"}, state, "team") M.review({**base,"reviewer_id":"MOTHER-1"}, state, "mother") M.review({**base,"reviewer_id":"TEAM-1"}, state, "team") record = M.register(item["module_id"], item["contribution_id"], state) self.assertEqual(record["state"], "ARRIVAL_REGISTERED_LOCAL_NOT_PUBLISHED") self.assertFalse(record["runtime_enabled"]) def test_same_reviewer_cannot_fill_both_gates(self): with tempfile.TemporaryDirectory() as temp: state = Path(temp) item = M.prepare_contribution(contribution(), state) base = {"module_id":item["module_id"],"contribution_id":item["contribution_id"],"candidate_sha256":item["candidate_sha256"],"decision":"ACCEPT","reviewer_id":"SAME"} M.review(base, state, "mother") M.review(base, state, "team") with self.assertRaisesRegex(M.MirrorError, "INDEPENDENT_REVIEWERS"): M.register(item["module_id"], item["contribution_id"], state) def test_hash_drift_and_replay_rejected(self): with tempfile.TemporaryDirectory() as temp: state = Path(temp) item = M.prepare_contribution(contribution(), state) with self.assertRaisesRegex(M.MirrorError, "CANDIDATE_HASH"): M.review({"module_id":item["module_id"],"contribution_id":item["contribution_id"],"candidate_sha256":"0"*64,"decision":"ACCEPT","reviewer_id":"M"}, state, "mother") with self.assertRaisesRegex(M.MirrorError, "REPLAY"): M.prepare_contribution(contribution(summary="changed"), state) if __name__ == "__main__": unittest.main()