Coverage for tests/test_served_page.py: 100%

58 statements  

« prev     ^ index     » next       coverage.py v7.17.2, created at 2026-11-09 14:41 +0000

1import dataclasses 

2import typing 

3 

4import hypothesis 

5import pytest 

6from hypothesis import strategies as st 

7 

8from archdocs import settings 

9from tests import served_page 

10from tests.diagram_rendering import build_named_settings 

11 

12 

13# Minifying the template by joining on "" used to glue attributes onto their tags, turning the 

14# mermaid script tag into `<scriptsrc=...>`: the served document is the only proof there is. 

15_SERVICE_NAME: typing.Final = "page-svc" 

16# A quote in the name closes the node label early and takes the whole render down with it, so 

17# it may not reach the page at all — the markup characters around it have to survive escaped. 

18_MARKED_UP_SERVICE_NAME: typing.Final = 'svc<b>&"x' 

19# The template ships a demo diagram inside its `<pre>`: the page has to replace that slot, not 

20# grow a second one next to it. 

21_TEMPLATE_DEMO_MARK: typing.Final = "Example Start Node" 

22_RENDERER_ARGUMENT: typing.Final = "render_page" 

23_HYPOTHESIS_EXAMPLES: typing.Final = 20 

24_TEMPLATE_WITHOUT_A_SLOT: typing.Final = "<!DOCTYPE html><html lang='en'><body>no room here</body></html>" 

25_REQUIRED_PAGE_TAGS: typing.Final = ( 

26 "<!DOCTYPE html>", 

27 '<html lang="en">', 

28 '<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js">', 

29 '<link href="https://fonts.googleapis.com/css2?family=Inter:wght@600&amp;display=swap" rel="stylesheet" />', 

30 '<pre class="archdoc__diagram">', 

31 "graph TB", 

32) 

33_NAME_ALPHABET: typing.Final = st.characters(whitelist_categories=["Ll", "Lu"], whitelist_characters=["_", "-"]) 

34_NAME_STRATEGY: typing.Final = st.text(min_size=1, max_size=10, alphabet=_NAME_ALPHABET) 

35 

36 

37@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

38def test_default_route_serves_a_whole_document(render_page: served_page.PageRenderer) -> None: 

39 page_html: typing.Final = render_page(build_named_settings(_SERVICE_NAME), None) 

40 

41 for one_required_tag in _REQUIRED_PAGE_TAGS: 

42 assert page_html.count(one_required_tag) == 1, one_required_tag 

43 assert _SERVICE_NAME in page_html 

44 assert _TEMPLATE_DEMO_MARK not in page_html 

45 

46 

47# Without settings the scan starts from the working directory, which under pytest is the 

48# repository itself: the page has to name the default service and draw its node. 

49@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

50def test_omitted_settings_draw_the_default(render_page: served_page.PageRenderer) -> None: 

51 page_html: typing.Final = render_page(None, None) 

52 

53 assert 'example_service{"example-service' in page_html 

54 

55 

56# A template shipped without a `<pre>` slot has nowhere to put the diagram: the page still has 

57# to be a page. 

58@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

59def test_template_without_a_slot_is_served_whole( 

60 render_page: served_page.PageRenderer, 

61 monkeypatch: pytest.MonkeyPatch, 

62) -> None: 

63 monkeypatch.setattr(settings, "UI_HTML_TEMPLATE", _TEMPLATE_WITHOUT_A_SLOT) 

64 

65 page_html: typing.Final = render_page(build_named_settings(_SERVICE_NAME), None) 

66 

67 assert page_html == _TEMPLATE_WITHOUT_A_SLOT 

68 

69 

70@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

71def test_markup_characters_are_escaped(render_page: served_page.PageRenderer) -> None: 

72 page_html: typing.Final = render_page(build_named_settings(_MARKED_UP_SERVICE_NAME), None) 

73 

74 assert 'svc&lt;b>&amp;x"}' in page_html 

75 assert "svc<b>" not in page_html 

76 

77 

78@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

79@hypothesis.settings(deadline=None, max_examples=_HYPOTHESIS_EXAMPLES) 

80@hypothesis.given(service_name=_NAME_STRATEGY, route_value=_NAME_STRATEGY) 

81def test_any_service_name_and_route_are_served( 

82 render_page: served_page.PageRenderer, 

83 service_name: str, 

84 route_value: str, 

85) -> None: 

86 page_html: typing.Final = render_page(build_named_settings(service_name), f"/{route_value}") 

87 

88 assert service_name in page_html 

89 

90 

91# A network that cannot reach the CDNs serves mermaid itself and goes without the font: neither 

92# public host may be left on the page, or the browser still waits on it. 

93@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

94def test_offline_page_names_no_public_host(render_page: served_page.PageRenderer) -> None: 

95 offline_settings: typing.Final = dataclasses.replace( 

96 build_named_settings(_SERVICE_NAME), 

97 mermaid_script_url="/static/mermaid.min.js?v=11&lang=en", 

98 font_stylesheet_url=None, 

99 ) 

100 

101 page_html: typing.Final = render_page(offline_settings, None) 

102 

103 assert '<script src="/static/mermaid.min.js?v=11&amp;lang=en"></script>' in page_html 

104 assert "cdn.jsdelivr.net" not in page_html 

105 assert "fonts.googleapis.com" not in page_html 

106 assert _SERVICE_NAME in page_html 

107 

108 

109@pytest.mark.parametrize(_RENDERER_ARGUMENT, served_page.ALL_PAGE_RENDERERS, ids=served_page.FRAMEWORK_IDS) 

110def test_own_font_replaces_the_default(render_page: served_page.PageRenderer) -> None: 

111 own_font_settings: typing.Final = dataclasses.replace( 

112 build_named_settings(_SERVICE_NAME), 

113 font_stylesheet_url="/static/inter.css", 

114 ) 

115 

116 page_html: typing.Final = render_page(own_font_settings, None) 

117 

118 assert '<link href="/static/inter.css" rel="stylesheet" />' in page_html 

119 assert "fonts.googleapis.com" not in page_html