"""HubAudio KiCad BOM: PNM column, grouped refs, no invented U13…U39 MPNs.""" from pathlib import Path from backend.periscopex.parsers import ic_mpn_skip_reason, parse_bom FIXTURE = Path(__file__).parent / "fixtures" / "hubaudio_kicad_bom.csv" SKIPPED = ("U13", "U15", "U25", "U28", "U34", "U35", "U36", "U39") def test_hubaudio_header_is_pnm_not_mpn(): header = FIXTURE.read_text(encoding="utf-8-sig").splitlines()[0] assert "PNM" in header assert "Manufacturer Part Number" not in header def test_hubaudio_pnm_and_value_fallback(): bom = parse_bom(FIXTURE, mpn_col="Manufacturer Part Number") assert bom["U1"]["mpn"] == "TPS22965DSGR" assert bom["U10"]["mpn"] == "W25Q128JVS" assert bom["U11"]["mpn"] == "ESP32-S31-WROOM-3" # PNM blank, Value is the orderable MPN; grouped Reference list. assert bom["U2"]["mpn"] == "PCA9534ARGTR" assert bom["U3"]["mpn"] == "PCA9534ARGTR" assert bom["U2"]["value"] == "PCA9534ARGTR" assert (bom["U4"]["mpn"] == "INA228AQDGSRQ1") and bom["U8"]["mpn"] == "INA228AQDGSRQ1" assert bom["L4"]["mpn"] == "BLM21PG121SN1D" assert bom["L4"]["value"] == "120 ohm" def test_hubaudio_skipped_refs_absent_no_invented_mpn(): bom = parse_bom(FIXTURE, mpn_col="Manufacturer Part Number") for ref in SKIPPED: assert ref not in bom reason = ic_mpn_skip_reason( ref, mpn=None, value="", footprint="MIKILAB_TPD2E007DCKR:SOT65P210X110-3N", in_bom=False, ) assert reason and reason.startswith("INSUFFICIENT_EVIDENCE") assert "not in BOM" in reason def test_ic_skip_empty_pnm_quotes_value(): reason = ic_mpn_skip_reason( "U99", mpn=None, value="", footprint="QFN", in_bom=True, bom_value="", ) assert reason and "PNM/MPN empty" in reason