Repository navigation
Expand file tree
/
Copy pathtest_lab_content.py
More file actions
74 lines (63 loc) · 2.91 KB
/
Copy pathtest_lab_content.py
File metadata and controls
74 lines (63 loc) · 2.91 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
"""Every lab file must have the required pedagogy sections."""
from __future__ import annotations
import re
from conftest import SECTION_PATTERNS, lab_path, load_course_spec
def test_all_labs_use_template_sections():
spec = load_course_spec()
for phase in spec["phases"]:
for lab in phase["labs"]:
p = lab_path(phase["id"], lab["id"])
text = p.read_text(encoding="utf-8")
for section, pattern in SECTION_PATTERNS.items():
assert pattern.search(text), (
f"{phase['id']}/{lab['id']} missing section: {section}"
)
# Header shape from AGENTS.md § "Callout conventions — Gotchas":
# Inline: > ⚠️ **Gotcha:** ...
# Cross-cutting: > ⚠️ **Gotcha — <error phrase>.** ... (must link to TROUBLESHOOTING.md#anchor)
_GOTCHA_INLINE = re.compile(r"^\s*>\s*⚠️\s*\*\*Gotcha:\*\*")
_GOTCHA_CROSSCUT = re.compile(r"^\s*>\s*⚠️\s*\*\*Gotcha\s+—")
_TROUBLESHOOTING_LINK = re.compile(r"TROUBLESHOOTING\.md#")
def _iter_blockquotes(text: str):
"""Yield (start_line, [line, ...]) for each contiguous `>`-prefixed block."""
lines = text.splitlines()
i, n = 0, len(lines)
while i < n:
if re.match(r"^\s*>", lines[i]):
start = i
block = []
while i < n and re.match(r"^\s*>", lines[i]):
block.append(lines[i])
i += 1
yield start, block
else:
i += 1
def test_gotcha_callouts_follow_convention():
"""Enforce the two-variant Gotcha rule from AGENTS.md.
- Inline (`> ⚠️ **Gotcha:**`): must be ≤ 4 lines of blockquote.
- Cross-cutting (`> ⚠️ **Gotcha — ...**`): must link to
`../TROUBLESHOOTING.md#<anchor>` inside the same blockquote.
"""
spec = load_course_spec()
violations: list[str] = []
for phase in spec["phases"]:
for lab in phase["labs"]:
p = lab_path(phase["id"], lab["id"])
text = p.read_text(encoding="utf-8")
rel = f"{phase['id']}/{lab['id']}.md"
for start, block in _iter_blockquotes(text):
header = block[0]
if _GOTCHA_CROSSCUT.search(header):
body = "\n".join(block)
if not _TROUBLESHOOTING_LINK.search(body):
violations.append(
f"{rel}:{start + 1} cross-cutting Gotcha missing "
f"TROUBLESHOOTING.md link"
)
elif _GOTCHA_INLINE.search(header) and len(block) > 4:
violations.append(
f"{rel}:{start + 1} inline Gotcha exceeds 4 lines "
f"({len(block)} lines) — split it out to "
f"labs/TROUBLESHOOTING.md and link instead"
)
assert not violations, "gotcha convention violations:\n " + "\n ".join(violations)