mirror of
https://github.com/Matysh/houseplan-card
synced 2026-07-31 08:28:31 +00:00
audit+refactor v1.7.0: drop hardcoded dacha bundle (293KB->83KB) + onboarding empty-state; remove migration & dead device_overrides/virtual_devices; extract src/logic.ts + validation.py (pure, unit-tested: 9 front + 10 back); fix latent bugs unmasked by truncated rules.ts (DOMAIN_PRIORITY, poly _defaultPositions); harden path sanitizers (leading-dot traversal); tsc strict in build+CI; sync versions
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
"""Юнит-тесты чистой валидации House Plan (загружаем validation.py по пути,
|
||||
без импорта пакета HA-интеграции)."""
|
||||
import importlib.util
|
||||
import os
|
||||
|
||||
import pytest
|
||||
import voluptuous as vol
|
||||
|
||||
_PATH = os.path.join(
|
||||
os.path.dirname(os.path.dirname(__file__)),
|
||||
"custom_components", "houseplan", "validation.py",
|
||||
)
|
||||
_spec = importlib.util.spec_from_file_location("hp_validation", _PATH)
|
||||
v = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(v)
|
||||
|
||||
|
||||
def test_sanitize_marker_id():
|
||||
assert v.sanitize_marker_id("../etc/passwd") == "_etc_passwd"
|
||||
assert v.sanitize_marker_id("..") == "misc" # чистый traversal → misc
|
||||
assert v.sanitize_marker_id(".") == "misc"
|
||||
assert v.sanitize_marker_id("") == "misc"
|
||||
assert len(v.sanitize_marker_id("a" * 200)) == 64
|
||||
|
||||
|
||||
def test_sanitize_filename_strips_path():
|
||||
assert v.sanitize_filename("/a/b/c/manual.pdf") == "manual.pdf"
|
||||
assert v.sanitize_filename("..\\..\\evil.pdf") == "evil.pdf" # обратные слэши = путь
|
||||
assert v.sanitize_filename("...hidden.pdf") == "hidden.pdf" # ведущие точки убраны
|
||||
|
||||
|
||||
def test_file_ext():
|
||||
assert v.file_ext("manual.PDF") == "pdf"
|
||||
assert v.file_ext("a/b/x.png") == "png"
|
||||
assert v.file_ext("noext") == ""
|
||||
|
||||
|
||||
def test_valid_space_id():
|
||||
assert v.valid_space_id("f1")
|
||||
assert v.valid_space_id("floor-2_a")
|
||||
assert not v.valid_space_id("Floor 1")
|
||||
assert not v.valid_space_id("a" * 65)
|
||||
assert not v.valid_space_id("../x")
|
||||
|
||||
|
||||
def test_room_schema_poly_or_rect():
|
||||
v.ROOM_SCHEMA({"id": "r1", "name": "A", "poly": [[0, 0], [1, 0], [1, 1]]})
|
||||
v.ROOM_SCHEMA({"id": "r2", "name": "B", "x": 0, "y": 0, "w": 1, "h": 1})
|
||||
with pytest.raises(vol.Invalid):
|
||||
v.ROOM_SCHEMA({"id": "r3", "name": "C"})
|
||||
with pytest.raises(vol.Invalid):
|
||||
v.ROOM_SCHEMA({"id": "r4", "name": "D", "poly": [[0, 0], [1, 1]]})
|
||||
|
||||
|
||||
def test_space_schema_aspect_range():
|
||||
ok = {"id": "f1", "title": "1", "aspect": 1.4, "view_box": [0, 0, 1, 1], "rooms": []}
|
||||
v.SPACE_SCHEMA(ok)
|
||||
with pytest.raises(vol.Invalid):
|
||||
v.SPACE_SCHEMA({**ok, "aspect": 0})
|
||||
with pytest.raises(vol.Invalid):
|
||||
v.SPACE_SCHEMA({**ok, "view_box": [0, 0, 1]})
|
||||
|
||||
|
||||
def test_marker_schema():
|
||||
v.MARKER_SCHEMA({"id": "m1", "binding": "device:abc"})
|
||||
v.MARKER_SCHEMA({"id": "m2", "binding": "virtual", "name": "X",
|
||||
"pdfs": [{"name": "a.pdf", "url": "/u/a.pdf"}]})
|
||||
with pytest.raises(vol.Invalid):
|
||||
v.MARKER_SCHEMA({"binding": "virtual"})
|
||||
|
||||
|
||||
def test_config_schema_defaults_and_extra():
|
||||
out = v.CONFIG_SCHEMA({"spaces": []})
|
||||
assert out["markers"] == [] and out["settings"] == {}
|
||||
out2 = v.CONFIG_SCHEMA({"spaces": [], "virtual_devices": [], "device_overrides": {}})
|
||||
assert "spaces" in out2
|
||||
|
||||
|
||||
def test_config_schema_full_roundtrip():
|
||||
cfg = {
|
||||
"spaces": [{
|
||||
"id": "f1", "title": "1 этаж", "plan_url": "/p/f1.svg",
|
||||
"aspect": 0.8, "view_box": [0, 0, 1, 1],
|
||||
"rooms": [{"id": "r1", "name": "Зал", "area": "hall",
|
||||
"poly": [[0, 0], [0.5, 0], [0.5, 0.5], [0, 0.5]]}],
|
||||
"segments": [[0, 0, 0.5, 0]],
|
||||
}],
|
||||
"markers": [{"id": "d1", "binding": "device:x", "model": "M", "link": "https://e.com"}],
|
||||
"settings": {"group_lights": True},
|
||||
}
|
||||
out = v.CONFIG_SCHEMA(cfg)
|
||||
assert out["spaces"][0]["rooms"][0]["area"] == "hall"
|
||||
assert out["markers"][0]["binding"] == "device:x"
|
||||
|
||||
|
||||
def test_layout_schema():
|
||||
v.LAYOUT_SCHEMA({"dev1": {"x": 0.1, "y": 0.2, "s": "f1"}})
|
||||
with pytest.raises(vol.Invalid):
|
||||
v.LAYOUT_SCHEMA({"dev1": {"x": 0.1}})
|
||||
Reference in New Issue
Block a user