96 lines
4.3 KiB
Python
96 lines
4.3 KiB
Python
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()
|