extends GutTest ## Integration tests for GameState.gd var game_state: GameState # Signal tracking var game_started_called: bool = false var game_ended_called: bool = false var card_played_called: bool = false var damage_dealt_called: bool = false var last_damage_player: int = -1 var last_damage_amount: int = 0 func before_each(): game_state = GameState.new() _setup_test_game() _connect_signals() func _setup_test_game(): # Manually setup players without CardDatabase autoload game_state.players.clear() game_state.players.append(Player.new(0, "Player 1")) game_state.players.append(Player.new(1, "Player 2")) # Add cards directly to decks (50 cards each for full game) for i in range(50): var card1 = CardInstance.new(TestCardData.create_forward( "p1-" + str(i), "Card " + str(i), Enums.Element.FIRE, 2 + (i % 4), 5000, true ), 0) var card2 = CardInstance.new(TestCardData.create_forward( "p2-" + str(i), "Card " + str(i), Enums.Element.ICE, 2 + (i % 4), 5000, true ), 1) game_state.players[0].deck.add_card(card1) game_state.players[1].deck.add_card(card2) game_state.players[0].deck.shuffle() game_state.players[1].deck.shuffle() func _connect_signals(): game_started_called = false game_ended_called = false card_played_called = false damage_dealt_called = false game_state.game_started.connect(func(): game_started_called = true) game_state.game_ended.connect(func(_winner): game_ended_called = true) game_state.card_played.connect(func(_card, _player): card_played_called = true) game_state.damage_dealt.connect(func(player, amount, _cards): damage_dealt_called = true last_damage_player = player last_damage_amount = amount ) ## Initialization Tests func test_initial_state(): assert_false(game_state.game_active) assert_eq(game_state.winner_index, -1) assert_eq(game_state.players.size(), 2) assert_not_null(game_state.turn_manager) ## Game Start Tests func test_start_game(): game_state.start_game(0) assert_true(game_state.game_active) assert_true(game_started_called) assert_eq(game_state.turn_manager.current_player_index, 0) # Player 0 has 5 (initial) + 1 (first turn draw) = 6 assert_eq(game_state.players[0].hand.get_count(), 6) # Player 1 still has 5 (hasn't drawn yet) assert_eq(game_state.players[1].hand.get_count(), 5) func test_start_game_player_1_first(): game_state.start_game(1) assert_eq(game_state.turn_manager.current_player_index, 1) assert_true(game_state.players[1].is_first_player) func test_start_game_auto_executes_phases(): game_state.start_game(0) # start_game calls turn_manager.start_game(0) # which emits phase_changed(ACTIVE) # GameState._on_phase_changed executes ACTIVE phase and advances to DRAW # Then executes DRAW phase and advances to MAIN_1 # After all that, we should be in MAIN_1 assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.MAIN_1) func test_first_turn_draws_one_card(): # Initial hands have 5 cards each # On first turn, current player draws 1 more (first turn rule) game_state.start_game(0) # Player 0 drew 5 (initial) + 1 (first turn draw) = total 6 in hand? No wait... # setup draws 5, then draw phase draws 1 more # Actually, start_game calls draw_cards(5) for both, THEN starts turn which draws again # Let me verify: draw_cards(5) then turn_manager.start_game triggers phase ACTIVE -> advance -> DRAW # DRAW phase draws get_draw_count() = 1 (first turn) assert_eq(game_state.players[0].hand.get_count(), 6) # 5 + 1 func test_subsequent_turn_draws_two(): game_state.start_game(0) # Progress through player 0's turn to end game_state.end_main_phase() # MAIN_1 -> ATTACK game_state.end_attack_phase() # ATTACK -> MAIN_2 game_state.end_main_phase() # MAIN_2 -> END -> auto -> ACTIVE -> DRAW -> MAIN_1 # Now player 1's turn, they drew 2 cards # Player 1 had 5 (initial) + 2 (normal draw) = 7 assert_eq(game_state.players[1].hand.get_count(), 7) ## Player Access Tests func test_get_current_player(): game_state.start_game(0) var current = game_state.get_current_player() assert_eq(current, game_state.players[0]) func test_get_opponent(): game_state.start_game(0) var opponent = game_state.get_opponent() assert_eq(opponent, game_state.players[1]) func test_get_player(): assert_eq(game_state.get_player(0), game_state.players[0]) assert_eq(game_state.get_player(1), game_state.players[1]) assert_null(game_state.get_player(2)) assert_null(game_state.get_player(-1)) ## Card Play Tests func test_play_card_success(): game_state.start_game(0) var player = game_state.get_current_player() var card = player.hand.get_top_card() player.cp_pool.add_cp(card.get_element(), card.card_data.cost) var spent = game_state.play_card(0, card) assert_false(spent.is_empty()) assert_true(card_played_called) func test_play_card_wrong_phase(): game_state.start_game(0) # Manually set phase to something other than main game_state.turn_manager.current_phase = Enums.TurnPhase.DRAW var player = game_state.get_current_player() var card = player.hand.get_top_card() player.cp_pool.add_cp(card.get_element(), 10) var spent = game_state.play_card(0, card) assert_true(spent.is_empty()) func test_play_card_wrong_player(): game_state.start_game(0) # Player 0's turn var opponent = game_state.get_opponent() var card = opponent.hand.get_top_card() opponent.cp_pool.add_cp(card.get_element(), 10) # Player 1 tries to play on player 0's turn var spent = game_state.play_card(1, card) assert_true(spent.is_empty()) func test_play_card_game_not_active(): # Don't start game var player = game_state.players[0] var card = CardInstance.new(TestCardData.create_forward(), 0) player.hand.add_card(card) player.cp_pool.add_cp(Enums.Element.FIRE, 5) var spent = game_state.play_card(0, card) assert_true(spent.is_empty()) ## CP Generation Tests func test_discard_for_cp(): game_state.start_game(0) var player = game_state.get_current_player() var card = player.hand.get_top_card() var result = game_state.discard_for_cp(0, card) assert_true(result) assert_eq(player.cp_pool.get_total_cp(), 2) func test_dull_backup_for_cp(): game_state.start_game(0) var player = game_state.get_current_player() var backup = CardInstance.new(TestCardData.create_backup("b", "Test Backup", Enums.Element.FIRE), 0) player.field_backups.add_card(backup) var result = game_state.dull_backup_for_cp(0, backup) assert_true(result) assert_eq(player.cp_pool.get_cp(Enums.Element.FIRE), 1) ## Turn Flow Tests func test_full_turn_cycle(): game_state.start_game(0) # After start_game, we're in MAIN_1 assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.MAIN_1) # End main phase 1 game_state.end_main_phase() assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.ATTACK) # End attack phase (no attacks) game_state.end_attack_phase() assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.MAIN_2) # End main phase 2 game_state.end_main_phase() # END phase auto-executes and advances to ACTIVE of next turn # ACTIVE auto-executes and advances to DRAW # DRAW auto-executes and advances to MAIN_1 # Should be player 1's turn now assert_eq(game_state.turn_manager.current_player_index, 1) assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.MAIN_1) ## Combat Tests func test_declare_attack(): game_state.start_game(0) # Add a forward that can attack var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker", Enums.Element.FIRE, 3, 7000), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) # Get to attack phase game_state.end_main_phase() # Start attack declaration var result = game_state.declare_attack(attacker) assert_true(result) assert_true(attacker.is_dull()) assert_true(attacker.attacked_this_turn) assert_eq(game_state.turn_manager.current_attacker, attacker) func test_declare_attack_with_brave(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_brave_forward(), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) game_state.end_main_phase() game_state.declare_attack(attacker) assert_true(attacker.is_active()) # Brave doesn't dull when attacking func test_declare_attack_invalid_card(): game_state.start_game(0) var player = game_state.get_current_player() var forward = CardInstance.new(TestCardData.create_forward(), 0) forward.turns_on_field = 0 # Summoning sickness player.field_forwards.add_card(forward) game_state.end_main_phase() var result = game_state.declare_attack(forward) assert_false(result) func test_declare_block(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker"), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) var opponent = game_state.get_opponent() var blocker = CardInstance.new(TestCardData.create_forward("blk", "Blocker"), 1) opponent.field_forwards.add_card(blocker) game_state.end_main_phase() game_state.declare_attack(attacker) var result = game_state.declare_block(blocker) assert_true(result) assert_eq(game_state.turn_manager.current_blocker, blocker) func test_skip_block(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker"), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) game_state.end_main_phase() game_state.declare_attack(attacker) var result = game_state.skip_block() assert_true(result) assert_null(game_state.turn_manager.current_blocker) func test_unblocked_attack_deals_damage(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker"), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) game_state.end_main_phase() game_state.declare_attack(attacker) game_state.skip_block() game_state.resolve_combat() assert_true(damage_dealt_called) assert_eq(last_damage_player, 1) # Opponent takes damage assert_eq(last_damage_amount, 1) assert_eq(game_state.players[1].damage_zone.get_count(), 1) func test_blocked_attack_combat(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker", Enums.Element.FIRE, 3, 7000, true), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) var opponent = game_state.get_opponent() var blocker = CardInstance.new(TestCardData.create_forward("blk", "Blocker", Enums.Element.ICE, 3, 5000, true), 1) opponent.field_forwards.add_card(blocker) game_state.end_main_phase() game_state.declare_attack(attacker) game_state.declare_block(blocker) game_state.resolve_combat() # Attacker 7000 vs Blocker 5000 # Blocker takes 7000 damage (>= 5000 power) -> broken # Attacker takes 5000 damage (< 7000 power) -> survives assert_eq(opponent.field_forwards.get_count(), 0) # Blocker broken assert_eq(opponent.break_zone.get_count(), 1) assert_eq(player.field_forwards.get_count(), 1) # Attacker survives func test_both_forwards_break(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker", Enums.Element.FIRE, 3, 5000, true), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) var opponent = game_state.get_opponent() var blocker = CardInstance.new(TestCardData.create_forward("blk", "Blocker", Enums.Element.ICE, 3, 5000, true), 1) opponent.field_forwards.add_card(blocker) game_state.end_main_phase() game_state.declare_attack(attacker) game_state.declare_block(blocker) game_state.resolve_combat() # Both 5000 power, both take lethal damage assert_eq(player.field_forwards.get_count(), 0) assert_eq(opponent.field_forwards.get_count(), 0) func test_multiple_attacks(): game_state.start_game(0) var player = game_state.get_current_player() var attacker1 = CardInstance.new(TestCardData.create_forward("a1", "Attacker 1", Enums.Element.FIRE, 2, 5000, true), 0) var attacker2 = CardInstance.new(TestCardData.create_forward("a2", "Attacker 2", Enums.Element.FIRE, 2, 5000, true), 0) attacker1.turns_on_field = 1 attacker2.turns_on_field = 1 player.field_forwards.add_card(attacker1) player.field_forwards.add_card(attacker2) game_state.end_main_phase() # First attack game_state.declare_attack(attacker1) game_state.skip_block() game_state.resolve_combat() # Second attack game_state.declare_attack(attacker2) game_state.skip_block() game_state.resolve_combat() assert_eq(game_state.players[1].damage_zone.get_count(), 2) ## Win Condition Tests func test_seven_damage_wins(): game_state.start_game(0) # Add 7 attackers for player 0 (all can attack on same turn) var player = game_state.players[0] var attackers: Array[CardInstance] = [] for i in range(7): var attacker = CardInstance.new(TestCardData.create_forward("a" + str(i), "Attacker", Enums.Element.FIRE, 2, 5000, true), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) attackers.append(attacker) # Go to attack phase game_state.end_main_phase() # Attack 7 times with different forwards for i in range(7): if not game_state.game_active: break game_state.declare_attack(attackers[i]) game_state.skip_block() game_state.resolve_combat() assert_true(game_ended_called) assert_false(game_state.game_active) assert_eq(game_state.winner_index, 0) # Player 0 wins ## Edge Cases func test_attack_phase_without_attacks(): game_state.start_game(0) game_state.end_main_phase() # -> ATTACK game_state.end_attack_phase() # -> MAIN_2 assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.MAIN_2) func test_declare_attack_not_in_attack_phase(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward(), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) # Still in MAIN_1 var result = game_state.declare_attack(attacker) assert_false(result) func test_declare_attack_card_not_on_field(): game_state.start_game(0) var attacker = CardInstance.new(TestCardData.create_forward(), 0) attacker.turns_on_field = 1 # Don't add to field game_state.end_main_phase() var result = game_state.declare_attack(attacker) assert_false(result) func test_block_with_dull_card_fails(): game_state.start_game(0) var player = game_state.get_current_player() var attacker = CardInstance.new(TestCardData.create_forward("att", "Attacker"), 0) attacker.turns_on_field = 1 player.field_forwards.add_card(attacker) var opponent = game_state.get_opponent() var blocker = CardInstance.new(TestCardData.create_forward("blk", "Blocker"), 1) blocker.dull() opponent.field_forwards.add_card(blocker) game_state.end_main_phase() game_state.declare_attack(attacker) var result = game_state.declare_block(blocker) assert_false(result) ## ============================================ ## ONLINE GAME STATE SYNCHRONIZATION TESTS ## These test that game state can be updated from ## network phase change messages ## ============================================ func test_turn_manager_player_index_can_be_set(): # Simulates receiving network phase_changed message game_state.start_game(0) # Manually set player index like network handler would game_state.turn_manager.current_player_index = 1 assert_eq(game_state.turn_manager.current_player_index, 1) func test_turn_manager_phase_can_be_set(): # Simulates receiving network phase_changed message game_state.start_game(0) # Manually set phase like network handler would game_state.turn_manager.current_phase = Enums.TurnPhase.ATTACK assert_eq(game_state.turn_manager.current_phase, Enums.TurnPhase.ATTACK) func test_turn_manager_turn_number_can_be_set(): # Simulates receiving network game_state_sync message game_state.start_game(0) # Manually set turn number like network handler would game_state.turn_manager.turn_number = 5 assert_eq(game_state.turn_manager.turn_number, 5) func test_turn_manager_initial_values(): # Verify initial turn manager state game_state.start_game(0) assert_eq(game_state.turn_manager.current_player_index, 0) assert_eq(game_state.turn_manager.turn_number, 1) func test_turn_manager_attack_step_can_be_set(): # For online games, attack step is managed by server game_state.start_game(0) game_state.end_main_phase() # Get to ATTACK phase # Manually set attack step like network handler would game_state.turn_manager.attack_step = Enums.AttackStep.BLOCK_DECLARATION assert_eq(game_state.turn_manager.attack_step, Enums.AttackStep.BLOCK_DECLARATION) func test_phase_changed_updates_current_player(): # Test that changing phase properly reflects in get_current_player() game_state.start_game(0) # Simulate switching turns from network game_state.turn_manager.current_player_index = 1 var current = game_state.get_current_player() assert_eq(current, game_state.players[1]) func test_phase_changed_updates_opponent(): # Test that changing phase properly reflects in get_opponent() game_state.start_game(0) # Simulate switching turns from network game_state.turn_manager.current_player_index = 1 var opponent = game_state.get_opponent() assert_eq(opponent, game_state.players[0]) func test_turn_manager_all_phases_valid(): # Verify all TurnPhase enum values can be set game_state.start_game(0) for phase in Enums.TurnPhase.values(): game_state.turn_manager.current_phase = phase assert_eq(game_state.turn_manager.current_phase, phase) func test_turn_manager_all_attack_steps_valid(): # Verify all AttackStep enum values can be set game_state.start_game(0) game_state.end_main_phase() # Get to ATTACK phase for step in Enums.AttackStep.values(): game_state.turn_manager.attack_step = step assert_eq(game_state.turn_manager.attack_step, step)