8FS-509 regression coverage for the lint rule registry and ``--explain`` flag.
12- Validate that every rule in ``_DEFAULT_RULES`` has a ``RuleInfo`` entry.
13- Validate ``get_rule_info()`` and ``list_rules()`` API surface.
14- Validate that ``--explain`` produces human-readable and JSON output.
15- Validate that ``--explain`` for an unknown rule returns exit code 1.
22 L3 — assertion details
27- Tests are read-only introspection of the rule registry; no files
31from __future__
import annotations
53 for rule_name
in _DEFAULT_RULES:
54 assert rule_name
in RULE_REGISTRY, (
55 f
"Rule '{rule_name}' is in _DEFAULT_RULES but missing from RULE_REGISTRY"
59 for rule_name
in RULE_REGISTRY:
60 assert rule_name
in _DEFAULT_RULES, (
61 f
"Rule '{rule_name}' is in RULE_REGISTRY but missing from _DEFAULT_RULES"
66 assert len(rules) == len(_DEFAULT_RULES)
69 for rule
in list_rules():
70 assert isinstance(rule, RuleInfo)
80 info = get_rule_info(
"header")
81 assert info
is not None
82 assert info.name ==
"header"
85 assert get_rule_info(
"nonexistent_rule")
is None
88 for name
in _DEFAULT_RULES:
89 info = get_rule_info(name)
90 assert info
is not None
91 assert info.name == name
100 @pytest.mark.parametrize("rule_name", list(_DEFAULT_RULES.keys()))
102 info = RULE_REGISTRY[rule_name]
103 assert info.label, f
"{rule_name}: label is empty"
104 assert info.rationale, f
"{rule_name}: rationale is empty"
105 assert info.correct_pattern, f
"{rule_name}: correct_pattern is empty"
106 assert info.common_mistake, f
"{rule_name}: common_mistake is empty"
107 assert info.spec_reference, f
"{rule_name}: spec_reference is empty"
109 @pytest.mark.parametrize("rule_name", list(_DEFAULT_RULES.keys()))
111 info = RULE_REGISTRY[rule_name]
112 assert len(info.profiles) >= 1, f
"{rule_name}: profiles is empty"
115 info = get_rule_info(
"header")
116 assert info
is not None
117 assert info.fixable
is True
120 for name
in (
"docstring",
"dbg_usage",
"tests_exist"):
121 info = get_rule_info(name)
122 assert info
is not None
123 assert info.fixable
is False
133 result = run_linter(tmp_path, [
"--explain",
"header"])
135 captured = capsys.readouterr()
136 assert "Rule: header" in captured.out
137 assert "Rationale:" in captured.out
138 assert "Correct pattern:" in captured.out
139 assert "Common mistake:" in captured.out
142 result = run_linter(tmp_path, [
"--explain",
"header",
"--json"])
144 captured = capsys.readouterr()
145 data = json.loads(captured.out)
146 assert data[
"name"] ==
"header"
147 assert "rationale" in data
148 assert "correct_pattern" in data
149 assert "common_mistake" in data
150 assert "spec_reference" in data
151 assert "fixable" in data
152 assert "profiles" in data
155 result = run_linter(tmp_path, [
"--explain",
"nonexistent"])
157 captured = capsys.readouterr()
158 assert "unknown rule" in captured.err
161 run_linter(tmp_path, [
"--explain",
"nonexistent"])
162 captured = capsys.readouterr()
163 assert "Available rules:" in captured.err
164 assert "header" in captured.err
test_explain_unknown_rule_lists_available(self, tmp_path, capsys)
test_explain_json_output(self, tmp_path, capsys)
test_explain_unknown_rule_returns_one(self, tmp_path, capsys)
test_explain_known_rule_returns_zero(self, tmp_path, capsys)
test_get_known_rule(self)
test_get_unknown_rule_returns_none(self)
test_get_rule_info_for_each_rule(self)
test_every_default_rule_has_registry_entry(self)
test_registry_has_no_extra_rules(self)
test_list_rules_returns_all_entries(self)
test_list_rules_returns_rule_info_instances(self)
test_all_string_fields_non_empty(self, str rule_name)
test_profiles_non_empty(self, str rule_name)
test_non_fixable_rules(self)
test_header_is_fixable(self)