File size: 1,935 Bytes
4b5fa98
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""Rule engine: every planted violation found, every planted NON-violation
rejected (no false accusations), deterministic output."""

from __future__ import annotations

from sensorflow.rotr.rules import RULESET_VERSION, detect


class TestPlantedViolations:
    def test_every_committed_violation_detected_with_expected_rule(
            self, bank_v1, detections_v1):
        for sc in bank_v1:
            if not sc.planted.committed:
                continue
            hits = detections_v1[sc.scenario_id]
            assert any(v.rule_id == sc.planted.expected_rule_id
                       for v in hits), \
                f"{sc.scenario_id} ({sc.planted.kind}) missed"

    def test_violations_carry_structured_evidence_and_version(
            self, bank_v1, detections_v1):
        for sc in bank_v1:
            for v in detections_v1[sc.scenario_id]:
                assert v.rule_version == RULESET_VERSION
                assert v.evidence, "evidence fields required"
                assert "ruleset_fingerprint" in v.evidence
                assert 0.0 < v.confidence <= 1.0
                assert v.provenance.model_version == "stack-v1"


class TestPlantedNonViolations:
    def test_no_false_accusations(self, bank_v1, detections_v1):
        """Lookalike scenarios (legal assertive merge, yield-with-ROW,
        green proceed) and non-committed variants must produce ZERO
        violations."""
        for sc in bank_v1:
            if sc.planted.committed:
                continue
            assert detections_v1[sc.scenario_id] == [], \
                f"false accusation on {sc.scenario_id} ({sc.planted.kind})"


class TestDeterminism:
    def test_detect_is_pure(self, bank_v1):
        sc = next(s for s in bank_v1 if s.planted.committed)
        a = [v.model_dump(exclude={"provenance"}) for v in detect(sc)]
        b = [v.model_dump(exclude={"provenance"}) for v in detect(sc)]
        assert a == b