test runner added
This commit is contained in:
180
tests/fixtures/test_card_data.gd
vendored
Normal file
180
tests/fixtures/test_card_data.gd
vendored
Normal file
@@ -0,0 +1,180 @@
|
||||
class_name TestCardData
|
||||
extends RefCounted
|
||||
|
||||
## Factory for creating test CardData objects without CardDatabase autoload
|
||||
## Use these methods in unit tests to create cards with specific properties
|
||||
|
||||
|
||||
static func create_forward(
|
||||
id: String = "test-forward-001",
|
||||
card_name: String = "Test Forward",
|
||||
element: Enums.Element = Enums.Element.FIRE,
|
||||
cost: int = 3,
|
||||
power: int = 5000,
|
||||
is_generic: bool = false,
|
||||
abilities: Array[CardDatabase.AbilityData] = []
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
card.elements.append(element)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
card.is_generic = is_generic
|
||||
card.abilities = abilities
|
||||
return card
|
||||
|
||||
|
||||
static func create_backup(
|
||||
id: String = "test-backup-001",
|
||||
card_name: String = "Test Backup",
|
||||
element: Enums.Element = Enums.Element.FIRE,
|
||||
cost: int = 2,
|
||||
is_generic: bool = false
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.BACKUP
|
||||
card.elements.append(element)
|
||||
card.cost = cost
|
||||
card.power = 0
|
||||
card.is_generic = is_generic
|
||||
return card
|
||||
|
||||
|
||||
static func create_summon(
|
||||
id: String = "test-summon-001",
|
||||
card_name: String = "Test Summon",
|
||||
element: Enums.Element = Enums.Element.FIRE,
|
||||
cost: int = 2,
|
||||
has_ex_burst: bool = false
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.SUMMON
|
||||
card.elements.append(element)
|
||||
card.cost = cost
|
||||
card.has_ex_burst = has_ex_burst
|
||||
return card
|
||||
|
||||
|
||||
static func create_light_forward(
|
||||
id: String = "test-light-001",
|
||||
card_name: String = "Light Forward",
|
||||
cost: int = 5,
|
||||
power: int = 9000
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
card.elements.append(Enums.Element.LIGHT)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
return card
|
||||
|
||||
|
||||
static func create_dark_forward(
|
||||
id: String = "test-dark-001",
|
||||
card_name: String = "Dark Forward",
|
||||
cost: int = 5,
|
||||
power: int = 9000
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
card.elements.append(Enums.Element.DARK)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
return card
|
||||
|
||||
|
||||
static func create_multi_element_forward(
|
||||
id: String = "test-multi-001",
|
||||
card_name: String = "Multi Element Forward",
|
||||
elements: Array[Enums.Element] = [Enums.Element.FIRE, Enums.Element.ICE],
|
||||
cost: int = 4,
|
||||
power: int = 7000
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
for elem in elements:
|
||||
card.elements.append(elem)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
return card
|
||||
|
||||
|
||||
static func create_haste_forward(
|
||||
id: String = "test-haste-001",
|
||||
card_name: String = "Haste Forward",
|
||||
element: Enums.Element = Enums.Element.FIRE,
|
||||
cost: int = 3,
|
||||
power: int = 5000
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
card.elements.append(element)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
|
||||
var haste_ability = CardDatabase.AbilityData.new()
|
||||
haste_ability.type = Enums.AbilityType.FIELD
|
||||
haste_ability.effect = "Haste"
|
||||
card.abilities.append(haste_ability)
|
||||
|
||||
return card
|
||||
|
||||
|
||||
static func create_brave_forward(
|
||||
id: String = "test-brave-001",
|
||||
card_name: String = "Brave Forward",
|
||||
element: Enums.Element = Enums.Element.WIND,
|
||||
cost: int = 4,
|
||||
power: int = 6000
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
card.elements.append(element)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
|
||||
var brave_ability = CardDatabase.AbilityData.new()
|
||||
brave_ability.type = Enums.AbilityType.FIELD
|
||||
brave_ability.effect = "Brave"
|
||||
card.abilities.append(brave_ability)
|
||||
|
||||
return card
|
||||
|
||||
|
||||
static func create_first_strike_forward(
|
||||
id: String = "test-first-strike-001",
|
||||
card_name: String = "First Strike Forward",
|
||||
element: Enums.Element = Enums.Element.LIGHTNING,
|
||||
cost: int = 3,
|
||||
power: int = 4000
|
||||
) -> CardDatabase.CardData:
|
||||
var card = CardDatabase.CardData.new()
|
||||
card.id = id
|
||||
card.name = card_name
|
||||
card.type = Enums.CardType.FORWARD
|
||||
card.elements.append(element)
|
||||
card.cost = cost
|
||||
card.power = power
|
||||
|
||||
var first_strike_ability = CardDatabase.AbilityData.new()
|
||||
first_strike_ability.type = Enums.AbilityType.FIELD
|
||||
first_strike_ability.effect = "First Strike"
|
||||
card.abilities.append(first_strike_ability)
|
||||
|
||||
return card
|
||||
530
tests/integration/test_game_state.gd
Normal file
530
tests/integration/test_game_state.gd
Normal file
@@ -0,0 +1,530 @@
|
||||
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)
|
||||
422
tests/unit/test_card_instance.gd
Normal file
422
tests/unit/test_card_instance.gd
Normal file
@@ -0,0 +1,422 @@
|
||||
extends GutTest
|
||||
|
||||
## Unit tests for CardInstance.gd
|
||||
|
||||
|
||||
## Initialization Tests
|
||||
|
||||
func test_initialization():
|
||||
var card_data = TestCardData.create_forward("test-id", "Test Card", Enums.Element.FIRE, 3, 5000)
|
||||
var card = CardInstance.new(card_data, 0)
|
||||
|
||||
assert_eq(card.card_data, card_data)
|
||||
assert_eq(card.owner_index, 0)
|
||||
assert_eq(card.controller_index, 0)
|
||||
assert_eq(card.current_power, 5000)
|
||||
assert_eq(card.state, Enums.CardState.ACTIVE)
|
||||
assert_eq(card.damage_received, 0)
|
||||
assert_eq(card.turns_on_field, 0)
|
||||
assert_false(card.attacked_this_turn)
|
||||
|
||||
|
||||
func test_initialization_with_null_data():
|
||||
var card = CardInstance.new(null, 0)
|
||||
|
||||
assert_null(card.card_data)
|
||||
assert_eq(card.current_power, 0)
|
||||
|
||||
|
||||
func test_unique_instance_ids():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var card3 = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
assert_ne(card1.instance_id, card2.instance_id)
|
||||
assert_ne(card2.instance_id, card3.instance_id)
|
||||
assert_ne(card1.instance_id, card3.instance_id)
|
||||
|
||||
|
||||
func test_controller_defaults_to_owner():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 1)
|
||||
|
||||
assert_eq(card.owner_index, 1)
|
||||
assert_eq(card.controller_index, 1)
|
||||
|
||||
|
||||
## Card Type Checks
|
||||
|
||||
func test_is_forward():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
var summon = CardInstance.new(TestCardData.create_summon(), 0)
|
||||
|
||||
assert_true(forward.is_forward())
|
||||
assert_false(backup.is_forward())
|
||||
assert_false(summon.is_forward())
|
||||
|
||||
|
||||
func test_is_backup():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
var summon = CardInstance.new(TestCardData.create_summon(), 0)
|
||||
|
||||
assert_false(forward.is_backup())
|
||||
assert_true(backup.is_backup())
|
||||
assert_false(summon.is_backup())
|
||||
|
||||
|
||||
func test_is_summon():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
var summon = CardInstance.new(TestCardData.create_summon(), 0)
|
||||
|
||||
assert_false(forward.is_summon())
|
||||
assert_false(backup.is_summon())
|
||||
assert_true(summon.is_summon())
|
||||
|
||||
|
||||
func test_type_check_with_null_data():
|
||||
var card = CardInstance.new(null, 0)
|
||||
|
||||
assert_false(card.is_forward())
|
||||
assert_false(card.is_backup())
|
||||
assert_false(card.is_summon())
|
||||
|
||||
|
||||
## State Management
|
||||
|
||||
func test_initial_state_is_active():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
assert_true(card.is_active())
|
||||
assert_false(card.is_dull())
|
||||
|
||||
|
||||
func test_dull():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
card.dull()
|
||||
|
||||
assert_true(card.is_dull())
|
||||
assert_false(card.is_active())
|
||||
|
||||
|
||||
func test_activate():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.dull()
|
||||
|
||||
card.activate()
|
||||
|
||||
assert_true(card.is_active())
|
||||
assert_false(card.is_dull())
|
||||
|
||||
|
||||
## Power Tests
|
||||
|
||||
func test_get_power_base():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
assert_eq(card.get_power(), 5000)
|
||||
|
||||
|
||||
func test_get_power_with_positive_modifier():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
card.power_modifiers.append(1000)
|
||||
|
||||
assert_eq(card.get_power(), 6000)
|
||||
|
||||
|
||||
func test_get_power_with_multiple_modifiers():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
card.power_modifiers.append(1000)
|
||||
card.power_modifiers.append(2000)
|
||||
card.power_modifiers.append(-500)
|
||||
|
||||
assert_eq(card.get_power(), 7500)
|
||||
|
||||
|
||||
func test_get_power_with_negative_modifier():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
card.power_modifiers.append(-2000)
|
||||
|
||||
assert_eq(card.get_power(), 3000)
|
||||
|
||||
|
||||
func test_get_power_floors_at_zero():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
card.power_modifiers.append(-10000)
|
||||
|
||||
assert_eq(card.get_power(), 0)
|
||||
|
||||
|
||||
## Attack Eligibility Tests
|
||||
|
||||
func test_can_attack_basic():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 1 # Been on field for a turn
|
||||
|
||||
assert_true(card.can_attack())
|
||||
|
||||
|
||||
func test_cannot_attack_if_dull():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 1
|
||||
card.dull()
|
||||
|
||||
assert_false(card.can_attack())
|
||||
|
||||
|
||||
func test_cannot_attack_if_already_attacked():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 1
|
||||
card.attacked_this_turn = true
|
||||
|
||||
assert_false(card.can_attack())
|
||||
|
||||
|
||||
func test_cannot_attack_summoning_sickness():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 0 # Just entered field
|
||||
|
||||
assert_false(card.can_attack())
|
||||
|
||||
|
||||
func test_can_attack_with_haste_on_first_turn():
|
||||
var card = CardInstance.new(TestCardData.create_haste_forward(), 0)
|
||||
card.turns_on_field = 0 # Just entered field
|
||||
|
||||
assert_true(card.has_haste())
|
||||
assert_true(card.can_attack())
|
||||
|
||||
|
||||
func test_backup_cannot_attack():
|
||||
var card = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
card.turns_on_field = 1
|
||||
|
||||
assert_false(card.can_attack())
|
||||
|
||||
|
||||
func test_summon_cannot_attack():
|
||||
var card = CardInstance.new(TestCardData.create_summon(), 0)
|
||||
card.turns_on_field = 1
|
||||
|
||||
assert_false(card.can_attack())
|
||||
|
||||
|
||||
## Block Eligibility Tests
|
||||
|
||||
func test_can_block_basic():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
assert_true(card.can_block())
|
||||
|
||||
|
||||
func test_cannot_block_if_dull():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.dull()
|
||||
|
||||
assert_false(card.can_block())
|
||||
|
||||
|
||||
func test_backup_cannot_block():
|
||||
var card = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
|
||||
assert_false(card.can_block())
|
||||
|
||||
|
||||
func test_can_block_even_on_first_turn():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 0 # Just entered field
|
||||
|
||||
# Blocking doesn't have summoning sickness
|
||||
assert_true(card.can_block())
|
||||
|
||||
|
||||
## Ability Detection Tests
|
||||
|
||||
func test_has_haste():
|
||||
var haste_card = CardInstance.new(TestCardData.create_haste_forward(), 0)
|
||||
var normal_card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
assert_true(haste_card.has_haste())
|
||||
assert_false(normal_card.has_haste())
|
||||
|
||||
|
||||
func test_has_brave():
|
||||
var brave_card = CardInstance.new(TestCardData.create_brave_forward(), 0)
|
||||
var normal_card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
assert_true(brave_card.has_brave())
|
||||
assert_false(normal_card.has_brave())
|
||||
|
||||
|
||||
func test_has_first_strike():
|
||||
var first_strike_card = CardInstance.new(TestCardData.create_first_strike_forward(), 0)
|
||||
var normal_card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
assert_true(first_strike_card.has_first_strike())
|
||||
assert_false(normal_card.has_first_strike())
|
||||
|
||||
|
||||
func test_ability_checks_with_null_data():
|
||||
var card = CardInstance.new(null, 0)
|
||||
|
||||
assert_false(card.has_haste())
|
||||
assert_false(card.has_brave())
|
||||
assert_false(card.has_first_strike())
|
||||
|
||||
|
||||
## Element Tests
|
||||
|
||||
func test_get_element():
|
||||
var fire_card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE), 0)
|
||||
var ice_card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.ICE), 0)
|
||||
|
||||
assert_eq(fire_card.get_element(), Enums.Element.FIRE)
|
||||
assert_eq(ice_card.get_element(), Enums.Element.ICE)
|
||||
|
||||
|
||||
func test_get_elements():
|
||||
var elements: Array[Enums.Element] = [Enums.Element.FIRE, Enums.Element.ICE]
|
||||
var multi_card = CardInstance.new(TestCardData.create_multi_element_forward("id", "Multi", elements), 0)
|
||||
|
||||
var card_elements = multi_card.get_elements()
|
||||
|
||||
assert_eq(card_elements.size(), 2)
|
||||
assert_true(Enums.Element.FIRE in card_elements)
|
||||
assert_true(Enums.Element.ICE in card_elements)
|
||||
|
||||
|
||||
func test_is_light_or_dark():
|
||||
var fire_card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var light_card = CardInstance.new(TestCardData.create_light_forward(), 0)
|
||||
var dark_card = CardInstance.new(TestCardData.create_dark_forward(), 0)
|
||||
|
||||
assert_false(fire_card.is_light_or_dark())
|
||||
assert_true(light_card.is_light_or_dark())
|
||||
assert_true(dark_card.is_light_or_dark())
|
||||
|
||||
|
||||
## Damage Tests
|
||||
|
||||
func test_apply_damage():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
var broken = card.apply_damage(3000)
|
||||
|
||||
assert_false(broken)
|
||||
assert_eq(card.damage_received, 3000)
|
||||
|
||||
|
||||
func test_apply_damage_breaks_card():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
var broken = card.apply_damage(5000) # Damage equals power
|
||||
|
||||
assert_true(broken)
|
||||
|
||||
|
||||
func test_apply_damage_accumulates():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
|
||||
card.apply_damage(3000)
|
||||
var broken = card.apply_damage(2000) # Total 5000 = power
|
||||
|
||||
assert_true(broken)
|
||||
assert_eq(card.damage_received, 5000)
|
||||
|
||||
|
||||
func test_apply_damage_to_backup_fails():
|
||||
var card = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
|
||||
var broken = card.apply_damage(1000)
|
||||
|
||||
assert_false(broken) # Backups can't receive combat damage
|
||||
|
||||
|
||||
func test_apply_damage_considers_modifiers():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000), 0)
|
||||
card.power_modifiers.append(2000) # Power is now 7000
|
||||
|
||||
var broken = card.apply_damage(5000) # 5000 < 7000
|
||||
|
||||
assert_false(broken)
|
||||
|
||||
|
||||
## Lifecycle Tests
|
||||
|
||||
func test_entered_field():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 5
|
||||
card.attacked_this_turn = true
|
||||
card.damage_received = 1000
|
||||
|
||||
card.entered_field()
|
||||
|
||||
assert_eq(card.turns_on_field, 0)
|
||||
assert_false(card.attacked_this_turn)
|
||||
assert_eq(card.damage_received, 0)
|
||||
|
||||
|
||||
func test_start_turn():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.turns_on_field = 0
|
||||
|
||||
card.start_turn()
|
||||
|
||||
assert_eq(card.turns_on_field, 1)
|
||||
|
||||
|
||||
func test_end_turn_cleanup():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.power_modifiers.append(1000)
|
||||
card.power_modifiers.append(-500)
|
||||
card.damage_received = 2000
|
||||
card.attacked_this_turn = true
|
||||
|
||||
card.end_turn_cleanup()
|
||||
|
||||
assert_eq(card.power_modifiers.size(), 0)
|
||||
assert_eq(card.damage_received, 0)
|
||||
assert_false(card.attacked_this_turn)
|
||||
|
||||
|
||||
## Display Tests
|
||||
|
||||
func test_get_display_name():
|
||||
var card = CardInstance.new(TestCardData.create_forward("id", "Cloud"), 0)
|
||||
|
||||
assert_eq(card.get_display_name(), "Cloud")
|
||||
|
||||
|
||||
func test_get_display_name_null_data():
|
||||
var card = CardInstance.new(null, 0)
|
||||
|
||||
assert_eq(card.get_display_name(), "Unknown Card")
|
||||
|
||||
|
||||
## Dull Ability Tests
|
||||
|
||||
func test_can_use_dull_ability():
|
||||
var card = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
card.turns_on_field = 1
|
||||
|
||||
assert_true(card.can_use_dull_ability())
|
||||
|
||||
|
||||
func test_cannot_use_dull_ability_summoning_sickness():
|
||||
var card = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
card.turns_on_field = 0
|
||||
|
||||
assert_false(card.can_use_dull_ability())
|
||||
|
||||
|
||||
func test_can_use_dull_ability_with_haste():
|
||||
var card = CardInstance.new(TestCardData.create_haste_forward(), 0)
|
||||
card.turns_on_field = 0
|
||||
|
||||
assert_true(card.can_use_dull_ability())
|
||||
372
tests/unit/test_cppool.gd
Normal file
372
tests/unit/test_cppool.gd
Normal file
@@ -0,0 +1,372 @@
|
||||
extends GutTest
|
||||
|
||||
## Unit tests for CPPool.gd
|
||||
|
||||
var cp_pool: CPPool
|
||||
|
||||
|
||||
func before_each():
|
||||
cp_pool = CPPool.new()
|
||||
|
||||
|
||||
## Initialization Tests
|
||||
|
||||
func test_initial_state_empty():
|
||||
assert_eq(cp_pool.get_total_cp(), 0)
|
||||
for element in Enums.Element.values():
|
||||
assert_eq(cp_pool.get_cp(element), 0)
|
||||
|
||||
|
||||
## Basic CP Operations
|
||||
|
||||
func test_add_cp():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.FIRE), 2)
|
||||
assert_eq(cp_pool.get_total_cp(), 2)
|
||||
|
||||
|
||||
func test_add_cp_multiple_elements():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 3)
|
||||
cp_pool.add_cp(Enums.Element.WIND, 1)
|
||||
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.FIRE), 2)
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.ICE), 3)
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.WIND), 1)
|
||||
assert_eq(cp_pool.get_total_cp(), 6)
|
||||
|
||||
|
||||
func test_add_cp_accumulates():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.FIRE), 5)
|
||||
|
||||
|
||||
func test_add_cp_floors_at_zero():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.FIRE, -5) # Try to go negative
|
||||
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.FIRE), 0)
|
||||
|
||||
|
||||
func test_clear_resets_all():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 3)
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 1)
|
||||
|
||||
cp_pool.clear()
|
||||
|
||||
assert_eq(cp_pool.get_total_cp(), 0)
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.FIRE), 0)
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.ICE), 0)
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.LIGHT), 0)
|
||||
|
||||
|
||||
## Light/Dark CP Tests
|
||||
|
||||
func test_get_non_light_dark_cp():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 3)
|
||||
cp_pool.add_cp(Enums.Element.DARK, 1)
|
||||
|
||||
assert_eq(cp_pool.get_non_light_dark_cp(), 2)
|
||||
assert_eq(cp_pool.get_total_cp(), 6)
|
||||
|
||||
|
||||
func test_get_non_light_dark_cp_no_regular():
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 3)
|
||||
cp_pool.add_cp(Enums.Element.DARK, 2)
|
||||
|
||||
assert_eq(cp_pool.get_non_light_dark_cp(), 0)
|
||||
|
||||
|
||||
## Single Element Card Affordability Tests
|
||||
|
||||
func test_can_afford_single_element_card_with_exact_element():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000)
|
||||
|
||||
# Have exactly 3 Fire CP
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
assert_true(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_can_afford_single_element_card_with_mixed_cp():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000)
|
||||
|
||||
# Have 1 Fire + 2 Ice = enough total with required element
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 2)
|
||||
|
||||
assert_true(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_single_element_without_matching_element():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000)
|
||||
|
||||
# Have plenty of CP but no Fire
|
||||
cp_pool.add_cp(Enums.Element.ICE, 5)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_single_element_insufficient_total():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000)
|
||||
|
||||
# Have Fire but not enough total
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 1)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
## Multi-Element Card Affordability Tests
|
||||
|
||||
func test_can_afford_multi_element_card():
|
||||
var elements: Array[Enums.Element] = [Enums.Element.FIRE, Enums.Element.ICE]
|
||||
var card = TestCardData.create_multi_element_forward("id", "Multi", elements, 4, 7000)
|
||||
|
||||
# Have 1 Fire + 1 Ice + 2 Wind = enough with both required elements
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 1)
|
||||
cp_pool.add_cp(Enums.Element.WIND, 2)
|
||||
|
||||
assert_true(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_multi_element_missing_one_element():
|
||||
var elements: Array[Enums.Element] = [Enums.Element.FIRE, Enums.Element.ICE]
|
||||
var card = TestCardData.create_multi_element_forward("id", "Multi", elements, 4, 7000)
|
||||
|
||||
# Have only Fire, missing Ice
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 4)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_multi_element_insufficient_total():
|
||||
var elements: Array[Enums.Element] = [Enums.Element.FIRE, Enums.Element.ICE]
|
||||
var card = TestCardData.create_multi_element_forward("id", "Multi", elements, 4, 7000)
|
||||
|
||||
# Have both elements but not enough total
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 1)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
## Light Card Affordability Tests
|
||||
|
||||
func test_can_afford_light_card():
|
||||
var card = TestCardData.create_light_forward("id", "Light Forward", 5, 9000)
|
||||
|
||||
# Light cards need at least 1 non-Light/Dark CP
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 4)
|
||||
|
||||
assert_true(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_light_card_with_only_light_dark_cp():
|
||||
var card = TestCardData.create_light_forward("id", "Light Forward", 5, 9000)
|
||||
|
||||
# Only Light/Dark CP - not valid
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 5)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_light_card_insufficient_total():
|
||||
var card = TestCardData.create_light_forward("id", "Light Forward", 5, 9000)
|
||||
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 2)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
## Dark Card Affordability Tests
|
||||
|
||||
func test_can_afford_dark_card():
|
||||
var card = TestCardData.create_dark_forward("id", "Dark Forward", 4, 8000)
|
||||
|
||||
# Dark cards also need at least 1 non-Light/Dark CP
|
||||
cp_pool.add_cp(Enums.Element.WATER, 1)
|
||||
cp_pool.add_cp(Enums.Element.DARK, 3)
|
||||
|
||||
assert_true(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_cannot_afford_dark_card_with_only_light_dark_cp():
|
||||
var card = TestCardData.create_dark_forward("id", "Dark Forward", 4, 8000)
|
||||
|
||||
cp_pool.add_cp(Enums.Element.DARK, 10)
|
||||
|
||||
assert_false(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
## CP Spending Tests
|
||||
|
||||
func test_spend_for_card_single_element():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000)
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 2)
|
||||
|
||||
var spent = cp_pool.spend_for_card(card)
|
||||
|
||||
assert_false(spent.is_empty())
|
||||
assert_eq(cp_pool.get_total_cp(), 1) # 4 - 3 = 1
|
||||
assert_true(Enums.Element.FIRE in spent) # Must have spent at least 1 Fire
|
||||
|
||||
|
||||
func test_spend_for_card_returns_empty_if_cannot_afford():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 3, 5000)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 2) # No Fire
|
||||
|
||||
var spent = cp_pool.spend_for_card(card)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
assert_eq(cp_pool.get_total_cp(), 2) # No CP should be spent
|
||||
|
||||
|
||||
func test_spend_for_card_multi_element():
|
||||
var elements: Array[Enums.Element] = [Enums.Element.FIRE, Enums.Element.ICE]
|
||||
var card = TestCardData.create_multi_element_forward("id", "Multi", elements, 4, 7000)
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 2)
|
||||
cp_pool.add_cp(Enums.Element.WIND, 1)
|
||||
|
||||
var spent = cp_pool.spend_for_card(card)
|
||||
|
||||
assert_false(spent.is_empty())
|
||||
assert_eq(cp_pool.get_total_cp(), 1) # 5 - 4 = 1
|
||||
assert_true(Enums.Element.FIRE in spent)
|
||||
assert_true(Enums.Element.ICE in spent)
|
||||
|
||||
|
||||
func test_spend_for_card_light():
|
||||
var card = TestCardData.create_light_forward("id", "Light Forward", 3, 7000)
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 3)
|
||||
|
||||
var spent = cp_pool.spend_for_card(card)
|
||||
|
||||
assert_false(spent.is_empty())
|
||||
assert_eq(cp_pool.get_total_cp(), 1) # 4 - 3 = 1
|
||||
|
||||
|
||||
func test_spend_for_card_prefers_non_light_dark():
|
||||
var card = TestCardData.create_forward("id", "Test", Enums.Element.FIRE, 2, 5000)
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.LIGHT, 2)
|
||||
|
||||
var spent = cp_pool.spend_for_card(card)
|
||||
|
||||
assert_false(spent.is_empty())
|
||||
# Should prefer spending Fire over Light
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.LIGHT), 2) # Light should be untouched
|
||||
|
||||
|
||||
## Ability Cost Tests
|
||||
|
||||
func test_can_afford_ability_no_cost():
|
||||
assert_true(cp_pool.can_afford_ability(null))
|
||||
|
||||
|
||||
func test_can_afford_ability_with_element_requirements():
|
||||
var cost = CardDatabase.CostData.new()
|
||||
cost.fire = 2
|
||||
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
|
||||
assert_true(cp_pool.can_afford_ability(cost))
|
||||
|
||||
|
||||
func test_cannot_afford_ability_insufficient_element():
|
||||
var cost = CardDatabase.CostData.new()
|
||||
cost.fire = 2
|
||||
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
|
||||
assert_false(cp_pool.can_afford_ability(cost))
|
||||
|
||||
|
||||
func test_can_afford_ability_with_generic():
|
||||
var cost = CardDatabase.CostData.new()
|
||||
cost.fire = 1
|
||||
cost.generic = 2
|
||||
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 2)
|
||||
|
||||
assert_true(cp_pool.can_afford_ability(cost))
|
||||
|
||||
|
||||
func test_spend_for_ability():
|
||||
var cost = CardDatabase.CostData.new()
|
||||
cost.fire = 1
|
||||
cost.generic = 1
|
||||
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 1)
|
||||
|
||||
var result = cp_pool.spend_for_ability(cost)
|
||||
|
||||
assert_true(result)
|
||||
# Spent 1 Fire (required) + 1 generic (from Fire or Ice)
|
||||
assert_eq(cp_pool.get_total_cp(), 1)
|
||||
|
||||
|
||||
func test_spend_for_ability_fails_if_cannot_afford():
|
||||
var cost = CardDatabase.CostData.new()
|
||||
cost.fire = 3
|
||||
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
|
||||
var result = cp_pool.spend_for_ability(cost)
|
||||
|
||||
assert_false(result)
|
||||
assert_eq(cp_pool.get_cp(Enums.Element.FIRE), 1) # No CP spent
|
||||
|
||||
|
||||
## Edge Cases
|
||||
|
||||
func test_can_afford_null_card():
|
||||
assert_false(cp_pool.can_afford_card(null))
|
||||
|
||||
|
||||
func test_spend_for_null_card():
|
||||
var spent = cp_pool.spend_for_card(null)
|
||||
assert_true(spent.is_empty())
|
||||
|
||||
|
||||
func test_zero_cost_card():
|
||||
var card = TestCardData.create_forward("id", "Free Card", Enums.Element.FIRE, 0, 3000)
|
||||
|
||||
# Zero cost still needs at least 1 of the element
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
|
||||
# Actually, checking the code: cost is 0, so get_total_cp() >= 0 is always true
|
||||
# But need 1 Fire for single element check
|
||||
# Let me verify this is correct behavior - a 0 cost card needs element CP
|
||||
# Actually no - if cost is 0, we still check get_total_cp() >= cost which is 0 >= 0
|
||||
# So we'd need 1 Fire (element check) but 0 total (cost check)
|
||||
# This might be a bug in the game logic - should test current behavior
|
||||
|
||||
assert_true(cp_pool.can_afford_card(card))
|
||||
|
||||
|
||||
func test_display_data():
|
||||
cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
cp_pool.add_cp(Enums.Element.ICE, 0) # Zero should not appear
|
||||
cp_pool.add_cp(Enums.Element.WIND, 1)
|
||||
|
||||
var display = cp_pool.get_display_data()
|
||||
|
||||
assert_true("Fire" in display)
|
||||
assert_eq(display["Fire"], 2)
|
||||
assert_true("Wind" in display)
|
||||
assert_eq(display["Wind"], 1)
|
||||
assert_false("Ice" in display) # Zero values excluded
|
||||
596
tests/unit/test_player.gd
Normal file
596
tests/unit/test_player.gd
Normal file
@@ -0,0 +1,596 @@
|
||||
extends GutTest
|
||||
|
||||
## Unit tests for Player.gd
|
||||
|
||||
var player: Player
|
||||
|
||||
|
||||
func before_each():
|
||||
player = Player.new(0, "Test Player")
|
||||
|
||||
|
||||
## Initialization Tests
|
||||
|
||||
func test_initialization():
|
||||
assert_eq(player.player_index, 0)
|
||||
assert_eq(player.player_name, "Test Player")
|
||||
assert_not_null(player.deck)
|
||||
assert_not_null(player.hand)
|
||||
assert_not_null(player.field_forwards)
|
||||
assert_not_null(player.field_backups)
|
||||
assert_not_null(player.damage_zone)
|
||||
assert_not_null(player.break_zone)
|
||||
assert_not_null(player.cp_pool)
|
||||
|
||||
|
||||
func test_default_name():
|
||||
var player1 = Player.new(0)
|
||||
var player2 = Player.new(1)
|
||||
|
||||
assert_eq(player1.player_name, "Player 1")
|
||||
assert_eq(player2.player_name, "Player 2")
|
||||
|
||||
|
||||
func test_zones_have_correct_owner():
|
||||
assert_eq(player.deck.owner_index, 0)
|
||||
assert_eq(player.hand.owner_index, 0)
|
||||
assert_eq(player.field_forwards.owner_index, 0)
|
||||
|
||||
|
||||
func test_constants():
|
||||
assert_eq(Player.MAX_HAND_SIZE, 5)
|
||||
assert_eq(Player.MAX_BACKUPS, 5)
|
||||
assert_eq(Player.DAMAGE_TO_LOSE, 7)
|
||||
|
||||
|
||||
## Drawing Tests
|
||||
|
||||
func test_draw_cards():
|
||||
# Manually add cards to deck
|
||||
for i in range(10):
|
||||
var card = CardInstance.new(TestCardData.create_forward("c" + str(i), "Card " + str(i)), 0)
|
||||
player.deck.add_card(card)
|
||||
|
||||
var drawn = player.draw_cards(5)
|
||||
|
||||
assert_eq(drawn.size(), 5)
|
||||
assert_eq(player.hand.get_count(), 5)
|
||||
assert_eq(player.deck.get_count(), 5)
|
||||
|
||||
|
||||
func test_draw_from_empty_deck():
|
||||
var drawn = player.draw_cards(3)
|
||||
|
||||
assert_eq(drawn.size(), 0)
|
||||
assert_eq(player.hand.get_count(), 0)
|
||||
|
||||
|
||||
func test_draw_partial_deck():
|
||||
for i in range(2):
|
||||
var card = CardInstance.new(TestCardData.create_forward("c" + str(i)), 0)
|
||||
player.deck.add_card(card)
|
||||
|
||||
var drawn = player.draw_cards(5) # Try to draw 5, only 2 available
|
||||
|
||||
assert_eq(drawn.size(), 2)
|
||||
assert_eq(player.hand.get_count(), 2)
|
||||
assert_eq(player.deck.get_count(), 0)
|
||||
|
||||
|
||||
func test_can_draw():
|
||||
assert_false(player.can_draw())
|
||||
|
||||
player.deck.add_card(CardInstance.new(TestCardData.create_forward(), 0))
|
||||
|
||||
assert_true(player.can_draw())
|
||||
|
||||
|
||||
## CP Generation Tests
|
||||
|
||||
func test_discard_for_cp():
|
||||
var card = CardInstance.new(TestCardData.create_forward("test", "Test Card", Enums.Element.FIRE), 0)
|
||||
player.hand.add_card(card)
|
||||
|
||||
var result = player.discard_for_cp(card)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(player.hand.get_count(), 0)
|
||||
assert_eq(player.break_zone.get_count(), 1)
|
||||
assert_eq(player.cp_pool.get_cp(Enums.Element.FIRE), 2)
|
||||
|
||||
|
||||
func test_discard_for_cp_generates_correct_element():
|
||||
var ice_card = CardInstance.new(TestCardData.create_forward("test", "Ice Card", Enums.Element.ICE), 0)
|
||||
player.hand.add_card(ice_card)
|
||||
|
||||
player.discard_for_cp(ice_card)
|
||||
|
||||
assert_eq(player.cp_pool.get_cp(Enums.Element.ICE), 2)
|
||||
assert_eq(player.cp_pool.get_cp(Enums.Element.FIRE), 0)
|
||||
|
||||
|
||||
func test_discard_light_card_for_cp_fails():
|
||||
var card = CardInstance.new(TestCardData.create_light_forward(), 0)
|
||||
player.hand.add_card(card)
|
||||
|
||||
var result = player.discard_for_cp(card)
|
||||
|
||||
assert_false(result)
|
||||
assert_eq(player.hand.get_count(), 1) # Still in hand
|
||||
assert_eq(player.cp_pool.get_total_cp(), 0)
|
||||
|
||||
|
||||
func test_discard_dark_card_for_cp_fails():
|
||||
var card = CardInstance.new(TestCardData.create_dark_forward(), 0)
|
||||
player.hand.add_card(card)
|
||||
|
||||
var result = player.discard_for_cp(card)
|
||||
|
||||
assert_false(result)
|
||||
assert_eq(player.hand.get_count(), 1)
|
||||
|
||||
|
||||
func test_discard_card_not_in_hand_fails():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
# Don't add to hand
|
||||
|
||||
var result = player.discard_for_cp(card)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_dull_backup_for_cp():
|
||||
var backup = CardInstance.new(TestCardData.create_backup("test", "Test Backup", Enums.Element.ICE), 0)
|
||||
player.field_backups.add_card(backup)
|
||||
|
||||
var result = player.dull_backup_for_cp(backup)
|
||||
|
||||
assert_true(result)
|
||||
assert_true(backup.is_dull())
|
||||
assert_eq(player.cp_pool.get_cp(Enums.Element.ICE), 1)
|
||||
|
||||
|
||||
func test_dull_already_dull_backup_fails():
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
backup.dull()
|
||||
player.field_backups.add_card(backup)
|
||||
|
||||
var result = player.dull_backup_for_cp(backup)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_dull_forward_for_cp_fails():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
player.field_backups.add_card(forward) # Incorrectly in backups zone
|
||||
|
||||
var result = player.dull_backup_for_cp(forward)
|
||||
|
||||
assert_false(result) # is_backup() check fails
|
||||
|
||||
|
||||
func test_dull_backup_not_on_field_fails():
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
player.hand.add_card(backup) # In hand, not on field
|
||||
|
||||
var result = player.dull_backup_for_cp(backup)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
## Card Play Tests - Forward
|
||||
|
||||
func test_play_forward_card():
|
||||
var card = CardInstance.new(TestCardData.create_forward("test", "Test Forward", Enums.Element.FIRE, 3), 0)
|
||||
player.hand.add_card(card)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
var spent = player.play_card(card)
|
||||
|
||||
assert_false(spent.is_empty())
|
||||
assert_eq(player.hand.get_count(), 0)
|
||||
assert_eq(player.field_forwards.get_count(), 1)
|
||||
assert_true(card.is_active())
|
||||
|
||||
|
||||
func test_play_forward_calls_entered_field():
|
||||
var card = CardInstance.new(TestCardData.create_forward("test", "Test Forward", Enums.Element.FIRE, 3), 0)
|
||||
card.turns_on_field = 5 # Artificially set
|
||||
player.hand.add_card(card)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
player.play_card(card)
|
||||
|
||||
assert_eq(card.turns_on_field, 0) # Reset by entered_field
|
||||
|
||||
|
||||
## Card Play Tests - Backup
|
||||
|
||||
func test_play_backup_enters_dull():
|
||||
var card = CardInstance.new(TestCardData.create_backup("test", "Test Backup", Enums.Element.EARTH, 2), 0)
|
||||
player.hand.add_card(card)
|
||||
player.cp_pool.add_cp(Enums.Element.EARTH, 2)
|
||||
|
||||
player.play_card(card)
|
||||
|
||||
assert_eq(player.field_backups.get_count(), 1)
|
||||
assert_true(card.is_dull()) # Backups enter dull
|
||||
|
||||
|
||||
func test_play_card_insufficient_cp():
|
||||
var card = CardInstance.new(TestCardData.create_forward("test", "Test Forward", Enums.Element.FIRE, 3), 0)
|
||||
player.hand.add_card(card)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 1) # Not enough
|
||||
|
||||
var spent = player.play_card(card)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
assert_eq(player.hand.get_count(), 1) # Still in hand
|
||||
assert_eq(player.field_forwards.get_count(), 0)
|
||||
|
||||
|
||||
func test_play_card_not_in_hand():
|
||||
var card = CardInstance.new(TestCardData.create_forward("test", "Test Forward", Enums.Element.FIRE, 3), 0)
|
||||
# Don't add to hand
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 5)
|
||||
|
||||
var spent = player.play_card(card)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
|
||||
|
||||
## Field Limit Tests
|
||||
|
||||
func test_max_backups_limit():
|
||||
# Fill backup field
|
||||
for i in range(5):
|
||||
var backup = CardInstance.new(TestCardData.create_backup("b" + str(i), "Backup " + str(i), Enums.Element.FIRE, 1, true), 0)
|
||||
player.field_backups.add_card(backup)
|
||||
|
||||
# Try to play 6th backup
|
||||
var sixth_backup = CardInstance.new(TestCardData.create_backup("b5", "Backup 6", Enums.Element.FIRE, 1, true), 0)
|
||||
player.hand.add_card(sixth_backup)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
|
||||
var spent = player.play_card(sixth_backup)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
assert_eq(player.field_backups.get_count(), 5)
|
||||
assert_eq(player.hand.get_count(), 1) # Still in hand
|
||||
|
||||
|
||||
## Unique Name Restriction Tests
|
||||
|
||||
func test_unique_name_restriction_forwards():
|
||||
var cloud1 = CardInstance.new(TestCardData.create_forward("c1", "Cloud", Enums.Element.FIRE, 2, 5000, false), 0)
|
||||
player.field_forwards.add_card(cloud1)
|
||||
|
||||
var cloud2 = CardInstance.new(TestCardData.create_forward("c2", "Cloud", Enums.Element.FIRE, 2, 5000, false), 0)
|
||||
player.hand.add_card(cloud2)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
var spent = player.play_card(cloud2)
|
||||
|
||||
assert_true(spent.is_empty()) # Can't play same name
|
||||
|
||||
|
||||
func test_unique_name_restriction_backups():
|
||||
var aerith1 = CardInstance.new(TestCardData.create_backup("a1", "Aerith", Enums.Element.EARTH, 2, false), 0)
|
||||
player.field_backups.add_card(aerith1)
|
||||
|
||||
var aerith2 = CardInstance.new(TestCardData.create_backup("a2", "Aerith", Enums.Element.EARTH, 2, false), 0)
|
||||
player.hand.add_card(aerith2)
|
||||
player.cp_pool.add_cp(Enums.Element.EARTH, 3)
|
||||
|
||||
var spent = player.play_card(aerith2)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
|
||||
|
||||
func test_unique_name_restriction_cross_zone():
|
||||
# Cloud as forward
|
||||
var cloud_forward = CardInstance.new(TestCardData.create_forward("cf", "Cloud", Enums.Element.FIRE, 3, 5000, false), 0)
|
||||
player.field_forwards.add_card(cloud_forward)
|
||||
|
||||
# Try to play Cloud as backup (same name)
|
||||
var cloud_backup = CardInstance.new(TestCardData.create_backup("cb", "Cloud", Enums.Element.FIRE, 2, false), 0)
|
||||
player.hand.add_card(cloud_backup)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
var spent = player.play_card(cloud_backup)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
|
||||
|
||||
func test_generic_cards_can_share_names():
|
||||
var mog1 = CardInstance.new(TestCardData.create_forward("m1", "Moogle", Enums.Element.WIND, 1, 3000, true), 0)
|
||||
player.field_forwards.add_card(mog1)
|
||||
|
||||
var mog2 = CardInstance.new(TestCardData.create_forward("m2", "Moogle", Enums.Element.WIND, 1, 3000, true), 0)
|
||||
player.hand.add_card(mog2)
|
||||
player.cp_pool.add_cp(Enums.Element.WIND, 2)
|
||||
|
||||
var spent = player.play_card(mog2)
|
||||
|
||||
assert_false(spent.is_empty()) # Generic cards allowed
|
||||
assert_eq(player.field_forwards.get_count(), 2)
|
||||
|
||||
|
||||
## Light/Dark Restriction Tests
|
||||
|
||||
func test_light_dark_restriction():
|
||||
var light1 = CardInstance.new(TestCardData.create_light_forward("l1", "Light Forward 1", 3, 7000), 0)
|
||||
player.field_forwards.add_card(light1)
|
||||
|
||||
var light2 = CardInstance.new(TestCardData.create_light_forward("l2", "Light Forward 2", 3, 7000), 0)
|
||||
player.hand.add_card(light2)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 5)
|
||||
|
||||
var spent = player.play_card(light2)
|
||||
|
||||
assert_true(spent.is_empty()) # Can't have two Light/Dark cards
|
||||
|
||||
|
||||
func test_light_dark_restriction_dark_after_light():
|
||||
var light = CardInstance.new(TestCardData.create_light_forward("l", "Light Forward", 3, 7000), 0)
|
||||
player.field_forwards.add_card(light)
|
||||
|
||||
var dark = CardInstance.new(TestCardData.create_dark_forward("d", "Dark Forward", 3, 7000), 0)
|
||||
player.hand.add_card(dark)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 5)
|
||||
|
||||
var spent = player.play_card(dark)
|
||||
|
||||
assert_true(spent.is_empty())
|
||||
|
||||
|
||||
func test_light_dark_restriction_backup_zone():
|
||||
var light_backup = CardInstance.new(TestCardData.create_backup("lb", "Light Backup", Enums.Element.LIGHT, 2), 0)
|
||||
player.field_backups.add_card(light_backup)
|
||||
|
||||
var light_forward = CardInstance.new(TestCardData.create_light_forward("lf", "Light Forward", 3, 7000), 0)
|
||||
player.hand.add_card(light_forward)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 5)
|
||||
|
||||
var spent = player.play_card(light_forward)
|
||||
|
||||
assert_true(spent.is_empty()) # Light in backups blocks light forward
|
||||
|
||||
|
||||
## Damage Tests
|
||||
|
||||
func test_take_damage():
|
||||
for i in range(10):
|
||||
var card = CardInstance.new(TestCardData.create_forward("d" + str(i)), 0)
|
||||
player.deck.add_card(card)
|
||||
|
||||
var damage_cards = player.take_damage(3)
|
||||
|
||||
assert_eq(damage_cards.size(), 3)
|
||||
assert_eq(player.damage_zone.get_count(), 3)
|
||||
assert_eq(player.deck.get_count(), 7)
|
||||
|
||||
|
||||
func test_take_damage_from_empty_deck():
|
||||
var damage_cards = player.take_damage(3)
|
||||
|
||||
assert_eq(damage_cards.size(), 0)
|
||||
assert_eq(player.damage_zone.get_count(), 0)
|
||||
|
||||
|
||||
func test_take_damage_partial_deck():
|
||||
for i in range(2):
|
||||
var card = CardInstance.new(TestCardData.create_forward("d" + str(i)), 0)
|
||||
player.deck.add_card(card)
|
||||
|
||||
var damage_cards = player.take_damage(5) # Try 5, only 2 in deck
|
||||
|
||||
assert_eq(damage_cards.size(), 2)
|
||||
assert_eq(player.damage_zone.get_count(), 2)
|
||||
|
||||
|
||||
func test_has_lost():
|
||||
for i in range(10):
|
||||
var card = CardInstance.new(TestCardData.create_forward("d" + str(i)), 0)
|
||||
player.deck.add_card(card)
|
||||
|
||||
player.take_damage(6)
|
||||
assert_false(player.has_lost())
|
||||
|
||||
player.take_damage(1)
|
||||
assert_true(player.has_lost())
|
||||
|
||||
|
||||
func test_get_damage_count():
|
||||
for i in range(5):
|
||||
var card = CardInstance.new(TestCardData.create_forward("d" + str(i)), 0)
|
||||
player.deck.add_card(card)
|
||||
|
||||
player.take_damage(3)
|
||||
|
||||
assert_eq(player.get_damage_count(), 3)
|
||||
|
||||
|
||||
## Break Card Tests
|
||||
|
||||
func test_break_forward():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
player.field_forwards.add_card(card)
|
||||
|
||||
var result = player.break_card(card)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(player.field_forwards.get_count(), 0)
|
||||
assert_eq(player.break_zone.get_count(), 1)
|
||||
|
||||
|
||||
func test_break_backup():
|
||||
var card = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
player.field_backups.add_card(card)
|
||||
|
||||
var result = player.break_card(card)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(player.field_backups.get_count(), 0)
|
||||
assert_eq(player.break_zone.get_count(), 1)
|
||||
|
||||
|
||||
func test_break_card_not_on_field():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
player.hand.add_card(card)
|
||||
|
||||
var result = player.break_card(card)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
## Turn Cleanup Tests
|
||||
|
||||
func test_activate_all():
|
||||
var forward = CardInstance.new(TestCardData.create_forward("f", "Forward"), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup("b", "Backup"), 0)
|
||||
forward.dull()
|
||||
backup.dull()
|
||||
player.field_forwards.add_card(forward)
|
||||
player.field_backups.add_card(backup)
|
||||
|
||||
player.activate_all()
|
||||
|
||||
assert_true(forward.is_active())
|
||||
assert_true(backup.is_active())
|
||||
|
||||
|
||||
func test_discard_to_hand_limit():
|
||||
# Add 8 cards to hand
|
||||
for i in range(8):
|
||||
var card = CardInstance.new(TestCardData.create_forward("h" + str(i)), 0)
|
||||
player.hand.add_card(card)
|
||||
|
||||
var discarded = player.discard_to_hand_limit()
|
||||
|
||||
assert_eq(discarded.size(), 3)
|
||||
assert_eq(player.hand.get_count(), 5)
|
||||
assert_eq(player.break_zone.get_count(), 3)
|
||||
|
||||
|
||||
func test_discard_to_hand_limit_already_under():
|
||||
for i in range(3):
|
||||
var card = CardInstance.new(TestCardData.create_forward("h" + str(i)), 0)
|
||||
player.hand.add_card(card)
|
||||
|
||||
var discarded = player.discard_to_hand_limit()
|
||||
|
||||
assert_eq(discarded.size(), 0)
|
||||
assert_eq(player.hand.get_count(), 3)
|
||||
|
||||
|
||||
func test_end_turn_cleanup():
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 3)
|
||||
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
forward.power_modifiers.append(1000)
|
||||
forward.damage_received = 2000
|
||||
forward.attacked_this_turn = true
|
||||
player.field_forwards.add_card(forward)
|
||||
|
||||
player.end_turn_cleanup()
|
||||
|
||||
assert_eq(player.cp_pool.get_total_cp(), 0)
|
||||
assert_eq(forward.power_modifiers.size(), 0)
|
||||
assert_eq(forward.damage_received, 0)
|
||||
assert_false(forward.attacked_this_turn)
|
||||
|
||||
|
||||
func test_start_turn():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
forward.turns_on_field = 0
|
||||
player.field_forwards.add_card(forward)
|
||||
|
||||
player.start_turn()
|
||||
|
||||
assert_eq(forward.turns_on_field, 1)
|
||||
|
||||
|
||||
## Query Tests
|
||||
|
||||
func test_get_all_field_cards():
|
||||
var forward1 = CardInstance.new(TestCardData.create_forward("f1"), 0)
|
||||
var forward2 = CardInstance.new(TestCardData.create_forward("f2"), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup("b"), 0)
|
||||
player.field_forwards.add_card(forward1)
|
||||
player.field_forwards.add_card(forward2)
|
||||
player.field_backups.add_card(backup)
|
||||
|
||||
var all_cards = player.get_all_field_cards()
|
||||
|
||||
assert_eq(all_cards.size(), 3)
|
||||
|
||||
|
||||
func test_get_attackable_forwards():
|
||||
var can_attack = CardInstance.new(TestCardData.create_forward("a"), 0)
|
||||
can_attack.turns_on_field = 1
|
||||
|
||||
var cannot_attack_dull = CardInstance.new(TestCardData.create_forward("b"), 0)
|
||||
cannot_attack_dull.turns_on_field = 1
|
||||
cannot_attack_dull.dull()
|
||||
|
||||
var cannot_attack_sickness = CardInstance.new(TestCardData.create_forward("c"), 0)
|
||||
cannot_attack_sickness.turns_on_field = 0
|
||||
|
||||
player.field_forwards.add_card(can_attack)
|
||||
player.field_forwards.add_card(cannot_attack_dull)
|
||||
player.field_forwards.add_card(cannot_attack_sickness)
|
||||
|
||||
var attackers = player.get_attackable_forwards()
|
||||
|
||||
assert_eq(attackers.size(), 1)
|
||||
assert_eq(attackers[0], can_attack)
|
||||
|
||||
|
||||
func test_get_blockable_forwards():
|
||||
var can_block = CardInstance.new(TestCardData.create_forward("a"), 0)
|
||||
|
||||
var cannot_block_dull = CardInstance.new(TestCardData.create_forward("b"), 0)
|
||||
cannot_block_dull.dull()
|
||||
|
||||
player.field_forwards.add_card(can_block)
|
||||
player.field_forwards.add_card(cannot_block_dull)
|
||||
|
||||
var blockers = player.get_blockable_forwards()
|
||||
|
||||
assert_eq(blockers.size(), 1)
|
||||
assert_eq(blockers[0], can_block)
|
||||
|
||||
|
||||
## Summon Tests
|
||||
|
||||
func test_cast_summon():
|
||||
var summon = CardInstance.new(TestCardData.create_summon("s", "Test Summon", Enums.Element.FIRE, 2), 0)
|
||||
player.hand.add_card(summon)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 2)
|
||||
|
||||
var cast = player.cast_summon(summon)
|
||||
|
||||
assert_eq(cast, summon)
|
||||
assert_eq(player.hand.get_count(), 0)
|
||||
|
||||
|
||||
func test_cast_summon_insufficient_cp():
|
||||
var summon = CardInstance.new(TestCardData.create_summon("s", "Test Summon", Enums.Element.FIRE, 2), 0)
|
||||
player.hand.add_card(summon)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 1)
|
||||
|
||||
var cast = player.cast_summon(summon)
|
||||
|
||||
assert_null(cast)
|
||||
assert_eq(player.hand.get_count(), 1)
|
||||
|
||||
|
||||
func test_cast_non_summon_fails():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
player.hand.add_card(forward)
|
||||
player.cp_pool.add_cp(Enums.Element.FIRE, 5)
|
||||
|
||||
var cast = player.cast_summon(forward)
|
||||
|
||||
assert_null(cast)
|
||||
416
tests/unit/test_turn_manager.gd
Normal file
416
tests/unit/test_turn_manager.gd
Normal file
@@ -0,0 +1,416 @@
|
||||
extends GutTest
|
||||
|
||||
## Unit tests for TurnManager.gd
|
||||
|
||||
var turn_manager: TurnManager
|
||||
|
||||
# Signal tracking
|
||||
var phase_changed_called: bool = false
|
||||
var last_phase: Enums.TurnPhase
|
||||
var turn_changed_called: bool = false
|
||||
var last_turn_player: int = -1
|
||||
var turn_started_called: bool = false
|
||||
var turn_ended_called: bool = false
|
||||
|
||||
|
||||
func before_each():
|
||||
turn_manager = TurnManager.new()
|
||||
phase_changed_called = false
|
||||
turn_changed_called = false
|
||||
turn_started_called = false
|
||||
turn_ended_called = false
|
||||
|
||||
turn_manager.phase_changed.connect(_on_phase_changed)
|
||||
turn_manager.turn_changed.connect(_on_turn_changed)
|
||||
turn_manager.turn_started.connect(_on_turn_started)
|
||||
turn_manager.turn_ended.connect(_on_turn_ended)
|
||||
|
||||
|
||||
func _on_phase_changed(phase: Enums.TurnPhase):
|
||||
phase_changed_called = true
|
||||
last_phase = phase
|
||||
|
||||
|
||||
func _on_turn_changed(player_index: int):
|
||||
turn_changed_called = true
|
||||
last_turn_player = player_index
|
||||
|
||||
|
||||
func _on_turn_started(_player_index: int, _turn_number: int):
|
||||
turn_started_called = true
|
||||
|
||||
|
||||
func _on_turn_ended(_player_index: int):
|
||||
turn_ended_called = true
|
||||
|
||||
|
||||
## Initialization Tests
|
||||
|
||||
func test_initial_state():
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.ACTIVE)
|
||||
assert_eq(turn_manager.current_player_index, 0)
|
||||
assert_eq(turn_manager.turn_number, 0)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.PREPARATION)
|
||||
assert_null(turn_manager.current_attacker)
|
||||
assert_null(turn_manager.current_blocker)
|
||||
|
||||
|
||||
## Start Game Tests
|
||||
|
||||
func test_start_game_player_0():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_eq(turn_manager.turn_number, 1)
|
||||
assert_eq(turn_manager.current_player_index, 0)
|
||||
assert_true(turn_manager.is_first_turn)
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.ACTIVE)
|
||||
assert_true(phase_changed_called)
|
||||
assert_true(turn_started_called)
|
||||
|
||||
|
||||
func test_start_game_player_1():
|
||||
turn_manager.start_game(1)
|
||||
|
||||
assert_eq(turn_manager.current_player_index, 1)
|
||||
assert_eq(turn_manager.turn_number, 1)
|
||||
|
||||
|
||||
## Phase Progression Tests
|
||||
|
||||
func test_phase_progression_sequence():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
# ACTIVE -> DRAW
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.DRAW)
|
||||
|
||||
# DRAW -> MAIN_1
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.MAIN_1)
|
||||
|
||||
# MAIN_1 -> ATTACK
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.ATTACK)
|
||||
|
||||
# ATTACK -> MAIN_2
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.MAIN_2)
|
||||
|
||||
# MAIN_2 -> END
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.END)
|
||||
|
||||
|
||||
func test_phase_progression_wraps_to_new_turn():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
# Progress through all phases
|
||||
for i in range(5): # ACTIVE -> DRAW -> MAIN_1 -> ATTACK -> MAIN_2
|
||||
turn_manager.advance_phase()
|
||||
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.END)
|
||||
|
||||
# END -> ACTIVE (new turn, player switches)
|
||||
turn_manager.advance_phase()
|
||||
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.ACTIVE)
|
||||
assert_eq(turn_manager.current_player_index, 1) # Player switched
|
||||
assert_eq(turn_manager.turn_number, 2)
|
||||
assert_false(turn_manager.is_first_turn)
|
||||
|
||||
|
||||
func test_phase_emits_signal():
|
||||
turn_manager.start_game(0)
|
||||
phase_changed_called = false
|
||||
|
||||
turn_manager.advance_phase()
|
||||
|
||||
assert_true(phase_changed_called)
|
||||
assert_eq(last_phase, Enums.TurnPhase.DRAW)
|
||||
|
||||
|
||||
func test_turn_changed_signal():
|
||||
turn_manager.start_game(0)
|
||||
turn_changed_called = false
|
||||
|
||||
# Progress through full turn
|
||||
for i in range(6):
|
||||
turn_manager.advance_phase()
|
||||
|
||||
assert_true(turn_changed_called)
|
||||
assert_eq(last_turn_player, 1)
|
||||
|
||||
|
||||
func test_turn_ended_signal():
|
||||
turn_manager.start_game(0)
|
||||
turn_ended_called = false
|
||||
|
||||
# Progress through full turn
|
||||
for i in range(6):
|
||||
turn_manager.advance_phase()
|
||||
|
||||
assert_true(turn_ended_called)
|
||||
|
||||
|
||||
## Phase Query Tests
|
||||
|
||||
func test_is_main_phase():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_false(turn_manager.is_main_phase()) # ACTIVE
|
||||
|
||||
turn_manager.advance_phase() # DRAW
|
||||
assert_false(turn_manager.is_main_phase())
|
||||
|
||||
turn_manager.advance_phase() # MAIN_1
|
||||
assert_true(turn_manager.is_main_phase())
|
||||
|
||||
turn_manager.advance_phase() # ATTACK
|
||||
assert_false(turn_manager.is_main_phase())
|
||||
|
||||
turn_manager.advance_phase() # MAIN_2
|
||||
assert_true(turn_manager.is_main_phase())
|
||||
|
||||
turn_manager.advance_phase() # END
|
||||
assert_false(turn_manager.is_main_phase())
|
||||
|
||||
|
||||
func test_is_attack_phase():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_false(turn_manager.is_attack_phase())
|
||||
|
||||
# Progress to attack phase
|
||||
turn_manager.advance_phase() # DRAW
|
||||
turn_manager.advance_phase() # MAIN_1
|
||||
turn_manager.advance_phase() # ATTACK
|
||||
|
||||
assert_true(turn_manager.is_attack_phase())
|
||||
|
||||
turn_manager.advance_phase() # MAIN_2
|
||||
assert_false(turn_manager.is_attack_phase())
|
||||
|
||||
|
||||
## Draw Count Tests
|
||||
|
||||
func test_first_turn_draws_one():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_eq(turn_manager.get_draw_count(), 1)
|
||||
|
||||
|
||||
func test_subsequent_turns_draw_two():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
# Complete first turn
|
||||
for i in range(6):
|
||||
turn_manager.advance_phase()
|
||||
|
||||
assert_false(turn_manager.is_first_turn)
|
||||
assert_eq(turn_manager.get_draw_count(), 2)
|
||||
|
||||
|
||||
## Attack Phase State Machine Tests
|
||||
|
||||
func test_attack_phase_initial_state():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.PREPARATION)
|
||||
assert_null(turn_manager.current_attacker)
|
||||
assert_null(turn_manager.current_blocker)
|
||||
|
||||
|
||||
func test_start_attack_declaration():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
var result = turn_manager.start_attack_declaration()
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.DECLARATION)
|
||||
|
||||
|
||||
func test_start_attack_declaration_invalid_state():
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration() # Now in DECLARATION
|
||||
|
||||
# Can't start again from DECLARATION
|
||||
var result = turn_manager.start_attack_declaration()
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_set_attacker():
|
||||
var attacker = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
|
||||
var result = turn_manager.set_attacker(attacker)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(turn_manager.current_attacker, attacker)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.BLOCK_DECLARATION)
|
||||
|
||||
|
||||
func test_set_attacker_null_fails():
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
|
||||
var result = turn_manager.set_attacker(null)
|
||||
|
||||
assert_false(result)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.DECLARATION)
|
||||
|
||||
|
||||
func test_set_attacker_wrong_state_fails():
|
||||
var attacker = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
turn_manager.start_game(0)
|
||||
# Don't call start_attack_declaration, still in PREPARATION
|
||||
|
||||
var result = turn_manager.set_attacker(attacker)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_set_blocker():
|
||||
var attacker = CardInstance.new(TestCardData.create_forward("att"), 0)
|
||||
var blocker = CardInstance.new(TestCardData.create_forward("blk"), 1)
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
turn_manager.set_attacker(attacker)
|
||||
|
||||
var result = turn_manager.set_blocker(blocker)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(turn_manager.current_blocker, blocker)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.DAMAGE_RESOLUTION)
|
||||
|
||||
|
||||
func test_set_blocker_null_valid():
|
||||
var attacker = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
turn_manager.set_attacker(attacker)
|
||||
|
||||
var result = turn_manager.set_blocker(null) # No block
|
||||
|
||||
assert_true(result)
|
||||
assert_null(turn_manager.current_blocker)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.DAMAGE_RESOLUTION)
|
||||
|
||||
|
||||
func test_set_blocker_wrong_state_fails():
|
||||
var blocker = CardInstance.new(TestCardData.create_forward(), 1)
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
# Don't set attacker, still in DECLARATION
|
||||
|
||||
var result = turn_manager.set_blocker(blocker)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_complete_attack():
|
||||
var attacker = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
turn_manager.set_attacker(attacker)
|
||||
turn_manager.set_blocker(null)
|
||||
|
||||
var result = turn_manager.complete_attack()
|
||||
|
||||
assert_true(result)
|
||||
assert_null(turn_manager.current_attacker)
|
||||
assert_null(turn_manager.current_blocker)
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.DECLARATION) # Ready for next attack
|
||||
|
||||
|
||||
func test_complete_attack_wrong_state_fails():
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
# Still in DECLARATION, not DAMAGE_RESOLUTION
|
||||
|
||||
var result = turn_manager.complete_attack()
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_end_attack_phase_from_preparation():
|
||||
turn_manager.start_game(0)
|
||||
# Advance to attack phase
|
||||
turn_manager.advance_phase() # DRAW
|
||||
turn_manager.advance_phase() # MAIN_1
|
||||
turn_manager.advance_phase() # ATTACK
|
||||
|
||||
var result = turn_manager.end_attack_phase()
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.MAIN_2)
|
||||
|
||||
|
||||
func test_end_attack_phase_from_declaration():
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.advance_phase() # DRAW
|
||||
turn_manager.advance_phase() # MAIN_1
|
||||
turn_manager.advance_phase() # ATTACK
|
||||
turn_manager.start_attack_declaration()
|
||||
|
||||
var result = turn_manager.end_attack_phase()
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(turn_manager.current_phase, Enums.TurnPhase.MAIN_2)
|
||||
|
||||
|
||||
func test_end_attack_phase_invalid_state():
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
turn_manager.set_attacker(CardInstance.new(TestCardData.create_forward(), 0))
|
||||
# Now in BLOCK_DECLARATION
|
||||
|
||||
var result = turn_manager.end_attack_phase()
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
func test_attack_state_reset_on_new_turn():
|
||||
var attacker = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
turn_manager.start_game(0)
|
||||
turn_manager.start_attack_declaration()
|
||||
turn_manager.set_attacker(attacker)
|
||||
|
||||
# Complete the turn
|
||||
for i in range(6):
|
||||
turn_manager.advance_phase()
|
||||
|
||||
# New turn should have reset attack state
|
||||
assert_eq(turn_manager.attack_step, Enums.AttackStep.PREPARATION)
|
||||
assert_null(turn_manager.current_attacker)
|
||||
assert_null(turn_manager.current_blocker)
|
||||
|
||||
|
||||
## Display String Tests
|
||||
|
||||
func test_get_phase_string():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_eq(turn_manager.get_phase_string(), "Active Phase")
|
||||
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.get_phase_string(), "Draw Phase")
|
||||
|
||||
turn_manager.advance_phase()
|
||||
assert_eq(turn_manager.get_phase_string(), "Main Phase 1")
|
||||
|
||||
|
||||
func test_get_attack_step_string():
|
||||
turn_manager.start_game(0)
|
||||
|
||||
assert_eq(turn_manager.get_attack_step_string(), "Preparation")
|
||||
|
||||
turn_manager.start_attack_declaration()
|
||||
assert_eq(turn_manager.get_attack_step_string(), "Declare Attacker")
|
||||
|
||||
turn_manager.set_attacker(CardInstance.new(TestCardData.create_forward(), 0))
|
||||
assert_eq(turn_manager.get_attack_step_string(), "Declare Blocker")
|
||||
|
||||
turn_manager.set_blocker(null)
|
||||
assert_eq(turn_manager.get_attack_step_string(), "Damage Resolution")
|
||||
349
tests/unit/test_zone.gd
Normal file
349
tests/unit/test_zone.gd
Normal file
@@ -0,0 +1,349 @@
|
||||
extends GutTest
|
||||
|
||||
## Unit tests for Zone.gd
|
||||
|
||||
var zone: Zone
|
||||
|
||||
|
||||
func before_each():
|
||||
zone = Zone.new(Enums.ZoneType.HAND, 0)
|
||||
|
||||
|
||||
## Initialization Tests
|
||||
|
||||
func test_zone_initializes_with_correct_type():
|
||||
assert_eq(zone.zone_type, Enums.ZoneType.HAND)
|
||||
assert_eq(zone.owner_index, 0)
|
||||
|
||||
|
||||
func test_zone_initializes_empty():
|
||||
assert_eq(zone.get_count(), 0)
|
||||
assert_true(zone.is_empty())
|
||||
|
||||
|
||||
func test_zone_default_owner_is_negative_one():
|
||||
var shared_zone = Zone.new(Enums.ZoneType.STACK)
|
||||
assert_eq(shared_zone.owner_index, -1)
|
||||
|
||||
|
||||
## Add Card Tests
|
||||
|
||||
func test_add_card_succeeds():
|
||||
var card_data = TestCardData.create_forward()
|
||||
var card = CardInstance.new(card_data, 0)
|
||||
|
||||
var result = zone.add_card(card)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(zone.get_count(), 1)
|
||||
assert_eq(card.zone_type, Enums.ZoneType.HAND)
|
||||
|
||||
|
||||
func test_add_duplicate_card_fails():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
var result = zone.add_card(card)
|
||||
|
||||
assert_false(result)
|
||||
assert_eq(zone.get_count(), 1)
|
||||
|
||||
|
||||
func test_add_card_at_top_default():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1", "Card 1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2", "Card 2"), 0)
|
||||
|
||||
zone.add_card(card1)
|
||||
zone.add_card(card2) # Default is at_top = true
|
||||
|
||||
assert_eq(zone.get_top_card(), card2)
|
||||
assert_eq(zone.get_bottom_card(), card1)
|
||||
|
||||
|
||||
func test_add_card_at_bottom():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1", "Card 1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2", "Card 2"), 0)
|
||||
|
||||
zone.add_card(card1, true) # at top
|
||||
zone.add_card(card2, false) # at bottom
|
||||
|
||||
assert_eq(zone.get_top_card(), card1)
|
||||
assert_eq(zone.get_bottom_card(), card2)
|
||||
|
||||
|
||||
func test_add_card_updates_card_zone_type():
|
||||
var deck_zone = Zone.new(Enums.ZoneType.DECK, 0)
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
card.zone_type = Enums.ZoneType.HAND
|
||||
|
||||
deck_zone.add_card(card)
|
||||
|
||||
assert_eq(card.zone_type, Enums.ZoneType.DECK)
|
||||
|
||||
|
||||
## Remove Card Tests
|
||||
|
||||
func test_remove_card_succeeds():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
var result = zone.remove_card(card)
|
||||
|
||||
assert_true(result)
|
||||
assert_eq(zone.get_count(), 0)
|
||||
|
||||
|
||||
func test_remove_nonexistent_card_fails():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
|
||||
var result = zone.remove_card(card)
|
||||
|
||||
assert_false(result)
|
||||
|
||||
|
||||
## Query Tests
|
||||
|
||||
func test_get_cards_returns_copy():
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
var cards = zone.get_cards()
|
||||
cards.clear() # Modify the returned array
|
||||
|
||||
assert_eq(zone.get_count(), 1) # Original should be unchanged
|
||||
|
||||
|
||||
func test_has_card():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2"), 0)
|
||||
zone.add_card(card1)
|
||||
|
||||
assert_true(zone.has_card(card1))
|
||||
assert_false(zone.has_card(card2))
|
||||
|
||||
|
||||
func test_get_card_at():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2"), 0)
|
||||
zone.add_card(card1)
|
||||
zone.add_card(card2)
|
||||
|
||||
assert_eq(zone.get_card_at(0), card1)
|
||||
assert_eq(zone.get_card_at(1), card2)
|
||||
assert_null(zone.get_card_at(2))
|
||||
assert_null(zone.get_card_at(-1))
|
||||
|
||||
|
||||
## Deck Operations Tests
|
||||
|
||||
func test_pop_top_card():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2"), 0)
|
||||
zone.add_card(card1)
|
||||
zone.add_card(card2)
|
||||
|
||||
var popped = zone.pop_top_card()
|
||||
|
||||
assert_eq(popped, card2)
|
||||
assert_eq(zone.get_count(), 1)
|
||||
assert_eq(zone.get_top_card(), card1)
|
||||
|
||||
|
||||
func test_pop_top_card_empty_zone():
|
||||
var popped = zone.pop_top_card()
|
||||
|
||||
assert_null(popped)
|
||||
|
||||
|
||||
func test_get_top_card_empty_zone():
|
||||
assert_null(zone.get_top_card())
|
||||
|
||||
|
||||
func test_get_bottom_card_empty_zone():
|
||||
assert_null(zone.get_bottom_card())
|
||||
|
||||
|
||||
func test_shuffle():
|
||||
# Add 10 cards
|
||||
for i in range(10):
|
||||
var card = CardInstance.new(TestCardData.create_forward("c" + str(i), "Card " + str(i)), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
var original_order = zone.get_cards().duplicate()
|
||||
zone.shuffle()
|
||||
var shuffled_order = zone.get_cards()
|
||||
|
||||
# With 10 cards, probability of same order after shuffle is 1/10! = ~0.000028%
|
||||
var different = false
|
||||
for i in range(10):
|
||||
if original_order[i] != shuffled_order[i]:
|
||||
different = true
|
||||
break
|
||||
assert_true(different, "Shuffle should change card order (statistically)")
|
||||
|
||||
|
||||
func test_clear():
|
||||
for i in range(5):
|
||||
var card = CardInstance.new(TestCardData.create_forward("c" + str(i)), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
var removed = zone.clear()
|
||||
|
||||
assert_eq(removed.size(), 5)
|
||||
assert_eq(zone.get_count(), 0)
|
||||
assert_true(zone.is_empty())
|
||||
|
||||
|
||||
## Type Filtering Tests
|
||||
|
||||
func test_get_forwards():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
var summon = CardInstance.new(TestCardData.create_summon(), 0)
|
||||
zone.add_card(forward)
|
||||
zone.add_card(backup)
|
||||
zone.add_card(summon)
|
||||
|
||||
var forwards = zone.get_forwards()
|
||||
|
||||
assert_eq(forwards.size(), 1)
|
||||
assert_eq(forwards[0], forward)
|
||||
|
||||
|
||||
func test_get_backups():
|
||||
var forward = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
var backup = CardInstance.new(TestCardData.create_backup(), 0)
|
||||
zone.add_card(forward)
|
||||
zone.add_card(backup)
|
||||
|
||||
var backups = zone.get_backups()
|
||||
|
||||
assert_eq(backups.size(), 1)
|
||||
assert_eq(backups[0], backup)
|
||||
|
||||
|
||||
## State Filtering Tests
|
||||
|
||||
func test_get_active_cards():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2"), 0)
|
||||
card2.dull()
|
||||
zone.add_card(card1)
|
||||
zone.add_card(card2)
|
||||
|
||||
var active = zone.get_active_cards()
|
||||
|
||||
assert_eq(active.size(), 1)
|
||||
assert_eq(active[0], card1)
|
||||
|
||||
|
||||
func test_get_dull_cards():
|
||||
var card1 = CardInstance.new(TestCardData.create_forward("c1"), 0)
|
||||
var card2 = CardInstance.new(TestCardData.create_forward("c2"), 0)
|
||||
card2.dull()
|
||||
zone.add_card(card1)
|
||||
zone.add_card(card2)
|
||||
|
||||
var dull = zone.get_dull_cards()
|
||||
|
||||
assert_eq(dull.size(), 1)
|
||||
assert_eq(dull[0], card2)
|
||||
|
||||
|
||||
## Search Tests
|
||||
|
||||
func test_find_cards_by_name():
|
||||
var cloud1 = CardInstance.new(TestCardData.create_forward("c1", "Cloud"), 0)
|
||||
var cloud2 = CardInstance.new(TestCardData.create_forward("c2", "Cloud"), 0)
|
||||
var tifa = CardInstance.new(TestCardData.create_forward("c3", "Tifa"), 0)
|
||||
zone.add_card(cloud1)
|
||||
zone.add_card(cloud2)
|
||||
zone.add_card(tifa)
|
||||
|
||||
var found = zone.find_cards_by_name("Cloud")
|
||||
|
||||
assert_eq(found.size(), 2)
|
||||
|
||||
|
||||
func test_find_cards_by_element():
|
||||
var fire_card = CardInstance.new(TestCardData.create_forward("c1", "Fire Card", Enums.Element.FIRE), 0)
|
||||
var ice_card = CardInstance.new(TestCardData.create_forward("c2", "Ice Card", Enums.Element.ICE), 0)
|
||||
zone.add_card(fire_card)
|
||||
zone.add_card(ice_card)
|
||||
|
||||
var fire_cards = zone.find_cards_by_element(Enums.Element.FIRE)
|
||||
|
||||
assert_eq(fire_cards.size(), 1)
|
||||
assert_eq(fire_cards[0], fire_card)
|
||||
|
||||
|
||||
func test_has_card_with_name():
|
||||
var card = CardInstance.new(TestCardData.create_forward("c1", "Cloud"), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
assert_true(zone.has_card_with_name("Cloud"))
|
||||
assert_false(zone.has_card_with_name("Sephiroth"))
|
||||
|
||||
|
||||
func test_has_light_or_dark_with_light():
|
||||
var fire_card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(fire_card)
|
||||
assert_false(zone.has_light_or_dark())
|
||||
|
||||
var light_card = CardInstance.new(TestCardData.create_light_forward(), 0)
|
||||
zone.add_card(light_card)
|
||||
assert_true(zone.has_light_or_dark())
|
||||
|
||||
|
||||
func test_has_light_or_dark_with_dark():
|
||||
var dark_card = CardInstance.new(TestCardData.create_dark_forward(), 0)
|
||||
zone.add_card(dark_card)
|
||||
|
||||
assert_true(zone.has_light_or_dark())
|
||||
|
||||
|
||||
## Callback Tests
|
||||
|
||||
func test_on_card_added_callback():
|
||||
# Use array to capture value since GDScript lambdas need reference types
|
||||
var result = [null]
|
||||
zone.on_card_added = func(c): result[0] = c
|
||||
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(card)
|
||||
|
||||
assert_eq(result[0], card)
|
||||
|
||||
|
||||
func test_on_card_removed_callback():
|
||||
var result = [null]
|
||||
zone.on_card_removed = func(c): result[0] = c
|
||||
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(card)
|
||||
zone.remove_card(card)
|
||||
|
||||
assert_eq(result[0], card)
|
||||
|
||||
|
||||
func test_pop_triggers_removed_callback():
|
||||
var result = [null]
|
||||
zone.on_card_removed = func(c): result[0] = c
|
||||
|
||||
var card = CardInstance.new(TestCardData.create_forward(), 0)
|
||||
zone.add_card(card)
|
||||
zone.pop_top_card()
|
||||
|
||||
assert_eq(result[0], card)
|
||||
|
||||
|
||||
func test_clear_triggers_removed_callbacks():
|
||||
var counter = [0]
|
||||
zone.on_card_removed = func(_card): counter[0] += 1
|
||||
|
||||
for i in range(3):
|
||||
zone.add_card(CardInstance.new(TestCardData.create_forward("c" + str(i)), 0))
|
||||
|
||||
zone.clear()
|
||||
|
||||
assert_eq(counter[0], 3)
|
||||
Reference in New Issue
Block a user