test+docs(simulation): address PR #159 review (T-1032)
- Hoshe: body_without_system_row test inserted a NULL system_id, impossible under production schema (system_id NOT NULL REFERENCES star_systems). Rewrite as body_with_orphan_system_id_* — non-NULL system_id with no matching star_systems row (the real case the LEFT JOIN guards) + NOT NULL in the test schema + corrected comment. - Tyre: cascade.rs PERF/TODO comment said the region path 'defers to T-1032'; T-1032 IS this PR, so production dispatch is now live. Update to track T-1028 only and note the cost is live in production. No production logic change. cargo test body_params_reader 7 pass, clippy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -286,8 +286,13 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn body_without_system_row_returns_null_stellar_fields() {
|
fn body_with_orphan_system_id_returns_null_stellar_fields() {
|
||||||
// Body has no system_id — LEFT JOIN produces NULL for star_systems columns.
|
// The LEFT JOIN guards against a body whose system_id has NO matching
|
||||||
|
// star_systems row (an incomplete DB — e.g. a body loaded before its
|
||||||
|
// system). It is NOT about a NULL system_id: production schema declares
|
||||||
|
// `bodies.system_id TEXT NOT NULL REFERENCES star_systems(system_id)`,
|
||||||
|
// so a NULL system_id can't exist. Mirror that NOT NULL here and test the
|
||||||
|
// real case — a non-NULL system_id pointing at an absent system row.
|
||||||
let n = SEQ.fetch_add(1, Ordering::Relaxed);
|
let n = SEQ.fetch_add(1, Ordering::Relaxed);
|
||||||
let path = std::env::temp_dir().join(format!("sr_bpr_ns_{}_{n}.db", std::process::id()));
|
let path = std::env::temp_dir().join(format!("sr_bpr_ns_{}_{n}.db", std::process::id()));
|
||||||
let _ = std::fs::remove_file(&path);
|
let _ = std::fs::remove_file(&path);
|
||||||
@@ -300,7 +305,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
CREATE TABLE bodies (
|
CREATE TABLE bodies (
|
||||||
body_id TEXT PRIMARY KEY,
|
body_id TEXT PRIMARY KEY,
|
||||||
system_id TEXT,
|
system_id TEXT NOT NULL,
|
||||||
hydrosphere TEXT,
|
hydrosphere TEXT,
|
||||||
atmosphere TEXT,
|
atmosphere TEXT,
|
||||||
planet_class TEXT,
|
planet_class TEXT,
|
||||||
@@ -309,9 +314,9 @@ mod tests {
|
|||||||
);",
|
);",
|
||||||
)
|
)
|
||||||
.expect("create tables");
|
.expect("create tables");
|
||||||
// Insert body with no system_id (no system row exists).
|
// Body points at a system that doesn't exist in star_systems (orphan FK).
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"INSERT INTO bodies (body_id, system_id, atmosphere) VALUES ('lonely', NULL, 'thin')",
|
"INSERT INTO bodies (body_id, system_id, atmosphere) VALUES ('lonely', 'GJ-ORPHAN', 'thin')",
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
.expect("insert");
|
.expect("insert");
|
||||||
@@ -320,7 +325,7 @@ mod tests {
|
|||||||
let reader = BodyParamsReader::open(&path).expect("open");
|
let reader = BodyParamsReader::open(&path).expect("open");
|
||||||
let params = reader.read_body_params("lonely").expect("read");
|
let params = reader.read_body_params("lonely").expect("read");
|
||||||
assert_eq!(params.atmosphere.as_deref(), Some("thin"));
|
assert_eq!(params.atmosphere.as_deref(), Some("thin"));
|
||||||
// No system row → stellar fields NULL from LEFT JOIN.
|
// No matching star_systems row → stellar fields NULL from the LEFT JOIN.
|
||||||
assert!(params.spectral_class.is_none());
|
assert!(params.spectral_class.is_none());
|
||||||
assert!(params.star_type.is_none());
|
assert!(params.star_type.is_none());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,11 +216,12 @@ pub fn run_cascade_from_heightmap(
|
|||||||
// result nor the TerrainAnalysis is stored on Layer1Output, so we re-run both
|
// result nor the TerrainAnalysis is stored on Layer1Output, so we re-run both
|
||||||
// here. Pure → determinism preserved, but the drainage re-run is NOT free at
|
// here. Pure → determinism preserved, but the drainage re-run is NOT free at
|
||||||
// the ~6 000-regions/body working scale (D-203).
|
// the ~6 000-regions/body working scale (D-203).
|
||||||
// PERF/TODO(T-1028/T-1032): cache TerrainAnalysis on Layer1Output to drop this
|
// PERF/TODO(T-1028): cache TerrainAnalysis on Layer1Output to drop this
|
||||||
// redundant drainage pass, and validate the combined cost against the D-239 §10
|
// redundant drainage pass, and validate the combined cost against the D-239 §10
|
||||||
// ~45 ms/body budget in the T-1031 verification harness. Latent for now — this
|
// ~45 ms/body budget in the T-1031 verification harness. This is now a LIVE
|
||||||
// path only runs when body_params is Some, which production defers to T-1032.
|
// production cost: T-1032 wired the real body_params read, so every analyzed
|
||||||
// If body_params is None, the region layer is skipped (e.g. unit tests without DB).
|
// body runs this path. If body_params is None, the region layer is skipped
|
||||||
|
// (e.g. unit tests without DB).
|
||||||
if up_to >= CascadeLayer::RegionProfile {
|
if up_to >= CascadeLayer::RegionProfile {
|
||||||
if let Some(params) = body_params {
|
if let Some(params) = body_params {
|
||||||
use crate::atlas::drainage;
|
use crate::atlas::drainage;
|
||||||
|
|||||||
Reference in New Issue
Block a user