Files
orisys-sdk/tests/test_config_source_consistency.py
T
ykcheungab 474dbf5a64 feat: 为每个 Sensor 绑定只读 SensorContext
构造时一次绑定 SN、配置、资产和当前生效的标定:只接受显式给出的 SN,不做猜测;标定路径只取当前生效的标定,清除后为 None;换标定时整体替换。帧身份复用 stream-contract 的 FrameIdentity。load_config 改为复用 resolve_config_source,使记录的配置来源与实际读取的文件一致,并补一致性测试。已发布 Sensor 数值和资产包解析不变。回归 275 项、lint 通过。
2026-10-21 11:04:02 +08:00

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)