import json from pathlib import Path import re import shutil import tempfile import unittest from verify import verify_bundle class ContractVerificationTest(unittest.TestCase): def setUp(self): self.temp = tempfile.TemporaryDirectory() self.addCleanup(self.temp.cleanup) self.root = Path(self.temp.name) / 'contracts' source = Path(__file__).parent self.root.mkdir() for name in ('schema', 'archive', 'examples'): shutil.copytree(source / name, self.root / name, ignore=shutil.ignore_patterns('.git', '__pycache__')) shutil.copyfile(source / 'manifest.json', self.root / 'manifest.json') def test_current_bundle(self): verify_bundle(self.root) def test_changed_schema_needs_no_duplicate_content_hash(self): path = self.root / 'schema/http-sip.schema.json' path.write_text(path.read_text() + '\n') verify_bundle(self.root) def test_missing_schema_reference_is_rejected(self): path = self.root / 'schema/config-read.schema.json' data = json.loads(path.read_text()) data['oneOf'][0]['$ref'] = './missing.schema.json#' path.write_text(json.dumps(data)) with self.assertRaisesRegex(ValueError, 'missing schema reference'): verify_bundle(self.root) def test_invalid_pointer_is_rejected(self): path = self.root / 'schema/config-read.schema.json' data = json.loads(path.read_text()) data['oneOf'][0]['$ref'] = './http-sip.schema.json#/$defs/missing' path.write_text(json.dumps(data)) with self.assertRaisesRegex(ValueError, 'invalid schema pointer'): verify_bundle(self.root) def test_archived_source_is_not_rehashed(self): path = next((self.root / 'archive/sources').glob('*.md')) path.write_text(path.read_text() + '\n') with self.assertRaisesRegex(ValueError, 'source hash mismatch'): verify_bundle(self.root) def test_changed_business_document_needs_no_duplicate_content_hash(self): path = self.root / 'schema/saas-dispatcher.md' path.write_text(path.read_text() + '\n') verify_bundle(self.root) def test_shared_directory_contains_only_integration_files(self): shared = self.root / 'schema' allowed = {'README.md', 'saas-dispatcher.md', 'mq-topology.json', 'examples'} for path in shared.iterdir(): if path.name not in allowed and not path.name.endswith('.schema.json'): self.fail(f'non-integration file in shared repository: {path.name}') self.assertFalse((shared / 'examples/invalid').exists()) self.assertTrue((self.root / 'examples/invalid').is_dir()) def test_readme_separates_schemas_examples_and_reference_links(self): shared = self.root / 'schema' readme = (shared / 'README.md').read_text() introduction, schemas, examples = re.split( r'^## (?:Schema 契约|Examples)\n', readme, flags=re.MULTILINE) self.assertNotIn('|', introduction) self.assertEqual(set(re.findall(r'\]\(([^)]+)\)', introduction)), {'saas-dispatcher.md', 'mq-topology.json'}) for section, expected in ( (schemas, {p.name for p in shared.glob('*.schema.json')}), (examples, {p.relative_to(shared).as_posix() for p in (shared / 'examples').glob('*.json')})): rows = section.strip().splitlines() self.assertEqual(rows[:2], ['| 文件 | 作用 |', '| --- | --- |']) self.assertTrue(all(re.fullmatch( r'\| \[`[^`]+`\]\([^)]+\) \| .+ \|', row) for row in rows[2:])) links = re.findall(r'\[`([^`]+)`\]\(([^)]+)\)', section) self.assertTrue(all(label == target for label, target in links)) self.assertEqual(len(links), len(expected), 'duplicate or missing file rows') self.assertEqual({target for _, target in links}, expected) links = re.findall(r'\]\(([^)]+)\)', readme) expected = {p.relative_to(shared).as_posix() for p in shared.rglob('*') if p.is_file() and p.name not in {'.git', 'README.md'}} self.assertEqual(len(links), len(set(links)), 'duplicate file links') self.assertEqual(set(links), expected) if __name__ == '__main__': unittest.main()