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