Stand up the Engine/PIP surface for MAT-WP-0001
Declare layer.yaml, add a Python engine over a local SQLite store, and cover deterministic assessment, the §13 gap register, stance-map inventory, claim guardrails, and the gate-house review path with tests. Assistant: grok Assistant-Session: 01a04ceb-150e-7e80-a542-ec8b1372e164
This commit is contained in:
parent
5c052ed106
commit
4cde4e489a
31 changed files with 2498 additions and 51 deletions
96
tests/test_compute.py
Normal file
96
tests/test_compute.py
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
from maturity_engine.compute import compute_level
|
||||
from maturity_engine.errors import UnevaluableCriterion
|
||||
from maturity_engine.models import Criterion, Evidence, Ladder, Level
|
||||
from conftest import INSTANT, evidence
|
||||
|
||||
|
||||
def test_floor_level_with_no_evidence(engine):
|
||||
assessment = engine.assess("estate", "asm", at=INSTANT)
|
||||
assert assessment.level == 0
|
||||
assert assessment.level_id == "ASM-0"
|
||||
assert assessment.next_level_id == "ASM-1"
|
||||
assert assessment.unmet
|
||||
|
||||
|
||||
def test_determinism_same_inputs_same_assessment(engine):
|
||||
kinds = ["central-secret-manager", "secret-rbac", "basic-secret-scanning", "basic-rotation"]
|
||||
for kind in kinds:
|
||||
engine.submit_evidence(evidence(engine, "estate", kind))
|
||||
first = engine.assess("estate", "asm", at=INSTANT)
|
||||
second = engine.assess("estate", "asm", at=INSTANT)
|
||||
assert first.id == second.id
|
||||
assert first.level == second.level == 1
|
||||
assert first.as_explanation() == second.as_explanation()
|
||||
|
||||
|
||||
def test_explainability_lists_met_and_next(engine):
|
||||
engine.submit_evidence(evidence(engine, "estate", "central-secret-manager"))
|
||||
assessment = engine.assess("estate", "asm", at=INSTANT)
|
||||
assert assessment.level == 0
|
||||
kinds_unmet = {item.evidence_kind for item in assessment.unmet}
|
||||
assert "secret-rbac" in kinds_unmet
|
||||
explanation = assessment.as_explanation()
|
||||
assert explanation["met"] == []
|
||||
assert explanation["next_level_id"] == "ASM-1"
|
||||
|
||||
|
||||
def test_expiry_demotes_a_subject(engine):
|
||||
until = INSTANT + timedelta(hours=1)
|
||||
kinds = ["central-secret-manager", "secret-rbac", "basic-secret-scanning", "basic-rotation"]
|
||||
for kind in kinds:
|
||||
engine.submit_evidence(evidence(engine, "estate", kind, until=until))
|
||||
high = engine.assess("estate", "asm", at=INSTANT)
|
||||
assert high.level == 1
|
||||
later = INSTANT + timedelta(hours=2)
|
||||
low = engine.assess("estate", "asm", at=later)
|
||||
assert low.level == 0
|
||||
assert low.level_id == "ASM-0"
|
||||
assert low.expired_evidence_ids
|
||||
history = engine.history("estate", "asm")
|
||||
assert [item.level for item in history] == [1, 0]
|
||||
|
||||
|
||||
def test_unevaluable_criterion_rejected():
|
||||
ladder = Ladder(
|
||||
id="bad",
|
||||
version="1",
|
||||
owner="nobody",
|
||||
name="bad",
|
||||
levels=(
|
||||
Level(0, "L0", "floor", ()),
|
||||
Level(
|
||||
1,
|
||||
"L1",
|
||||
"judged",
|
||||
(Criterion("c1", "judgment", "a human decides"),),
|
||||
),
|
||||
),
|
||||
)
|
||||
with pytest.raises(UnevaluableCriterion):
|
||||
ladder.validate()
|
||||
|
||||
|
||||
def test_compute_is_pure(engine):
|
||||
item = Evidence(
|
||||
id="e1",
|
||||
subject="s",
|
||||
kind="central-secret-manager",
|
||||
submitted_by="t",
|
||||
submitted_at=INSTANT,
|
||||
valid_from=INSTANT,
|
||||
valid_until=None,
|
||||
)
|
||||
ladder = engine.store.get_ladder("asm")
|
||||
a = compute_level(ladder, (item,), subject="s", instant=INSTANT)
|
||||
b = compute_level(ladder, (item,), subject="s", instant=INSTANT)
|
||||
assert a == b
|
||||
other = compute_level(
|
||||
ladder, (item,), subject="s", instant=datetime(2026, 8, 30, tzinfo=timezone.utc)
|
||||
)
|
||||
assert other.id != a.id
|
||||
Loading…
Add table
Add a link
Reference in a new issue