Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0e241-8285-7a63-8c0c-51c9cb824dc3
114 lines
4.2 KiB
Python
114 lines
4.2 KiB
Python
"""Reject broken preview contracts before publishing generated references."""
|
|
import hashlib
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import shutil
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT / 'scripts'))
|
|
spec = importlib.util.spec_from_file_location('reference_preview', ROOT / 'scripts/reference_preview.py')
|
|
preview = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(preview)
|
|
|
|
|
|
@pytest.fixture
|
|
def copy(tmp_path):
|
|
for name in ('research', 'canon', 'workplans', 'sequences', 'technologies', 'reference'):
|
|
shutil.copytree(ROOT / name, tmp_path / name)
|
|
shutil.copy(ROOT / 'START.md', tmp_path / 'START.md')
|
|
(tmp_path / '.git').symlink_to(ROOT / '.git', target_is_directory=True)
|
|
return tmp_path
|
|
|
|
|
|
def mutate(root, edit):
|
|
path = root / 'reference/0.1/manifest.json'
|
|
data = json.loads(path.read_text())
|
|
edit(data)
|
|
path.write_text(json.dumps(data))
|
|
|
|
|
|
def test_current_export():
|
|
for name, content in preview.build().items():
|
|
assert (ROOT / 'reference/0.1' / name).read_text() == content
|
|
|
|
|
|
@pytest.mark.parametrize('field,value', [('target', 'CAND-9999'), ('type', 'magically-guarantees'), ('condition', ''), ('evidence_refs', ['missing.md'])])
|
|
def test_bad_relation(copy, field, value):
|
|
mutate(copy, lambda m: m['relations'][0].update({field: value}))
|
|
with pytest.raises(ValueError):
|
|
preview.build(copy)
|
|
|
|
|
|
def test_duplicate_identity(copy):
|
|
mutate(copy, lambda m: m['core'].append(m['core'][0]))
|
|
with pytest.raises(ValueError):
|
|
preview.build(copy)
|
|
|
|
|
|
def test_live_candidate_change_does_not_break_frozen_preview(copy):
|
|
with (copy / 'research/candidates/ConflictCopy.md').open('a') as f:
|
|
f.write('\nChanged contract.\n')
|
|
assert preview.build(copy) == preview.build(ROOT)
|
|
import research_evolution as evolution
|
|
with pytest.raises(ValueError, match='Stale profile'):
|
|
evolution.validate(evolution.load(copy), copy)
|
|
|
|
|
|
def test_escaping_reference(copy):
|
|
mutate(copy, lambda m: m['entrypoints'].update(start='/etc/passwd'))
|
|
with pytest.raises(ValueError, match='Published manifest changed'):
|
|
preview.build(copy)
|
|
|
|
|
|
def test_missing_metadata(copy):
|
|
path = copy / 'reference/0.1/manifest.json'
|
|
data = json.loads(path.read_text())
|
|
del data['relations'][0]['rationale']
|
|
path.write_text(json.dumps(data))
|
|
with pytest.raises((ValueError, KeyError)):
|
|
preview.build(copy)
|
|
|
|
|
|
def test_legitimate_new_research_remains_separate_from_old_release(copy):
|
|
import research_evolution as evolution
|
|
candidate = copy / 'research/candidates/ConflictCopy.md'
|
|
candidate.write_text(candidate.read_text() + '\nNew research clarification.\n')
|
|
digest = hashlib.sha256(candidate.read_bytes()).hexdigest()
|
|
profiles = copy / 'research/evolution/profiles.json'
|
|
data = json.loads(profiles.read_text())
|
|
data['profiles'][0]['sha256'] = digest
|
|
profiles.write_text(json.dumps(data))
|
|
classes = copy / 'research/catalog/ClassificationCoverage.json'
|
|
data = json.loads(classes.read_text())
|
|
next(c for c in data['candidates'] if c['path'] == 'research/candidates/ConflictCopy.md')['sha256'] = digest
|
|
classes.write_text(json.dumps(data))
|
|
evolution.validate(evolution.load(copy), copy)
|
|
assert preview.build(copy, '0.1') == preview.build(ROOT, '0.1')
|
|
|
|
|
|
def test_unknown_version_and_forged_release_pin(copy):
|
|
with pytest.raises(ValueError, match='Unknown'):
|
|
preview.build(copy, '9.9')
|
|
path = copy / 'reference/releases.json'
|
|
data = json.loads(path.read_text())
|
|
data['releases']['0.1']['manifest_sha256'] = '0' * 64
|
|
path.write_text(json.dumps(data))
|
|
with pytest.raises(ValueError, match='digest mismatch'):
|
|
preview.build(copy)
|
|
|
|
|
|
def test_published_cases_cannot_be_rewritten(copy):
|
|
path = copy / 'reference/0.1/development-cases.json'
|
|
path.write_text('{}')
|
|
with pytest.raises(ValueError, match='Published artifact changed'):
|
|
preview.build(copy)
|
|
|
|
|
|
@pytest.mark.parametrize('path', ['/etc/passwd', '../README.md', 'research/../README.md', 'research//README.md'])
|
|
def test_git_blob_rejects_escaping_paths(path):
|
|
with pytest.raises(ValueError, match='Invalid reference'):
|
|
preview.git_blob(ROOT, '0' * 40, path)
|