forked from ChelseaKR/ceqa-preflight
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_check_rule_sources.py
More file actions
136 lines (105 loc) · 5.25 KB
/
Copy pathtest_check_rule_sources.py
File metadata and controls
136 lines (105 loc) · 5.25 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
"""Tests for scripts/check_rule_sources.py.
No test in this file makes a real network call: `fetch` is always a fake injected in place of
the script's real `urllib`-backed default, per the script's own no-hidden-network-path rule.
"""
from __future__ import annotations
import sys
import unittest
import urllib.error
from pathlib import Path
from unittest import mock
SCRIPTS = str(Path(__file__).resolve().parent.parent / "scripts")
if SCRIPTS not in sys.path:
sys.path.insert(0, SCRIPTS)
import check_rule_sources # type: ignore[import-not-found] # noqa: E402
from check_rule_sources import UrlCheck # type: ignore[import-not-found] # noqa: E402
def _run(fetch: check_rule_sources.Fetcher, argv: list[str] | None = None) -> int:
return check_rule_sources.main(argv or [], fetch=fetch)
class RulesByUrlTests(unittest.TestCase):
def test_built_in_catalog_has_source_urls(self) -> None:
by_url = check_rule_sources._rules_by_url()
self.assertGreater(len(by_url), 0)
for url, rule_ids in by_url.items():
self.assertTrue(url.startswith("https://"))
self.assertGreater(len(rule_ids), 0)
def test_every_rule_is_attributed_to_a_url(self) -> None:
by_url = check_rule_sources._rules_by_url()
catalog = check_rule_sources.default_catalog()
attributed = sum(len(rule_ids) for rule_ids in by_url.values())
self.assertEqual(attributed, len(catalog.rules))
class MainVerdictTests(unittest.TestCase):
def test_all_urls_ok_passes(self) -> None:
self.assertEqual(_run(lambda url, timeout: UrlCheck(True, "200 HEAD")), 0)
def test_any_broken_url_fails(self) -> None:
self.assertEqual(_run(lambda url, timeout: UrlCheck(False, "HTTP 404 GET")), 1)
def test_reports_the_rule_ids_that_cite_a_broken_url(self) -> None:
seen: list[str] = []
def fetch(url: str, timeout: float) -> UrlCheck:
seen.append(url)
return UrlCheck(True, "200 HEAD")
self.assertEqual(_run(fetch), 0)
by_url = check_rule_sources._rules_by_url()
self.assertEqual(sorted(seen), sorted(by_url))
def test_timeout_flag_is_forwarded_to_fetch(self) -> None:
seen_timeouts: set[float] = set()
def fetch(url: str, timeout: float) -> UrlCheck:
seen_timeouts.add(timeout)
return UrlCheck(True, "200 HEAD")
self.assertEqual(_run(fetch, ["--timeout", "3"]), 0)
self.assertEqual(seen_timeouts, {3.0})
class DefaultFetchTests(unittest.TestCase):
"""Exercise the real `_fetch`'s branches with `urlopen` mocked — never real network I/O."""
def test_non_http_scheme_is_rejected_without_opening_a_connection(self) -> None:
with mock.patch.object(check_rule_sources.urllib.request, "urlopen") as urlopen:
result = check_rule_sources._fetch("file:///etc/passwd", timeout=1.0)
urlopen.assert_not_called()
self.assertFalse(result.ok)
self.assertEqual(result.detail, "unsupported URL scheme")
def test_connection_failure_is_reported_as_broken(self) -> None:
with mock.patch.object(
check_rule_sources.urllib.request,
"urlopen",
side_effect=urllib.error.URLError("mock: name resolution failed"),
):
result = check_rule_sources._fetch("https://example.invalid/", timeout=1.0)
self.assertFalse(result.ok)
self.assertIn("failed", result.detail)
def test_http_404_is_reported_as_broken(self) -> None:
with mock.patch.object(
check_rule_sources.urllib.request,
"urlopen",
side_effect=urllib.error.HTTPError("https://example.com/", 404, "Not Found", {}, None),
):
result = check_rule_sources._fetch("https://example.com/", timeout=1.0)
self.assertFalse(result.ok)
self.assertIn("404", result.detail)
def test_head_405_falls_back_to_a_successful_get(self) -> None:
response = mock.MagicMock()
response.status = 200
response.__enter__.return_value = response
response.__exit__.return_value = False
def fake_urlopen(request: object, timeout: float) -> mock.MagicMock:
method = request.get_method() # type: ignore[attr-defined]
if method == "HEAD":
raise urllib.error.HTTPError(
"https://example.com/", 405, "Method Not Allowed", {}, None
)
return response
with mock.patch.object(
check_rule_sources.urllib.request, "urlopen", side_effect=fake_urlopen
):
result = check_rule_sources._fetch("https://example.com/", timeout=1.0)
self.assertTrue(result.ok)
self.assertEqual(result.detail, "200 GET")
def test_both_methods_rejected_is_reported_as_broken(self) -> None:
def fake_urlopen(request: object, timeout: float) -> None:
raise urllib.error.HTTPError(
"https://example.com/", 405, "Method Not Allowed", {}, None
)
with mock.patch.object(
check_rule_sources.urllib.request, "urlopen", side_effect=fake_urlopen
):
result = check_rule_sources._fetch("https://example.com/", timeout=1.0)
self.assertFalse(result.ok)
if __name__ == "__main__":
unittest.main()