Coverage for tests/test_diagram_layout.py: 100%
42 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 types
2import typing
4import pytest
6from archdocs.main import SettingsForArchdocs
7from tests.diagram_parts import collect_edge_ends, collect_group_of_every_node, extract_edge_lines
8from tests.rendered_diagram import (
9 ALL_EXAMPLE_SETTINGS,
10 SETTINGS_ARGUMENT,
11 SHOWCASE_SETTINGS,
12 build_named_settings,
13 render_example_diagram,
14)
17# Mermaid has no coordinates: a diagram is laid out from the direction of its arrows and from
18# the order its lines are written in, so both are part of the contract.
19_INBOUND_GROUP: typing.Final = "Inbound API"
20_OUTBOUND_GROUP: typing.Final = "Outbound calls"
21_MESSAGING_GROUP: typing.Final = "Messaging & tasks"
22_DATA_STORES_GROUP: typing.Final = "Data stores"
23_CONFIGURATION_GROUP: typing.Final = "Configuration"
24_SHOWCASE_NODE_ID: typing.Final = "showcase_service"
25_EXPECTED_SHOWCASE_GROUPS: typing.Final = types.MappingProxyType(
26 {
27 "external_client": _INBOUND_GROUP,
28 "rabbit": _MESSAGING_GROUP,
29 "kafka": _MESSAGING_GROUP,
30 "nats": _MESSAGING_GROUP,
31 "redis": _MESSAGING_GROUP,
32 "TaskQueue_Worker": _MESSAGING_GROUP,
33 "External_API": _OUTBOUND_GROUP,
34 "PersistentVolume": _DATA_STORES_GROUP,
35 "redisdb": _DATA_STORES_GROUP,
36 "redisdb_cluster0": _DATA_STORES_GROUP,
37 "redisdb_cluster1": _DATA_STORES_GROUP,
38 "redisdb_cluster2": _DATA_STORES_GROUP,
39 "redisdb_sentinel0": _DATA_STORES_GROUP,
40 "redisdb_sentinel1": _DATA_STORES_GROUP,
41 "redisdb_sentinel2": _DATA_STORES_GROUP,
42 "postgresql_asyncpgdb": _DATA_STORES_GROUP,
43 "postgresql_psycopgdb0": _DATA_STORES_GROUP,
44 "postgresql_psycopgdb1": _DATA_STORES_GROUP,
45 "postgresql_psycopgdb2": _DATA_STORES_GROUP,
46 "sqlite_aiosqlitedb": _DATA_STORES_GROUP,
47 "ConfigMap_app_config": _CONFIGURATION_GROUP,
48 "Secret_app_secrets": _CONFIGURATION_GROUP,
49 "ConfigMap_app_tuning": _CONFIGURATION_GROUP,
50 },
51)
52_PLACEMENT_ORDER_OF_MARKS: typing.Final = (
53 'subgraph group_configuration["Configuration"]',
54 'subgraph service_row[" "]',
55 "direction LR",
56 'subgraph group_inbound_api["Inbound API"]',
57 'external_client["User/Client"]',
58 'showcase_service{"',
59 'subgraph group_messaging_and_tasks["Messaging & tasks"]',
60 'subgraph group_outbound_calls["Outbound calls"]',
61 "style service_row fill:none,stroke:none",
62 'subgraph group_data_stores["Data stores"]',
63)
64_ALL_SERVICE_NAMES: typing.Final = (
65 ("payments-api", "payments_api"),
66 ("svc.v2", "svc_v2"),
67 ("Billing Service", "Billing_Service"),
68 ("!!!", "archdocs_service"),
69)
72@pytest.mark.parametrize(SETTINGS_ARGUMENT, ALL_EXAMPLE_SETTINGS)
73def test_arrows_agree_with_their_side(arch_settings: SettingsForArchdocs) -> None:
74 rendered_diagram: typing.Final = render_example_diagram(arch_settings)
76 group_of_node: typing.Final = collect_group_of_every_node(rendered_diagram)
77 all_edge_ends: typing.Final = collect_edge_ends(rendered_diagram)
78 all_target_groups: typing.Final = {group_of_node.get(one_ends.target_id) for one_ends in all_edge_ends}
79 all_source_groups: typing.Final = {group_of_node.get(one_ends.source_id) for one_ends in all_edge_ends}
80 assert all_edge_ends
81 assert _INBOUND_GROUP not in all_target_groups
82 assert _OUTBOUND_GROUP not in all_source_groups
85def test_showcase_nodes_sit_in_their_groups() -> None:
86 rendered_diagram: typing.Final = render_example_diagram(SHOWCASE_SETTINGS)
88 assert collect_group_of_every_node(rendered_diagram) == _EXPECTED_SHOWCASE_GROUPS
89 assert all(_SHOWCASE_NODE_ID in one_edge_line for one_edge_line in extract_edge_lines(rendered_diagram))
92def test_groups_sit_around_the_service() -> None:
93 all_lines: typing.Final = [one_line.strip() for one_line in render_example_diagram(SHOWCASE_SETTINGS).split("\n")]
95 all_positions: typing.Final = [
96 next(line_index for line_index, one_line in enumerate(all_lines) if one_line.startswith(one_mark))
97 for one_mark in _PLACEMENT_ORDER_OF_MARKS
98 ]
99 assert all_positions == sorted(all_positions)
102@pytest.mark.parametrize(("service_name", "expected_node_id"), _ALL_SERVICE_NAMES)
103def test_service_name_becomes_the_node_id(service_name: str, expected_node_id: str) -> None:
104 rendered_diagram: typing.Final = render_example_diagram(build_named_settings(service_name))
106 assert f'{expected_node_id}{{"{service_name}"}}' in rendered_diagram
107 assert all(expected_node_id in one_edge_line for one_edge_line in extract_edge_lines(rendered_diagram))
110def test_credentials_never_reach_the_diagram() -> None:
111 rendered_diagram: typing.Final = render_example_diagram(build_named_settings("secretive-svc"))
113 assert "user:password" not in rendered_diagram
114 assert "://***@" in rendered_diagram