85 lines
3.4 KiB
Python
85 lines
3.4 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import unittest
|
|
from copy import deepcopy
|
|
from pathlib import Path
|
|
|
|
from techviz.layout import build_layout
|
|
from techviz.renderers.svg import render_svg
|
|
from techviz.spec import VizSpec
|
|
from techviz.validate import has_errors, validate_spec
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
|
|
|
|
class CompositionTests(unittest.TestCase):
|
|
def setUp(self) -> None:
|
|
self.spec_data = json.loads((ROOT / "examples/work/payment/spec.json").read_text(encoding="utf-8"))
|
|
self.context = json.loads((ROOT / "examples/work/payment/context.json").read_text(encoding="utf-8"))
|
|
|
|
def test_disconnected_cards_are_rejected(self) -> None:
|
|
data = deepcopy(self.spec_data)
|
|
data["nodes"] = data["nodes"][:3]
|
|
data["edges"] = []
|
|
data["composition"] = {
|
|
"profile": "component-flow",
|
|
"diagram_only": True,
|
|
"reference_ids": ["payment-event-flow"],
|
|
"rationale": "Synthetic disconnected case.",
|
|
}
|
|
issues = validate_spec(VizSpec.from_dict(data), self.context)
|
|
self.assertTrue(any(issue.code == "missing-central-relation" for issue in issues), issues)
|
|
|
|
def test_comparison_requires_comparable_details(self) -> None:
|
|
data = deepcopy(self.spec_data)
|
|
data["type"] = "concept"
|
|
data["nodes"] = data["nodes"][:2]
|
|
data["edges"] = []
|
|
data["composition"] = {
|
|
"profile": "comparison",
|
|
"diagram_only": True,
|
|
"reference_ids": ["contract-comparison"],
|
|
"rationale": "Compare two explicit contracts.",
|
|
}
|
|
for node in data["nodes"]:
|
|
node["role"] = "contract"
|
|
node["details"] = ["responsibility", "lifecycle"]
|
|
issues = validate_spec(VizSpec.from_dict(data))
|
|
self.assertFalse(has_errors(issues), issues)
|
|
|
|
del data["nodes"][0]["details"]
|
|
issues = validate_spec(VizSpec.from_dict(data))
|
|
self.assertTrue(any(issue.code == "comparison-missing-details" for issue in issues), issues)
|
|
|
|
def test_svg_is_diagram_only(self) -> None:
|
|
spec = VizSpec.from_dict(self.spec_data)
|
|
svg = render_svg(spec, build_layout(spec))
|
|
self.assertNotIn('class="diagram-title"', svg)
|
|
self.assertNotIn('class="diagram-question"', svg)
|
|
self.assertNotIn('class="footer"', svg)
|
|
self.assertNotIn("Generated from grounded VizSpec", svg)
|
|
self.assertNotIn("<filter", svg)
|
|
self.assertNotIn("<linearGradient", svg)
|
|
self.assertIn("<title id=", svg)
|
|
self.assertIn("<desc id=", svg)
|
|
|
|
def test_reference_profile_mismatch_is_rejected(self) -> None:
|
|
data = deepcopy(self.spec_data)
|
|
data["composition"]["profile"] = "ports-adapters"
|
|
data["composition"]["reference_ids"] = ["payment-event-flow"]
|
|
issues = validate_spec(VizSpec.from_dict(data), self.context)
|
|
self.assertTrue(any(issue.code == "reference-profile-mismatch" for issue in issues), issues)
|
|
|
|
def test_context_rejects_unselected_profile(self) -> None:
|
|
data = deepcopy(self.spec_data)
|
|
data["composition"]["profile"] = "ports-adapters"
|
|
data["composition"]["reference_ids"] = ["order-ports-adapters"]
|
|
issues = validate_spec(VizSpec.from_dict(data), self.context)
|
|
self.assertTrue(any(issue.code == "profile-not-supported-by-context" for issue in issues), issues)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|