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::().get_tile((x, y)) { *app.world_mut().get_mut::(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 app.world_mut().run_system_once( move |grid: Res, mut tile_query: Query<&mut TileState>, mut inventory: ResMut, 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::(); let tile_entity = grid.get_tile((1, 1)).unwrap(); let tile_state = app.world().entity(tile_entity).get::().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::(); // 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::() { assert_eq!(stack.amount, 0, "Item amount should be 0"); } else { // Entity might be despawned but still in inventory list until cleanup? // Inventory struct implementation removes it from vector if using update_item_stack properly? // update_item_stack does `self.items.remove(index);` // So the list should be empty. 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 ); app.world_mut().run_system_once( move |grid: Res, mut tile_query: Query<&mut TileState>, mut inventory: ResMut, 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::(); let tile_entity = grid.get_tile((1, 1)).unwrap(); let tile_state = app.world().entity(tile_entity).get::().unwrap(); if let TileState::Empty = tile_state { // Correct } else { panic!("Tile should remain Empty, found {:?}", tile_state); } }