Downgrade-only clamp and fail-soft coverage stay in src. Inherited copies remain on disk. Self-host billing is NullBilling.
91 lines
3.0 KiB
Python
91 lines
3.0 KiB
Python
"""Post-review services load from src; Emmaforo clamp contracts stay downgrade-only."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from backend.periscopex.models import Finding
|
|
from backend.services.billing_hook import NullBilling, get_billing
|
|
from backend.services.dedupe_findings import _build_deduped
|
|
from backend.services.normalize_findings import _build_normalized
|
|
import backend.services.billing_hook as billing_hook
|
|
import backend.services.dedupe_findings as dedupe_findings
|
|
import backend.services.normalize_findings as normalize_findings
|
|
|
|
|
|
def test_postpass_modules_are_src():
|
|
for mod, name in (
|
|
(normalize_findings, "normalize_findings.py"),
|
|
(dedupe_findings, "dedupe_findings.py"),
|
|
(billing_hook, "billing_hook.py"),
|
|
):
|
|
path = Path(mod.__file__).resolve()
|
|
assert path.name == name
|
|
assert "src" in path.parts
|
|
assert "Native Periscope overlay" not in path.read_text(encoding="utf-8")[:500]
|
|
|
|
|
|
def test_self_host_billing_is_null():
|
|
hook = get_billing()
|
|
assert isinstance(hook, NullBilling)
|
|
assert hook.credits_for_api_cost(12.0) == 0.0
|
|
|
|
|
|
def test_emmaforo_uart_normalize_cannot_promote_warning():
|
|
"""U2-001 class: hedged UART abs-max must stay WARNING."""
|
|
originals = [
|
|
Finding(
|
|
designator="U2",
|
|
mpn="CH340E",
|
|
finding="5V UART into MCU",
|
|
why="Unverified: abs-max for PA14 not confirmed",
|
|
status="WARNING",
|
|
recommendation="level shift",
|
|
source_page=11,
|
|
source_quote="",
|
|
reference="",
|
|
)
|
|
]
|
|
raw = [{
|
|
"merged_from": [1],
|
|
"finding": "CH340E 5V will damage the MCU",
|
|
"why": "will damage the MCU",
|
|
"status": "ERROR",
|
|
"recommendation": "level shift",
|
|
"change_rationale": "graded ERROR",
|
|
}]
|
|
built = _build_normalized(raw, [], originals)
|
|
assert built is not None
|
|
kept, _ = built
|
|
assert kept[0].status == "WARNING"
|
|
assert kept[0].why.lower().startswith("unverified:")
|
|
|
|
|
|
def test_emmaforo_uart_dedupe_collapses_both_endpoints():
|
|
originals = [
|
|
Finding(
|
|
designator="U2", mpn="CH340E", finding="5V out on UART",
|
|
why="w", status="ERROR", recommendation="", source_page=11,
|
|
source_quote="q2", reference="r2",
|
|
),
|
|
Finding(
|
|
designator="U3", mpn="ESP32", finding="PA14 overvoltage",
|
|
why="w", status="ERROR", recommendation="", source_page=25,
|
|
source_quote="q3", reference="r3",
|
|
),
|
|
]
|
|
groups = [{
|
|
"member_indices": [1, 2],
|
|
"primary_index": 2,
|
|
"finding": "CH340E 5V into MCU UART pin",
|
|
"why": "same UART interface",
|
|
"status": "ERROR",
|
|
"recommendation": "level shift",
|
|
"change_rationale": "merged 1+2",
|
|
}]
|
|
built = _build_deduped(groups, originals)
|
|
assert built is not None
|
|
assert len(built) == 1
|
|
assert built[0].designator == "U3"
|
|
assert built[0].source_page == 25
|