Files
aios/tests/governance/test_semantic_governance.py
T
AIOS Mission Convergence 03da23a1e3
autonomy-unit-tests Mission source convergence checks passed at exact head
autonomy-smoke-tests Mission source convergence checks passed at exact head
git-diff-check Mission source convergence checks passed at exact head
py-compile Mission source convergence checks passed at exact head
reconciliation-contract Mission source convergence checks passed at exact head
release-state-machine Mission source convergence checks passed at exact head
secret-scan Mission source convergence checks passed at exact head
signer-validator-contract Mission source convergence checks passed at exact head
owner-approval-gate Mission source convergence checks passed at exact head
campaign-approval-gate Mission source convergence checks passed at exact head
feat(governance): add semantic and ref reconciliation primitives
2026-08-08 22:55:14 +08:00

79 lines
3.8 KiB
Python

import subprocess
from pathlib import Path
import pytest
from governance.ref_reconciler import RefReconciler, RefReconciliationError
from governance.semantic_protection import semantic_digest, semantic_policy, transport_digest
def protection(**overrides):
value = {
"branch_name": "main",
"enable_push": False,
"enable_force_push": False,
"enable_merge_whitelist": True,
"merge_whitelist_usernames": ["owner"],
"required_approvals": 0,
"enable_status_check": True,
"status_check_contexts": ["owner-approval-gate", "py-compile"],
"block_admin_merge_override": True,
"block_on_official_review_requests": True,
"block_on_outdated_branch": True,
"block_on_rejected_reviews": True,
"dismiss_stale_approvals": True,
"ignore_stale_approvals": False,
"enable_approvals_whitelist": True,
"approvals_whitelist_username": ["owner"],
"enable_bypass_allowlist": False,
"enable_force_push_allowlist": False,
"enable_push_whitelist": False,
"protected_file_patterns": "[]",
"unprotected_file_patterns": "[]",
}
value.update(overrides)
return value
def test_transport_metadata_does_not_change_semantic_digest():
before = protection(updated_at="old")
after = protection(updated_at="new")
assert transport_digest(before) != transport_digest(after)
assert semantic_digest(before) == semantic_digest(after)
def test_stage1_whitelist_is_a_semantic_change():
before = protection()
after = protection(merge_whitelist_usernames=["owner", "aios-autonomy-agent"])
assert semantic_policy(before)["merge_whitelist_user_ids"] == [2]
assert semantic_policy(after)["merge_whitelist_user_ids"] == [2, 153]
assert semantic_digest(before) != semantic_digest(after)
def test_ref_reconciler_rejects_gitea_mismatch(tmp_path: Path):
reconciler = RefReconciler(tmp_path / "missing.git")
with pytest.raises(RefReconciliationError, match="GITEA_MAIN_EXPECTATION_MISMATCH"):
reconciler.reconcile_main(expected_oid="a" * 40, merged_oid="b" * 40, gitea_main_oid="c" * 40)
def test_ref_reconciler_ff_cas_and_idempotency(tmp_path: Path):
repo = tmp_path / "repo.git"
subprocess.run(["git", "init", "--bare", str(repo)], check=True, capture_output=True)
worktree = tmp_path / "work"
subprocess.run(["git", "clone", str(repo), str(worktree)], check=True, capture_output=True)
subprocess.run(["git", "-C", str(worktree), "config", "user.email", "test@example.invalid"], check=True)
subprocess.run(["git", "-C", str(worktree), "config", "user.name", "test"], check=True)
(worktree / "one.txt").write_text("one\n", encoding="utf-8")
subprocess.run(["git", "-C", str(worktree), "add", "one.txt"], check=True)
subprocess.run(["git", "-C", str(worktree), "commit", "-m", "one"], check=True, capture_output=True)
old = subprocess.check_output(["git", "-C", str(worktree), "rev-parse", "HEAD"], text=True).strip()
subprocess.run(["git", "-C", str(worktree), "push", "origin", "HEAD:refs/heads/main"], check=True, capture_output=True)
(worktree / "two.txt").write_text("two\n", encoding="utf-8")
subprocess.run(["git", "-C", str(worktree), "add", "two.txt"], check=True)
subprocess.run(["git", "-C", str(worktree), "commit", "-m", "two"], check=True, capture_output=True)
new = subprocess.check_output(["git", "-C", str(worktree), "rev-parse", "HEAD"], text=True).strip()
subprocess.run(["git", "-C", str(worktree), "push", "origin", "HEAD:refs/heads/main"], check=True, capture_output=True)
reconciler = RefReconciler(repo)
assert reconciler.reconcile_main(expected_oid=old, merged_oid=new, gitea_main_oid=new) == new
assert reconciler.reconcile_main(expected_oid=old, merged_oid=new, gitea_main_oid=new) == new