"""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"