diff --git a/server/src/npc/disclosure.rs b/server/src/npc/disclosure.rs index 864eff35e..27064676d 100644 --- a/server/src/npc/disclosure.rs +++ b/server/src/npc/disclosure.rs @@ -708,8 +708,10 @@ mod tests { )]); // Set computed_tick = 1 (non-zero). Tick 5 is within the 30-tick refresh window. - let mut candidates = DisclosureCandidates::default(); - candidates.computed_tick = 1; + let candidates = DisclosureCandidates { + computed_tick: 1, + ..Default::default() + }; let npc = app .world_mut() diff --git a/server/src/npc/mood.rs b/server/src/npc/mood.rs index e23edd421..705f0d7a1 100644 --- a/server/src/npc/mood.rs +++ b/server/src/npc/mood.rs @@ -656,7 +656,7 @@ mod tests { // from derive_mood(). They must be set externally by other systems // (e.g., observation pipeline for Suspicious, activity scheduler for Focused). // This test documents the invariant: derive_mood never emits these states. - use std::collections::HashSet; + use std::collections::BTreeSet; let phases = [ DayPhase::Morning, @@ -668,7 +668,7 @@ mod tests { let thresholds: &[i16] = &[0, 1, 50, 100]; let warm_flags = [false, true]; - let mut observed = HashSet::new(); + let mut observed = BTreeSet::new(); for &phase in &phases { for &stress in stresses { for &threshold in thresholds { diff --git a/server/src/npc/vision.rs b/server/src/npc/vision.rs index 5872e6bb7..3ff38fffd 100644 --- a/server/src/npc/vision.rs +++ b/server/src/npc/vision.rs @@ -358,10 +358,12 @@ mod tests { let target_sid = registry.register(target); spatial.update(target, pos(16, 14)); - // Wall between NPC and target - let mut walkability = world.resource_mut::(); - walkability.set_walkable(&pos(16, 15), false); - drop(walkability); + // Wall between NPC and target — scope the mutable resource borrow so it + // is released before the world.insert_resource calls below. + { + let mut walkability = world.resource_mut::(); + walkability.set_walkable(&pos(16, 15), false); + } world.insert_resource(registry); world.insert_resource(spatial); diff --git a/server/src/settings/types.rs b/server/src/settings/types.rs index 277fa3b46..9b335d47a 100644 --- a/server/src/settings/types.rs +++ b/server/src/settings/types.rs @@ -82,7 +82,7 @@ mod tests { let values = vec![ SettingValue::String("hello".into()), SettingValue::Int(-1), - SettingValue::Float(3.14), + SettingValue::Float(2.5), SettingValue::Bool(false), ]; for val in &values { diff --git a/server/src/simulation/input.rs b/server/src/simulation/input.rs index dbcb3e746..74f4cd721 100644 --- a/server/src/simulation/input.rs +++ b/server/src/simulation/input.rs @@ -2601,9 +2601,7 @@ mod tests { }); world.insert_resource(registry); - world - .spawn((PlayerCharacter, TilePosition::new(5, 5, 0))) - .id(); + world.spawn((PlayerCharacter, TilePosition::new(5, 5, 0))); world } diff --git a/server/src/simulation/listening.rs b/server/src/simulation/listening.rs index a1105eb07..87fce6437 100644 --- a/server/src/simulation/listening.rs +++ b/server/src/simulation/listening.rs @@ -441,13 +441,9 @@ mod tests { fn eavesdrop_threshold_careful_less_than_normal() { // T4: The careful threshold MUST be strictly less than the normal // threshold — careful stance rewards patience with faster eavesdrop - // activation (D-053, D-018). - assert!( - EAVESDROP_THRESHOLD_CAREFUL < EAVESDROP_THRESHOLD, - "EAVESDROP_THRESHOLD_CAREFUL ({}) must be < EAVESDROP_THRESHOLD ({})", - EAVESDROP_THRESHOLD_CAREFUL, - EAVESDROP_THRESHOLD, - ); + // activation (D-053, D-018). Compile-time invariant: pins the ordering + // so a future const edit can't silently invert it. + const _: () = assert!(EAVESDROP_THRESHOLD_CAREFUL < EAVESDROP_THRESHOLD); } // ----------------------------------------------------------------------- diff --git a/server/src/simulation/monologue.rs b/server/src/simulation/monologue.rs index 2b853ee85..55d3c3c65 100644 --- a/server/src/simulation/monologue.rs +++ b/server/src/simulation/monologue.rs @@ -819,12 +819,13 @@ mod tests { queue.push_anomaly(42, 0); // Pre-fill the monologue buffer (as if trigger_monologue already wrote) - let mut buffer = MonologueBuffer::default(); - buffer.event = Some(MonologueEvent { - id: "existing_line".to_string(), - text: "I should keep this.".to_string(), - duration_seconds: 5.0, - }); + let buffer = MonologueBuffer { + event: Some(MonologueEvent { + id: "existing_line".to_string(), + text: "I should keep this.".to_string(), + duration_seconds: 5.0, + }), + }; world.spawn(( PlayerCharacter, @@ -1074,12 +1075,13 @@ mod tests { }); // Pre-fill MonologueBuffer (e.g., from trigger_monologue) - let mut buffer = MonologueBuffer::default(); - buffer.event = Some(MonologueEvent { - id: "existing_line".to_string(), - text: "Already have something to say.".to_string(), - duration_seconds: 5.0, - }); + let buffer = MonologueBuffer { + event: Some(MonologueEvent { + id: "existing_line".to_string(), + text: "Already have something to say.".to_string(), + duration_seconds: 5.0, + }), + }; world.spawn(( PlayerCharacter, diff --git a/server/src/simulation/save_io.rs b/server/src/simulation/save_io.rs index f78605290..92d020303 100644 --- a/server/src/simulation/save_io.rs +++ b/server/src/simulation/save_io.rs @@ -756,11 +756,11 @@ mod tests { /// The warn! in process_player_input fires; here we just confirm last-write-wins. #[test] fn pending_command_overwrite_last_write_wins() { - let mut pending = SaveLoadPending::default(); - - pending.pending = Some(SaveLoadCommand::Save { - path: PathBuf::from("/tmp/first.msgpack"), - }); + let mut pending = SaveLoadPending { + pending: Some(SaveLoadCommand::Save { + path: PathBuf::from("/tmp/first.msgpack"), + }), + }; // Overwrite with a Load command pending.pending = Some(SaveLoadCommand::Load { path: PathBuf::from("/tmp/second.msgpack"), diff --git a/server/src/simulation/sound.rs b/server/src/simulation/sound.rs index 98e7cf124..d1caa8b1e 100644 --- a/server/src/simulation/sound.rs +++ b/server/src/simulation/sound.rs @@ -17,7 +17,7 @@ use crate::simulation::movement::TilePosition; /// Typed sound event categories. /// Client maps each kind to its audio asset registry key (D-038). -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub enum SoundEventKind { /// Footstep — emitted by moving entities. Intensity varies by stance. Footstep, @@ -464,7 +464,7 @@ mod tests { "all five SoundEventKind variants collected" ); - let collected_kinds: std::collections::HashSet = + let collected_kinds: std::collections::BTreeSet = queue.events.iter().map(|e| e.kind).collect(); for kind in [ SoundEventKind::Footstep, diff --git a/server/tests/cross_room_transitions.rs b/server/tests/cross_room_transitions.rs index 92cdd1f95..4ea36ea00 100644 --- a/server/tests/cross_room_transitions.rs +++ b/server/tests/cross_room_transitions.rs @@ -641,8 +641,6 @@ fn t8_sprint_blocks_eavesdrop_then_careful_enables_accumulation() { } // Verify D-071 invariant: Careful threshold is strictly less than normal. - assert!( - EAVESDROP_THRESHOLD_CAREFUL < EAVESDROP_THRESHOLD, - "T8: Careful threshold must be < normal threshold (D-071 invariant)" - ); + // Compile-time invariant — pins the ordering against a future const edit. + const _: () = assert!(EAVESDROP_THRESHOLD_CAREFUL < EAVESDROP_THRESHOLD); } diff --git a/server/tests/information_boundaries.rs b/server/tests/information_boundaries.rs index 5d52147f3..af08935ae 100644 --- a/server/tests/information_boundaries.rs +++ b/server/tests/information_boundaries.rs @@ -59,7 +59,7 @@ fn player_kg_has_no_passive_npc_leakage() { // Negative assertion: a freshly created KG contains no entity references. assert!( - player_kg.entities.get(&npc_id).is_none(), + !player_kg.entities.contains_key(&npc_id), "IB-1: fresh KnowledgeGraph must not contain any entity (passive leakage — D-010 principle 2)" ); assert!( @@ -78,7 +78,7 @@ fn player_kg_has_no_passive_npc_leakage() { // Player's KG must be empty regardless of NPCs existing nearby. let kg = world.get::(player).unwrap(); assert!( - kg.entities.get(&npc_id).is_none(), + !kg.entities.contains_key(&npc_id), "IB-1: spawning an NPC in the world must not passively populate the player's KG" ); assert!( @@ -249,7 +249,7 @@ fn save_state_npc_kg_isolation() { // NPC_A's KG entry for NPC_B is NPC_A's OBSERVATION DATA (where NPC_A saw NPC_B). // This is not NPC_B's own KG — it's NPC_A's record of NPC_B's position. assert!( - kg.entities.get(&npc_b_id).is_some(), + kg.entities.contains_key(&npc_b_id), "IB-4 sanity: NPC_A's KG should still contain its observation of NPC_B after roundtrip" ); } diff --git a/server/tests/shadowcast_bench.rs b/server/tests/shadowcast_bench.rs index 91ec6b660..841713b78 100644 --- a/server/tests/shadowcast_bench.rs +++ b/server/tests/shadowcast_bench.rs @@ -7,7 +7,7 @@ use rand::Rng; use rand::SeedableRng; use rand_chacha::ChaCha8Rng; use settled_reach_server::perception::shadowcast::{recursive_shadowcast, symmetric_shadowcast}; -use std::collections::HashSet; +use std::collections::BTreeSet; use std::time::Instant; /// Configuration for a benchmark run @@ -20,9 +20,9 @@ struct BenchConfig { } /// Generate a random wall map with specified density -fn generate_wall_map(size: i32, density: f64, seed: u64) -> HashSet<(i32, i32)> { +fn generate_wall_map(size: i32, density: f64, seed: u64) -> BTreeSet<(i32, i32)> { let mut rng = ChaCha8Rng::seed_from_u64(seed); - let mut walls = HashSet::new(); + let mut walls = BTreeSet::new(); for x in 0..size { for y in 0..size { @@ -203,7 +203,7 @@ fn symmetric_algorithm_is_symmetric() { println!("\n=== Testing Symmetric Property (simplified) ===\n"); // Simple open field test - perfect symmetry should hold here - let no_walls: HashSet<(i32, i32)> = HashSet::new(); + let no_walls: BTreeSet<(i32, i32)> = BTreeSet::new(); let is_opaque = |x: i32, y: i32| no_walls.contains(&(x, y)); let test_positions = vec![(0, 0), (3, 3), (5, 2), (1, 7)]; @@ -246,14 +246,14 @@ fn both_algorithms_agree_on_basic_cases() { println!("\n=== Comparing Algorithm Results ===\n"); let test_cases = vec![ - ("Open field", HashSet::new()), + ("Open field", BTreeSet::new()), ("Single wall at (2,0)", { - let mut w = HashSet::new(); + let mut w = BTreeSet::new(); w.insert((2, 0)); w }), ("L-shaped corridor", { - let mut w = HashSet::new(); + let mut w = BTreeSet::new(); for i in 0..5 { w.insert((i, 2)); w.insert((2, i)); diff --git a/server/tests/triangle_validation.rs b/server/tests/triangle_validation.rs index aa67621c2..6d45bf498 100644 --- a/server/tests/triangle_validation.rs +++ b/server/tests/triangle_validation.rs @@ -390,7 +390,7 @@ fn triangle_validation_cross_template_deterministic() { &mut world1, hub_id, bar_id, - &[overridden.clone()], + std::slice::from_ref(&overridden), &mut SimRng::new(42), );