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

1import re as py_re 

2import typing 

3 

4import pytest 

5 

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 

15 

16 

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

26 

27 

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) 

31 

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) 

50 

51 

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) 

55 

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