197 lines
6.9 KiB
Python
197 lines
6.9 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from k1link.web.map_view_api import (
|
|
MapCamera,
|
|
MapViewContent,
|
|
MapViewPut,
|
|
MapViewStore,
|
|
default_map_view,
|
|
)
|
|
|
|
|
|
def test_default_map_view_matches_the_canonical_foundry_scene_and_cache_policy() -> None:
|
|
document = default_map_view()
|
|
|
|
assert document.schema_version == "missioncore.map-view/v3"
|
|
assert document.revision == 0
|
|
assert document.view.camera is not None
|
|
assert document.view.camera.longitude == pytest.approx(37.618423)
|
|
assert document.view.camera.latitude == pytest.approx(55.751244)
|
|
assert document.view.camera.height == pytest.approx(40_000.0)
|
|
assert document.view.selected_subject_id is None
|
|
assert document.view.layer_visibility.imagery is True
|
|
assert document.view.layer_visibility.grid is True
|
|
assert document.view.layer_visibility.targets is True
|
|
assert document.view.visual_settings.imagery_saturation == 0
|
|
assert document.view.visual_settings.imagery_alpha == 27
|
|
assert document.view.visual_settings.imagery_gamma == 57
|
|
assert document.view.visual_settings.background_color == "#08090d"
|
|
assert document.view.cache_intent.enabled is True
|
|
assert document.view.cache_intent.no_overwrite is True
|
|
assert document.view.ghost_pins.count == 12
|
|
assert document.view.ghost_pins.maximum_speed_meters_per_second == 18
|
|
assert document.view.ghost_pins.positions == []
|
|
assert document.view.ghost_pins.presentation.stem_height_meters == 1_500
|
|
|
|
|
|
def test_map_view_round_trip_uses_optimistic_revision(tmp_path: Path) -> None:
|
|
store = MapViewStore(tmp_path / "mission-data" / "map-view")
|
|
initial = store.read()
|
|
view = initial.view.model_copy(
|
|
update={
|
|
"camera": MapCamera(
|
|
longitude=37.6176,
|
|
latitude=55.7558,
|
|
height=2500.0,
|
|
heading=12.0,
|
|
pitch=-48.0,
|
|
roll=0.0,
|
|
),
|
|
"inspector_open_sections": ["camera"],
|
|
}
|
|
)
|
|
|
|
saved = store.save(MapViewPut(revision=initial.revision, view=view))
|
|
restored = MapViewStore(store.root).read()
|
|
|
|
assert saved.revision == 1
|
|
assert restored == saved
|
|
assert restored.view.camera is not None
|
|
assert restored.view.camera.longitude == pytest.approx(37.6176)
|
|
|
|
with pytest.raises(RuntimeError, match="revision changed"):
|
|
store.save(MapViewPut(revision=0, view=view))
|
|
|
|
|
|
def test_map_view_contract_cannot_store_secret_or_provider_fields() -> None:
|
|
payload: dict[str, object] = {
|
|
"camera": None,
|
|
"visual_settings": {},
|
|
"map_height": 720,
|
|
"inspector_open_sections": [],
|
|
"cache_intent": {"enabled": True, "no_overwrite": True},
|
|
"selected_subject_id": None,
|
|
"layer_visibility": {},
|
|
"token": "forbidden",
|
|
"gateway_url": "http://private.internal",
|
|
}
|
|
|
|
with pytest.raises(ValidationError):
|
|
MapViewContent.model_validate(payload)
|
|
|
|
|
|
def test_selection_inspector_requires_real_stable_subject() -> None:
|
|
with pytest.raises(ValidationError, match="selected stable subject"):
|
|
MapViewContent(inspector_open_sections=["selection"])
|
|
|
|
selected = MapViewContent(
|
|
selected_subject_id="map.moving_object/device-006",
|
|
inspector_open_sections=["selection"],
|
|
)
|
|
assert selected.selected_subject_id == "map.moving_object/device-006"
|
|
|
|
|
|
def test_ghost_pin_speed_cannot_exceed_configured_maximum() -> None:
|
|
with pytest.raises(ValidationError, match="speed exceeds configured maximum"):
|
|
MapViewContent.model_validate(
|
|
{
|
|
"ghost_pins": {
|
|
"maximum_speed_meters_per_second": 5,
|
|
"positions": [
|
|
{
|
|
"id": "ghost-fast-1",
|
|
"label": "123456",
|
|
"longitude": 37.618423,
|
|
"latitude": 55.751244,
|
|
"heading_degrees": 90,
|
|
"speed_meters_per_second": 6,
|
|
}
|
|
],
|
|
}
|
|
}
|
|
)
|
|
|
|
|
|
def test_persisted_document_contains_no_runtime_or_credential_material(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
store = MapViewStore(tmp_path / "map-view")
|
|
initial = store.read()
|
|
store.save(MapViewPut(revision=0, view=initial.view))
|
|
|
|
payload = json.loads(store.document_path.read_text(encoding="utf-8"))
|
|
serialized = json.dumps(payload).lower()
|
|
assert "token" not in serialized
|
|
assert "gateway" not in serialized
|
|
assert "provider" not in serialized
|
|
assert "cesium" not in serialized
|
|
|
|
|
|
def test_v1_map_view_is_upgraded_without_rewriting_the_source_file(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
store = MapViewStore(tmp_path / "map-view")
|
|
store.root.mkdir(parents=True)
|
|
legacy = {
|
|
"schema_version": "missioncore.map-view/v1",
|
|
"revision": 7,
|
|
"view": {
|
|
"camera": None,
|
|
"visual_settings": {
|
|
"atmosphere_enabled": True,
|
|
"lighting_enabled": False,
|
|
"monochrome_enabled": True,
|
|
"terrain_exaggeration": 2,
|
|
"buildings_maximum_screen_space_error": 12,
|
|
"camera_animation_enabled": False,
|
|
},
|
|
"map_height": 720,
|
|
"inspector_open_sections": [],
|
|
"cache_intent": {"enabled": True, "no_overwrite": True},
|
|
"selected_subject_id": None,
|
|
"layer_visibility": {
|
|
"imagery": True,
|
|
"terrain": True,
|
|
"buildings": True,
|
|
"grid": False,
|
|
"targets": True,
|
|
},
|
|
},
|
|
}
|
|
store.document_path.write_text(json.dumps(legacy), encoding="utf-8")
|
|
|
|
upgraded = store.read()
|
|
|
|
assert upgraded.schema_version == "missioncore.map-view/v3"
|
|
assert upgraded.revision == 7
|
|
assert upgraded.view.camera is None
|
|
assert upgraded.view.visual_settings.atmosphere_enabled is True
|
|
assert upgraded.view.visual_settings.sun_enabled is False
|
|
assert upgraded.view.visual_settings.monochrome_enabled is True
|
|
assert upgraded.view.ghost_pins.positions == []
|
|
assert json.loads(store.document_path.read_text(encoding="utf-8")) == legacy
|
|
|
|
|
|
def test_v2_map_view_receives_the_default_ghost_pin_layer_without_rewrite(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
store = MapViewStore(tmp_path / "map-view")
|
|
store.root.mkdir(parents=True)
|
|
current = default_map_view().model_dump(mode="json")
|
|
current["schema_version"] = "missioncore.map-view/v2"
|
|
current["view"].pop("ghost_pins")
|
|
store.document_path.write_text(json.dumps(current), encoding="utf-8")
|
|
|
|
upgraded = store.read()
|
|
|
|
assert upgraded.schema_version == "missioncore.map-view/v3"
|
|
assert upgraded.view.ghost_pins.count == 12
|
|
assert upgraded.view.ghost_pins.presentation.label_mode == "attributes"
|
|
assert json.loads(store.document_path.read_text(encoding="utf-8")) == current
|