test runner added

This commit is contained in:
2026-01-28 11:22:20 -05:00
parent 8b5a7b1989
commit f4c7bab6b0
154 changed files with 18934 additions and 22 deletions

View 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
View 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
View 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)

View 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
View 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)