"""Read-only recovery must never repeat an uncertain delivery mutation.""" import importlib.util from pathlib import Path import unittest PATH = Path(__file__).resolve().parents[3] / 'harness/agents/impl/skills/gitea-issue-implementation/scripts/implementation_delivery.py' class ReadOnlyAdapter: def __init__(self, head): self.head = head self.calls = [] self.proof = True def push(self, head): raise AssertionError('reconciliation must not push') def create_or_reuse_pr(self, head): raise AssertionError('reconciliation must not create or patch') def read_remote_head(self): self.calls.append('branch') return self.head def read_pr(self, number): self.calls.append('pr') return {'number': number, 'head': self.head, 'state': 'open'} def verify_reconciliation(self, head, number): self.calls.append('complete_intent') return self.proof class ReconciliationTests(unittest.TestCase): @classmethod def setUpClass(cls): spec = importlib.util.spec_from_file_location('delivery_reconciliation_under_test', PATH) cls.module = importlib.util.module_from_spec(spec) spec.loader.exec_module(cls.module) def test_read_only_recovery_returns_exact_state_without_repeated_mutation(self): adapter = ReadOnlyAdapter('a' * 40) result = self.module.reconcile_implementation(adapter, head=adapter.head, number=19, declaration={'mode': 'none'}) self.assertEqual(result, {'remote_head': adapter.head, 'pr': {'number': 19, 'head': adapter.head, 'state': 'open'}}) self.assertEqual(adapter.calls, ['branch', 'pr', 'complete_intent']) def test_missing_declaration_blocks_all_reads(self): adapter = ReadOnlyAdapter('a' * 40) with self.assertRaises(self.module.AdoptionError): self.module.reconcile_implementation(adapter, head=adapter.head, number=19, declaration=None) self.assertEqual(adapter.calls, []) def test_invalid_identifiers_block_all_reads(self): for head, number in [('bad', 19), ('a' * 40, True), ('a' * 40, 0)]: adapter = ReadOnlyAdapter('a' * 40) with self.subTest(head=head, number=number), self.assertRaises(self.module.AdoptionError): self.module.reconcile_implementation(adapter, head=head, number=number, declaration={'mode': 'none'}) self.assertEqual(adapter.calls, []) def test_branch_mismatch_blocks_pr_and_proof_reads(self): adapter = ReadOnlyAdapter('b' * 40) with self.assertRaises(self.module.AdoptionError): self.module.reconcile_implementation(adapter, head='a' * 40, number=19, declaration={'mode': 'none'}) self.assertEqual(adapter.calls, ['branch']) def test_body_snapshot_scope_or_collection_failure_cannot_complete(self): for proof in [False, None, 1, {}, 'yes']: adapter = ReadOnlyAdapter('a' * 40) adapter.proof = proof with self.subTest(proof=proof), self.assertRaises(self.module.AdoptionError): self.module.reconcile_implementation(adapter, head=adapter.head, number=19, declaration={'mode': 'none'}) self.assertEqual(adapter.calls, ['branch', 'pr', 'complete_intent']) def test_loopback_committed_write_with_bad_ack_recovers_read_only(self): import json import threading from http.server import BaseHTTPRequestHandler, HTTPServer from urllib.request import Request, urlopen spec = importlib.util.spec_from_file_location('response_reconciliation_integration', PATH.with_name('delivery_response.py')) assert spec is not None and spec.loader is not None response = importlib.util.module_from_spec(spec) spec.loader.exec_module(response) head = 'a' * 40 state = {'writes': 0, 'body': '', 'head': head, 'number': 19, 'state': 'open', 'base': 'main', 'repository': 'owner/repo', 'author': 6, 'issue': {'body': 'approved', 'updated_at': 'pinned', 'comments': []}, 'files': ['one.py'], 'commits': [head], 'collections_complete': True} expected_issue = dict(state['issue']) class Handler(BaseHTTPRequestHandler): def do_PATCH(self): state['writes'] += 1 state['body'] = self.rfile.read(int(self.headers['Content-Length'])).decode() self.send_response(201) self.end_headers() self.wfile.write(b'{') # Committed, but malformed acknowledgement. def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(json.dumps(state).encode()) def log_message(self, format, *args): pass server = HTTPServer(('127.0.0.1', 0), Handler) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() url = 'http://127.0.0.1:' + str(server.server_port) def send(method='GET'): request = Request(url, data=b'intended' if method == 'PATCH' else None, method=method) with urlopen(request, timeout=2) as reply: return reply.status, reply.read(response.MAX_RESPONSE_BYTES + 1) def read(): return response.deliver_once(send, expected_status=200) class Adapter(ReadOnlyAdapter): def read_remote_head(self): return read()['head'] def read_pr(self, number): return read() def verify_reconciliation(self, head, number): actual = read() return (actual['body'] == 'intended' and actual['head'] == head and actual['number'] == number and actual['state'] == 'open' and actual['base'] == 'main' and actual['repository'] == 'owner/repo' and actual['author'] == 6 and actual['issue'] == expected_issue and actual['files'] == ['one.py'] and actual['commits'] == [head] and actual['collections_complete'] is True) try: with self.assertRaises(response.AcknowledgementUncertainError): response.deliver_once(lambda: send('PATCH'), expected_status=201) result = self.module.reconcile_implementation(Adapter(head), head=head, number=19, declaration={'mode': 'none'}) self.assertEqual(result['remote_head'], head) for key, bad in [('body', 'competing change'), ('issue', {}), ('collections_complete', False)]: saved = state[key] state[key] = bad with self.subTest(key=key), self.assertRaises(self.module.AdoptionError): self.module.reconcile_implementation(Adapter(head), head=head, number=19, declaration={'mode': 'none'}) state[key] = saved self.assertEqual(state['writes'], 1) finally: server.shutdown() server.server_close() thread.join(timeout=2) self.assertFalse(thread.is_alive()) def test_closed_pr_cannot_complete(self): adapter = ReadOnlyAdapter('a' * 40) adapter.read_pr = lambda number: {'number': number, 'head': adapter.head, 'state': 'closed'} with self.assertRaises(self.module.AdoptionError): self.module.reconcile_implementation(adapter, head=adapter.head, number=19, declaration={'mode': 'none'}) self.assertNotIn('complete_intent', adapter.calls) if __name__ == '__main__': unittest.main()