474dbf5a64
构造时一次绑定 SN、配置、资产和当前生效的标定:只接受显式给出的 SN,不做猜测;标定路径只取当前生效的标定,清除后为 None;换标定时整体替换。帧身份复用 stream-contract 的 FrameIdentity。load_config 改为复用 resolve_config_source,使记录的配置来源与实际读取的文件一致,并补一致性测试。已发布 Sensor 数值和资产包解析不变。回归 275 项、lint 通过。
98 lines
3.9 KiB
Python
98 lines
3.9 KiB
Python
"""``resolve_config_source`` and ``load_config`` must agree on what is read.
|
|
|
|
``SensorContext.config_source`` promises the JSON file that was actually loaded.
|
|
That promise only holds while the recorded lookup and the loading lookup are the
|
|
same implementation, so this module checks it against every installed config
|
|
name, the alias table, an explicit path and the legacy ``"name"`` field, and
|
|
pins the preserved error types/messages for a missing or non-JSON input.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from orisys import configs
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
|
SENSORS = REPO_ROOT / "sensors"
|
|
|
|
|
|
def _read(path: str | Path) -> dict:
|
|
return json.loads(Path(path).read_text(encoding="utf-8-sig"))
|
|
|
|
|
|
def test_every_listed_name_resolves_to_the_file_load_config_reads() -> None:
|
|
names = configs.list_config()
|
|
assert names
|
|
for name in names:
|
|
source = configs.resolve_config_source(name)
|
|
assert source is not None, name
|
|
assert _read(source) == configs.load_config(name), name
|
|
# The recorded source can be loaded directly with the same result.
|
|
assert configs.load_config(source) == configs.load_config(name), name
|
|
|
|
|
|
def test_aliases_resolve_like_loading() -> None:
|
|
aliases = dict(configs.CONFIG_ALIASES)
|
|
assert aliases
|
|
for alias, canonical in aliases.items():
|
|
source = configs.resolve_config_source(alias)
|
|
assert source is not None, alias
|
|
assert _read(source) == configs.load_config(alias), alias
|
|
canonical_source = configs.resolve_config_source(canonical)
|
|
if canonical_source is not None:
|
|
assert source == canonical_source, alias
|
|
|
|
|
|
def test_explicit_paths_resolve_to_themselves(tmp_path: Path) -> None:
|
|
bundle_config = SENSORS / "box" / "sensor.json"
|
|
assert configs.resolve_config_source(bundle_config) == bundle_config.resolve()
|
|
assert configs.load_config(bundle_config) == configs.load_config("box")
|
|
|
|
copied = tmp_path / "copied-config.json"
|
|
copied.write_text(bundle_config.read_text(encoding="utf-8"), encoding="utf-8")
|
|
assert configs.resolve_config_source(copied) == copied.resolve()
|
|
assert configs.load_config(copied) == configs.load_config(bundle_config)
|
|
|
|
|
|
def test_legacy_name_field_scan_agrees_with_loading() -> None:
|
|
"""A config's JSON ``"name"`` may differ from its file stem (``prototype``)."""
|
|
found = False
|
|
for name in configs.list_config():
|
|
source = configs.resolve_config_source(name)
|
|
if source is None:
|
|
continue
|
|
field = _read(source).get("name")
|
|
if (
|
|
isinstance(field, str)
|
|
and field
|
|
and field not in configs.list_config()
|
|
):
|
|
resolved = configs.resolve_config_source(field)
|
|
assert resolved is not None, field
|
|
assert _read(resolved) == configs.load_config(field), field
|
|
found = True
|
|
assert found, "expected at least one legacy name field in the installed configs"
|
|
|
|
|
|
def test_missing_and_non_json_inputs_keep_their_error_contract(tmp_path: Path) -> None:
|
|
assert configs.resolve_config_source("no-such-config-name") is None
|
|
with pytest.raises(FileNotFoundError, match="Input config file not found"):
|
|
configs.load_config("no-such-config-name")
|
|
|
|
absolute_missing = tmp_path / "missing.json"
|
|
assert configs.resolve_config_source(absolute_missing) is None
|
|
with pytest.raises(FileNotFoundError, match="not found at absolute path"):
|
|
configs.load_config(absolute_missing)
|
|
|
|
not_json = tmp_path / "config.txt"
|
|
not_json.write_text("{}", encoding="utf-8")
|
|
# The source lookup names the existing file; loading still refuses a non-JSON
|
|
# file with the historical ValueError instead of silently reading it.
|
|
assert configs.resolve_config_source(not_json) == not_json.resolve()
|
|
with pytest.raises(ValueError, match="must be a JSON file"):
|
|
configs.load_config(not_json)
|