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.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_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