aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/tests/test_readme_links.py
blob: 8e997a7320bc0f6c5dccf790a4e1ce6b14e4fead (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
"""README.md relative links must stay inside the repo and point to existing files."""
import re
import unittest
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent
LINK = re.compile(r'!?\[[^\]]*\]\(([^)\s]+)(?:\s+"[^"]*")?\)')


def bad_links(text, root=ROOT):
    bad = []
    for target in LINK.findall(text):
        if re.match(r'^([a-zA-Z][a-zA-Z0-9+.-]*:|#|//)', target):
            continue
        path = target.split('#')[0].split('?')[0]
        if not path:
            continue
        p = (root / path).resolve()
        if root.resolve() not in (p, *p.parents):
            bad.append((target, 'escapes repo'))
        elif not p.exists():
            bad.append((target, 'missing'))
    return bad


class ReadmeLinks(unittest.TestCase):
    def test_current_readme(self):
        self.assertEqual(bad_links((ROOT / 'README.md').read_text()), [])

    def test_detects_escape(self):
        self.assertEqual(bad_links('[v](../worldmap-viewer)'),
                         [('../worldmap-viewer', 'escapes repo')])

    def test_detects_missing(self):
        self.assertEqual(bad_links('![i](nope/none.png)'),
                         [('nope/none.png', 'missing')])


if __name__ == '__main__':
    unittest.main()