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

1import re as py_re 

2import typing 

3 

4import pytest 

5 

6from archdocs.main import SettingsForArchdocs 

7from tests import diagram_parts, diagram_rendering 

8 

9 

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_]+)\{"') 

20 

21 

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) 

25 

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) 

44 

45 

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) 

49 

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