aboutsummaryrefslogtreecommitdiff
path: root/tests/discipline/anchors.test.py
blob: 0e5e8d7d38cccf7159353d7d5e7061047df86c12 (plain)
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
"""Deprecation-URL anchor contract.

A deprecation warning points users at ``docs/upgrading/...#anchor``. Shape-checking
the URL alone passes even when the anchor 404s. This test extracts every anchor
referenced by a deprecation stub, parses the upgrade guides for their actual
``.. _label:`` targets, and asserts each referenced anchor exists.

Edify 1.0 ships stub-free, so the deprecation-URL registry is empty today; the
test still verifies that every anchor the upgrade guides *declare* is well-formed
and unique, so the contract is enforced the moment the first stub lands.
"""

import re
from pathlib import Path

_REPO_ROOT = Path(__file__).parent.parent.parent
_UPGRADING_DIR = _REPO_ROOT / "docs" / "upgrading"
_ANCHOR_PATTERN = re.compile(r"^\.\.\s+_([a-zA-Z0-9][a-zA-Z0-9\-]*):\s*$")

# Anchors referenced by deprecation-warning URLs. Each entry is (guide-stem, anchor).
# Empty in 1.0 (stub-free); every future deprecation stub adds its (guide, anchor) row.
_DEPRECATION_URL_ANCHORS: frozenset[tuple[str, str]] = frozenset()


def _declared_anchors(rst_path: Path) -> list[str]:
    anchors: list[str] = []
    for line in rst_path.read_text().splitlines():
        matched = _ANCHOR_PATTERN.match(line)
        if matched is not None:
            anchors.append(matched.group(1))
    return anchors


def _anchors_by_guide() -> dict[str, list[str]]:
    result: dict[str, list[str]] = {}
    for rst_path in sorted(_UPGRADING_DIR.glob("*.rst")):
        result[rst_path.stem] = _declared_anchors(rst_path)
    return result


def test_upgrade_guide_anchors_are_unique_within_each_guide():
    for guide_stem, anchors in _anchors_by_guide().items():
        assert len(anchors) == len(set(anchors)), (
            f"duplicate anchor(s) in docs/upgrading/{guide_stem}.rst: "
            f"{sorted({a for a in anchors if anchors.count(a) > 1})}"
        )


def test_every_deprecation_url_anchor_exists_in_its_guide():
    anchors_by_guide = _anchors_by_guide()
    for guide_stem, anchor in _DEPRECATION_URL_ANCHORS:
        declared = anchors_by_guide.get(guide_stem, [])
        assert anchor in declared, (
            f"deprecation URL references #{anchor} in docs/upgrading/{guide_stem}.rst, "
            f"but no `.. _{anchor}:` target exists there."
        )


def test_the_0_3_to_1_0_guide_declares_the_license_anchor():
    anchors = _anchors_by_guide()["0.3-to-1.0"]
    assert "license-mit" in anchors


def test_the_0_3_to_1_0_guide_declares_the_python_floor_anchor():
    anchors = _anchors_by_guide()["0.3-to-1.0"]
    assert "python-floor" in anchors