"""Taxonomy loader is src; JSON is located via repo_paths (not Path(__file__)).""" from __future__ import annotations from pathlib import Path import pytest import backend.periscopex.taxonomy as taxonomy from backend.periscopex.taxonomy import ( SIMPLE_TYPES, add_subtype, get_subtype, list_subtypes, load_subtypes, type_for_ref, validate_subtype, ) from backend.repo_paths import taxonomy_dir def test_taxonomy_module_is_src(): path = Path(taxonomy.__file__).resolve() assert path.parts[-5:] == ("periscope", "src", "backend", "periscopex", "taxonomy.py") text = path.read_text(encoding="utf-8") assert "from backend.repo_paths import taxonomy_dir" in text assert "TAXONOMY_DIR = taxonomy_dir()" in text assert "Native Periscope overlay" not in text def test_taxonomy_dir_points_at_dependency_json(): d = taxonomy_dir() assert d.parts[-2:] == ("dependency", "taxonomy") assert (d / "ic.json").is_file() assert taxonomy.TAXONOMY_DIR == d assert not (Path(taxonomy.__file__).resolve().parent.parent.parent / "taxonomy" / "ic.json").is_file() def test_emmaforo_ref_prefixes(): """Emmaforo-style designators: MCU, USB, LEDs, passives — not layout primitives.""" assert type_for_ref("U1") == "ic" assert type_for_ref("U3") == "ic" assert type_for_ref("LED1") == "discrete" assert type_for_ref("L1") == "passive" assert type_for_ref("C12") == "passive" assert type_for_ref("R10") == "passive" assert type_for_ref("J1") == "connector" assert type_for_ref("Y1") == "crystal" assert type_for_ref("X1") == "crystal" assert type_for_ref("TP1") == "test_point" assert type_for_ref("FB1") == "passive" assert type_for_ref("SW1") == "switch" def test_emmaforo_ic_json_has_mcu_and_interface_keys(): ic = load_subtypes("ic") assert "ic.mcu" in ic assert ic["ic.mcu"]["example_mpn"] == "MSPM0G3507SPTR" assert get_subtype("ic.interface.usb_uart_bridge") is not None assert "ic.mcu" in list_subtypes("ic") def test_validate_subtype_and_unknown_top(): assert validate_subtype("IC.MCU") == "ic.mcu" assert validate_subtype("passive.resistor") == "passive.resistor" with pytest.raises(ValueError, match="Unknown top-level"): validate_subtype("notatype.foo") with pytest.raises(ValueError, match="Invalid component_subtype"): validate_subtype("ic..mcu") assert type_for_ref("9") is None assert type_for_ref("") is None assert type_for_ref("RN4") is None # RN is not a taxonomy prefix (graph still maps RN→resistor) def test_layout_primitives_are_not_taxonomy_types(): """Pad≠Via≠Track≠Zone: layout kinds are not component_subtype tops.""" for bogus in ("pad.smd", "via.through", "track.microstrip", "zone.copper"): with pytest.raises(ValueError, match="Unknown top-level"): validate_subtype(bogus) assert type_for_ref("VIA1") is None assert type_for_ref("PAD1") is None def test_simple_types_exclude_ic_and_passive(): assert "ic" not in SIMPLE_TYPES assert "passive" not in SIMPLE_TYPES assert "connector" in SIMPLE_TYPES def test_add_subtype_writes_only_given_directory(tmp_path: Path): add_subtype("ic.sensor.humidity", "Humidity sensor IC", directory=tmp_path) written = tmp_path / "ic.json" assert written.is_file() assert "ic.sensor.humidity" in written.read_text(encoding="utf-8") live = (taxonomy_dir() / "ic.json").read_text(encoding="utf-8") assert "ic.sensor.humidity" not in live