421 lines
11 KiB
GDScript
421 lines
11 KiB
GDScript
extends GutTest
|
|
|
|
## Tests for the ConditionChecker class
|
|
## Tests various condition types and logical operators
|
|
|
|
|
|
# =============================================================================
|
|
# SETUP
|
|
# =============================================================================
|
|
|
|
var checker: ConditionChecker
|
|
|
|
|
|
func before_each() -> void:
|
|
checker = ConditionChecker.new()
|
|
|
|
|
|
# =============================================================================
|
|
# BASIC TESTS
|
|
# =============================================================================
|
|
|
|
func test_checker_instantiates() -> void:
|
|
assert_not_null(checker, "ConditionChecker should instantiate")
|
|
|
|
|
|
func test_empty_condition_returns_true() -> void:
|
|
var result = checker.evaluate({}, {})
|
|
assert_true(result, "Empty condition should return true (unconditional)")
|
|
|
|
|
|
func test_unknown_condition_type_returns_false() -> void:
|
|
var condition = {"type": "NONEXISTENT_TYPE"}
|
|
var result = checker.evaluate(condition, {})
|
|
assert_false(result, "Unknown condition type should return false")
|
|
|
|
|
|
# =============================================================================
|
|
# COMPARISON HELPER TESTS
|
|
# =============================================================================
|
|
|
|
func test_compare_eq() -> void:
|
|
assert_true(checker._compare(5, "EQ", 5), "5 == 5")
|
|
assert_false(checker._compare(5, "EQ", 3), "5 != 3")
|
|
|
|
|
|
func test_compare_neq() -> void:
|
|
assert_true(checker._compare(5, "NEQ", 3), "5 != 3")
|
|
assert_false(checker._compare(5, "NEQ", 5), "5 == 5")
|
|
|
|
|
|
func test_compare_gt() -> void:
|
|
assert_true(checker._compare(5, "GT", 3), "5 > 3")
|
|
assert_false(checker._compare(5, "GT", 5), "5 not > 5")
|
|
assert_false(checker._compare(3, "GT", 5), "3 not > 5")
|
|
|
|
|
|
func test_compare_gte() -> void:
|
|
assert_true(checker._compare(5, "GTE", 3), "5 >= 3")
|
|
assert_true(checker._compare(5, "GTE", 5), "5 >= 5")
|
|
assert_false(checker._compare(3, "GTE", 5), "3 not >= 5")
|
|
|
|
|
|
func test_compare_lt() -> void:
|
|
assert_true(checker._compare(3, "LT", 5), "3 < 5")
|
|
assert_false(checker._compare(5, "LT", 5), "5 not < 5")
|
|
assert_false(checker._compare(5, "LT", 3), "5 not < 3")
|
|
|
|
|
|
func test_compare_lte() -> void:
|
|
assert_true(checker._compare(3, "LTE", 5), "3 <= 5")
|
|
assert_true(checker._compare(5, "LTE", 5), "5 <= 5")
|
|
assert_false(checker._compare(5, "LTE", 3), "5 not <= 3")
|
|
|
|
|
|
# =============================================================================
|
|
# LOGICAL OPERATOR TESTS
|
|
# =============================================================================
|
|
|
|
func test_and_with_all_true() -> void:
|
|
var condition = {
|
|
"type": "AND",
|
|
"conditions": [
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 3},
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "LTE", "value": 5}
|
|
]
|
|
}
|
|
|
|
var context = _create_mock_context_with_damage(4)
|
|
var result = checker.evaluate(condition, context)
|
|
assert_true(result, "AND with all true conditions should return true")
|
|
|
|
|
|
func test_and_with_one_false() -> void:
|
|
var condition = {
|
|
"type": "AND",
|
|
"conditions": [
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 3},
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "LTE", "value": 2}
|
|
]
|
|
}
|
|
|
|
var context = _create_mock_context_with_damage(4)
|
|
var result = checker.evaluate(condition, context)
|
|
assert_false(result, "AND with one false condition should return false")
|
|
|
|
|
|
func test_or_with_one_true() -> void:
|
|
var condition = {
|
|
"type": "OR",
|
|
"conditions": [
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 10},
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 3}
|
|
]
|
|
}
|
|
|
|
var context = _create_mock_context_with_damage(5)
|
|
var result = checker.evaluate(condition, context)
|
|
assert_true(result, "OR with one true condition should return true")
|
|
|
|
|
|
func test_or_with_all_false() -> void:
|
|
var condition = {
|
|
"type": "OR",
|
|
"conditions": [
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 10},
|
|
{"type": "DAMAGE_RECEIVED", "comparison": "EQ", "value": 0}
|
|
]
|
|
}
|
|
|
|
var context = _create_mock_context_with_damage(5)
|
|
var result = checker.evaluate(condition, context)
|
|
assert_false(result, "OR with all false conditions should return false")
|
|
|
|
|
|
func test_not_inverts_true() -> void:
|
|
var condition = {
|
|
"type": "NOT",
|
|
"condition": {"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 10}
|
|
}
|
|
|
|
var context = _create_mock_context_with_damage(5)
|
|
var result = checker.evaluate(condition, context)
|
|
assert_true(result, "NOT should invert false to true")
|
|
|
|
|
|
func test_not_inverts_false() -> void:
|
|
var condition = {
|
|
"type": "NOT",
|
|
"condition": {"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 3}
|
|
}
|
|
|
|
var context = _create_mock_context_with_damage(5)
|
|
var result = checker.evaluate(condition, context)
|
|
assert_false(result, "NOT should invert true to false")
|
|
|
|
|
|
# =============================================================================
|
|
# DAMAGE_RECEIVED CONDITION TESTS
|
|
# =============================================================================
|
|
|
|
func test_damage_received_gte() -> void:
|
|
var condition = {
|
|
"type": "DAMAGE_RECEIVED",
|
|
"comparison": "GTE",
|
|
"value": 5
|
|
}
|
|
|
|
# Not enough damage
|
|
var context = _create_mock_context_with_damage(3)
|
|
assert_false(checker.evaluate(condition, context), "3 damage not >= 5")
|
|
|
|
# Exactly enough
|
|
context = _create_mock_context_with_damage(5)
|
|
assert_true(checker.evaluate(condition, context), "5 damage >= 5")
|
|
|
|
# More than enough
|
|
context = _create_mock_context_with_damage(7)
|
|
assert_true(checker.evaluate(condition, context), "7 damage >= 5")
|
|
|
|
|
|
func test_damage_received_no_game_state() -> void:
|
|
var condition = {
|
|
"type": "DAMAGE_RECEIVED",
|
|
"comparison": "GTE",
|
|
"value": 5
|
|
}
|
|
|
|
var context = {"game_state": null, "player_id": 0}
|
|
var result = checker.evaluate(condition, context)
|
|
assert_false(result, "Should return false without game state")
|
|
|
|
|
|
# =============================================================================
|
|
# FORWARD STATE TESTS
|
|
# =============================================================================
|
|
|
|
func test_forward_state_dull() -> void:
|
|
var condition = {
|
|
"type": "FORWARD_STATE",
|
|
"state": "DULL",
|
|
"check_self": false
|
|
}
|
|
|
|
# Create mock card
|
|
var card = _create_mock_forward()
|
|
card.is_dull = true
|
|
|
|
var context = {"target_card": card}
|
|
assert_true(checker.evaluate(condition, context), "Dull card should match DULL state")
|
|
|
|
card.is_dull = false
|
|
assert_false(checker.evaluate(condition, context), "Active card should not match DULL state")
|
|
|
|
card.free()
|
|
|
|
|
|
func test_forward_state_active() -> void:
|
|
var condition = {
|
|
"type": "FORWARD_STATE",
|
|
"state": "ACTIVE",
|
|
"check_self": false
|
|
}
|
|
|
|
var card = _create_mock_forward()
|
|
card.is_dull = false
|
|
|
|
var context = {"target_card": card}
|
|
assert_true(checker.evaluate(condition, context), "Active card should match ACTIVE state")
|
|
|
|
card.is_dull = true
|
|
assert_false(checker.evaluate(condition, context), "Dull card should not match ACTIVE state")
|
|
|
|
card.free()
|
|
|
|
|
|
func test_forward_state_check_self() -> void:
|
|
var condition = {
|
|
"type": "FORWARD_STATE",
|
|
"state": "DULL",
|
|
"check_self": true
|
|
}
|
|
|
|
var source_card = _create_mock_forward()
|
|
source_card.is_dull = true
|
|
|
|
var context = {"source_card": source_card, "target_card": null}
|
|
assert_true(checker.evaluate(condition, context), "check_self=true should use source_card")
|
|
|
|
source_card.free()
|
|
|
|
|
|
func test_forward_state_no_card() -> void:
|
|
var condition = {
|
|
"type": "FORWARD_STATE",
|
|
"state": "DULL",
|
|
"check_self": false
|
|
}
|
|
|
|
var context = {"target_card": null}
|
|
assert_false(checker.evaluate(condition, context), "Should return false without target card")
|
|
|
|
|
|
# =============================================================================
|
|
# COST COMPARISON TESTS
|
|
# =============================================================================
|
|
|
|
func test_cost_comparison_lte() -> void:
|
|
var condition = {
|
|
"type": "COST_COMPARISON",
|
|
"comparison": "LTE",
|
|
"value": 3
|
|
}
|
|
|
|
var card = _create_mock_forward_with_cost(2)
|
|
var context = {"target_card": card}
|
|
assert_true(checker.evaluate(condition, context), "Cost 2 <= 3")
|
|
|
|
card.card_data.cost = 3
|
|
assert_true(checker.evaluate(condition, context), "Cost 3 <= 3")
|
|
|
|
card.card_data.cost = 5
|
|
assert_false(checker.evaluate(condition, context), "Cost 5 not <= 3")
|
|
|
|
card.free()
|
|
|
|
|
|
func test_cost_comparison_gte() -> void:
|
|
var condition = {
|
|
"type": "COST_COMPARISON",
|
|
"comparison": "GTE",
|
|
"value": 4
|
|
}
|
|
|
|
var card = _create_mock_forward_with_cost(5)
|
|
var context = {"target_card": card}
|
|
assert_true(checker.evaluate(condition, context), "Cost 5 >= 4")
|
|
|
|
card.card_data.cost = 4
|
|
assert_true(checker.evaluate(condition, context), "Cost 4 >= 4")
|
|
|
|
card.card_data.cost = 3
|
|
assert_false(checker.evaluate(condition, context), "Cost 3 not >= 4")
|
|
|
|
card.free()
|
|
|
|
|
|
# =============================================================================
|
|
# POWER COMPARISON TESTS
|
|
# =============================================================================
|
|
|
|
func test_power_comparison_lt_value() -> void:
|
|
var condition = {
|
|
"type": "POWER_COMPARISON",
|
|
"comparison": "LT",
|
|
"value": 8000
|
|
}
|
|
|
|
var card = _create_mock_forward_with_power(5000)
|
|
var context = {"target_card": card}
|
|
assert_true(checker.evaluate(condition, context), "Power 5000 < 8000")
|
|
|
|
card.current_power = 8000
|
|
assert_false(checker.evaluate(condition, context), "Power 8000 not < 8000")
|
|
|
|
card.free()
|
|
|
|
|
|
func test_power_comparison_self_power() -> void:
|
|
var condition = {
|
|
"type": "POWER_COMPARISON",
|
|
"comparison": "LT",
|
|
"compare_to": "SELF_POWER"
|
|
}
|
|
|
|
var attacker = _create_mock_forward_with_power(8000)
|
|
var blocker = _create_mock_forward_with_power(5000)
|
|
|
|
var context = {
|
|
"source_card": attacker,
|
|
"target_card": blocker
|
|
}
|
|
|
|
assert_true(checker.evaluate(condition, context), "Blocker 5000 < Attacker 8000")
|
|
|
|
blocker.current_power = 9000
|
|
assert_false(checker.evaluate(condition, context), "Blocker 9000 not < Attacker 8000")
|
|
|
|
attacker.free()
|
|
blocker.free()
|
|
|
|
|
|
# =============================================================================
|
|
# HELPER FUNCTIONS
|
|
# =============================================================================
|
|
|
|
func _create_mock_context_with_damage(damage: int) -> Dictionary:
|
|
var mock_game_state = MockGameState.new(damage)
|
|
return {
|
|
"game_state": mock_game_state,
|
|
"player_id": 0
|
|
}
|
|
|
|
|
|
func _create_mock_forward() -> CardInstance:
|
|
var card = CardInstance.new()
|
|
var data = CardDatabase.CardData.new()
|
|
data.type = Enums.CardType.FORWARD
|
|
data.cost = 3
|
|
data.power = 7000
|
|
card.card_data = data
|
|
card.is_dull = false
|
|
return card
|
|
|
|
|
|
func _create_mock_forward_with_cost(cost: int) -> CardInstance:
|
|
var card = _create_mock_forward()
|
|
card.card_data.cost = cost
|
|
return card
|
|
|
|
|
|
func _create_mock_forward_with_power(power: int) -> CardInstance:
|
|
var card = _create_mock_forward()
|
|
card.card_data.power = power
|
|
card.current_power = power
|
|
return card
|
|
|
|
|
|
# =============================================================================
|
|
# MOCK CLASSES
|
|
# =============================================================================
|
|
|
|
class MockGameState:
|
|
var players: Array
|
|
|
|
func _init(player_damage: int = 0):
|
|
players = [MockPlayer.new(player_damage), MockPlayer.new(0)]
|
|
|
|
func get_player_damage(player_id: int) -> int:
|
|
if player_id < players.size():
|
|
return players[player_id].damage
|
|
return 0
|
|
|
|
func get_field_cards(player_id: int) -> Array:
|
|
if player_id < players.size():
|
|
return players[player_id].field
|
|
return []
|
|
|
|
func get_break_zone(player_id: int) -> Array:
|
|
if player_id < players.size():
|
|
return players[player_id].break_zone
|
|
return []
|
|
|
|
|
|
class MockPlayer:
|
|
var damage: int = 0
|
|
var field: Array = []
|
|
var break_zone: Array = []
|
|
|
|
func _init(p_damage: int = 0):
|
|
damage = p_damage
|