forked from ChelseaKR/disclosed
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_site_origin.py
More file actions
276 lines (229 loc) · 11.3 KB
/
Copy pathtest_site_origin.py
File metadata and controls
276 lines (229 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
"""The guard that decides whether the published site may name the host it names.
``.github/scripts/check_site_origin.py`` is the only executable in this repository that was held
to none of its standards. It is not under ``src``, so ``ruff check src tests`` never linted it,
``mypy`` with ``files = ["src"]`` never typed it, ``--cov=disclosed`` never measured a line of
it, and no test ever ran it. The one thing standing between the published site and issue #2 --
616 canonical links naming a host that served a 404 -- was the least-checked file here.
That is the coverage-gate failure in its purest form: not a module scoring badly, but a module
outside the denominator, so the 98% the coverage report prints was 98% of the code it looked at.
Each test below runs the real script over a real rendered site and asserts a specific way it
must refuse. The three checks it makes can break independently -- a page can self-canonicalise
elsewhere while the sitemap is fine, a sitemap can list a page that was never built -- so they
are broken independently here, one at a time, against a site that is otherwise correct.
"""
from __future__ import annotations
import importlib.util
import re
import sys
from pathlib import Path
from types import ModuleType
from typing import Any
import pytest
from disclosed import site
_ROOT = Path(__file__).resolve().parent.parent
_SCRIPT = _ROOT / ".github" / "scripts" / "check_site_origin.py"
_ORIGIN = "https://example.test/disclosed"
def _load() -> ModuleType:
"""Import the checker from its path, since it is a script rather than a package module."""
assert _SCRIPT.is_file(), (
f"{_SCRIPT} is gone. It is the only thing comparing the origin stamped into every "
"canonical link against the origin Pages actually serves; issue #2 was exactly that "
"comparison not existing."
)
spec = importlib.util.spec_from_file_location("check_site_origin", _SCRIPT)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
check = _load()
_REPORT: dict[str, Any] = {
"scope": {
"kind": "sample",
"source": "College Scorecard",
"institutions": 1,
"states": 1,
"universe": 6300,
"coverage": 1 / 6300,
"note": "A slice.",
},
"institutions": 1,
"ungradeable": 0,
"overall": {
"label": "all institutions",
"graded": 1,
"ungradeable": 0,
"mean_score": 1.0,
"worst_fields": [],
},
"by_state": [
{"label": "CA", "graded": 1, "ungradeable": 0, "mean_score": 1.0, "worst_fields": []}
],
"implausible": [],
"grades": [
{
"unit_id": "1",
"name": "A College",
"state": "CA",
"score": 1.0,
"letter": "A",
"fields": {"Admission rate": "reported"},
}
],
}
@pytest.fixture
def built(tmp_path: Path) -> Path:
"""A real site rendered by the real generator, at the origin the checker will be given.
Rendered rather than hand-written, because the property under test is that the renderer and
the checker agree about the shape of a URL. A fixture of hand-typed HTML would agree with
whichever of the two it was copied from and prove nothing about the other.
"""
out = tmp_path / "site"
site.build(_REPORT, out, origin=_ORIGIN, generated="2026-08-05")
return out
def _run(site_dir: Path, base: str = _ORIGIN) -> int:
result: int = check.main(["check_site_origin.py", str(site_dir), base])
return result
class TestASiteThatAgreesWithItsDeployTarget:
def test_a_correctly_rendered_site_passes(self, built: Path) -> None:
assert _run(built) == 0
def test_every_page_kind_is_actually_examined(self, built: Path) -> None:
"""The nested paths are the ones a URL-shape bug would hide in.
``state/CA`` and ``institution/1`` are two directories deep; the home page is zero. If
the checker only ever agreed with the renderer about the root, this would still pass, so
the page set is asserted rather than assumed.
"""
pages = sorted(p.parent.relative_to(built).as_posix() for p in built.rglob("index.html"))
assert pages == [".", "institution/1", "methodology", "state/CA"]
def test_a_trailing_slash_on_the_deploy_target_is_not_a_disagreement(self, built: Path) -> None:
"""The Pages API reports the base URL with a trailing slash on some repositories, and the
workflow strips it before rendering. A checker that did not would reject every page."""
assert _run(built, _ORIGIN + "/") == 0
class TestEachCheckCanFail:
"""One broken promise at a time, against a site that is otherwise correct."""
def test_a_page_that_self_canonicalises_elsewhere_is_refused(self, built: Path) -> None:
"""Issue #2 itself: a link telling crawlers to index somewhere that is not here."""
page = built / "institution" / "1" / "index.html"
page.write_text(
page.read_text(encoding="utf-8").replace(
f'<link rel="canonical" href="{_ORIGIN}/institution/1/"',
'<link rel="canonical" href="https://elsewhere.test/institution/1/"',
),
encoding="utf-8",
)
assert _run(built) == 1
def test_a_page_with_no_canonical_at_all_is_refused(self, built: Path) -> None:
page = built / "index.html"
page.write_text(
page.read_text(encoding="utf-8").replace('<link rel="canonical"', "<link rel='x'"),
encoding="utf-8",
)
assert _run(built) == 1
def test_a_sitemap_promising_a_page_that_was_never_built_is_refused(self, built: Path) -> None:
"""A sitemap is a promise the URLs in it exist. An entry with no file behind it is a 404
with an invitation attached."""
sitemap = built / "sitemap.xml"
sitemap.write_text(
sitemap.read_text(encoding="utf-8").replace(
"</urlset>", f"<url><loc>{_ORIGIN}/institution/999/</loc></url></urlset>"
),
encoding="utf-8",
)
assert _run(built) == 1
def test_a_sitemap_that_omits_a_built_page_is_refused(self, built: Path) -> None:
sitemap = built / "sitemap.xml"
sitemap.write_text(
sitemap.read_text(encoding="utf-8").replace(
f"<url><loc>{_ORIGIN}/institution/1/</loc></url>", ""
),
encoding="utf-8",
)
assert _run(built) == 1
def test_a_missing_sitemap_is_refused(self, built: Path) -> None:
(built / "sitemap.xml").unlink()
assert _run(built) == 1
def test_robots_advertising_another_origin_is_refused(self, built: Path) -> None:
robots = built / "robots.txt"
robots.write_text(
robots.read_text(encoding="utf-8").replace(_ORIGIN, "https://elsewhere.test"),
encoding="utf-8",
)
assert _run(built) == 1
def test_a_missing_robots_is_refused(self, built: Path) -> None:
(built / "robots.txt").unlink()
assert _run(built) == 1
class TestItRefusesRatherThanCertifyingNothing:
"""The three ways this script could have passed over nothing at all.
A checker that exits 0 because it found no pages, or because it was handed an empty origin
to compare against, is the failure the site it guards exists to describe. Each of these
exits 2 -- distinct from the 1 that means "checked and disagreed" -- so a reader of the CI
log can tell a refusal from a finding.
"""
def test_an_empty_deploy_target_is_refused_rather_than_matched(self, built: Path) -> None:
"""Every canonical would compare against ``""``, and a site rendered with an empty origin
would agree with it. Passing here would certify a site that self-canonicalises to ``/``."""
assert _run(built, "") == 2
assert _run(built, "/") == 2
def test_a_directory_with_no_pages_in_it_is_refused(self, tmp_path: Path) -> None:
"""The whole script is loops over the page list. Over an empty list they all pass."""
empty = tmp_path / "nothing"
empty.mkdir()
assert _run(empty) == 2
assert _run(tmp_path / "does-not-exist") == 2
def test_the_wrong_number_of_arguments_is_refused(self) -> None:
assert check.main(["check_site_origin.py"]) == 2
assert check.main(["check_site_origin.py", "site", "https://x.test", "extra"]) == 2
class TestTheReportIsReadable:
"""The failure message, which is the only part of this a person actually reads.
One wrong origin breaks all 616 pages identically, so the report has to be bounded or the
sitemap and robots failures -- the two that say something the first line does not -- are
pushed off the end of the log by 600 copies of one sentence. Bounded output is only honest
if it says it is bounded, so the count of what was dropped is asserted too.
"""
@pytest.fixture
def many(self, tmp_path: Path) -> Path:
"""A site with comfortably more disagreements than the report will print."""
out = tmp_path / "many"
site.build(
{
**_REPORT,
"grades": [
{**_REPORT["grades"][0], "unit_id": str(i), "name": f"College {i}"}
for i in range(check.MAX_REPORTED + 5)
],
},
out,
origin=_ORIGIN,
generated="2026-08-05",
)
return out
def _stderr(self, captured: pytest.CaptureFixture[str]) -> list[str]:
return captured.readouterr().err.splitlines()
def test_a_wrong_origin_reports_a_bounded_number_of_lines(
self, many: Path, capsys: pytest.CaptureFixture[str]
) -> None:
assert _run(many, "https://elsewhere.test") == 1
reported = self._stderr(capsys)
assert "https://elsewhere.test" in reported[0]
# One header, MAX_REPORTED problems, one line saying how many were not printed.
assert len(reported) == check.MAX_REPORTED + 2, reported
def test_more_problems_than_it_prints_are_counted_rather_than_dropped(
self, many: Path, capsys: pytest.CaptureFixture[str]
) -> None:
"""A truncated list that does not say it is truncated is a count nobody can check.
Matched as a whole line rather than by searching for the word. The first version of this
test looked for "more" anywhere in the last line and passed against a build with the
truncation notice deleted, because pytest's temporary directory is named after the test
and the test has the word "more" in its name. A gate that its own fixture satisfies is
the thing this file is about.
"""
assert _run(many, "https://elsewhere.test") == 1
assert re.fullmatch(r"\s*\.\.\. and \d+ more", self._stderr(capsys)[-1])
def test_a_report_small_enough_to_print_whole_is_not_truncated(
self, built: Path, capsys: pytest.CaptureFixture[str]
) -> None:
"""The other side of the bound: a four-page site must not claim anything was dropped."""
assert _run(built, "https://elsewhere.test") == 1
reported = self._stderr(capsys)
assert len(reported) < check.MAX_REPORTED + 2
assert not [line for line in reported if "more" in line.split()]