Add lock_provenance metadata on gitea_lock_issue writes and fail closed at gitea_create_pr when provenance is missing. Final-report validation now requires explicit External-state mutations disclosure for issue-lock read/write/delete and blocks mixed author PR creation with reviewer approval. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
130 lines
4.7 KiB
Python
130 lines
4.7 KiB
Python
"""Tests for issue-lock provenance and external-state disclosure (#447)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
import issue_lock_provenance as ilp # noqa: E402
|
|
from final_report_validator import assess_final_report_validator # noqa: E402
|
|
|
|
|
|
def _sanctioned_lock(**overrides):
|
|
work_lease = {
|
|
"operation_type": "author_issue_work",
|
|
"issue_number": 447,
|
|
"branch": "feat/issue-447-lock-provenance",
|
|
"claimant": {"username": "jcwalker3", "profile": "prgs-author"},
|
|
"expires_at": "2999-01-01T00:00:00Z",
|
|
}
|
|
data = {
|
|
"issue_number": 447,
|
|
"branch_name": "feat/issue-447-lock-provenance",
|
|
"work_lease": work_lease,
|
|
"lock_provenance": ilp.build_sanctioned_lock_provenance(
|
|
tool="gitea_lock_issue",
|
|
claimant=work_lease["claimant"],
|
|
),
|
|
}
|
|
data.update(overrides)
|
|
return data
|
|
|
|
|
|
class TestLockProvenanceForCreatePr(unittest.TestCase):
|
|
def test_sanctioned_lock_passes(self):
|
|
result = ilp.assess_lock_file_for_create_pr(_sanctioned_lock())
|
|
self.assertTrue(result["proven"])
|
|
self.assertFalse(result["block"])
|
|
|
|
def test_manual_seed_without_provenance_blocked(self):
|
|
result = ilp.assess_lock_file_for_create_pr(
|
|
{"issue_number": 420, "branch_name": "feat/x", "work_lease": {}}
|
|
)
|
|
self.assertTrue(result["block"])
|
|
self.assertIn("lock_provenance", result["reasons"][0])
|
|
|
|
def test_operator_override_requires_reason(self):
|
|
result = ilp.assess_lock_file_for_create_pr(
|
|
_sanctioned_lock(
|
|
lock_provenance=ilp.build_sanctioned_lock_provenance(
|
|
tool="operator_override",
|
|
source=ilp.SOURCE_OPERATOR_OVERRIDE,
|
|
)
|
|
)
|
|
)
|
|
self.assertTrue(result["block"])
|
|
|
|
|
|
class TestExternalStateReportRules(unittest.TestCase):
|
|
def test_seed_with_external_none_blocked(self):
|
|
report = (
|
|
"Restored /tmp/gitea_issue_lock.json to unblock PR creation.\n"
|
|
"- External-state mutations: none\n"
|
|
)
|
|
result = ilp.assess_issue_lock_external_state_report(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_seed_with_disclosure_passes(self):
|
|
report = (
|
|
"Restored /tmp/gitea_issue_lock.json after MCP restart.\n"
|
|
"- External-state mutations: wrote /tmp/gitea_issue_lock.json\n"
|
|
)
|
|
result = ilp.assess_issue_lock_external_state_report(report)
|
|
self.assertTrue(result["proven"])
|
|
|
|
def test_remove_claimed_as_cleanup_only_blocked(self):
|
|
report = (
|
|
"rm /tmp/gitea_issue_lock.json after PR creation.\n"
|
|
"- Cleanup mutations: lock removed\n"
|
|
"- External-state mutations: none\n"
|
|
)
|
|
result = ilp.assess_issue_lock_external_state_report(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_manual_lock_pr_without_override_blocked(self):
|
|
report = (
|
|
"Programmatically seeded gitea_issue_lock.json then gitea_create_pr.\n"
|
|
"PR #444 created.\n"
|
|
)
|
|
result = ilp.assess_manual_lock_pr_without_override(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
def test_author_reviewer_same_run_blocked(self):
|
|
report = (
|
|
"gitea_create_pr opened PR #444.\n"
|
|
"Submitted approve review on PR #444.\n"
|
|
)
|
|
result = ilp.assess_author_reviewer_same_run_report(report)
|
|
self.assertTrue(result["block"])
|
|
|
|
|
|
class TestFinalReportValidatorIntegration(unittest.TestCase):
|
|
def test_work_issue_blocks_hidden_lock_mutation(self):
|
|
report = (
|
|
"## Controller Handoff\n"
|
|
"- Task: work issue #420\n"
|
|
"- External-state mutations: none\n"
|
|
"Restored /tmp/gitea_issue_lock.json before PR creation.\n"
|
|
)
|
|
result = assess_final_report_validator(report, "work_issue")
|
|
rule_ids = {f["rule_id"] for f in result["findings"]}
|
|
self.assertIn("shared.issue_lock_external_state", rule_ids)
|
|
self.assertTrue(result["blocked"])
|
|
|
|
def test_review_pr_blocks_create_and_approve(self):
|
|
report = (
|
|
"## Controller Handoff\n"
|
|
"- Task: review PR #444\n"
|
|
"- Review decision: approve\n"
|
|
"Created PR #444 via gitea_create_pr earlier in this run.\n"
|
|
)
|
|
result = assess_final_report_validator(report, "review_pr")
|
|
rule_ids = {f["rule_id"] for f in result["findings"]}
|
|
self.assertIn("shared.author_reviewer_same_run", rule_ids)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main() |