Files
2026-09-30 21:00:33 +08:00

242 lines
15 KiB
Python

"""Temporary filesystem tests only; no process/brain/GPU execution."""
import copy
import json
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
import append_ledger as L
def put(path, value):
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(value if type(value) is bytes else L.raw_json(value))
return L.identity(path)
class LedgerTests(unittest.TestCase):
def setUp(self):
self.temp=tempfile.TemporaryDirectory();self.addCleanup(self.temp.cleanup)
self.root=Path(self.temp.name).resolve()/'normal';self.root.mkdir()
self.reg=self.root/L.REGISTRATION; info=put(self.reg,{'only':'synthetic-registration'})
self.initial={L.REGISTRATION:info};self.sha=info['sha256']
self.ledger=L.Ledger(self.root,self.sha,self.initial,1<<20,0)
def job(self,name):
job=self.root/'jobs'/(name+'.json');put(job,{'entry_id':name})
self.ledger.begin(name,job,{'synthetic':True})
return job
def completion(self,name,job,*,payload=b'output',changes=None):
out=self.root/'runs'/name/'payload.dat';put(out,payload)
process=self.root/'receipts'/(name+'.json')
value=dict(entry_id=name,registration_sha256=self.sha,job_sha256=L.digest(job),complete=True,
exit_code=0,timed_out=False,exit_observed_by_parent=True)
value.update(changes or {});put(process,value)
inputs={str(self.reg):L.identity(self.reg),str(job):L.identity(job)}
outputs={str(p):L.identity(p) for p in (job,out,process)}
metadata=dict(job_file=str(job),process_receipt_file=str(process),scope='synthetic completed parent job only',observed_returncode=0)
return inputs,outputs,metadata
def commit(self,name):
job=self.job(name);args=self.completion(name,job)
return self.ledger.append(name,*args)
def test_two_entries_chain_full_tree_and_cumulative_bytes(self):
one=self.commit('one');two=self.commit('two')
second=L.read_json(self.root/L.CONTROL/'entries/0000000000000002.json')
self.assertEqual(second['prev_entry_sha256'],one['entry_sha256'])
self.assertEqual(two['entry_sha256'],L.digest(self.root/L.CONTROL/'entries/0000000000000002.json'))
inspected=self.ledger.inspect();self.assertEqual(inspected['entry_ids'],['one','two'])
self.assertFalse(inspected['retry_authorized']);self.assertTrue(inspected['all_tracked_history_rehashed'])
final=self.ledger.final_verify(['one','two']);self.assertTrue(final['pass_all'])
self.assertEqual(final['actual_total_bytes'],sum(p.stat().st_size for p in self.root.rglob('*') if p.is_file()))
self.assertFalse(final['scientific_gate_claimed'])
def test_reopen_independent_read_verifies_no_reexecution_or_duplicate_append(self):
self.commit('one')
reopened=L.Ledger(self.root,self.sha,self.initial,1<<20,0)
self.assertEqual(reopened.inspect()['entry_ids'],['one'])
with self.assertRaisesRegex(ValueError,'already started/committed'):
reopened.begin('one',self.root/'jobs/one.json')
with self.assertRaisesRegex(ValueError,'No unique uncommitted'):
reopened.append('one',{}, {},dict(job_file='x',process_receipt_file='y',scope='s',observed_returncode=0))
def test_pending_or_live_handle_is_unknown_and_never_retry_authority(self):
job=self.job('live');state=self.ledger.inspect()
self.assertEqual(state['uncommitted_state'],'STARTED-UNCOMMITTED-STATE-UNKNOWN')
self.assertFalse(state['retry_authorized'])
with self.assertRaisesRegex(ValueError,'uncommitted start'):self.ledger.begin('another',job)
args=list(self.completion('live',job));args[2]['observed_returncode']=None
with self.assertRaisesRegex(ValueError,'Live/failed'):self.ledger.append('live',*args)
final=self.ledger.final_verify(['live']);self.assertFalse(final['pass_all']);self.assertTrue(final['uncommitted_state_unknown'])
reopened=L.Ledger(self.root,self.sha,self.initial,1<<20,0)
self.assertEqual(reopened.inspect()['uncommitted_entry_ids'],['live'])
def test_actual_parent_receipt_required_not_worker_success_or_bool_exit(self):
job=self.job('one')
for mutation in ({'exit_code':False},{'complete':False},{'exit_observed_by_parent':False},
{'timed_out':True},{'registration_sha256':'0'*64},{'job_sha256':'0'*64},{'entry_id':'other'}):
args=self.completion('one',job,changes=mutation)
with self.subTest(mutation=mutation),self.assertRaisesRegex(ValueError,'parent process receipt'):
self.ledger.append('one',*args)
def test_same_size_same_mtime_mutation_fails_actual_SHA(self):
job=self.job('one');args=self.completion('one',job,payload=b'aaaa')
out=self.root/'runs/one/payload.dat';stat=out.stat();out.write_bytes(b'bbbb')
os.utime(out,ns=(stat.st_atime_ns,stat.st_mtime_ns))
with self.assertRaisesRegex(ValueError,'Actual file SHA/bytes'):self.ledger.append('one',*args)
def test_missing_extra_changed_files_reported_at_full_audit(self):
self.commit('one');out=self.root/'runs/one/payload.dat'
extra=self.root/'unexpected.log';put(extra,b'extra')
final=self.ledger.final_verify(['one']);self.assertFalse(final['pass_all']);self.assertIn('unexpected.log',final['extra_files'])
out.unlink();final=self.ledger.final_verify(['one'])
self.assertIn('runs/one/payload.dat',final['missing_files'])
put(out,b'different');final=self.ledger.final_verify(['one'])
self.assertIn('runs/one/payload.dat',final['changed_files'])
def test_tail_and_middle_tamper_not_trusted_by_inspect(self):
self.commit('one');self.commit('two')
first=self.root/L.CONTROL/'entries/0000000000000001.json'
value=L.read_json(first);value['metadata']['scope']='tampered'
put(first,value)
with self.assertRaisesRegex(ValueError,'chain/start identity'):self.ledger.inspect()
self.assertFalse(self.ledger.final_verify(['one','two'])['pass_all'])
def test_previously_observed_head_rewrite_rejected_even_new_valid_JSON(self):
self.commit('one');path=self.root/L.CONTROL/'entries/0000000000000001.json'
value=L.read_json(path);value['metadata']['scope']='changed';put(path,value)
with self.assertRaisesRegex(ValueError,'head changed'):self.ledger.inspect()
def test_registered_final_entry_order_and_coverage_are_exact(self):
self.commit('one');self.commit('two')
for expected in (['two','one'],['one'],['one','two','three']):
report=self.ledger.final_verify(expected)
self.assertFalse(report['pass_all']);self.assertFalse(report['expected_order_exact'])
with self.assertRaisesRegex(ValueError,'expected entry order'):self.ledger.final_verify(['one','one'])
def test_path_escape_aliases_and_overwrite_rejected(self):
self.commit('one');job=self.job('two');inputs,outputs,meta=self.completion('two',job)
outside=self.root.parent/'outside.dat';put(outside,b'external')
outputs[str(outside)]=L.identity(outside)
with self.assertRaisesRegex(ValueError,'escapes experiment'):self.ledger.append('two',inputs,outputs,meta)
del outputs[str(outside)];old=self.root/'runs/one/payload.dat';outputs[str(old)]=L.identity(old)
with self.assertRaisesRegex(ValueError,'previously tracked'):self.ledger.append('two',inputs,outputs,meta)
del outputs[str(old)];outputs['runs/two/payload.dat']=outputs[str(self.root/'runs/two/payload.dat')]
with self.assertRaisesRegex(ValueError,'Duplicate path aliases'):self.ledger.append('two',inputs,outputs,meta)
with self.assertRaisesRegex(ValueError,'Escaping/noncanonical'):L.relative_name('../escape')
def test_consumed_tracked_input_cannot_be_silently_reauthorized_with_new_SHA(self):
self.commit('one');job=self.job('two');inputs,outputs,meta=self.completion('two',job)
prior=self.root/'runs/one/payload.dat';put(prior,b'overwritten')
inputs[str(prior)]=L.identity(prior)
with self.assertRaisesRegex(ValueError,'committed identity'):self.ledger.append('two',inputs,outputs,meta)
def test_external_inputs_allowed_but_declared_bytes_reverified(self):
job=self.job('one');inputs,outputs,meta=self.completion('one',job)
external=self.root.parent/'frozen-reference.json';put(external,{'read_only':'data'})
inputs[str(external)]=L.identity(external);self.ledger.append('one',inputs,outputs,meta)
row=L.read_json(self.root/L.CONTROL/'entries/0000000000000001.json')
self.assertIsNone(next(r for r in row['consumed_inputs'] if r['path']==str(external))['relative_path'])
put(external,{'read_only':'changed'})
with self.assertRaisesRegex(ValueError,'Actual file SHA/bytes'):self.ledger.inspect()
def test_full_audit_hashes_shared_external_input_once_per_invocation(self):
external=self.root.parent/'large-shared-reference';put(external,b'fixed external bytes')
expected=L.identity(external)
for name in ('one','two','three'):
job=self.job(name);inputs,outputs,meta=self.completion(name,job)
inputs[str(external)]=expected;self.ledger.append(name,inputs,outputs,meta)
original=L.digest;counts={}
def counted(path):
key=str(Path(path));counts[key]=counts.get(key,0)+1
return original(path)
with patch.object(L,'digest',side_effect=counted):
self.assertTrue(self.ledger.final_verify(['one','two','three'])['pass_all'])
self.assertEqual(counts[str(external)],1)
counts.clear();self.ledger.inspect();self.assertEqual(counts[str(external)],1)
counts.clear();self.ledger.inspect();self.assertEqual(counts[str(external)],1)
oldstat=external.stat();external.write_bytes(b'other external bytes')
os.utime(external,ns=(oldstat.st_atime_ns,oldstat.st_mtime_ns))
with self.assertRaisesRegex(ValueError,'Actual file SHA/bytes'):self.ledger.inspect()
def test_append_does_not_iterate_or_sum_historical_maps_or_search_ordered_ids(self):
self.commit('one')
class LookupOnlyDict(dict):
def __iter__(self):raise AssertionError('Historical map iteration in fast append')
def keys(self):raise AssertionError('Historical map copy in fast append')
def values(self):raise AssertionError('Historical map sum in fast append')
def items(self):raise AssertionError('Historical map iteration in fast append')
class NoLinearMembership(list):
def __contains__(self,value):raise AssertionError('Linear completed-ID membership')
self.ledger._tracked=LookupOnlyDict(self.ledger._tracked)
self.ledger._controls=LookupOnlyDict(self.ledger._controls)
self.ledger._completed=NoLinearMembership(self.ledger._completed)
self.commit('two')
self.assertEqual(self.ledger._completed,['one','two'])
self.assertTrue(self.ledger.final_verify(['one','two'])['pass_all'])
def test_head_is_freshly_checked_after_cached_full_tree_walk(self):
self.commit('one');tail=self.root/L.CONTROL/'entries/0000000000000001.json'
original_replay=self.ledger._replay;original_identity=self.ledger._identity_once
state={'walking':False,'mutated':False}
def replay(verified):
original_replay(verified);state['walking']=True
def during_walk(path,verified):
if state['walking'] and not state['mutated']:
value=L.read_json(tail);value['metadata']['scope']='mutation during final walk'
put(tail,value);state['mutated']=True
return original_identity(path,verified)
with patch.object(self.ledger,'_replay',side_effect=replay),\
patch.object(self.ledger,'_identity_once',side_effect=during_walk):
report=self.ledger.final_verify(['one'])
self.assertTrue(state['mutated']);self.assertFalse(report['pass_all'])
self.assertTrue(any('Known ledger tail changed' in message for message in report['errors']))
def test_shared_mutable_build_separate_current_inventory_and_cap(self):
build=self.root/'extension-build/cache.bin';put(build,b'first');self.commit('one')
put(build,b'changed build is allowed');self.commit('two')
report=self.ledger.final_verify(['one','two']);self.assertTrue(report['pass_all'])
self.assertEqual(report['shared_mutable_build_bytes'],len(b'changed build is allowed'))
self.assertNotIn('extension-build/cache.bin',report['extra_files'])
put(build,b'x'*(1<<20));report=self.ledger.final_verify(['one','two'])
self.assertFalse(report['pass_all']);self.assertFalse(report['output_cap_respected'])
def test_stale_writer_lock_and_partial_publication_never_auto_cleared(self):
lock=self.root/L.CONTROL/'writer.lock';put(lock,b'unknown owner')
with self.assertRaises(FileExistsError):self.ledger.begin('one',self.reg)
with self.assertRaisesRegex(ValueError,'state unknown'):self.ledger.inspect()
self.assertEqual(lock.read_bytes(),b'unknown owner')
def test_exclusive_atomic_publication_and_preserved_failed_temporary(self):
target=self.root/'standalone.json';info=L.publish_exclusive(target,{'a':1})
self.assertEqual(info,L.identity(target))
with self.assertRaisesRegex(ValueError,'already exists'):L.publish_exclusive(target,{'a':2})
self.assertEqual(L.read_json(target),{'a':1})
target2=self.root/'failed.json'
with patch.object(L.os,'link',side_effect=OSError('synthetic unsupported filesystem')):
with self.assertRaises(OSError):L.publish_exclusive(target2,{'a':2})
self.assertFalse(target2.exists());self.assertEqual(len(list(self.root.glob('.publishing-*'))),1)
def test_linked_output_rejected_if_platform_supports_links(self):
source=self.root.parent/'other';source.mkdir();link=self.root/'linked'
try:link.symlink_to(source,target_is_directory=True)
except OSError:self.skipTest('Platform disallows synthetic symlink')
job=self.job('one');inputs,outputs,meta=self.completion('one',job)
put(source/'payload',b'x');outputs[str(link/'payload')]=L.identity(source/'payload')
with self.assertRaisesRegex(ValueError,'Linked'):self.ledger.append('one',inputs,outputs,meta)
def test_initial_and_header_authority_are_not_optional(self):
for change in ('registration','options'):
with self.subTest(change=change),self.assertRaises(ValueError):
L.Ledger(self.root,'0'*64 if change=='registration' else self.sha,self.initial,
2<<20 if change=='options' else 1<<20,0)
header=self.root/L.CONTROL/'header.json';value=L.read_json(header);value['max_output_bytes']+=1;put(header,value)
with self.assertRaisesRegex(ValueError,'header changed'):self.ledger.inspect()
if __name__=='__main__':unittest.main()