Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
49 lines
2.9 KiB
Python
49 lines
2.9 KiB
Python
import hashlib
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
spec = importlib.util.spec_from_file_location('transition', Path(__file__).parents[1] / 'tools' / 'verify_publication_transition.py')
|
|
m = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(m)
|
|
|
|
class TransitionTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.tmp.cleanup)
|
|
self.root = Path(self.tmp.name)
|
|
self.doc = {'id':'a','revision_path':'a/revisions/1/index.html','revision':'1','source_digest':'old','source_revision':'old-head'}
|
|
self.old = self.root / 'baseline.json'
|
|
self.old.write_text(json.dumps({'documents':[self.doc]}))
|
|
self.audit = self.root / 'audit.json'
|
|
self.audit.write_text(json.dumps({'immutable_results':[{'id':'a','revision_path':self.doc['revision_path'],'http_status':200,'served_html_sha256':hashlib.sha256(b'original').hexdigest()}]}))
|
|
self.page = self.root / self.doc['revision_path']
|
|
self.page.parent.mkdir(parents=True)
|
|
self.page.write_bytes(b'original')
|
|
self.write_candidate(self.doc | {'source_revision':'new-head'})
|
|
def write_candidate(self, doc):
|
|
(self.root / 'publication-manifest.json').write_text(json.dumps({'documents':[doc]}))
|
|
def verify(self, allow=()):
|
|
return m.verify(self.root, self.old, self.audit, allow)
|
|
def test_new_head_same_markdown_preserves_original_html(self):
|
|
self.assertEqual(self.verify()['history_preserved'], 1)
|
|
self.page.write_bytes(b'regenerated new-head')
|
|
with self.assertRaisesRegex(ValueError,'immutable bytes changed'): self.verify()
|
|
def test_reviewed_new_revision_still_requires_previous_history(self):
|
|
self.write_candidate(self.doc | {'revision':'2','source_digest':'new'})
|
|
with self.assertRaisesRegex(ValueError,'unreviewed'): self.verify()
|
|
self.verify(['a'])
|
|
self.page.unlink()
|
|
with self.assertRaisesRegex(ValueError,'absent'): self.verify(['a'])
|
|
def test_allowlist_cannot_approve_history_rewrite(self):
|
|
self.page.write_bytes(b'rewrite')
|
|
with self.assertRaisesRegex(ValueError,'immutable bytes changed'): self.verify(['a'])
|
|
def test_failed_capture_refuses(self):
|
|
self.audit.write_text(json.dumps({'immutable_results':[{'id':'a','revision_path':self.doc['revision_path'],'http_status':404}]}))
|
|
with self.assertRaisesRegex(ValueError,'not successfully captured'): self.verify()
|
|
def test_path_traversal_refuses(self):
|
|
self.doc['revision_path']='../outside'
|
|
self.old.write_text(json.dumps({'documents':[self.doc]}))
|
|
self.audit.write_text(json.dumps({'immutable_results':[{'id':'a','revision_path':'../outside','http_status':200,'served_html_sha256':'a'*64}]}))
|
|
with self.assertRaisesRegex(ValueError,'unsafe'): self.verify()
|