Repository navigation
Expand file tree
/
Copy pathtest_doc_links.py
More file actions
182 lines (153 loc) · 6.64 KB
/
Copy pathtest_doc_links.py
File metadata and controls
182 lines (153 loc) · 6.64 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
from __future__ import annotations
from pathlib import Path
import subprocess
import tempfile
import unittest
REPO = Path(__file__).resolve().parents[1]
CHECKER = REPO / "tools" / "check-doc-links"
class DocumentationLinkTests(unittest.TestCase):
def test_claude_statusline_preservation_and_ledgers_are_documented(self) -> None:
changelog = (REPO / "CHANGELOG.md").read_text(encoding="utf-8")
updates = (REPO / "docs/update-and-rollback.md").read_text(encoding="utf-8")
uninstall = (REPO / "docs/uninstall.md").read_text(encoding="utf-8")
for text in (changelog, updates):
self.assertIn("--force", text)
self.assertIn("claude-statusline-backups.json", text)
self.assertIn("claude-integration.json", text)
self.assertIn("claude-statusline-backups.json", uninstall)
self.assertIn("claude-integration.json", uninstall)
self.assertIn("restores", uninstall)
self.assertIn("session-kit-quota", uninstall)
def test_repository_local_links_resolve(self) -> None:
result = subprocess.run(
[str(CHECKER), str(REPO)],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertIn("documentation link check passed", result.stdout)
def test_a_fragment_naming_no_heading_is_rejected(self) -> None:
"""A renamed heading turns every link to it into a link to nowhere."""
with tempfile.TemporaryDirectory(
prefix="session-kit-doc-anchors.",
dir=REPO.parent,
) as temporary:
root = Path(temporary)
(root / "guide.md").write_text("# Real Heading\n", encoding="utf-8")
(root / "README.md").write_text(
"# Top\n"
"[good](guide.md#real-heading)\n"
"[gone](guide.md#renamed-heading)\n"
"[here](#top)\n"
"[nowhere](#never-existed)\n",
encoding="utf-8",
)
result = subprocess.run(
[str(CHECKER), str(root)],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("no such heading: guide.md#renamed-heading", result.stdout)
self.assertIn("no such heading: #never-existed", result.stdout)
self.assertNotIn("real-heading", result.stdout)
self.assertNotIn("#top", result.stdout)
def test_a_heading_inside_a_fence_is_not_a_heading(self) -> None:
"""Otherwise a shell comment in an example invents anchors."""
with tempfile.TemporaryDirectory(
prefix="session-kit-doc-fences.",
dir=REPO.parent,
) as temporary:
root = Path(temporary)
(root / "README.md").write_text(
"# Top\n```bash\n# Not A Heading\n```\n[bad](#not-a-heading)\n",
encoding="utf-8",
)
result = subprocess.run(
[str(CHECKER), str(root)],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("no such heading: #not-a-heading", result.stdout)
def test_repeated_headings_get_numbered_anchors(self) -> None:
"""GitHub suffixes the second one; a checker that does not would lie."""
with tempfile.TemporaryDirectory(
prefix="session-kit-doc-repeats.",
dir=REPO.parent,
) as temporary:
root = Path(temporary)
(root / "README.md").write_text(
"# Notes\n## Detail\n## Detail\n"
"[first](#detail)\n[second](#detail-1)\n",
encoding="utf-8",
)
result = subprocess.run(
[str(CHECKER), str(root)],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
def test_html_navigation_and_images_are_checked(self) -> None:
"""The README's nav is <a href>, and every picture on it is <img src>."""
with tempfile.TemporaryDirectory(
prefix="session-kit-doc-html.",
dir=REPO.parent,
) as temporary:
root = Path(temporary)
(root / "shot.png").write_bytes(b"\x89PNG\r\n")
(root / "README.md").write_text(
"# Top\n"
'<p><a href="#top">here</a> · <a href="#gone">nowhere</a></p>\n'
'<img src="shot.png" alt="">\n'
'<img src="missing.png" alt="">\n'
'<img src="https://example.com/x.png" alt="">\n'
'<img src="data:image/svg+xml;base64,AAAA" alt="">\n',
encoding="utf-8",
)
result = subprocess.run(
[str(CHECKER), str(root)],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("no such heading: #gone", result.stdout)
self.assertIn("missing target: missing.png", result.stdout)
self.assertNotIn("shot.png", result.stdout)
self.assertNotIn("example.com", result.stdout)
self.assertNotIn("data:image", result.stdout)
def test_missing_and_escaping_links_are_rejected(self) -> None:
with tempfile.TemporaryDirectory(
prefix="session-kit-doc-links.",
dir=REPO.parent,
) as temporary:
root = Path(temporary)
(root / "README.md").write_text(
"[missing](docs/missing.md)\n"
"[escape](../outside.md)\n"
"[external](https://example.com/)\n",
encoding="utf-8",
)
result = subprocess.run(
[str(CHECKER), str(root)],
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
self.assertNotEqual(result.returncode, 0)
self.assertIn("missing target", result.stdout)
self.assertIn("link escapes repository", result.stdout)
self.assertNotIn("example.com", result.stdout)
if __name__ == "__main__":
unittest.main()