test(simulation): QA coverage for Phase 2 verbs, sprint suppression, occupancy, inventory
Add comprehensive test suites across sprint 6 systems: - Phase 2 observer filter: Confront injection, archetype label relabeling, contradiction marking, POI priority ordering (12 tests) - Sprint anomaly: detection during sprint, queue semantics, delay timing, D-055 visible-but-suppressed integration (7 tests) - Wire format: Confront and CharacterArchetype msgpack roundtrip (4 tests) - TilePresence: 4-layer coexistence on same tile (1 test) - Inventory: Take/Place full roundtrip, capacity enforcement (3 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -819,4 +819,105 @@ mod tests {
|
||||
assert!(world.get::<TilePosition>(item).is_some(), "item stays on ground");
|
||||
assert!(world.get::<CarriedBy>(item).is_none(), "no CarriedBy when full");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_then_place_roundtrip() {
|
||||
// D-065: full cycle — item on ground → Take → carried → Place → ground again
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime::default());
|
||||
world.init_resource::<crate::knowledge::EntityRegistry>();
|
||||
|
||||
let player = world
|
||||
.spawn((PlayerCharacter, TilePosition::new(5, 5, 0)))
|
||||
.id();
|
||||
let player_sid = world.resource_mut::<crate::knowledge::EntityRegistry>().register(player);
|
||||
|
||||
let item = world
|
||||
.spawn((
|
||||
TilePosition::new(5, 4, 0),
|
||||
ItemName("Manifest Copy".into()),
|
||||
))
|
||||
.id();
|
||||
let item_sid = world.resource_mut::<crate::knowledge::EntityRegistry>().register(item);
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(process_player_input);
|
||||
|
||||
// Step 1: Take
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 0,
|
||||
action: PlayerAction::Interact {
|
||||
target_entity_id: Some(item_sid.0),
|
||||
verb: Some("Take".into()),
|
||||
},
|
||||
});
|
||||
schedule.run(&mut world);
|
||||
|
||||
assert!(world.get::<TilePosition>(item).is_none(), "item off ground after Take");
|
||||
assert_eq!(world.get::<CarriedBy>(item).unwrap().0, player_sid);
|
||||
assert_eq!(world.get::<InventorySlot>(item).unwrap().0, 0);
|
||||
|
||||
// Step 2: Place
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 1,
|
||||
action: PlayerAction::Interact {
|
||||
target_entity_id: Some(item_sid.0),
|
||||
verb: Some("Place".into()),
|
||||
},
|
||||
});
|
||||
world.resource_mut::<SimulationTime>().tick = 1;
|
||||
schedule.run(&mut world);
|
||||
|
||||
let pos = world.get::<TilePosition>(item).expect("item back on ground after Place");
|
||||
assert_eq!(*pos, TilePosition::new(5, 5, 0), "placed at player position");
|
||||
assert!(world.get::<CarriedBy>(item).is_none(), "CarriedBy removed after Place");
|
||||
assert!(world.get::<InventorySlot>(item).is_none(), "InventorySlot removed after Place");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn take_without_target_id_is_noop() {
|
||||
// Edge case: Take verb with no target_entity_id should not panic
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime::default());
|
||||
world.init_resource::<crate::knowledge::EntityRegistry>();
|
||||
|
||||
world.spawn((PlayerCharacter, TilePosition::new(5, 5, 0)));
|
||||
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 0,
|
||||
action: PlayerAction::Interact {
|
||||
target_entity_id: None,
|
||||
verb: Some("Take".into()),
|
||||
},
|
||||
});
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(process_player_input);
|
||||
schedule.run(&mut world); // should not panic
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn place_without_target_id_is_noop() {
|
||||
// Edge case: Place verb with no target_entity_id should not panic
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(InputQueue::default());
|
||||
world.insert_resource(SimulationTime::default());
|
||||
world.init_resource::<crate::knowledge::EntityRegistry>();
|
||||
|
||||
world.spawn((PlayerCharacter, TilePosition::new(5, 5, 0)));
|
||||
|
||||
world.resource_mut::<InputQueue>().push(PlayerInput {
|
||||
tick: 0,
|
||||
action: PlayerAction::Interact {
|
||||
target_entity_id: None,
|
||||
verb: Some("Place".into()),
|
||||
},
|
||||
});
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(process_player_input);
|
||||
schedule.run(&mut world); // should not panic
|
||||
}
|
||||
}
|
||||
|
||||
@@ -857,4 +857,50 @@ mod tests {
|
||||
"two Fixtures on same tile should collide"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_four_layers_coexist_on_same_tile() {
|
||||
// D-054: Standing + Prone + Seated + Fixture all share one tile
|
||||
let mut world = bevy_ecs::world::World::new();
|
||||
world.insert_resource(WalkabilityMap::new(10, 10, 1));
|
||||
|
||||
let target = TilePosition::new(5, 4, 0);
|
||||
|
||||
// Fixture and Prone already at tile
|
||||
world.spawn((target, TilePresence::Fixture));
|
||||
world.spawn((target, TilePresence::Prone));
|
||||
|
||||
// Standing mover enters
|
||||
let standing = world
|
||||
.spawn((
|
||||
TilePosition::new(5, 5, 0),
|
||||
TilePresence::Standing,
|
||||
MoveIntent { target },
|
||||
))
|
||||
.id();
|
||||
|
||||
// Seated mover enters from elsewhere
|
||||
let seated = world
|
||||
.spawn((
|
||||
TilePosition::new(5, 3, 0),
|
||||
TilePresence::Seated,
|
||||
MoveIntent { target },
|
||||
))
|
||||
.id();
|
||||
|
||||
let mut schedule = bevy_ecs::schedule::Schedule::default();
|
||||
schedule.add_systems(validate_movement);
|
||||
schedule.run(&mut world);
|
||||
|
||||
assert_eq!(
|
||||
*world.get::<TilePosition>(standing).unwrap(),
|
||||
target,
|
||||
"Standing should share tile with Fixture + Prone"
|
||||
);
|
||||
assert_eq!(
|
||||
*world.get::<TilePosition>(seated).unwrap(),
|
||||
target,
|
||||
"Seated should share tile with Fixture + Prone + Standing"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user