Files
periscope/tests/test_validation_deterministic_seed.py
michele 82d185ccf4 Treat KiCad PNM as MPN and skip missing U* as INSUFFICIENT_EVIDENCE.
HubAudio CSV has no U13–U39 rows; those refs stay without an invented part number. IC Value fills MPN when PNM is blank. Progress no longer labels netlist-only ICs as “no MPN in BOM”.
2026-09-21 00:53:06 +02:00

84 lines
2.9 KiB
Python

"""Integration: deterministic findings are seeded into report.json and skipped
ICs surface under not_reviewed — driven through validate_design_async with no
datasheet PDFs (so the LLM review loop is empty and no API call is made)."""
from __future__ import annotations
import json
import pytest
from backend.periscopex.models import (
Component,
ComponentConstraints,
ComponentType,
DesignGraph,
Net,
NetType,
Pin,
PinConnection,
)
from backend.services import validation as val
@pytest.mark.asyncio
async def test_deterministic_findings_seeded_and_not_reviewed(tmp_path):
proj = tmp_path / "proj"
extracted = proj / "extracted"
extracted.mkdir(parents=True)
pdf_dir = proj / "pdfs"
pdf_dir.mkdir()
# U3: a UART5_TX net on the RX-only pin (pin-mux ERROR). U6: no MPN -> no
# datasheet -> not_reviewed. No PDFs anywhere -> review loop is empty.
graph = DesignGraph(
components={
"U3": Component(reference="U3", value="", footprint="",
component_type=ComponentType.IC, mpn="MCUX",
pins={"54": "MCU-UART5-TX"}),
"U6": Component(reference="U6", value="", footprint="",
component_type=ComponentType.IC, mpn=None,
pins={"1": "I2C1-SCL-3V3"}),
},
nets={
"MCU-UART5-TX": Net(name="MCU-UART5-TX", net_type=NetType.SIGNAL,
pins=[PinConnection(component_ref="U3", pin_number="54")]),
"I2C1-SCL-3V3": Net(name="I2C1-SCL-3V3", net_type=NetType.SIGNAL,
pins=[PinConnection(component_ref="U6", pin_number="1")]),
},
)
graph_path = proj / "design_graph.json"
graph_path.write_text(graph.model_dump_json())
cons = ComponentConstraints(
mpn="MCUX",
pintable=[Pin(number=54, name="PD2", functions=["UART5_RX"])],
absolute_maximum_ratings=[], rules=[],
)
(extracted / "MCUX.json").write_text(cons.model_dump_json())
report_path = proj / "report.json"
await val.validate_design_async(
graph_path=str(graph_path),
output_path=str(report_path),
datasheets_dir=str(extracted),
pdf_dir=str(pdf_dir),
storage=None,
)
data = json.loads(report_path.read_text())
det = [f for f in data["findings"] if f.get("source") == "pin_mux_check"]
assert len(det) == 1
assert det[0]["status"] == "ERROR"
assert det[0]["designator"] == "U3"
assert det[0]["finding_id"] # assign_finding_ids ran over it
assert det[0]["source_page"] is None
assert data["summary"]["ERROR"] >= 1
assert {x["designator"] for x in data["not_reviewed"]} == {"U3", "U6"}
by_ref = {x["designator"]: x["reason"] for x in data["not_reviewed"]}
assert by_ref["U3"] == "no datasheet PDF"
assert by_ref["U6"].startswith("INSUFFICIENT_EVIDENCE")
assert "not in BOM" in by_ref["U6"]