Coverage for tests/test_mermaid_syntax.py: 100%
37 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 22:03 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-18 22:03 +0000
1import re as py_re
2import typing
4import pytest
6from archdocs.main import SettingsForArchdocs
7from tests import diagram_parts, diagram_rendering
10# Every rule here breaks the render silently: the page stays hidden until `postRenderCallback`
11# that never fires.
12_GROUP_OPENING_MARK: typing.Final = "subgraph "
13_GROUP_CLOSING_LINE: typing.Final = "end"
14_LINE_INDENT: typing.Final = " " * 4
15# An edge is a bare node id on each end of the arrow and a label either absent or quoted whole:
16# a quote or a pipe inside the label closes it early and the whole diagram stops rendering.
17_WELL_FORMED_EDGE_PATTERN: typing.Final = py_re.compile(r'[A-Za-z0-9_]+ --> (?:\|"[^"|]+"\| )?[A-Za-z0-9_]+')
18_ID_LESS_NODE_PATTERN: typing.Final = py_re.compile(r"(?m)^\s*\{")
19_SERVICE_NODE_PATTERN: typing.Final = py_re.compile(r'(?m)^\s*(?P<node_id>[A-Za-z0-9_]+)\{"')
22@pytest.mark.parametrize(diagram_rendering.SETTINGS_ARGUMENT, diagram_rendering.ALL_EXAMPLE_SETTINGS)
23def test_every_line_is_valid_mermaid(arch_settings: SettingsForArchdocs) -> None:
24 rendered_diagram: typing.Final = diagram_rendering.render_example_diagram(arch_settings)
26 all_lines: typing.Final = rendered_diagram.split("\n")
27 all_content_lines: typing.Final = [one_line for one_line in all_lines if one_line.strip() != _GROUP_CLOSING_LINE]
28 all_group_openings: typing.Final = [
29 one_line.strip() for one_line in all_lines if one_line.strip().startswith(_GROUP_OPENING_MARK)
30 ]
31 all_broken_edges: typing.Final = [
32 one_line
33 for one_line in diagram_parts.extract_edge_lines(rendered_diagram)
34 if not _WELL_FORMED_EDGE_PATTERN.fullmatch(one_line)
35 ]
36 assert not _ID_LESS_NODE_PATTERN.search(rendered_diagram)
37 assert "<--" not in rendered_diagram
38 assert all(one_line.startswith(_LINE_INDENT) for one_line in all_lines)
39 assert all(one_line.count(diagram_parts.EDGE_ARROW) <= 1 for one_line in all_lines)
40 assert all_broken_edges == []
41 assert len(all_content_lines) == len(set(all_content_lines))
42 assert len(all_group_openings) == len(set(all_group_openings))
43 assert len(all_group_openings) == len(all_lines) - len(all_content_lines)
46@pytest.mark.parametrize(diagram_rendering.SETTINGS_ARGUMENT, diagram_rendering.ALL_EXAMPLE_SETTINGS)
47def test_nodes_are_defined_once_and_grouped(arch_settings: SettingsForArchdocs) -> None:
48 rendered_diagram: typing.Final = diagram_rendering.render_example_diagram(arch_settings)
50 all_defined_ids: typing.Final = diagram_parts.collect_defined_node_ids(rendered_diagram)
51 all_service_ids: typing.Final = {
52 one_match.group("node_id") for one_match in _SERVICE_NODE_PATTERN.finditer(rendered_diagram)
53 }
54 all_drawn_ids: typing.Final = {
55 one_node_id
56 for one_edge_ends in diagram_parts.collect_edge_ends(rendered_diagram)
57 for one_node_id in one_edge_ends
58 }
59 assert len(all_service_ids) == 1
60 assert f'{{"{arch_settings.service_name}' in rendered_diagram
61 assert len(all_defined_ids) == len(set(all_defined_ids))
62 assert set(all_defined_ids) == set(diagram_parts.collect_group_of_every_node(rendered_diagram))
63 assert all_drawn_ids <= set(all_defined_ids) | all_service_ids