|
9 | 9 | """ |
10 | 10 |
|
11 | 11 | import importlib.util |
| 12 | +import re |
12 | 13 | import subprocess |
13 | 14 | import sys |
14 | 15 | from pathlib import Path |
|
17 | 18 |
|
18 | 19 | # Path to the snippets directory |
19 | 20 | SNIPPETS_DIR = Path(__file__).parent.parent / "source" / "_snippets" |
| 21 | +# Path to the docs source directory |
| 22 | +SOURCE_DIR = Path(__file__).parent.parent / "source" |
| 23 | +# Path to the repository root |
| 24 | +REPO_ROOT = Path(__file__).parent.parent.parent |
20 | 25 |
|
21 | 26 |
|
22 | 27 | def get_testable_snippet_files(): |
@@ -160,3 +165,66 @@ def test_snippet_markers_are_balanced(): |
160 | 165 | pytest.fail( |
161 | 166 | f"Snippet {snippet_file.name} has unmatched end marker: {marker}" |
162 | 167 | ) |
| 168 | + |
| 169 | + |
| 170 | +def get_rst_files(): |
| 171 | + """Collect all RST files in the source directory. |
| 172 | +
|
| 173 | + Returns |
| 174 | + ------- |
| 175 | + list[Path] |
| 176 | + List of paths to all RST files. |
| 177 | + """ |
| 178 | + return sorted(SOURCE_DIR.rglob("*.rst")) |
| 179 | + |
| 180 | + |
| 181 | +def extract_github_file_links(content: str) -> list[tuple[str, str]]: |
| 182 | + """Extract GitHub file links from RST content. |
| 183 | +
|
| 184 | + Finds links of the form: |
| 185 | + https://github.com/SimonBlanke/Hyperactive/blob/master/path/to/file.py |
| 186 | +
|
| 187 | + Parameters |
| 188 | + ---------- |
| 189 | + content : str |
| 190 | + RST file content. |
| 191 | +
|
| 192 | + Returns |
| 193 | + ------- |
| 194 | + list[tuple[str, str]] |
| 195 | + List of (full_url, relative_path) tuples. |
| 196 | + """ |
| 197 | + # Pattern matches GitHub blob URLs to this repo |
| 198 | + pattern = ( |
| 199 | + r"https://github\.com/SimonBlanke/Hyperactive/blob/master/([^\s>`\"\']+)" |
| 200 | + ) |
| 201 | + matches = re.findall(pattern, content) |
| 202 | + return [ |
| 203 | + (f"https://github.com/SimonBlanke/Hyperactive/blob/master/{path}", path) |
| 204 | + for path in matches |
| 205 | + ] |
| 206 | + |
| 207 | + |
| 208 | +def test_github_example_links_exist(): |
| 209 | + """Test that all GitHub example links in RST files point to existing files. |
| 210 | +
|
| 211 | + This verifies that documentation links to example files are not broken. |
| 212 | + Only checks links to files within this repository. |
| 213 | + """ |
| 214 | + broken_links = [] |
| 215 | + |
| 216 | + for rst_file in get_rst_files(): |
| 217 | + content = rst_file.read_text() |
| 218 | + links = extract_github_file_links(content) |
| 219 | + |
| 220 | + for full_url, rel_path in links: |
| 221 | + local_path = REPO_ROOT / rel_path |
| 222 | + if not local_path.exists(): |
| 223 | + broken_links.append( |
| 224 | + f"{rst_file.name}: {rel_path} (file not found)" |
| 225 | + ) |
| 226 | + |
| 227 | + if broken_links: |
| 228 | + msg = f"Found {len(broken_links)} broken GitHub file link(s):\n" |
| 229 | + msg += "\n".join(f" - {link}" for link in broken_links) |
| 230 | + pytest.fail(msg) |
0 commit comments