Files
settled-reach/server/tests/window_derivation_golden.rs
T

306 lines
11 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.
//! Window-derivation golden regression (T-1162).
//!
//! Pins `derive_at_metres` (District/Quarter rungs) and
//! `derive_orbital_at_metres` (Region rung) output at a fixed
//! (seed, body, coords) sweep, across all three rung cutoffs — the window
//! path `layer_proxy::derive_window_cell` actually calls in production.
//! Mirrors `tests/derivation_harness.rs`'s golden pattern exactly (same
//! regen convention, same double-derive determinism check, same JSON-value
//! comparison so formatting drift doesn't false-positive).
//!
//! **T-1162 regen rationale:** this golden is generated AFTER the T-1162
//! octave-extension changes (extended coast-warp band, sub-district relief at
//! Quarter, vegetation patchiness) — it deliberately pins the NEW output.
//! There is no "pre-T-1162" golden to preserve: this file did not exist
//! before this ticket, so there is nothing to regress against except itself
//! from this point forward. Any future change to the coast warp, the voxel
//! relief band, the vegetation-patchiness field, or the MIN_WL_BANDS_M
//! quantization will change this golden's values — regenerate deliberately
//! (per the asset-pipeline discipline: source changes, not hand-edits).
//!
//! Run: `cargo test --test window_derivation_golden`
//! Regenerate: `UPDATE_GOLDEN=1 cargo test --test window_derivation_golden`
use std::path::PathBuf;
use settled_reach_server::atlas::district_profile::{
derive_at_metres, derive_orbital_at_metres, BodyParams, ClimateConstants,
};
use settled_reach_server::atlas::drainage;
use settled_reach_server::atlas::features::TerrainAnalysis;
use settled_reach_server::atlas::heightmap::BodyHeightmap;
use settled_reach_server::atlas::scale;
use settled_reach_server::seed::{SeedChain, SeedDomain};
const GOLDEN_FILE: &str = "tests/golden/window_derivation_golden.json";
/// Compact representation of a `DistrictProfile` sample for golden pinning.
/// Integer-discriminant fields only (D-010) — no float equality flakiness.
#[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone)]
struct GoldenSample {
label: String,
rung: String,
wx_m: i64,
wy_m: i64,
min_wl_m: i64,
morphology: u8,
tectonic: u8,
glaciation: u8,
precipitation: u8,
slope_q: i32,
elev_q: i32,
ocean_fraction_q: i32,
temperature_dc: i32, // deci-°C, i32::MIN sentinel for None (airless)
moisture_q: i32,
vegetation: u8,
}
fn sample_hm() -> BodyHeightmap {
// Deterministic gradient with enough variance for coast/relief/vegetation
// content to actually differ across the sweep positions — same shape
// convention as district_profile.rs's own test_hm / zoom_ladder_bench's
// bench_hm, sized a bit larger so the sweep coordinates land on distinct
// heightmap cells rather than a single interpolated patch.
let (w, h) = (128u32, 64u32);
let n = (w * h) as usize;
let data = (0..n)
.map(|i| {
let r = (i / w as usize) as f32 / h as f32;
let c = (i % w as usize) as f32 / w as f32;
// A gentle sine ripple on top of the linear gradient gives the
// coastline invention real slope/ocean-mask variance to warp.
let ripple = (c * std::f32::consts::TAU * 3.0).sin() * 0.08;
(r * 0.55 + c * 0.35 + ripple + 0.05).clamp(0.0, 1.0)
})
.collect();
BodyHeightmap {
body_id: "golden_body".into(),
width: w,
height: h,
data,
sea_level: 0.32,
}
}
fn sample_ta(hm: &BodyHeightmap) -> TerrainAnalysis {
let dr = drainage::analyze(&hm.data, hm.width, hm.height, hm.sea_level);
TerrainAnalysis::analyze(hm, &dr)
}
fn sample_params() -> BodyParams {
BodyParams {
hydrosphere: Some("ocean".into()),
atmosphere: Some("breathable".into()),
planet_class: Some("temperate".into()),
body_radius_km: Some(6371.0),
..Default::default()
}
}
/// Fixed sweep positions (world metres from origin) — a handful of points
/// spanning a coastal stretch (per the heightmap's ripple) plus a couple of
/// clearly inland/high-latitude points, so the golden exercises coast warp,
/// sub-district relief, and vegetation patchiness all at once.
fn sweep_positions() -> Vec<(&'static str, f64, f64)> {
vec![
("coastal_a", 2_000_000.0, 1_500_000.0),
("coastal_b", 2_050_000.0, 1_500_000.0),
("coastal_c", 2_100_000.0, 1_560_000.0),
("inland", 500_000.0, 3_000_000.0),
("high_lat", 1_200_000.0, 8_500_000.0),
]
}
fn derive_golden_sample(
label: &str,
rung: &str,
seed: SeedChain,
body_id: &str,
params: &BodyParams,
ta: &TerrainAnalysis,
climate: &ClimateConstants,
wx: f64,
wy: f64,
min_wl_m: f64,
orbital: bool,
) -> GoldenSample {
let prof = if orbital {
derive_orbital_at_metres(seed, body_id, params, ta, wx, wy, climate)
} else {
derive_at_metres(seed, body_id, params, ta, wx, wy, climate, min_wl_m)
};
GoldenSample {
label: label.to_string(),
rung: rung.to_string(),
wx_m: wx as i64,
wy_m: wy as i64,
min_wl_m: min_wl_m as i64,
morphology: prof.morphology_zone as u8,
tectonic: prof.tectonic_class as u8,
glaciation: prof.glaciation_grade as u8,
precipitation: prof.precipitation_class as u8,
slope_q: prof.slope_q,
elev_q: prof.elev_q,
ocean_fraction_q: prof.ocean_fraction_q,
temperature_dc: prof
.temperature_c
.map(|t| (t * 10.0).round() as i32)
.unwrap_or(i32::MIN),
moisture_q: prof.moisture_q,
vegetation: prof.vegetation_class as u8,
}
}
/// District's real quantized `min_wl_m` band (`layer_proxy::MIN_WL_BANDS_M`'s
/// pre-existing finest entry — District's own Nyquist floor, matching
/// `detail_scatter::OCTAVE_WAVELENGTHS_M`'s finest octave). NOT `2 *
/// DISTRICT_M` by construction coincidence alone — see the T-1162 discovery
/// documented on `MIN_WL_BANDS_M` and on `district_floor_cutoff_is_stable_and_deterministic`
/// in `district_profile.rs`: a District-rung request in production quantizes
/// to exactly this value, so the golden pins the SAME cutoff a real window
/// request would actually carry.
const DISTRICT_MIN_WL_M: f64 = 4_096.0;
/// Quarter's real quantized `min_wl_m` band (`layer_proxy::MIN_WL_BANDS_M`'s
/// new T-1162 entry — Quarter's own Nyquist floor, `2 * QUARTER_M`).
const QUARTER_MIN_WL_M: f64 = 1_024.0;
/// Build the full fixed sweep: every position × the three rung cutoffs
/// (District / Quarter use their REAL production `MIN_WL_BANDS_M` values /
/// Region via `derive_orbital_at_metres`, which takes no cutoff parameter —
/// see its own doc on why).
fn golden_samples() -> Vec<GoldenSample> {
let hm = sample_hm();
let ta = sample_ta(&hm);
let params = sample_params();
let climate = ClimateConstants::default();
let seed = SeedChain::root(0xC0FFEE_u64).derive(SeedDomain::Body, 7);
let body_id = "golden_body";
let mut out = Vec::new();
for (label, wx, wy) in sweep_positions() {
out.push(derive_golden_sample(
label,
"district",
seed,
body_id,
&params,
&ta,
&climate,
wx,
wy,
DISTRICT_MIN_WL_M,
false,
));
out.push(derive_golden_sample(
label,
"quarter",
seed,
body_id,
&params,
&ta,
&climate,
wx,
wy,
QUARTER_MIN_WL_M,
false,
));
out.push(derive_golden_sample(
label, "region", seed, body_id, &params, &ta, &climate, wx, wy, 0.0, true,
));
}
out
}
#[test]
fn window_derivation_golden_regression() {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let golden_path = manifest.join(GOLDEN_FILE);
// Double-derive determinism check (D-010) before ever touching the golden.
let run1 = golden_samples();
let run2 = golden_samples();
assert_eq!(
run1, run2,
"double-derivation mismatch — determinism is broken (D-010)"
);
let actual_json = serde_json::to_string_pretty(&run1).expect("serialize") + "\n";
if std::env::var("UPDATE_GOLDEN").is_ok() {
std::fs::create_dir_all(golden_path.parent().unwrap()).expect("mkdir golden");
std::fs::write(&golden_path, &actual_json).expect("write golden");
eprintln!(
"Golden written: {} ({} bytes)",
golden_path.display(),
actual_json.len()
);
return;
}
let golden_json = std::fs::read_to_string(&golden_path).unwrap_or_else(|e| {
panic!(
"Golden file not found: {}.\n\
First run: UPDATE_GOLDEN=1 cargo test --test window_derivation_golden\n{e}",
golden_path.display()
)
});
let actual_v: serde_json::Value = serde_json::from_str(&actual_json).expect("reparse actual");
let golden_v: serde_json::Value = serde_json::from_str(&golden_json).expect("parse golden");
if actual_v != golden_v {
panic!(
"Window-derivation golden mismatch — derivation chain changed.\n\
Update: UPDATE_GOLDEN=1 cargo test --test window_derivation_golden\n\
Golden: {}\nActual: {}",
golden_json.trim(),
actual_json.trim()
);
}
}
/// Cross-rung coherence sanity check on the golden's own fixed sweep: for
/// each coastal label, the Quarter-rung sample must differ from the
/// District-rung sample at the SAME position (the whole point of the
/// extension — Quarter sees finer content District's coarser cutoff
/// truncates). This is a structural companion to the golden file itself,
/// not a replacement for it — it fails loudly if the golden ever gets
/// regenerated with `min_wl_m` accidentally identical across rungs.
#[test]
fn quarter_and_district_rungs_diverge_at_the_same_position() {
let samples = golden_samples();
let mut any_diverged = false;
for (label, _, _) in sweep_positions() {
let district = samples
.iter()
.find(|s| s.label == label && s.rung == "district")
.unwrap();
let quarter = samples
.iter()
.find(|s| s.label == label && s.rung == "quarter")
.unwrap();
if district.elev_q != quarter.elev_q
|| district.slope_q != quarter.slope_q
|| district.moisture_q != quarter.moisture_q
{
any_diverged = true;
}
}
assert!(
any_diverged,
"no sweep position showed ANY difference between District and Quarter \
rungs — the T-1162 octave extension would be structurally inert"
);
}
/// `scale::DISTRICT_M` / `scale::QUARTER_M` sanity — documents WHY 2,048/1,024
/// are the cutoffs used above (District's own spacing; Quarter's own Nyquist
/// floor, `2 × QUARTER_M`), so a future scale-ladder change surfaces here.
#[test]
fn golden_cutoffs_match_the_scale_ladder() {
assert_eq!(scale::DISTRICT_M, 2_048);
assert_eq!(scale::QUARTER_M, 512);
assert_eq!(2 * scale::QUARTER_M, 1_024);
}