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(" 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()