# tests/test_instrument_config.py
from pathlib import Path
import pytest
import yaml
from ewoksmx.config.detectors_catalog import load_detectors_by_serial
from ewoksmx.config.instrument_config import InstrumentConfigRegistry
VALUE_CASES = [
pytest.param(
"id30a1",
("grenades_fastproc", "hdf5_lib_path"),
Path,
id="id30a1:hdf5-lib-path-type",
),
pytest.param(
"id30a1",
("XIA2_DIALS", "slurm_mem"),
int,
id="id30a1:slurm-mem-type",
),
pytest.param(
"default",
("grenades_fastproc", "slurm_mem"),
int,
id="default:grenades_dials_slurm-mem-type",
),
]
[docs]
@pytest.fixture(scope="session")
def detector_serial():
detectors = load_detectors_by_serial()
if not detectors:
pytest.skip("No detectors found in detectors_catalog.yml")
return next(iter(detectors.keys()))
@pytest.fixture(autouse=True)
def _config_with_detector_serials(detector_serial, tmp_path, monkeypatch):
original_path = InstrumentConfigRegistry._config_path
data = yaml.safe_load(original_path.read_text())
for instrument, cfg in data.items():
if instrument == "default":
continue
if cfg is None:
cfg = {}
data[instrument] = cfg
cfg["detector_serial_number"] = detector_serial
patched_path = tmp_path / "instrument_config.yml"
patched_path.write_text(yaml.safe_dump(data, sort_keys=False))
monkeypatch.setattr(InstrumentConfigRegistry, "_config_path", patched_path)
InstrumentConfigRegistry._instance = None
yield
InstrumentConfigRegistry._instance = None
[docs]
@pytest.mark.parametrize("beam,field,expected_type", VALUE_CASES)
def test_get_config_value_returns_expected(beam, field, expected_type):
cfg = InstrumentConfigRegistry.load()
out = cfg.get(*field, instrument=beam)
assert isinstance(out, expected_type)
if expected_type is int:
assert out > 0
elif expected_type is Path:
assert str(out)
[docs]
def test_detector_loaded_from_catalog(detector_serial):
cfg = InstrumentConfigRegistry.load()
detector = cfg.get("detector", instrument="id30a1")
assert detector.serial_number == detector_serial
assert detector.nx > 0
assert detector.ny > 0
assert detector.pixel_size_x > 0
assert detector.pixel_size_y > 0
[docs]
def test_missing_detector_serial_raises(tmp_path, monkeypatch):
data = yaml.safe_load(InstrumentConfigRegistry._config_path.read_text())
data["bm07"]["detector_serial_number"] = ""
bad_path = tmp_path / "instrument_config.yml"
bad_path.write_text(yaml.safe_dump(data, sort_keys=False))
monkeypatch.setattr(InstrumentConfigRegistry, "_config_path", bad_path)
InstrumentConfigRegistry._instance = None
with pytest.raises(
ValueError, match="Missing detector_serial_number for instrument 'bm07'"
):
InstrumentConfigRegistry.load()
[docs]
def test_unknown_detector_serial_raises(tmp_path, monkeypatch):
data = yaml.safe_load(InstrumentConfigRegistry._config_path.read_text())
data["bm07"]["detector_serial_number"] = "UNKNOWN_SERIAL"
bad_path = tmp_path / "instrument_config.yml"
bad_path.write_text(yaml.safe_dump(data, sort_keys=False))
monkeypatch.setattr(InstrumentConfigRegistry, "_config_path", bad_path)
InstrumentConfigRegistry._instance = None
with pytest.raises(ValueError, match="not found in detectors catalog"):
InstrumentConfigRegistry.load()
[docs]
def test_detector_block_in_instrument_config_is_rejected(
detector_serial, tmp_path, monkeypatch
):
data = yaml.safe_load(InstrumentConfigRegistry._config_path.read_text())
data["bm07"]["detector"] = {"serial_number": detector_serial}
bad_path = tmp_path / "instrument_config.yml"
bad_path.write_text(yaml.safe_dump(data, sort_keys=False))
monkeypatch.setattr(InstrumentConfigRegistry, "_config_path", bad_path)
InstrumentConfigRegistry._instance = None
with pytest.raises(
ValueError,
match="Detector config must be defined in detectors_catalog.yml",
):
InstrumentConfigRegistry.load()