Files
NODEDC_MISSION_CORE/tests/test_map_view_api.py

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