File size: 16,238 Bytes
1931f90 8d155aa 1931f90 37102eb 1931f90 8d155aa 1931f90 37102eb 1931f90 8d155aa 1931f90 8d155aa 1931f90 37102eb f3a39c1 | 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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | """nexa validate: the gate, and every way it is supposed to close.
`cmd_validate` is the only thing standing between a broken rule and a corpus
that ships it. Everything downstream trusts it: `nexa build` runs it, the
Modelfiles are generated from rules that passed it, and the retrieval index
embeds them.
A validator whose failure modes are untested is a validator that can quietly
stop failing, and nothing else in the repository would notice -- a rule with a
pattern that matches nothing looks exactly like a rule with a pattern that
works. So there is one test per way the gate closes, each breaking exactly one
thing in an otherwise-valid rule.
The fixtures write a corpus directory of their own. Validating the real corpus
is what `nexa validate` does; what is under test here is the gate.
"""
import io
import json
import pytest
import corpus
# A rule that passes every check, so each test can break exactly one thing and
# be sure that is what the error is about.
def good_rule(**overrides):
rule = {
"id": "python.a-sample-rule",
"stack": "odoo",
"severity": "high",
# The second axis. `unmeasured` is the default the build gives a rule
# nothing has placed, and it derives advisory false.
"likelihood": "unmeasured",
"title": "A sample rule for the validator tests",
"rule": "Do the correct thing rather than the incorrect thing.",
"why": "Because the incorrect thing fails silently and late.",
"wrong": "value = eval(payload)",
"correct": "value = json.loads(payload)",
"tags": ["sample"],
}
rule.update(overrides)
return rule
@pytest.fixture()
def corpus_dir(tmp_path, monkeypatch):
"""Point the loader at a corpus this test owns."""
path = tmp_path / "rules"
path.mkdir()
monkeypatch.setattr(corpus, "RULES_DIR", str(path))
def write(rules, name="odoo.json"):
with io.open(str(path / name), "w", encoding="utf-8") as handle:
handle.write(json.dumps(rules, ensure_ascii=False))
return write
def failures(capsys):
"""The error lines from the run that just happened."""
out = capsys.readouterr().out
return [line.strip() for line in out.splitlines() if line.startswith(" ")]
# ------------------------------------------------------------------ the gate
def test_a_valid_rule_passes(corpus_dir, capsys):
corpus_dir([good_rule()])
assert corpus.cmd_validate() == 0
assert "PASS" in capsys.readouterr().out
def test_an_empty_corpus_is_a_failure_not_a_pass(corpus_dir, capsys):
"""Nothing to validate is not the same as nothing wrong. A build that
silently produced an empty corpus would pass every check below it."""
corpus_dir([])
assert corpus.cmd_validate() == 1
assert "corpus is empty" in capsys.readouterr().out
@pytest.mark.parametrize("field", ["id", "stack", "severity", "likelihood",
"title", "rule", "why", "wrong", "correct",
"tags"])
def test_a_missing_required_field_fails(corpus_dir, capsys, field):
rule = good_rule()
del rule[field]
corpus_dir([rule])
assert corpus.cmd_validate() == 1
assert any("missing required field '{0}'".format(field) in line
for line in failures(capsys))
def test_an_unknown_field_fails(corpus_dir, capsys):
"""additionalProperties is false, so a typo in a key name is a silent
no-op rather than the setting somebody thought they wrote."""
corpus_dir([good_rule(violation_patern=r"eval\(")])
assert corpus.cmd_validate() == 1
assert any("unknown field 'violation_patern'" in line
for line in failures(capsys))
def test_a_duplicate_id_fails(corpus_dir, capsys):
"""Ids are what a model cites. Two rules answering to one id means a
citation cannot be checked against anything."""
corpus_dir([good_rule(), good_rule(title="A second rule with the same id")])
assert corpus.cmd_validate() == 1
assert any("duplicate id" in line for line in failures(capsys))
def test_an_id_with_an_unknown_origin_prefix_fails(corpus_dir, capsys):
"""The prefix names where a rule came from -- odoo. or python. -- and it is
what every measurement and every citation is keyed on. A rule whose prefix
is neither is orphaned from its own classification artifact, silently."""
corpus_dir([good_rule(id="ruff.a-sample-rule")])
assert corpus.cmd_validate() == 1
assert any("id prefix is not one of" in line for line in failures(capsys))
def test_both_origin_prefixes_are_accepted_under_the_one_stack(corpus_dir):
"""python.decimal-for-money-not-float carries stack odoo. The prefix is
provenance, the stack is selection, and they are allowed to differ."""
corpus_dir([good_rule(id="odoo.a-sample-rule"),
good_rule(id="python.another-sample-rule",
title="A second sample rule for the validator")])
assert corpus.cmd_validate() == 0
def test_ui_only_enforcement_must_say_what_bypasses_it(corpus_dir, capsys):
"""A rule enforced only in the UI is a rule every import, XML-RPC call and
sudo write goes around. Declaring the layer without the escape routes is
the claim that reads as safest and is least true."""
corpus_dir([good_rule(enforcement_layer="ui-only", bypassed_by=[])])
assert corpus.cmd_validate() == 1
assert any("must list what bypasses it" in line for line in failures(capsys))
def test_identical_wrong_and_correct_samples_fail(corpus_dir, capsys):
"""The pair is the rule's whole explanation. Identical, it teaches nothing
and every pattern self-test below passes vacuously."""
corpus_dir([good_rule(wrong="x = 1", correct="x = 1")])
assert corpus.cmd_validate() == 1
assert any("identical" in line for line in failures(capsys))
# --------------------------------------------------------- pattern self-tests
def test_a_pattern_that_does_not_compile_fails(corpus_dir, capsys):
corpus_dir([good_rule(violation_pattern=r"eval\(")])
assert corpus.cmd_validate() == 0, "the control case compiles and matches"
corpus_dir([good_rule(violation_pattern=r"eval\((")])
assert corpus.cmd_validate() == 1
assert any("does not compile" in line for line in failures(capsys))
def test_a_pattern_that_misses_its_own_wrong_sample_fails(corpus_dir, capsys):
"""It catches nothing, and nothing downstream would ever say so."""
corpus_dir([good_rule(violation_pattern=r"exec\(")])
assert corpus.cmd_validate() == 1
assert any("does not match its own 'wrong' sample" in line
for line in failures(capsys))
def test_a_pattern_that_fires_on_its_own_correct_sample_fails(corpus_dir, capsys):
"""Worse than no pattern: it fires on good code and trains the reader to
ignore the checker."""
corpus_dir([good_rule(violation_pattern=r"payload")])
assert corpus.cmd_validate() == 1
assert any("fires on its own 'correct' sample" in line
for line in failures(capsys))
def test_a_satisfied_pattern_that_clears_the_wrong_sample_fails(corpus_dir, capsys):
"""It clears the violation on the very code the rule is about, so the
check is inert everywhere."""
corpus_dir([good_rule(violation_pattern=r"eval\(",
satisfied_pattern=r"value")])
assert corpus.cmd_validate() == 1
assert any("makes the check inert" in line for line in failures(capsys))
def test_a_satisfied_pattern_alone_fails(corpus_dir, capsys):
"""There is no violation for it to clear, so it does nothing at all."""
corpus_dir([good_rule(satisfied_pattern=r"json")])
assert corpus.cmd_validate() == 1
assert any("has no effect" in line for line in failures(capsys))
# -------------------------------------------------------- semantic self-tests
ODOO_WRONG = (
"@api.depends('line_ids')\n"
"def _compute_total(self):\n"
" for rec in self:\n"
" rec.total = sum(l.price for l in rec.line_ids)"
)
ODOO_CORRECT = (
"@api.depends('line_ids', 'line_ids.price')\n"
"def _compute_total(self):\n"
" for rec in self:\n"
" rec.total = sum(l.price for l in rec.line_ids)"
)
def odoo_rule(**overrides):
fields = {"id": "odoo.a-sample-rule", "stack": "odoo",
"wrong": ODOO_WRONG, "correct": ODOO_CORRECT}
fields.update(overrides)
return good_rule(**fields)
def test_a_semantic_check_self_tests_like_a_pattern(corpus_dir, capsys):
corpus_dir([odoo_rule(semantic_check="depends_covers_compute_reads")],
name="odoo.json")
assert corpus.cmd_validate() == 0, capsys.readouterr().out
def test_a_semantic_check_that_does_not_exist_fails(corpus_dir, capsys):
"""A renamed checker that silently returned clean would turn a critical
rule into a permanent false pass."""
corpus_dir([odoo_rule(semantic_check="no_such_checker")], name="odoo.json")
assert corpus.cmd_validate() == 1
assert any("no semantic checker named" in line for line in failures(capsys))
def test_a_semantic_check_that_misses_its_own_wrong_sample_fails(corpus_dir,
capsys):
corpus_dir([odoo_rule(wrong=ODOO_CORRECT, correct=ODOO_WRONG,
semantic_check="depends_covers_compute_reads")],
name="odoo.json")
assert corpus.cmd_validate() == 1
lines = failures(capsys)
assert any("does not fire on its own" in line for line in lines)
assert any("fires on its own 'correct'" in line for line in lines)
def test_a_rule_cannot_carry_both_kinds_of_check(corpus_dir, capsys):
"""The domain layer defers to the semantic layer, so the pattern would be
silently ignored -- and a pattern nobody runs is one nobody maintains."""
corpus_dir([odoo_rule(semantic_check="depends_covers_compute_reads",
violation_pattern=r"api\.depends")],
name="odoo.json")
assert corpus.cmd_validate() == 1
assert any("both a violation_pattern and a semantic_check" in line
for line in failures(capsys))
# ------------------------------------------------------------- check_value
def test_a_schema_keyword_this_checker_does_not_know_is_an_error():
"""Deliberately not a general JSON Schema implementation: it fails loudly
on a keyword it does not understand rather than skipping it silently,
which would validate a rule against less than the schema says."""
errors = []
corpus.check_value("x", {"type": "string", "multipleOf": 2}, "p", errors)
assert any("unsupported keyword 'multipleOf'" in e for e in errors)
@pytest.mark.parametrize("value, spec, fragment", [
(5, {"type": "string"}, "expected string"),
("z", {"enum": ["a", "b"]}, "not in"),
("Nope", {"pattern": r"^[a-z]+$"}, "does not match"),
("ab", {"minLength": 5}, "below minimum"),
("abcdef", {"maxLength": 3}, "above maximum"),
([], {"minItems": 1}, "minimum"),
])
def test_each_keyword_reports_its_own_failure(value, spec, fragment):
errors = []
corpus.check_value(value, spec, "p", errors)
assert errors and any(fragment in e for e in errors), errors
def test_a_bool_is_not_an_integer():
"""True == 1 in Python, so a schema saying integer would accept a boolean
unless the checker says otherwise."""
errors = []
corpus.check_value(True, {"type": "integer"}, "p", errors)
assert errors
errors = []
corpus.check_value(True, {"type": "boolean"}, "p", errors)
assert not errors
def test_items_are_checked_individually_and_named_by_index():
errors = []
corpus.check_value(["ok", 7], {"type": "array", "items": {"type": "string"}},
"tags", errors)
assert any("tags[1]" in e for e in errors)
assert not any("tags[0]" in e for e in errors)
def test_a_type_failure_stops_the_other_keywords():
"""One error about the value being the wrong type, not five about a string
keyword failing on an integer."""
errors = []
corpus.check_value(5, {"type": "string", "minLength": 3,
"pattern": r"^[a-z]+$"}, "p", errors)
assert len(errors) == 1
# ------------------------------------------- the two axes, and the derived gate
def test_a_likelihood_outside_the_vocabulary_fails(corpus_dir, capsys):
corpus_dir([good_rule(likelihood="probably-fine")])
assert corpus.cmd_validate() == 1
assert any("likelihood" in line for line in failures(capsys))
def test_advisory_disagreeing_with_likelihood_fails(corpus_dir, capsys):
"""advisory is derived from likelihood by the build. The two can only
disagree if the JSON was hand-edited, and a rule that gates while grading
itself a preference gates on something nobody decided."""
corpus_dir([good_rule(likelihood="preference", advisory=False)])
assert corpus.cmd_validate() == 1
assert any("derived, not authored" in line for line in failures(capsys))
def test_advisory_claimed_by_a_measured_rule_fails(corpus_dir, capsys):
"""The other direction: a `defect` rule cannot opt out of gating by setting
the flag, which is exactly how a one-bit axis used to be overridable."""
corpus_dir([good_rule(likelihood="defect", advisory=True)])
assert corpus.cmd_validate() == 1
assert any("derived, not authored" in line for line in failures(capsys))
def test_both_advisory_grades_derive_the_gate(corpus_dir, capsys):
for grade in corpus.ADVISORY_LIKELIHOODS:
corpus_dir([good_rule(likelihood=grade, advisory=True)])
assert corpus.cmd_validate() == 0, capsys.readouterr().out
def test_likelihood_rank_orders_the_vocabulary():
"""The enum is in rank order and report.py reads findings in it, so the
order is part of the contract rather than an accident of the literal."""
ranked = sorted(corpus.LIKELIHOOD_ORDER, key=corpus.likelihood_rank)
assert ranked == list(corpus.LIKELIHOOD_ORDER)
assert corpus.likelihood_rank("defect") < corpus.likelihood_rank("construct")
assert corpus.likelihood_rank("construct") < corpus.likelihood_rank("preference")
def test_an_unknown_grade_sorts_last_rather_than_raising():
"""Report ordering is not the place to fail a run; validate has already
refused an unknown grade against the schema enum."""
assert corpus.likelihood_rank("nonsense") == len(corpus.LIKELIHOOD_ORDER)
# --------------------------------------------- the order findings are read in
class _Hit(object):
"""A stand-in for layers.SourceHit: rank_findings reads three attributes."""
def __init__(self, rule, line, likelihood):
self.rule = rule
self.line = line
self.likelihood = likelihood
def test_findings_are_ranked_by_likelihood_before_line():
"""History has an opinion about which rule is worth reading. sudo landed on
a line somebody changed 13 times in 41; prefer-sql did it none in 49. Line
order tells the reader nothing about that."""
prefer_sql = _Hit("odoo.prefer-sql-constraint", 12, "preference")
sudo = _Hit("odoo.sudo-bypasses-record-rules", 900, "defect")
ranked = corpus.rank_findings([prefer_sql, sudo])
assert [h.rule for h in ranked] == [sudo.rule, prefer_sql.rule]
def test_line_order_survives_inside_a_grade():
"""So a run of equally likely findings still reads down the file."""
late = _Hit("odoo.a-rule", 90, "defect")
early = _Hit("odoo.a-rule", 4, "defect")
assert [h.line for h in corpus.rank_findings([late, early])] == [4, 90]
def test_every_grade_sorts_in_the_documented_order():
hits = [_Hit("r." + g, 1, g) for g in reversed(corpus.LIKELIHOOD_ORDER)]
ranked = [h.likelihood for h in corpus.rank_findings(hits)]
assert ranked == list(corpus.LIKELIHOOD_ORDER)
def test_a_finding_with_no_grade_still_ranks():
"""Ordering a report is not the place to raise. A hit from something that
predates the axis sorts last rather than taking the run down."""
class Bare(object):
rule, line = "odoo.a-rule", 3
ranked = corpus.rank_findings([Bare(), _Hit("odoo.b-rule", 99, "defect")])
assert ranked[0].rule == "odoo.b-rule"
|