Files
pomomon-garden/tests/interaction.rs
2025-12-02 15:46:48 +01:00

253 lines
7.7 KiB
Rust

use bevy::ecs::system::RunSystemOnce;
use pomomon_garden::features::grid::components::{Grid, Tile, TileState};
use pomomon_garden::features::inventory::components::{Inventory, ItemStack, ItemType};
use pomomon_garden::features::pom::actions::InteractionAction;
use pomomon_garden::prelude::*;
fn setup_interaction_app(
grid_width: u32,
grid_height: u32,
initial_tile_states: &[(u32, u32, TileState)],
initial_inventory: Vec<(ItemType, u32)>,
) -> App {
let mut app = App::new();
app.add_plugins(MinimalPlugins);
app.add_plugins(AssetPlugin::default()); // Needed for asset server if used, though we mock or avoid visuals here
// Grid Setup
let mut grid_tiles = Vec::with_capacity(grid_width as usize);
for x in 0..grid_width {
let mut column = Vec::with_capacity(grid_height as usize);
for y in 0..grid_height {
let entity = app
.world_mut()
.spawn((Tile { x, y }, TileState::Unclaimed))
.id();
column.push(entity);
}
grid_tiles.push(column);
}
app.world_mut().insert_resource(Grid {
width: grid_width,
height: grid_height,
tiles: grid_tiles,
});
for &(x, y, ref state) in initial_tile_states {
if let Ok(entity) = app.world().resource::<Grid>().get_tile((x, y)) {
*app.world_mut().get_mut::<TileState>(entity).unwrap() = state.clone();
}
}
// Inventory Setup
let mut inventory_items = Vec::new();
for (item_type, amount) in initial_inventory {
let id = app.world_mut().spawn(ItemStack { item_type, amount }).id();
inventory_items.push(id);
}
app.world_mut().insert_resource(Inventory {
items: inventory_items,
});
app
}
#[test]
fn test_plant_seed_interaction() {
let seed_type = ItemType::BerrySeed {
name: "TestSeed".into(),
};
let mut app = setup_interaction_app(
3,
3,
&[(1, 1, TileState::Empty)],
vec![(seed_type.clone(), 1)],
);
// Run the interaction logic as a system or closure
let _ = app.world_mut().run_system_once(
move |grid: Res<Grid>,
mut tile_query: Query<&mut TileState>,
mut inventory: ResMut<Inventory>,
mut item_stack_query: Query<&mut ItemStack>,
mut commands: Commands| {
let action = InteractionAction::Plant(seed_type.clone());
action.execute(
(1, 1),
&grid,
&mut tile_query,
&mut inventory,
&mut item_stack_query,
&mut commands,
);
},
);
// Apply commands (despawns etc.)
app.update();
// Assert Tile State
let grid = app.world().resource::<Grid>();
let tile_entity = grid.get_tile((1, 1)).unwrap();
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
if let TileState::Occupied { seed, .. } = tile_state {
assert_eq!(
*seed,
ItemType::BerrySeed {
name: "TestSeed".into()
}
);
} else {
panic!("Tile should be Occupied with seed, found {:?}", tile_state);
}
// Assert Inventory Empty
let inventory = app.world().resource::<Inventory>();
// Item stack entity should be despawned or amount 0
if !inventory.items.is_empty() {
// If the entity still exists, check amount
if let Some(entity) = inventory.items.first() {
if let Some(stack) = app.world().entity(*entity).get::<ItemStack>() {
assert_eq!(stack.amount, 0, "Item amount should be 0");
} else {
panic!(
"Inventory items list should be empty or point to valid 0 amount entities. Found phantom entity."
);
}
}
} else {
assert!(inventory.items.is_empty());
}
}
#[test]
fn test_plant_seed_no_inventory() {
let seed_type = ItemType::BerrySeed {
name: "TestSeed".into(),
};
let mut app = setup_interaction_app(
3,
3,
&[(1, 1, TileState::Empty)],
vec![], // Empty inventory
);
let _ = app.world_mut().run_system_once(
move |grid: Res<Grid>,
mut tile_query: Query<&mut TileState>,
mut inventory: ResMut<Inventory>,
mut item_stack_query: Query<&mut ItemStack>,
mut commands: Commands| {
let action = InteractionAction::Plant(seed_type.clone());
action.execute(
(1, 1),
&grid,
&mut tile_query,
&mut inventory,
&mut item_stack_query,
&mut commands,
);
},
);
app.update();
// Assert Tile State Unchanged
let grid = app.world().resource::<Grid>();
let tile_entity = grid.get_tile((1, 1)).unwrap();
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
if let TileState::Empty = tile_state {
// Correct
} else {
panic!("Tile should remain Empty, found {:?}", tile_state);
}
}
#[test]
fn test_water_crop() {
let seed_type = ItemType::BerrySeed {
name: "TestSeed".into(),
};
let mut app = setup_interaction_app(
3,
3,
&[(
1,
1,
TileState::Occupied {
seed: seed_type.clone(),
watered: false,
},
)],
vec![],
);
// Verify Water option is available
let _ = app.world_mut().run_system_once(
move |grid: Res<Grid>,
tile_query: Query<&TileState>,
inventory: Res<Inventory>,
item_query: Query<&ItemStack>| {
let tile_entity = grid.get_tile((1, 1)).unwrap();
let tile_state = tile_query.get(tile_entity).unwrap();
let options = InteractionAction::list_options(tile_state, &inventory, item_query);
assert!(
options.contains(&InteractionAction::Water),
"Water option should be available"
);
},
);
// Execute Water
let _ = app.world_mut().run_system_once(
move |grid: Res<Grid>,
mut tile_query: Query<&mut TileState>,
mut inventory: ResMut<Inventory>,
mut item_stack_query: Query<&mut ItemStack>,
mut commands: Commands| {
let action = InteractionAction::Water;
action.execute(
(1, 1),
&grid,
&mut tile_query,
&mut inventory,
&mut item_stack_query,
&mut commands,
);
},
);
app.update();
// Assert Tile State Watered
let grid = app.world().resource::<Grid>();
let tile_entity = grid.get_tile((1, 1)).unwrap();
let tile_state = app.world().entity(tile_entity).get::<TileState>().unwrap();
if let TileState::Occupied { watered, .. } = tile_state {
assert!(watered, "Tile should be watered");
} else {
panic!("Tile should be Occupied, found {:?}", tile_state);
}
// Verify Water option is NOT available
let _ = app.world_mut().run_system_once(
move |grid: Res<Grid>,
tile_query: Query<&TileState>,
inventory: Res<Inventory>,
item_query: Query<&ItemStack>| {
let tile_entity = grid.get_tile((1, 1)).unwrap();
let tile_state = tile_query.get(tile_entity).unwrap();
let options = InteractionAction::list_options(tile_state, &inventory, item_query);
assert!(
!options.contains(&InteractionAction::Water),
"Water option should NOT be available"
);
},
);
}