Files
settled-reach/server/tests/gen_fixtures.rs
T
jpmschweitzerandClaude Fable 5 8da9670e0f feat(simulation): feature-name pipeline wired + legacy window_granularity u32 retired (T-1169, T-1159)
One commit for two tickets whose changes share the bridge/plugin
plumbing files. T-1169 connects the three dormant feature-name pieces:
atlas_feature_names populated at regen (17,891 rows — 15,190 mountain,
2,701 river — via populate_atlas_feature_names mirroring the city-names
importer; systems.db regenerated, stamp fresh), attach_feature_names
wired into the cascade's Topography block with name pools threaded
DB-free through AnalyzeBody (D-225 pattern) and assignments stored on
Layer1Output/BodyWorldState for future consumers, and a
FeatureNamesRequest/Response read proxy as the bridge's 7th tagged
envelope (D-236 pattern, both SimBridge impls). Client label DRAW is
deliberately NOT here — implementation proved both river and mountain
labels need a wire-carried position (the pool is position-free; course
polylines aren't correlated with the named attractors by construction) —
deferred to T-1195's single design pass. cascade_layer1 golden re-pinned
(additive feature_names field).

T-1159 retires the legacy u32 granularity field fully shadowed by
window_granularity_v2: AtlasLayerRequest.window_granularity,
DistrictWindowLayer.granularity echo, the u32::MAX sentinel, and
resolve_window_granularity are gone server-side; client encode paths and
the caller-less atlas_window_cache legacy key component dropped;
msgpack fixtures regenerated; the T-1150 aliasing regression test now
drives through the surviving enum field. The district_window carrier
itself survives byte-compatible per D-255(c).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 16:10:27 +02:00

758 lines
26 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Generate MessagePack fixture files for cross-language testing (D-030 Layer 1).
//! Run with: cargo test --test gen_fixtures -- --ignored
use settled_reach_server::atlas::body_world_state::{DrainageBasin, RiverNetwork};
use settled_reach_server::atlas::layer1::Layer1Output;
use settled_reach_server::atlas::layer_proxy::{
AtlasLayerResponse, AtlasLayerStatus, DistrictWindowLayer, QuarterFootprintEntry,
QuarterFootprintLayer, RegionGridLayer, RoadGraphEdge, RoadGraphLayer, RoadGraphNode,
SettlementEntry, SettlementLayer, SettlementSizeClass, WindowGranularity, REGION_TEMP_NONE_DC,
};
use settled_reach_server::atlas::region_profile::{SeasonPhase, WeatherState};
use settled_reach_server::atlas::road_graph::RoadNodeKind;
use settled_reach_server::bridge::types::*;
use settled_reach_server::simulation::generator::{
AttractorType, DistrictType, GeographicAttractor, MaintenanceAuthority, SubBiomeVariant,
ZoningType,
};
use settled_reach_server::simulation::poi::PoiCategory;
use settled_reach_server::simulation::time::{DayPhase, TickRate};
use std::collections::BTreeMap;
use std::fs;
use std::path::Path;
fn write_fixture(name: &str, bytes: &[u8]) {
// Write directly into the Godot project's test fixtures (single source of truth)
let dir = Path::new("../client/tests/fixtures/msgpack");
fs::create_dir_all(dir).expect("create fixture dir");
let path = dir.join(format!("{}.msgpack", name));
fs::write(&path, bytes).expect("write fixture");
eprintln!("Wrote {} ({} bytes)", path.display(), bytes.len());
}
/// Helper to create a minimal snapshot for fixtures (D-192: no version field)
fn fixture_snapshot(tick: u64, entities: Vec<VisibleEntity>) -> ObserverSnapshot {
ObserverSnapshot {
tick,
game_time: GameTime {
day: 0,
time_of_day: 0,
day_phase: DayPhase::Morning,
tick_rate: TickRate::Full,
},
player_facing: FacingDirection::North,
player_stance: MovementStance::default(),
player_inventory: vec![],
entities,
visible_tiles: vec![],
nearby_interactions: vec![],
current_monologue: None,
pending_recognitions: vec![],
dialogue_response: None,
blocked_entities: vec![],
scan_events: vec![],
sound_events: vec![],
follow_state: None,
character_pressure: None,
rng_seed: None,
poi_list: vec![],
examine_result: None,
player_knowledge: None,
save_result: None,
triangle_crisis_events: vec![],
state_hash: None,
debug_response: None,
sim_errors: vec![],
current_ticker: None,
settings_response: None,
economy_snapshot: None,
bookmark_catalog: None,
}
}
#[test]
#[ignore] // Run manually: cargo test --test gen_fixtures -- --ignored
fn generate_msgpack_fixtures() {
// Snapshot with one NPC entity
let snapshot = fixture_snapshot(
42,
vec![VisibleEntity {
entity_id: 1,
x: 10.0,
y: 20.0,
z: 0,
kind: EntityKind::Npc,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
}],
);
write_fixture(
"snapshot_one_npc",
&rmp_serde::to_vec_named(&snapshot).unwrap(),
);
// Empty snapshot
let empty = fixture_snapshot(0, vec![]);
write_fixture("snapshot_empty", &rmp_serde::to_vec_named(&empty).unwrap());
// PlayerInput: MoveNorth
let input_north = PlayerInput {
tick: 100,
action: PlayerAction::MoveNorth,
};
write_fixture(
"input_move_north",
&rmp_serde::to_vec_named(&input_north).unwrap(),
);
// PlayerInput: UsePerceptionMode
let input_perception = PlayerInput {
tick: 200,
action: PlayerAction::UsePerceptionMode("thermal".to_string()),
};
write_fixture(
"input_perception_mode",
&rmp_serde::to_vec_named(&input_perception).unwrap(),
);
// Snapshot with Player entity
let snapshot_player = fixture_snapshot(
1,
vec![VisibleEntity {
entity_id: 100,
x: 16.5,
y: 16.5,
z: 0,
kind: EntityKind::Player,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
}],
);
write_fixture(
"snapshot_player",
&rmp_serde::to_vec_named(&snapshot_player).unwrap(),
);
// Snapshot with multiple entities and all EntityKind variants
let snapshot_multi = fixture_snapshot(
999,
vec![
VisibleEntity {
entity_id: 1,
x: 16.5,
y: 16.5,
z: 0,
kind: EntityKind::Player,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Known,
observation: EntityVisibility::Visible,
tell_state: None,
},
VisibleEntity {
entity_id: 2,
x: 5.0,
y: 10.0,
z: 0,
kind: EntityKind::Npc,
visibility: VisibilitySector::Peripheral,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
},
VisibleEntity {
entity_id: 3,
x: 15.5,
y: 3.0,
z: 1,
kind: EntityKind::Object,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
},
VisibleEntity {
entity_id: 4,
x: 0.0,
y: 0.0,
z: -1,
kind: EntityKind::Terrain,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
},
],
);
write_fixture(
"snapshot_multi_entity",
&rmp_serde::to_vec_named(&snapshot_multi).unwrap(),
);
// v2 snapshot with visible_tiles and game_time populated
let snapshot_v2_full = ObserverSnapshot {
tick: 500,
game_time: GameTime {
day: 1,
time_of_day: 720,
day_phase: DayPhase::Evening,
tick_rate: TickRate::Full,
},
player_facing: FacingDirection::Southeast,
player_stance: MovementStance::default(),
player_inventory: vec![],
entities: vec![VisibleEntity {
entity_id: 1,
x: 10.5,
y: 10.5,
z: 0,
kind: EntityKind::Player,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
}],
visible_tiles: vec![
VisibleTile {
x: 10,
y: 10,
z: 0,
visibility: VisibilitySector::Forward,
tile_kind: TileKind::Floor,
zone_id: Some(1),
},
VisibleTile {
x: 11,
y: 10,
z: 0,
visibility: VisibilitySector::Peripheral,
tile_kind: TileKind::Floor,
zone_id: Some(1),
},
VisibleTile {
x: 10,
y: 9,
z: 0,
visibility: VisibilitySector::Forward,
tile_kind: TileKind::Floor,
zone_id: None,
},
],
nearby_interactions: vec![],
current_monologue: None,
pending_recognitions: vec![],
dialogue_response: None,
blocked_entities: vec![],
scan_events: vec![],
sound_events: vec![],
follow_state: None,
character_pressure: None,
rng_seed: None,
poi_list: vec![],
examine_result: None,
player_knowledge: None,
save_result: None,
triangle_crisis_events: vec![],
state_hash: None,
debug_response: None,
sim_errors: vec![],
current_ticker: None,
settings_response: None,
economy_snapshot: None,
bookmark_catalog: None,
};
write_fixture(
"snapshot_v2_full",
&rmp_serde::to_vec_named(&snapshot_v2_full).unwrap(),
);
// Batch input: Vec<PlayerInput> with two actions (D-030 Layer 1 bidirectional symmetry)
let input_batch = vec![
PlayerInput {
tick: 0,
action: PlayerAction::MoveNorth,
},
PlayerInput {
tick: 0,
action: PlayerAction::Interact {
target_entity_id: None,
verb: None,
},
},
];
write_fixture(
"input_batch_two",
&rmp_serde::to_vec_named(&input_batch).unwrap(),
);
// PlayerInput: DialogueResponse (#539)
let input_dialogue_response = PlayerInput {
tick: 300,
action: PlayerAction::DialogueResponse {
target_entity_id: 42,
response_id: "kael-davan_d_001".to_string(),
},
};
write_fixture(
"input_dialogue_response",
&rmp_serde::to_vec_named(&input_dialogue_response).unwrap(),
);
// Diagonal movement fixtures (clockwise: NE, SE, SW, NW)
for (name, action) in [
("input_move_northeast", PlayerAction::MoveNortheast),
("input_move_southeast", PlayerAction::MoveSoutheast),
("input_move_southwest", PlayerAction::MoveSouthwest),
("input_move_northwest", PlayerAction::MoveNorthwest),
] {
let input = PlayerInput { tick: 100, action };
write_fixture(name, &rmp_serde::to_vec_named(&input).unwrap());
}
// === #271 fixtures: named fixtures for cross-language Layer 1 testing ===
// snapshot_minimal: tick=0, one Player entity, all optionals absent
let snapshot_minimal = fixture_snapshot(
0,
vec![VisibleEntity {
entity_id: 1,
x: 0.0,
y: 0.0,
z: 0,
kind: EntityKind::Player,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
}],
);
write_fixture(
"snapshot_minimal",
&rmp_serde::to_vec_named(&snapshot_minimal).unwrap(),
);
// snapshot_full: tick=42, monologue + dialogue + inventory + POIs + KG dump
let snapshot_full = ObserverSnapshot {
tick: 42,
game_time: GameTime {
day: 3,
time_of_day: 840,
day_phase: DayPhase::Evening,
tick_rate: TickRate::Full,
},
player_facing: FacingDirection::East,
player_stance: MovementStance::Walk,
player_inventory: vec![InventoryItem {
item_id: 7,
name: "Forged Customs Cert".to_string(),
slot: 0,
}],
entities: vec![VisibleEntity {
entity_id: 1,
x: 10.0,
y: 10.0,
z: 0,
kind: EntityKind::Player,
visibility: VisibilitySector::Forward,
relationship: RelationshipState::Unknown,
observation: EntityVisibility::Visible,
tell_state: None,
}],
visible_tiles: vec![],
nearby_interactions: vec![],
current_monologue: Some(MonologueEvent {
id: "test_monologue_001".to_string(),
text: "Something feels off about this place.".to_string(),
duration_seconds: 4.0,
}),
pending_recognitions: vec![PendingRecognitionWire {
entity_id: 7,
x: 12.0,
y: 8.0,
z: 0,
remaining_ticks: 3,
total_delay_ticks: 10,
}],
dialogue_response: Some(DialogueResponseEvent {
line_id: "kael_d_001".to_string(),
text: "We need to talk about the shipment.".to_string(),
speaker_entity_id: 99,
speaker_color_index: 2,
speaker_name: "Kael".to_string(),
}),
blocked_entities: vec![5, 6],
scan_events: vec![],
sound_events: vec![],
follow_state: None,
character_pressure: None,
rng_seed: Some(0xDEADBEEF),
poi_list: vec![PoiWire {
poi_id: "docking_bay_7".to_string(),
name: "Docking Bay 7".to_string(),
x: 50,
y: 30,
z: 0,
category: PoiCategory::Location,
}],
examine_result: Some(ExamineResultWire {
entity_id: 42,
text: "A smuggler, probably. The way they hold themselves.".to_string(),
confidence: KnowledgeConfidence::KnowsOf,
}),
save_result: None,
player_knowledge: Some(PlayerKnowledgeWire {
entities: vec![KnownEntityWire {
entity_id: 99,
name: "Kael".to_string(),
confidence: KnowledgeConfidence::KnowsDetails,
source: "DirectObservation".to_string(),
state: KnowledgeState::Active,
relationship: RelationshipState::Known,
last_observed_tick: 40,
}],
facts: vec![KnownFactWire {
fact_id: "poi.docking_bay_7".to_string(),
confidence: KnowledgeConfidence::KnowsOf,
source: "DirectObservation".to_string(),
state: KnowledgeState::Active,
acquired_tick: 10,
}],
}),
triangle_crisis_events: vec![],
state_hash: None,
debug_response: None,
sim_errors: vec![],
current_ticker: None,
settings_response: None,
economy_snapshot: None,
bookmark_catalog: None,
};
write_fixture(
"snapshot_full",
&rmp_serde::to_vec_named(&snapshot_full).unwrap(),
);
// player_input_move: tick=1, MoveNorth
let input_move = PlayerInput {
tick: 1,
action: PlayerAction::MoveNorth,
};
write_fixture(
"player_input_move",
&rmp_serde::to_vec_named(&input_move).unwrap(),
);
// player_input_interact: tick=2, Interact { target: 99, verb: "Talk" }
let input_interact = PlayerInput {
tick: 2,
action: PlayerAction::Interact {
target_entity_id: Some(99),
verb: Some("Talk".to_string()),
},
};
write_fixture(
"player_input_interact",
&rmp_serde::to_vec_named(&input_interact).unwrap(),
);
// malformed: intentionally truncated bytes — tests error handling in both Rust and GDScript
// 0x82 = fixmap with 2 entries, 0xa4 = fixstr of length 4 — incomplete map, no key/value follows
write_fixture("malformed", &[0x82u8, 0xa4u8]);
// === Boundary value fixtures (#472) ===
// 14 raw integer values at encoding format boundaries (Appendix C).
// These are Rust-encoded MessagePack that GDScript must decode correctly.
// Covers every encoding format transition and the int16/int32 asymmetry zones.
let boundary_raw: [(u64, &str); 14] = [
// pos fixint boundaries
(0, "boundary_raw_0"),
(127, "boundary_raw_127"),
// uint 8 boundaries
(128, "boundary_raw_128"),
(255, "boundary_raw_255"),
// int16/uint16 asymmetry zone (GDScript: int_16, Rust: uint_16)
(256, "boundary_raw_256"),
(32767, "boundary_raw_32767"),
// uint 16 boundaries
(32768, "boundary_raw_32768"),
(65535, "boundary_raw_65535"),
// int32/uint32 asymmetry zone (GDScript: int_32, Rust: uint_32)
(65536, "boundary_raw_65536"),
(2147483647, "boundary_raw_2147483647"),
// uint 32 boundaries
(2147483648, "boundary_raw_2147483648"),
(4294967295, "boundary_raw_4294967295"),
// int 64 boundaries
(4294967296, "boundary_raw_4294967296"),
(u64::MAX >> 1, "boundary_raw_i64_max"), // 2^63-1 = i64::MAX
];
for (value, name) in &boundary_raw {
// Encode as u64 (matches how entity_id/tick are encoded in snapshots)
let bytes = rmp_serde::to_vec(value).expect("encode boundary value");
write_fixture(name, &bytes);
}
// 5 snapshot fixtures at boundary tick values.
// Tests that GDScript can decode full ObserverSnapshot structs when the tick
// field crosses encoding format boundaries.
let boundary_snapshots: [(u64, &str); 5] = [
(0, "snapshot_boundary_tick_0"), // pos fixint
(127, "snapshot_boundary_tick_127"), // pos fixint max
(32767, "snapshot_boundary_tick_32767"), // int16/uint16 asymmetry
(2147483647, "snapshot_boundary_tick_2b31m1"), // int32/uint32 asymmetry
(4294967296, "snapshot_boundary_tick_2b32"), // int64 minimum
];
for (tick, name) in &boundary_snapshots {
let snapshot = fixture_snapshot(*tick, vec![]);
write_fixture(name, &rmp_serde::to_vec_named(&snapshot).unwrap());
}
}
/// Generate a snapshot fixture with a populated `BookmarkCatalog`.
///
/// Client (#618) uses this to validate GDScript MessagePack decode against
/// real `rmp_serde` output — field order and string encoding may diverge
/// from GDScript-constructed data.
#[test]
#[ignore] // Run manually: cargo test --test gen_fixtures -- --ignored
fn generate_snapshot_with_bookmark_catalog() {
let catalog = BookmarkCatalog {
bookmarks: vec![BookmarkWire {
id: "tycoon".into(),
title: "Tycoon".into(),
subtitle: "Small business owner on the make.".into(),
flavor: String::new(),
default_location: "GJ 35".into(),
allowed_locations: vec!["GJ 35".into()],
allowed_locations_cultures: vec!["frontier_industrial".into()],
career: CareerKindWire::Tycoon,
starting_capital_tractus: 5_000,
}],
};
let mut snapshot = fixture_snapshot(0, vec![]);
snapshot.bookmark_catalog = Some(catalog);
let bytes =
rmp_serde::to_vec_named(&snapshot).expect("serialize snapshot_with_bookmark_catalog");
write_fixture("snapshot_with_bookmark_catalog", &bytes);
}
/// Atlas layer-stream responses (#969, D-225) — the client decodes these to
/// render the per-layer Atlas overlays (#960). Covers Ready (with a small
/// Layer1Output), Pending, and NotFound.
#[test]
#[ignore] // Run manually: cargo test --test gen_fixtures -- --ignored
fn generate_atlas_layer_response_fixtures() {
let layer1 = Layer1Output {
body_id: "GJ1c".into(),
river_network: RiverNetwork {
river_cells: vec![(12, 58), (12, 59)],
confluences: vec![],
mouths: vec![(12, 58)],
river_class: vec![1, 2],
river_downstream: vec![2, 8], // 2=E direction; 8=MOUTH sentinel
river_seaward: vec![(0, 0), (12, 60)], // meaningful only for the MOUTH entry
},
drainage_basins: vec![DrainageBasin {
basin_id: 1,
boundary: vec![(0, 0), (0, 10), (10, 10), (10, 0)],
area_pct: 0.42,
territorial_status:
settled_reach_server::simulation::generator::TerritorialStatus::FrontierUnclaimed,
}],
attractors: vec![GeographicAttractor {
position: (12, 58),
attractor_type: AttractorType::CoastalAccess,
strength: 90,
sub_biome: SubBiomeVariant::CoastalLowland,
terrain_modification_cost: 170,
water_bearing: 90,
}],
grid_w: 512,
grid_h: 256,
survey_basin_dirs: std::collections::BTreeMap::new(),
feature_names: vec![],
};
// T-960 §1/§2: a small populated RoadGraphLayer + SettlementLayer, one
// settlement (a capital) connected to one waypoint-free short edge.
let road_graph = RoadGraphLayer {
nodes: vec![
RoadGraphNode {
position: (12, 58),
kind: RoadNodeKind::Settlement,
city_id: Some(1),
},
RoadGraphNode {
position: (20, 70),
kind: RoadNodeKind::Settlement,
city_id: Some(2),
},
],
edges: vec![RoadGraphEdge {
from: 0,
to: 1,
path: vec![(12, 58), (16, 64), (20, 70)],
maintenance: MaintenanceAuthority::Administrative,
is_rail: false,
named_route_id: None,
}],
};
let settlements = SettlementLayer {
settlements: vec![
SettlementEntry {
city_id: 1,
name: "Port Aldren".into(),
position: (12, 58),
size_class: SettlementSizeClass::Major,
is_capital: true,
is_port: true,
},
SettlementEntry {
city_id: 2,
name: "Farmstead Rell".into(),
position: (20, 70),
size_class: SettlementSizeClass::Minor,
is_capital: false,
is_port: false,
},
],
};
// T-1113: a small populated RegionGridLayer — a 2×1 covering grid, one
// temperate region and one airless-style region (mean_temp_dc sentinel),
// mirroring the values exercised by
// `build_region_grid_encodes_dense_quantized_climate` in layer_proxy.rs.
let region_grid = RegionGridLayer {
cols: 2,
rows: 1,
season: vec![SeasonPhase::Summer as u8, SeasonPhase::Winter as u8],
weather: vec![WeatherState::Clear as u8, WeatherState::Snow as u8],
mean_temp_dc: vec![
123,
settled_reach_server::atlas::layer_proxy::REGION_TEMP_NONE_DC,
],
moisture_q: vec![80, 5],
};
// T-1119 (D-226 T-1112 amendment): a populated QuarterFootprintLayer
// covering both fixture settlements — city_id 1 (Port Aldren, a dense
// Commercial-dominant quarter with landmarks/corridors) and city_id 2
// (Farmstead Rell, a sparse Residential-dominant quarter with neither),
// so the fixture exercises both a "rich" entry and a "minimal" entry
// rather than only one shape.
let quarter_footprints = QuarterFootprintLayer {
entries: BTreeMap::from([
(
1,
QuarterFootprintEntry {
city_id: 1,
density_avg_pct: 62,
dominant_district_type: DistrictType::Commercial,
dominant_zoning: ZoningType::Commercial,
landmark_count: 3,
corridor_count: 4,
},
),
(
2,
QuarterFootprintEntry {
city_id: 2,
density_avg_pct: 18,
dominant_district_type: DistrictType::Residential,
dominant_zoning: ZoningType::Residential,
landmark_count: 0,
corridor_count: 1,
},
),
]),
};
let ready = AtlasLayerResponse {
body_id: "GJ1c".into(),
status: AtlasLayerStatus::Ready,
layer1: Some(layer1),
district_grid: None,
road_graph: Some(road_graph),
settlements: Some(settlements),
region_grid: Some(region_grid),
district_window: None,
quarter_footprints: Some(quarter_footprints),
};
write_fixture(
"atlas_response_ready",
&rmp_serde::to_vec_named(&ready).unwrap(),
);
let pending = AtlasLayerResponse {
body_id: "GJ1c".into(),
status: AtlasLayerStatus::Pending,
layer1: None,
district_grid: None,
road_graph: None,
settlements: None,
region_grid: None,
district_window: None,
quarter_footprints: None,
};
write_fixture(
"atlas_response_pending",
&rmp_serde::to_vec_named(&pending).unwrap(),
);
let not_found = AtlasLayerResponse {
body_id: "ghost".into(),
status: AtlasLayerStatus::NotFound,
layer1: None,
district_grid: None,
road_graph: None,
settlements: None,
region_grid: None,
district_window: None,
quarter_footprints: None,
};
write_fixture(
"atlas_response_not_found",
&rmp_serde::to_vec_named(&not_found).unwrap(),
);
// D-226 T-1124 amendment, T-1137: a Ready response carrying a populated
// district_window — the windowed-family field, distinct from the five
// whole-body layers above. A small n=2 window keeps the fixture readable
// while exercising every field (including the REGION_TEMP_NONE_DC
// sentinel and VegetationClass::Marine = 6, both non-negotiable per the
// amendment §3).
let window = DistrictWindowLayer {
center: (10, -5),
n: 2,
granularity_v2: WindowGranularity::District,
min_wl_m: 0,
morphology: vec![0, 8, 14, 16], // OpenOcean, AlluvialPlain, Alpine, Wetland
elev_q: vec![0, 45, 98, 60],
temp_dc: vec![205, 150, REGION_TEMP_NONE_DC, 80], // 20.5°C, 15.0°C, airless sentinel, 8.0°C
moisture_q: vec![90, 55, 0, 100],
vegetation: vec![6, 3, 0, 5], // Marine, Forest, Absent, RiparianThicket
glaciation: vec![0, 0, 4, 1], // None, None, IceCap, Light
courses: vec![],
};
let ready_with_window = AtlasLayerResponse {
body_id: "GJ1c".into(),
status: AtlasLayerStatus::Ready,
layer1: None,
district_grid: None,
road_graph: None,
settlements: None,
region_grid: None,
district_window: Some(window),
quarter_footprints: None,
};
write_fixture(
"atlas_response_ready_with_window",
&rmp_serde::to_vec_named(&ready_with_window).unwrap(),
);
}