Add /api/library datasheet import and component GET/PUT with no exam. Reorganize pytest into datasheet, library, schematic, PCB, and AF+AI. Document Rust criteria (none chosen; no rustup) and coding conformity.
116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
"""Prove native review parse/tools match inherited PinScope before switching the live loop."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
|
|
from backend.periscopex.constraints_lookup import match_constraints
|
|
from backend.periscopex.finding_engine import complete_finding
|
|
from backend.periscopex.models import DesignGraph
|
|
from backend.periscopex.review_parse import parse_submit_review
|
|
from backend.periscopex.validate import _load_datasheets, _match_constraints, _parse_review
|
|
from backend.periscopex.validation_tools import execute_tool as inherited_execute
|
|
from backend.periscopex.review_tools import execute_tool as native_execute
|
|
from tests.paths import SIMPLE_PROJECT
|
|
|
|
|
|
_PAYLOAD = {
|
|
"findings": [
|
|
{
|
|
"finding": "FB5 floating",
|
|
"why": "FB5 is NC; DCDC5 SW is loaded.",
|
|
"status": "ERROR",
|
|
"source_page": 12,
|
|
"source_quote": "Connect FB5 to the output sense node.",
|
|
"recommendation": "Connect FB5.",
|
|
},
|
|
{
|
|
"finding": "no quote",
|
|
"why": "maybe missing cap",
|
|
"status": "ERROR",
|
|
"source_page": 3,
|
|
"source_quote": "",
|
|
},
|
|
],
|
|
"checked_areas": ["power", "decoupling"],
|
|
}
|
|
|
|
|
|
def test_native_parse_matches_inherited():
|
|
inherited = _parse_review(_PAYLOAD, "U16", "AXP2101")
|
|
native = parse_submit_review(_PAYLOAD, "U16", "AXP2101")
|
|
assert len(native.findings) == len(inherited.findings)
|
|
assert native.checked_areas == inherited.checked_areas
|
|
for a, b in zip(native.findings, inherited.findings, strict=True):
|
|
assert a.model_dump() == b.model_dump()
|
|
|
|
|
|
def test_native_llm_finding_is_review_and_recommended_not_error():
|
|
native = parse_submit_review(_PAYLOAD, "U16", "AXP2101")
|
|
f = native.findings[0]
|
|
complete_finding(f)
|
|
assert f.finding_class == "REVIEW"
|
|
assert f.facts == "FB5 floating"
|
|
assert f.requirement
|
|
assert f.status != "ERROR"
|
|
f2 = native.findings[1]
|
|
complete_finding(f2)
|
|
assert f2.status != "ERROR"
|
|
assert f2.why.startswith("Unverified:")
|
|
|
|
|
|
def test_match_constraints_matches_inherited():
|
|
datasheets = _load_datasheets(SIMPLE_PROJECT / "extracted")
|
|
if not datasheets:
|
|
datasheets = _load_datasheets(SIMPLE_PROJECT / "datasheets" / "extracted")
|
|
mpns = list(datasheets)[:5] or ["MSPM0G3507SPTR"]
|
|
for mpn in mpns:
|
|
assert match_constraints(mpn, datasheets) == _match_constraints(mpn, datasheets)
|
|
assert match_constraints(None, datasheets) is None
|
|
|
|
|
|
def test_native_graph_tools_match_inherited_on_simple_project():
|
|
graph = DesignGraph.model_validate(
|
|
json.loads((SIMPLE_PROJECT / "design_graph.json").read_text())
|
|
)
|
|
cmap = {}
|
|
native_txt, _ = native_execute(
|
|
graph, cmap, "get_net_for_pin", {"designator": "U3", "pin": "1"},
|
|
)
|
|
inherited_txt, _ = inherited_execute(
|
|
graph, cmap, "get_net_for_pin", {"designator": "U3", "pin": "1"},
|
|
)
|
|
assert native_txt == inherited_txt
|
|
n2, _ = native_execute(
|
|
graph, cmap, "find_connected_components",
|
|
{"designator": "U3", "pin": "1", "designator_filter": "C"},
|
|
)
|
|
i2, _ = inherited_execute(
|
|
graph, cmap, "find_connected_components",
|
|
{"designator": "U3", "pin": "1", "designator_filter": "C"},
|
|
)
|
|
assert n2 == i2
|
|
|
|
|
|
def test_native_session_does_not_import_pinscope_loop():
|
|
import ast
|
|
from pathlib import Path
|
|
|
|
import backend.services.review_session as rs
|
|
|
|
tree = ast.parse(Path(rs.__file__).read_text())
|
|
imported = [
|
|
node.module
|
|
for node in ast.walk(tree)
|
|
if isinstance(node, ast.ImportFrom) and node.module
|
|
]
|
|
assert "backend.periscopex.validate" not in imported
|
|
assert "backend.periscopex.validation_tools" not in imported
|
|
assert "backend.services.llm" in imported
|
|
assert "backend.periscopex.review_tools" in imported
|
|
assert "backend.periscopex.review_parse" in imported
|
|
|
|
from backend.services import validation as val
|
|
import backend.services.review_session as rs
|
|
assert val.review_ic_async is rs.review_ic_async
|