Files
settled-reach/server/tests/gen_fixtures.rs
T
jpmschweitzer 3e87fd5b4f feat(simulation): T-1151 window par_iter + T-1150 granularity carrier (five touch points + aliasing tests)
T-1151: build_district_window_layer dispatches one Rayon task per row
(pure derive_window_cell via derive_at_metres), scattered row-major into
the flat arrays; a cfg(test) serial path backs the bit-identical
parallel-vs-serial golden.

T-1150: serde-default window_granularity (1=district, 4=quarter) +
window_min_wl_m on AtlasLayerRequest — additive, no sixth demux shape,
old frames decode unchanged (tested). Quarter mode = full
reclassification at 512m spacing over the same world rect ((4n)x(4n)
cells); WIRE_CAP_CELLS=4096 enforces n*granularity <= cap (quarter
clamps n to 16, the design doc's worked example). Granularity + min_wl
key ALL five touch points: DistrictWindowLayer echo, server FIFO-256
cache key (now a 5-tuple), per-connection coalescing key, client
request codec (omitted-at-default wire fields), client LRU key.

Mandatory aliasing regressions on both ends: identical (body, center, n)
at granularity 1 vs 4 produce distinct cache entries and correct
per-granularity payload shapes (server, 3-thread queue to avoid the
AnalyzeBody thread contention found while writing it) and distinct
client cache keys (gdUnit). Replay fixture regenerated — the layer
struct grew two echoed fields (231->254 bytes, content verified).

Client requests stay district-granularity by default — quarter requests
arrive with T-1153's rung selection.
2026-07-22 00:27:40 +02:00

754 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, REGION_TEMP_NONE_DC,
WINDOW_GRANULARITY_DISTRICT,
};
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)],
},
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,
district_basin_dirs: std::collections::BTreeMap::new(),
};
// 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: WINDOW_GRANULARITY_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
};
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(),
);
}