Files
settled-reach/server/tests/contamination.rs
T
jpmschweitzerandClaude Opus 4.6 aa79dd97e7 fix(simulation): Clippy cleanup and CI enforcement (#635)
Fix all Clippy warnings across the server codebase (2411 insertions, 1341
deletions). Raise type-complexity-threshold to 750 and too-many-arguments
to 12 in .clippy.toml for idiomatic Bevy ECS system signatures. The server
now passes `cargo clippy -- --deny warnings` cleanly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 10:33:15 +01:00

161 lines
5.3 KiB
Rust

//! Integration test: contamination activation mechanic (#254).
//!
//! Verifies that the storyteller activates contamination after the configured
//! delay and that all ActiveFork triangles receive pressure.
use bevy_ecs::prelude::*;
use bevy_ecs::schedule::Schedule;
use std::collections::BTreeMap;
use settled_reach_server::knowledge::types::StableId;
use settled_reach_server::simulation::tier::ActiveSim;
use settled_reach_server::simulation::time::SimulationTime;
use settled_reach_server::simulation::triangle::{
RoleId, TemplateId, TriangleClassification, TriangleId, TrianglePhase, TriangleState,
};
use settled_reach_server::storyteller::{
tick_contamination_activation, ContaminationActive, ContaminationEventQueue,
CONTAMINATION_DELAY_TICKS, CONTAMINATION_PRESSURE_DELTA,
};
fn make_triangle(
slug: &str,
classification: TriangleClassification,
base_ids: [u64; 3],
) -> TriangleState {
let mut role_assignments = BTreeMap::new();
role_assignments.insert(RoleId::new("role-a"), StableId(base_ids[0]));
role_assignments.insert(RoleId::new("role-b"), StableId(base_ids[1]));
role_assignments.insert(RoleId::new("role-c"), StableId(base_ids[2]));
TriangleState {
triangle_id: TriangleId::from_seed_and_slug(0, slug),
role_assignments,
tension: 0,
phase: match classification {
TriangleClassification::ActiveFork => TrianglePhase::Simmering,
TriangleClassification::PassiveTension => TrianglePhase::Dormant,
},
tension_rate: 1,
template_id: TemplateId::from_seed_and_slug(0, "authored"),
classification,
}
}
/// Acceptance test: run past CONTAMINATION_DELAY_TICKS, assert ContaminationActive
/// is set and all ActiveFork TriangleState entities have tension > 0.
#[test]
fn contamination_activates_after_delay() {
let mut world = World::new();
world.init_resource::<SimulationTime>();
world.init_resource::<ContaminationActive>();
world.init_resource::<ContaminationEventQueue>();
// Spawn 3 ActiveFork + 2 PassiveTension triangles (matching real content)
let fork1 = world
.spawn((
make_triangle("hub-power", TriangleClassification::ActiveFork, [1, 2, 3]),
ActiveSim,
))
.id();
let fork2 = world
.spawn((
make_triangle(
"bar-tensions",
TriangleClassification::ActiveFork,
[4, 5, 6],
),
ActiveSim,
))
.id();
let fork3 = world
.spawn((
make_triangle(
"informant-question",
TriangleClassification::ActiveFork,
[7, 8, 9],
),
ActiveSim,
))
.id();
let passive1 = world
.spawn((
make_triangle(
"worried-knowledge",
TriangleClassification::PassiveTension,
[10, 11, 12],
),
ActiveSim,
))
.id();
let passive2 = world
.spawn((
make_triangle(
"worried-partner",
TriangleClassification::PassiveTension,
[13, 14, 15],
),
ActiveSim,
))
.id();
let mut schedule = Schedule::default();
schedule.add_systems(tick_contamination_activation);
// Simulate past the delay threshold (CONTAMINATION_DELAY_TICKS = 300)
for tick in 0..=CONTAMINATION_DELAY_TICKS {
world.resource_mut::<SimulationTime>().tick = tick;
schedule.run(&mut world);
}
// Assert ContaminationActive is set
assert!(
world.resource::<ContaminationActive>().0,
"ContaminationActive must be true after CONTAMINATION_DELAY_TICKS ({}) ticks",
CONTAMINATION_DELAY_TICKS
);
// Assert all ActiveFork triangles have tension > 0
for (label, entity) in [
("hub-power", fork1),
("bar-tensions", fork2),
("informant-question", fork3),
] {
let state = world
.get::<TriangleState>(entity)
.unwrap_or_else(|| panic!("{} triangle entity should exist", label));
assert!(
state.tension > 0,
"ActiveFork triangle '{}' should have tension > 0 after contamination, got {}",
label,
state.tension
);
assert_eq!(
state.tension, CONTAMINATION_PRESSURE_DELTA,
"ActiveFork triangle '{}' tension should be exactly {} (contamination delta)",
label, CONTAMINATION_PRESSURE_DELTA
);
}
// Assert PassiveTension triangles were NOT pressured
for (label, entity) in [
("worried-knowledge", passive1),
("worried-partner", passive2),
] {
let state = world
.get::<TriangleState>(entity)
.unwrap_or_else(|| panic!("{} triangle entity should exist", label));
assert_eq!(
state.tension, 0,
"PassiveTension triangle '{}' should have tension 0 (not affected by contamination)",
label
);
}
// Assert exactly one ContaminationEvent was emitted
let events = world.resource_mut::<ContaminationEventQueue>().drain();
assert_eq!(events.len(), 1, "exactly one ContaminationEvent expected");
assert_eq!(events[0].tick, CONTAMINATION_DELAY_TICKS);
assert_eq!(events[0].triangles_affected, 3);
}