"""core.config: round-trip, tolerance, and the helios_port regression. The old dict-based writer rebuilt the JSON from only the main window's fields, silently discarding helios_port every time a port was edited. ScanDefaults.save() always writes every field. """ import json from core.config import ScanDefaults def test_roundtrip(tmp_path): p = tmp_path / "defaults.json" d = ScanDefaults(t3r_port="/dev/ttyACM3", helios_port="/dev/ttyUSB9") d.save(p) loaded = ScanDefaults.load(p) assert loaded == d def test_missing_file_creates_defaults(tmp_path): p = tmp_path / "defaults.json" d = ScanDefaults.load(p) assert d == ScanDefaults() assert p.exists() def test_corrupt_file_falls_back(tmp_path): p = tmp_path / "defaults.json" p.write_text("{not json") assert ScanDefaults.load(p) == ScanDefaults() def test_unknown_keys_ignored(tmp_path): p = tmp_path / "defaults.json" p.write_text(json.dumps({"t3r_port": "/dev/ttyACM7", "laser_freq_hz": 20000.0})) d = ScanDefaults.load(p) assert d.t3r_port == "/dev/ttyACM7" assert d.helios_port == ScanDefaults().helios_port def test_helios_port_survives_partial_update(tmp_path): """Regression: editing main-window ports must not clobber helios_port.""" p = tmp_path / "defaults.json" ScanDefaults(helios_port="/dev/ttyUSB7").save(p) d = ScanDefaults.load(p) d.t3r_port = "/dev/ttyACM1" # what _persist_defaults does d.save(p) assert ScanDefaults.load(p).helios_port == "/dev/ttyUSB7"