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
79 lines
3.8 KiB
Python
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
|