mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
84 lines
3.2 KiB
Python
84 lines
3.2 KiB
Python
"""Семантическая валидация маршрутов карт при config/set и импорте (#162)."""
|
|
import pytest
|
|
|
|
from custom_components.houseplan.validation import (
|
|
MarkerControlError,
|
|
validate_marker_vacuum_routes,
|
|
)
|
|
from custom_components.houseplan.vacuum_routes import VAC_ROUTE_ERROR
|
|
|
|
IDENTITY = [1, 0, 0, 0, 1, 0]
|
|
|
|
|
|
def _config(routes, spaces=("floor1", "floor2")):
|
|
return {
|
|
"spaces": [{"id": space} for space in spaces],
|
|
"markers": [{"id": "m1", "space": "floor1", "vacuum": {"map_routes": routes}}],
|
|
}
|
|
|
|
|
|
def _route(**over):
|
|
base = {"id": "r1", "source": "camera.robot", "map_id": "m1",
|
|
"space": "floor1", "calibration": IDENTITY}
|
|
base.update(over)
|
|
return base
|
|
|
|
|
|
def test_valid_routes_pass():
|
|
validate_marker_vacuum_routes(_config([_route(), _route(id="r2", map_id="m2", space="floor2")]))
|
|
|
|
|
|
def test_error_code_is_the_public_contract_code():
|
|
assert VAC_ROUTE_ERROR == "invalid_vacuum_map_route"
|
|
with pytest.raises(MarkerControlError) as excinfo:
|
|
validate_marker_vacuum_routes(_config([_route(space="нет такого")]))
|
|
assert excinfo.value.code == VAC_ROUTE_ERROR
|
|
|
|
|
|
@pytest.mark.parametrize("routes", [
|
|
[_route(space="удалённое")],
|
|
[_route(), _route(id="r2")],
|
|
[_route(), _route(map_id="m2")],
|
|
[_route(calibration=[1, 2, 3])],
|
|
[_route(source="не-entity")],
|
|
[_route(id="")],
|
|
["строка вместо объекта"],
|
|
])
|
|
def test_invalid_routes_are_rejected(routes):
|
|
with pytest.raises(MarkerControlError):
|
|
validate_marker_vacuum_routes(_config(routes))
|
|
|
|
|
|
def test_untouched_invalid_routes_do_not_block_an_unrelated_save():
|
|
"""Смена другого поля не обязана чинить чужие/будущие данные."""
|
|
broken = _config([_route(space="исчезнувшее")])
|
|
previous = {"markers": [dict(broken["markers"][0])], "spaces": broken["spaces"]}
|
|
validate_marker_vacuum_routes(broken, previous)
|
|
# но правка самих маршрутов обязана оставить их валидными
|
|
edited = _config([_route(space="исчезнувшее"), _route(id="r2", map_id="m2")])
|
|
with pytest.raises(MarkerControlError):
|
|
validate_marker_vacuum_routes(edited, previous)
|
|
|
|
|
|
def test_import_validates_everything():
|
|
broken = _config([_route(space="исчезнувшее")])
|
|
with pytest.raises(MarkerControlError):
|
|
validate_marker_vacuum_routes(broken, None, validate_all=True)
|
|
|
|
|
|
def test_missing_spaces_list_skips_the_referential_check_only():
|
|
config = {"markers": [{"id": "m1", "vacuum": {"map_routes": [_route(space="floor9")]}}]}
|
|
validate_marker_vacuum_routes(config)
|
|
config["markers"][0]["vacuum"]["map_routes"] = [_route(space="floor9", calibration=[1])]
|
|
with pytest.raises(MarkerControlError):
|
|
validate_marker_vacuum_routes(config)
|
|
|
|
|
|
def test_legacy_marker_without_routes_is_untouched():
|
|
config = {
|
|
"spaces": [{"id": "floor1"}],
|
|
"markers": [{"id": "m1", "space": "floor1",
|
|
"vacuum": {"source": "camera.robot", "calibration": {"m1": IDENTITY}}}],
|
|
}
|
|
validate_marker_vacuum_routes(config, None, validate_all=True)
|