import copy import importlib.util import json import os import subprocess import sys import tempfile import time import unittest from pathlib import Path from harness.tests._support.paths import ROOT SCRIPT = ROOT / 'harness/agents/review/skills/gitea-code-review/scripts/native_review.py' def load(path, name): spec = importlib.util.spec_from_file_location(name, path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module from harness.tests._support.review import ( FakeAPI, FakeLease, plan_fixture, publication_fixture, support, ) class TestNativePublication(unittest.TestCase): def module(self): self.assertTrue(SCRIPT.is_file(), 'shared native review adapter is missing') m = load(SCRIPT, 'native_review_test_' + str(id(self))) # Every test gets a private registry root; production has no test-only # root override, so isolate through the journal module seam. private_root = tempfile.TemporaryDirectory() self.addCleanup(private_root.cleanup) root = Path(private_root.name) / 'native-review-journals' m._journal.root_for = lambda anchor, root=root: root return m @staticmethod def canonical(m, plan): return m.canonical_journal_path(plan) def test_payload_is_exact_bound_native_create(self): m = self.module() p = plan_fixture() self.assertEqual(m.build_payload(p), {'commit_id': p['target']['head'], 'body': p['body'], 'event': 'APPROVED', 'comments': []}) bad = copy.deepcopy(p); bad['target']['pull'] = True with self.assertRaises(m.ValidationError): m.build_payload(bad) bad = copy.deepcopy(p); bad['body'] = bad['body'].replace('a' * 40, 'd' * 40) with self.assertRaises(m.ValidationError): m.build_payload(bad) def test_verify_exact_target_and_bound_provider_changes(self): m = self.module() self.assertTrue(callable(getattr(m, 'verify_publication', None)), 'readback verifier is missing') p, before, after, exact = publication_fixture() previous = after['reviews'][0] previous.update(official=False, dismissed=True, updated_at=exact['submitted_at']) after['commits'][0]['files'].reverse() result = m.verify_publication(p, before, after, exact) self.assertEqual(result['review_id'], 10) self.assertEqual(result['projected_review_ids'], [8]) self.assertTrue(result['commit_file_order_normalized']) for mutation in ['head', 'body', 'unrelated_review', 'commit_file']: bad = copy.deepcopy(after) if mutation == 'head': bad['pr']['head']['sha'] = 'e' * 40 elif mutation == 'body': bad['reviews'][-1]['body'] += ' altered' elif mutation == 'unrelated_review': bad['reviews'][1]['body'] += ' altered' else: bad['commits'][0]['files'][0]['status'] = 'removed' with self.subTest(mutation=mutation), self.assertRaises(m.ValidationError): m.verify_publication(p, before, bad, exact) def test_publish_once_and_reconcile_durable_attempt_without_reposting(self): m = self.module() self.assertTrue(callable(getattr(m, 'publish', None)), 'shared publisher is missing') p, before, after, exact = publication_fixture() api = FakeAPI(p, before, after, exact); lease = FakeLease() with tempfile.TemporaryDirectory() as td: result = m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'}) self.assertEqual(result['mode'], 'created') self.assertEqual(api.posts, 1) self.assertEqual(m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'})['mode'], 'no_op') self.assertEqual(api.posts, 1) self.assertNotIn('body', json.loads((self.canonical(m, p) / 'intent.json').read_text())) for applies in [True, False]: m = self.module(); p, before, after, exact = publication_fixture() api = FakeAPI(p, before, after, exact); api.timeout = True; api.apply = applies with self.subTest(applies=applies), tempfile.TemporaryDirectory() as td: if applies: self.assertEqual(m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'})['mode'], 'reconciled') self.assertEqual(m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'})['mode'], 'no_op') else: for _ in range(2): with self.assertRaises(m.UncertainPublication) as error: m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'}) self.assertNotIn('secret transport detail', str(error.exception)) self.assertEqual(api.posts, 1) def test_empty_review_comments_no_header_contract_is_explicit(self): text = (ROOT / 'harness/agents/review/skills/gitea-code-review/references/native-review.md').read_text() self.assertIn('### Empty review-comments readback without a count header', text) section = text.split('### Empty review-comments readback without a count header', 1)[1].split('\n### ', 1)[0] for rule in ['X-Total-Count', 'comments_count=0', 'JSON `[]`', 'declared_total=None', 'rel=next']: self.assertIn(rule, section) def test_cli_preflight_and_saved_wire_verification_are_read_only(self): m = self.module(); p, before, after, exact = publication_fixture() with tempfile.TemporaryDirectory() as td: root = Path(td) for name, value in [('plan', p), ('before', before), ('after', after), ('exact', exact)]: (root / (name + '.json')).write_text(json.dumps(value, ensure_ascii=False)) base = [sys.executable, str(SCRIPT)] env = {'PATH': os.environ['PATH'], 'PYTHONDONTWRITEBYTECODE': '1'} pre = subprocess.run(base + ['preflight', '--plan', str(root / 'plan.json'), '--baseline', str(root / 'before.json')], capture_output=True, text=True, env=env) self.assertEqual(pre.returncode, 0, pre.stderr) self.assertIn('"ready": true', pre.stdout, 'read-only preflight CLI is missing') verified = subprocess.run(base + ['verify', '--plan', str(root / 'plan.json'), '--before', str(root / 'before.json'), '--after', str(root / 'after.json'), '--exact', str(root / 'exact.json')], capture_output=True, text=True, env=env) self.assertEqual(verified.returncode, 0, verified.stderr) self.assertTrue(json.loads(verified.stdout)['read_only']) self.assertEqual(json.loads(verified.stdout)['review_id'], 10) unsupported = subprocess.run(base + ['publish'], capture_output=True, text=True, env=env) self.assertNotEqual(unsupported.returncode, 0) def test_spec_approval_may_reference_unchanged_surrounding_test_paths(self): m = self.module(); p, before, after, exact = publication_fixture() p['spec_paths'] = ['src/a.py', 'tests/existing.py'] spec_result = support.parse_review(before['reviews'][1]['body'], {'src/a.py'}) spec_result['criteria'][0]['test_paths'] = ['tests/existing.py'] before['reviews'][1]['body'] = support.render_review(spec_result, set(p['spec_paths']), declaration={'mode': 'none'}) try: m.validate_snapshot(p, before) except m.ValidationError: self.fail('spec approval must use its declared scope, not only changed files') def test_malformed_historical_actor_and_missing_exact_review_flags_fail_closed(self): m = self.module(); p, before, after, exact = publication_fixture() bad = copy.deepcopy(before); bad['reviews'][0]['user'] = {'id': True, 'login': 'x'} with self.assertRaises(m.ValidationError): m.validate_snapshot(p, bad) p, before, after, exact = publication_fixture() del exact['official']; del after['reviews'][-1]['official'] with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) def test_invalid_plan_bindings_are_not_normalized(self): m = self.module() mutations = [lambda p: p['target'].__setitem__('issue', True), lambda p: p['target'].__setitem__('head', 'A' * 40), lambda p: p['target'].__setitem__('owner', 'acme/other'), lambda p: p.__setitem__('verdict', 'blocked'), lambda p: p.__setitem__('body', p['body'] + '- Verdict: `approve`\n'), lambda p: p['provider_contract'].__setitem__('evidence', []), lambda p: p.__setitem__('spec_paths', ['../secret']), lambda p: p.__setitem__('spec_paths', ['src/a.py', 'src/a.py']), lambda p: p['reviewer'].__setitem__('id', p['spec_reviewer']['id'])] for i, mutate in enumerate(mutations): with self.subTest(case=i): p = plan_fixture(); mutate(p) with self.assertRaises(m.ValidationError): m.build_payload(p) def test_wrong_role_and_expired_lease_prevent_any_post(self): m = self.module() for kind in ['wrong_role', 'expired']: with self.subTest(kind=kind), tempfile.TemporaryDirectory() as td: p, before, after, exact = publication_fixture(); api = FakeAPI(p, before, after, exact); lease = FakeLease() if kind == 'expired': lease.valid = False else: api.identity = lambda: p['spec_reviewer'] with self.assertRaises(m.ValidationError): m.publish(p, before, api, lease, self.canonical(m, p), declaration={'mode': 'none'}) self.assertEqual(api.posts, 0) self.assertFalse((self.canonical(m, p) / 'intent.json').exists()) def test_fresh_head_contract_and_source_linkage_drift_prevent_post(self): m = self.module() changes = [lambda s: s['pr']['head'].__setitem__('sha', 'b' * 40), lambda s: s['issue'].__setitem__('body', 'changed contract'), lambda s: s['pr'].__setitem__('body', 'Closes #4'), lambda s: s['pr']['base'].__setitem__('ref', 'other'), lambda s: s['pr'].__setitem__('merged', True)] for i, change in enumerate(changes): with self.subTest(case=i), tempfile.TemporaryDirectory() as td: p, before, after, exact = publication_fixture(); api = FakeAPI(p, copy.deepcopy(before), after, exact) change(api.before) with self.assertRaises(m.ValidationError): m.publish(p, before, api, FakeLease(), self.canonical(m, p), declaration={'mode': 'none'}) self.assertEqual(api.posts, 0) def test_pagination_total_duplicate_and_truncation_are_hard_errors(self): m = self.module() changes = [lambda s: s['pagination']['files'][0].__setitem__('declared_total', 2), lambda s: s['pagination']['reviews'][0].__setitem__('link', '; rel="next"'), lambda s: s['pagination']['commits'][0].__setitem__('count', True), lambda s: s['files'].append(copy.deepcopy(s['files'][0])), lambda s: s['review_comments'].pop('8')] for i, change in enumerate(changes): with self.subTest(case=i): p, before, after, exact = publication_fixture(); change(before) with self.assertRaises(m.ValidationError): m.validate_snapshot(p, before) def test_spec_approval_body_head_role_and_state_are_bound(self): m = self.module() changes = [lambda s: s['reviews'][1].__setitem__('state', 'REQUEST_CHANGES'), lambda s: s['reviews'][1].__setitem__('commit_id', 'b' * 40), lambda s: s['reviews'][1].__setitem__('dismissed', True), lambda s: s['reviews'][1].__setitem__('body', s['reviews'][1]['body'].replace('충족', '임의 변경', 1)), lambda s: s['reviews'][1]['user'].__setitem__('id', 23)] for i, change in enumerate(changes): with self.subTest(case=i): p, before, after, exact = publication_fixture(); change(before) with self.assertRaises(m.ValidationError): m.validate_snapshot(p, before) def test_normal_supersession_is_allowed_without_projection_opt_in(self): for already_dismissed in (False, True): with self.subTest(already_dismissed=already_dismissed): m = self.module() p, before, after, exact = publication_fixture() p['provider_contract'].update(supersede_same_author=False, retouch_dismissed=False, unordered_commit_files=False, evidence=[]) if already_dismissed: before['reviews'][0].update(official=False, dismissed=True) after['reviews'][0].update(official=False, dismissed=True, updated_at=exact['submitted_at']) saved_plan = copy.deepcopy(p) saved_before = copy.deepcopy(before) try: result = m.verify_publication(p, before, after, exact) except m.ValidationError as exc: self.fail(f'normal same-author replacement was rejected: {exc}') self.assertTrue(result['verified']) self.assertEqual(result['projected_review_ids'], [8]) self.assertEqual(p, saved_plan) self.assertEqual(before, saved_before) def test_legacy_replacement_flags_do_not_require_external_evidence(self): m = self.module() p, before, after, exact = publication_fixture() p['provider_contract'].update(unordered_commit_files=False, evidence=[]) after['reviews'][0].update(official=False, dismissed=True, updated_at=exact['submitted_at']) try: result = m.verify_publication(p, before, after, exact) except m.ValidationError as exc: self.fail(f'compatibility-only replacement flags required an opt-in: {exc}') self.assertTrue(result['verified']) self.assertEqual(m.build_payload(p)['body'], p['body']) # Unordered file comparisons remain a separate, evidence-backed exception. p['provider_contract']['unordered_commit_files'] = True with self.assertRaisesRegex(m.ValidationError, 'provider projection requires evidence'): m.validate_plan(p) def test_normal_replacement_does_not_allow_historical_content_mutation(self): m = self.module() for field, value in [('body', 'altered'), ('commit_id', 'e' * 40), ('state', 'APPROVED'), ('user', {'id': 72, 'login': 'other'}), ('submitted_at', '2026-10-04T00:03:00Z'), ('official', True), ('dismissed', False), ('updated_at', '2026-10-04T00:03:00Z')]: with self.subTest(field=field): p, before, after, exact = publication_fixture() p['provider_contract'].update(supersede_same_author=False, retouch_dismissed=False, unordered_commit_files=False, evidence=[]) after['reviews'][0].update(official=False, dismissed=True, updated_at=exact['submitted_at']) after['reviews'][0][field] = value with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) p, before, after, exact = publication_fixture() p['provider_contract'].update(supersede_same_author=False, retouch_dismissed=False, unordered_commit_files=False, evidence=[]) after['reviews'][0].update(official=False, dismissed=True, updated_at=exact['submitted_at']) for snap in (before, after): snap['review_comments']['8'] = [{'id': 71, 'body': 'original'}] snap['reviews'][0]['comments_count'] = 1 snap['pagination']['review_comments_8'][0].update(count=1, declared_total=1) after['review_comments']['8'][0]['body'] = 'altered' with self.assertRaisesRegex(m.ValidationError, 'historical review comments changed'): m.verify_publication(p, before, after, exact) def test_normal_replacement_reconciles_original_intent_without_second_post(self): m = self.module() p, before, after, exact = publication_fixture() p['provider_contract'].update(supersede_same_author=False, retouch_dismissed=False, unordered_commit_files=False, evidence=[]) after['reviews'][0].update(official=False, dismissed=True, updated_at=exact['submitted_at']) api = FakeAPI(p, before, after, exact) original_read = api.read_review api.read_review = lambda target, rid: (_ for _ in ()).throw(TimeoutError('unavailable')) with self.assertRaises(m.ValidationError): m.publish(p, before, api, FakeLease(), self.canonical(m, p), declaration={'mode': 'none'}) self.assertEqual(api.posts, 1) journal = self.canonical(m, p) intent = (journal / 'intent.json').read_bytes() self.assertFalse((journal / 'verified.json').exists()) api.read_review = original_read result = m.publish(p, before, api, FakeLease(), journal, declaration={'mode': 'none'}) self.assertTrue(result['verified']) self.assertFalse(result['wrote']) self.assertEqual(result['projected_review_ids'], [8]) self.assertEqual(api.posts, 1) self.assertEqual((journal / 'intent.json').read_bytes(), intent) self.assertEqual(json.loads(intent)['fingerprint'], __import__('hashlib').sha256(m.canonical(p)).hexdigest()) self.assertTrue(json.loads((journal / 'verified.json').read_text())['verified']) def test_provider_projection_requires_explicit_bound_evidence(self): m = self.module() for kind in ['wrong_initial_flags', 'wrong_timestamp', 'wrong_author']: with self.subTest(kind=kind): p, before, after, exact = publication_fixture() after['reviews'][0].update(official=False, dismissed=True, updated_at=exact['submitted_at']) if kind == 'wrong_initial_flags': before['reviews'][0].update(official=True, dismissed=True) elif kind == 'wrong_timestamp': after['reviews'][0]['updated_at'] = '2026-10-04T00:02:00Z' else: before['reviews'][0]['user'] = {'id': 72, 'login': 'other'} after['reviews'][0]['user'] = {'id': 72, 'login': 'other'} with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) def test_order_normalization_does_not_mask_file_contents_or_parent_order(self): m = self.module() p, before, after, exact = publication_fixture() after['commits'][0]['files'].reverse() self.assertTrue(m.verify_publication(p, before, after, exact)['commit_file_order_normalized']) p['provider_contract']['unordered_commit_files'] = False with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) p, before, after, exact = publication_fixture() after['commits'][0]['files'][0]['status'] = 'removed' with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) p, before, after, exact = publication_fixture() before['commits'][0]['parents'] = [{'sha': 'e' * 40}, {'sha': 'f' * 40}] after['commits'][0]['parents'] = list(reversed(before['commits'][0]['parents'])) with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) def test_historical_head_state_body_and_comments_remain_immutable(self): m = self.module() changes = [lambda s: s['reviews'][0].__setitem__('body', 'altered'), lambda s: s['reviews'][0].__setitem__('commit_id', 'e' * 40), lambda s: s['reviews'][0].__setitem__('state', 'APPROVED')] for i, change in enumerate(changes): with self.subTest(case=i): p, before, after, exact = publication_fixture(); change(after) with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) p, before, after, exact = publication_fixture() for snap in [before, after]: snap['review_comments']['8'] = [{'id': 71, 'body': 'original'}] snap['reviews'][0]['comments_count'] = 1 snap['pagination']['review_comments_8'][0].update(count=1, declared_total=1) after['review_comments']['8'][0]['body'] = 'altered' with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) def test_existing_canonical_review_is_write_free_no_op(self): m = self.module(); p, before, after, exact = publication_fixture() api = FakeAPI(p, before, after, exact); api.created = True with tempfile.TemporaryDirectory() as td: result = m.publish(p, after, api, FakeLease(), self.canonical(m, p), declaration={'mode': 'none'}) self.assertEqual(result['mode'], 'no_op'); self.assertFalse(result['wrote']) self.assertEqual(api.posts, 0) self.assertFalse((self.canonical(m, p) / 'intent.json').exists()) def test_duplicate_or_conflicting_canonical_review_is_not_reposted(self): m = self.module() for kind in ['body', 'duplicate']: with self.subTest(kind=kind), tempfile.TemporaryDirectory() as td: p, before, after, exact = publication_fixture() if kind == 'body': after['reviews'][-1]['body'] += 'other' else: extra = copy.deepcopy(exact); extra['id'] = 11; after['reviews'].append(extra) after['review_comments']['11'] = [] after['pagination']['reviews'][0].update(count=4, declared_total=4) after['pagination']['review_comments_11'] = [{'page': 1, 'count': 0, 'declared_total': 0, 'link': None}] api = FakeAPI(p, before, after, exact); api.created = True with self.assertRaises(m.ValidationError): m.publish(p, before, api, FakeLease(), self.canonical(m, p), declaration={'mode': 'none'}) self.assertEqual(api.posts, 0) def test_issue_repository_mismatch_is_not_accepted(self): m = self.module(); p, before, after, exact = publication_fixture() before['issue']['repository']['full_name'] = 'other/repo' with self.assertRaises(m.ValidationError): m.validate_snapshot(p, before) def test_fork_head_and_nonofficial_native_states_preserve_accepted_contract(self): m = self.module(); p, before, after, exact = publication_fixture() for snapshot in [before, after]: snapshot['pr']['head']['repo']['full_name'] = 'contributor/repo' snapshot['reviews'][1]['official'] = False after['reviews'][-1]['official'] = exact['official'] = False self.assertTrue(m.verify_publication(p, before, after, exact)['verified']) after['pr']['head']['repo']['full_name'] = 'different/repo' with self.assertRaises(m.ValidationError): m.verify_publication(p, before, after, exact) def test_alternate_path_is_rejected_before_publication(self): m = load(SCRIPT, 'native_review_alternate_test') fixtures = load(ROOT / 'harness/tests/_support/review.py', 'native_review_alternate_fixture_test') plan, before, after, exact = fixtures.publication_fixture() with tempfile.TemporaryDirectory() as td: with self.assertRaises(m.ValidationError): m.publish(plan, before, fixtures.FakeAPI(plan, before, after, exact), fixtures.FakeLease(), td, declaration={'mode': 'none'}) if __name__ == '__main__': unittest.main()