[docs] 1# @Test suite for the ref_url_field configuration, TEST_NEED_ID_REFS_CONFIG_1, test, [IMPL_LNK_1]
2"""``ref_url_field``: ubCode's per-project key, accepted by both readers, and its gate."""
3
4from collections.abc import Callable
5from pathlib import Path
6
7import pytest
8from sphinx.testing.util import SphinxTestApp
9
10from sphinx_codelinks.config import (
11 CodeLinksConfig,
12 check_configuration,
13 generate_project_configs,
14 need_id_refs_field,
15)
16from sphinx_needs_testkit import assert_no_warnings
17
18_TOML_HEAD = "[codelinks]\nset_remote_url = true\n\n"
19_PROJECT = (
20 "[codelinks.projects.p]\n"
21 'remote_url_pattern = "https://example.com/{commit}/{path}#L{line}"\n'
22 "EXTRA"
23 "[codelinks.projects.p.source_discover]\n"
24 'src_dir = "./src"\n'
25)
26
27
28def _project(root: Path, *, toml: str | None = None, conf_extra: str = "") -> Path:
29 (root / "conf.py").write_text(
30 "extensions = ['sphinx_needs', 'sphinx_codelinks']\n"
31 "exclude_patterns = ['_build']\n" + conf_extra,
32 encoding="utf-8",
33 )
34 (root / "index.rst").write_text("T\n=\n", encoding="utf-8")
35 (root / "src").mkdir()
36 if toml is not None:
37 (root / "ubproject.toml").write_text(toml, encoding="utf-8")
38 return root
39
40
41def test_toml_accepts_ref_url_field(
42 tmp_path: Path, make_app: Callable[..., SphinxTestApp]
43) -> None:
44 """ubCode's ``ref_url_field`` in a shared ``ubproject.toml`` is no longer a hard
45 error (``Additional properties are not allowed ('ref_url_field' was unexpected)``)."""
46 _project(
47 tmp_path,
48 toml=_TOML_HEAD + _PROJECT.replace("EXTRA", 'ref_url_field = "impl_url"\n'),
49 )
50 app = make_app(srcdir=tmp_path, freshenv=True)
51 app.build()
52
53 assert_no_warnings(app)
54 assert app.config.src_trace_projects["p"]["ref_url_field"] == "impl_url"
55 assert "impl_url" in app.config.needs_string_links
56
57
58def test_conf_py_accepts_ref_url_field(
59 tmp_path: Path, make_app: Callable[..., SphinxTestApp]
60) -> None:
61 _project(
62 tmp_path,
63 conf_extra=(
64 "src_trace_config_from_toml = None\n"
65 "src_trace_set_remote_url = True\n"
66 "src_trace_projects = {'p': {\n"
67 " 'remote_url_pattern': 'https://example.com/{commit}/{path}#L{line}',\n"
68 " 'ref_url_field': 'impl_url',\n"
69 " 'source_discover': {'src_dir': './src'},\n"
70 "}}\n"
71 ),
72 )
73 app = make_app(srcdir=tmp_path, freshenv=True)
74 app.build()
75
76 assert_no_warnings(app)
77 assert "impl_url" in app.config.needs_string_links
78
79
80@pytest.mark.parametrize(
81 ("value", "error"),
82 [
83 pytest.param(5, "ref_url_field must be a string", id="not-a-string"),
84 pytest.param(
85 "remote-url",
86 "ref_url_field 'remote-url' must differ from local_url_field and "
87 "remote_url_field",
88 id="the-remote-url-field",
89 ),
90 ],
91)
92def test_invalid_ref_url_field_is_a_configuration_error(
93 value: object, error: str
94) -> None:
95 projects = {
96 "p": {
97 "remote_url_pattern": "https://example.com/{path}",
98 "ref_url_field": value,
99 "source_discover": {"src_dir": "./src"},
100 }
101 }
102 generate_project_configs(projects) # ty: ignore[invalid-argument-type]
103 config = CodeLinksConfig(set_remote_url=True, projects=projects) # ty: ignore[invalid-argument-type]
104
105 errors = check_configuration(config)
106 assert errors == ["Project 'p' has the following errors:", error]
107
108
109def _gate(
110 *,
111 set_remote_url: bool = True,
112 set_local_url: bool = False,
113 project: dict[str, object] | None = None,
114) -> str | None:
115 project_config: dict[str, object] = {
116 "remote_url_pattern": "https://example.com/{path}",
117 "source_discover": {"src_dir": "./src"},
118 **(project or {}),
119 }
120 projects = {"p": project_config}
121 generate_project_configs(projects) # ty: ignore[invalid-argument-type]
122 config = CodeLinksConfig(
123 set_remote_url=set_remote_url,
124 set_local_url=set_local_url,
125 projects=projects, # ty: ignore[invalid-argument-type]
126 )
127 return need_id_refs_field(config, config.projects["p"])
128
129
130@pytest.mark.parametrize(
131 ("kwargs", "expected"),
132 [
133 pytest.param({}, "code_url", id="default-field"),
134 pytest.param(
135 {"set_remote_url": False, "set_local_url": True},
136 "code_url",
137 id="local-urls-only",
138 ),
139 pytest.param({"set_remote_url": False}, None, id="no-urls"),
140 pytest.param({"project": {"ref_url_field": ""}}, None, id="empty-field"),
141 pytest.param(
142 {"project": {"ref_url_field": "test_url"}}, "test_url", id="named-field"
143 ),
144 pytest.param(
145 {"project": {"analyse": {"get_need_id_refs": False}}},
146 None,
147 id="refs-not-extracted",
148 ),
149 pytest.param(
150 {"project": {"analyse": {"need_id_refs": {"markers": []}}}},
151 None,
152 id="no-markers",
153 ),
154 ],
155)
156def test_attach_gate(kwargs: dict[str, object], expected: str | None) -> None:
157 """ubCode's gate: URLs on, references extracted, a marker, and a non-empty field."""
158 assert _gate(**kwargs) == expected # ty: ignore[invalid-argument-type]