extends Node var passed: int = 0 var failed: int = 0 var _game_root: Node = null func _ready() -> void: pass # run() is called by the test runner; avoid double-execution. func run() -> void: print("\n=== TEST: Ascension Buff System ===\n") TestUtils.set_test_name("ascension") _test_node_resource() _test_node_validation() _test_node_parent_checking() _test_catalogue_queries() _test_purchase_flow() _test_prestige_does_not_reset_prestige_buffs() _test_multiplier_computation() _test_save_load_roundtrip() _print_summary() #region Node Resource Tests func _test_node_resource() -> void: print("--- Node Resource ---") var node: PrestigeBuffNode = PrestigeBuffNode.new() node.id = &"test_node" node.display_name = "Test Node" node.effect_type = PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER node.effect_value = 2.0 node.cost_mantissa = 5.0 node.cost_exponent = 2 # cost = 5e2 = 500 var cost: BigNumber = node.get_cost() TestUtils.assert_equals(5.0, cost.mantissa, "cost mantissa matches") TestUtils.assert_equals(2, cost.exponent, "cost exponent matches") TestUtils.assert_false(node.has_parents(), "node with no parents returns false") func _test_node_validation() -> void: print("--- Node Validation ---") var valid: PrestigeBuffNode = PrestigeBuffNode.new() valid.id = &"valid_id" valid.cost_mantissa = 1.0 TestUtils.assert_true(valid.is_valid(), "node with id and positive cost is valid") var no_id: PrestigeBuffNode = PrestigeBuffNode.new() no_id.cost_mantissa = 1.0 TestUtils.assert_false(no_id.is_valid(), "node with empty id is invalid") var zero_cost: PrestigeBuffNode = PrestigeBuffNode.new() zero_cost.id = &"has_id" zero_cost.cost_mantissa = 0.0 TestUtils.assert_false(zero_cost.is_valid(), "node with zero cost is invalid") func _test_node_parent_checking() -> void: print("--- Parent Checking ---") var unlocked: Dictionary = {} unlocked[&"parent_a"] = true unlocked[&"parent_b"] = false var node: PrestigeBuffNode = PrestigeBuffNode.new() node.id = &"child" node.parent_ids = [&"parent_a"] TestUtils.assert_true(node.all_parents_unlocked(unlocked), "all parents unlocked when all are true") node.parent_ids = [&"parent_a", &"parent_b"] TestUtils.assert_false(node.all_parents_unlocked(unlocked), "not all parents unlocked when one is false") node.parent_ids = [&"nonexistent"] TestUtils.assert_false(node.all_parents_unlocked(unlocked), "missing parent treated as not unlocked") #endregion #region Catalogue Tests func _test_catalogue_queries() -> void: print("--- Catalogue Queries ---") var root: PrestigeBuffNode = _make_node(&"root", [], 0, 0.0) var child_a: PrestigeBuffNode = _make_node(&"child_a", [&"root"], 1, 0.0) var child_b: PrestigeBuffNode = _make_node(&"child_b", [&"root"], 1, 1.0) var grandchild: PrestigeBuffNode = _make_node(&"grandchild", [&"child_a", &"child_b"], 2, 0.0) var cat: PrestigeBuffCatalogue = PrestigeBuffCatalogue.new() cat.nodes = [root, child_a, child_b, grandchild] TestUtils.assert_equals(root, cat.get_node_by_id(&"root"), "get_node_by_id finds root") TestUtils.assert_equals(null, cat.get_node_by_id(&"nonexistent"), "get_node_by_id returns null for unknown id") var ids: Array[StringName] = cat.get_all_ids() TestUtils.assert_equals(4, ids.size(), "get_all_ids returns 4 ids") var roots: Array[PrestigeBuffNode] = cat.get_root_nodes() TestUtils.assert_equals(1, roots.size(), "one root node") TestUtils.assert_equals(&"root", roots[0].id, "root node id is 'root'") var children: Array[PrestigeBuffNode] = cat.get_children_of(&"root") TestUtils.assert_equals(2, children.size(), "root has 2 children") var grandkids: Array[PrestigeBuffNode] = cat.get_children_of(&"child_a") TestUtils.assert_equals(1, grandkids.size(), "child_a has 1 child") #endregion #region Purchase Flow Tests func _test_purchase_flow() -> void: print("--- Purchase Flow ---") var scene: PackedScene = load("res://docs/gyms/tiny_sword/tiny_sword.tscn") _game_root = scene.instantiate() # Use a unique save path to avoid contamination from previous test runs. var save_path: String = "user://test_ascension_%d.json" % randi() DirAccess.remove_absolute(ProjectSettings.globalize_path(save_path)) var gs: LevelGameState = _game_root.get_node("LevelGameState") gs.save_file_path = save_path add_child(_game_root) await _wait() gs = _game_root.find_child("LevelGameState") if gs == null: print("[ERROR] LevelGameState not found") return # Give ascension currency for purchasing gs.add_currency_by_id(&"ascension", BigNumber.from_float(100.0)) await _wait() # Verify catalogue has our two nodes var cat: PrestigeBuffCatalogue = gs.prestige_buff_catalogue TestUtils.assert_not_null(cat, "prestige_buff_catalogue is set") if cat == null: return TestUtils.assert_true(cat.nodes.size() >= 2, "catalogue has at least 2 nodes") # Verify nodes initialized in state TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost starts locked") TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost starts locked") # gold_boost is a root node (no parents), farm_boost requires gold_boost as parent. TestUtils.assert_true(gs.can_purchase_prestige_buff(&"gold_boost"), "gold_boost is purchasable") TestUtils.assert_false(gs.can_purchase_prestige_buff(&"farm_boost"), "farm_boost is NOT purchasable (parent gold_boost not yet unlocked)") # Check cost var cost: BigNumber = gs.get_prestige_buff_cost(&"gold_boost") TestUtils.assert_greater_than(cost.mantissa, 0.0, "gold_boost cost > 0") # Purchase gold_boost var purchased: bool = gs.purchase_prestige_buff(&"gold_boost") TestUtils.assert_true(purchased, "purchase_prestige_buff returns true") TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost is now unlocked") TestUtils.assert_false(gs.can_purchase_prestige_buff(&"gold_boost"), "cannot repurchase already unlocked node") # farm_boost should still be available (both are roots, independent) TestUtils.assert_true(gs.can_purchase_prestige_buff(&"farm_boost"), "farm_boost is still purchasable") # Verify available buffs var available: Array[PrestigeBuffNode] = gs.get_available_prestige_buffs() var found_farm: bool = false var found_gold: bool = false for node in available: if node.id == &"farm_boost": found_farm = true if node.id == &"gold_boost": found_gold = true TestUtils.assert_true(found_farm, "farm_boost appears in available buffs") TestUtils.assert_false(found_gold, "gold_boost does NOT appear in available buffs (already unlocked)") # Test cannot purchase with insufficient currency gs._prestige_buff_unlocked.clear() gs._initialize_prestige_buffs() # Drain ascension currency var bal: BigNumber = gs.get_currency_amount_by_id(&"ascension") gs.spend_currency_by_id(&"ascension", bal) TestUtils.assert_false(gs.can_purchase_prestige_buff(&"gold_boost"), "cannot purchase with no currency") # Restore currency, re-unlock gold_boost for later tests gs.add_currency_by_id(&"ascension", BigNumber.from_float(100.0)) gs.purchase_prestige_buff(&"gold_boost") await _wait() #endregion func _test_prestige_does_not_reset_prestige_buffs() -> void: print("--- Prestige Does Not Reset Prestige ---") if _game_root == null: return var gs: LevelGameState = _game_root.find_child("LevelGameState") if gs == null: return # gold_boost should be unlocked from previous test TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost unlocked before prestige") # Give enough currency to prestige gs.add_currency_by_id(&"gold", BigNumber.new(1.0, 10)) # 1e10 gold await _wait() var pm: PrestigeManager = _game_root.find_child("PrestigeManager") as PrestigeManager if pm == null: print("[WARN] PrestigeManager not found; skipping prestige test") return # Perform prestige if possible if pm.can_prestige(): pm.perform_prestige() await _wait() # After prestige, gold_boost should STILL be unlocked TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost still unlocked after prestige") else: # Manual simulation: call reset_for_prestige directly and verify ascension survives gs.reset_for_prestige(true, false, [&"ascension"]) await _wait() TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost still unlocked after reset_for_prestige") #endregion func _test_multiplier_computation() -> void: print("--- Multiplier Computation ---") if _game_root == null: return var gs: LevelGameState = _game_root.find_child("LevelGameState") if gs == null: return # gold_boost is already unlocked (from purchase test). Effect type 1 = GENERATOR_PRODUCTION_MULTIPLIER var mult: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, &"goldmine") TestUtils.assert_greater_than(mult, 1.0, "multiplier for goldmine is > 1.0 with gold_boost unlocked") # farm_boost is NOT unlocked, multiplier for farm should be 1.0 var farm_mult: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, &"farm") TestUtils.assert_equals(1.0, farm_mult, "multiplier for farm is 1.0 (farm_boost not unlocked)") # Unlock farm_boost and check again gs.add_currency_by_id(&"ascension", BigNumber.from_float(100.0)) gs.purchase_prestige_buff(&"farm_boost") await _wait() var farm_mult2: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.CURRENCY_PRODUCTION_MULTIPLIER, &"farm") TestUtils.assert_greater_than(farm_mult2, 1.0, "multiplier for farm is > 1.0 after unlocking farm_boost") # Check that unrelated effect type returns 1.0 var unrelated: float = gs.get_prestige_buff_multiplier(PrestigeBuffNode.EffectType.RESEARCH_XP_MULTIPLIER, &"") TestUtils.assert_equals(1.0, unrelated, "unrelated effect type returns 1.0") #endregion func _test_save_load_roundtrip() -> void: print("--- Save/Load Roundtrip ---") if _game_root == null: return var gs: LevelGameState = _game_root.find_child("LevelGameState") if gs == null: return # Both gold_boost and farm_boost should be unlocked TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost unlocked before save") TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost unlocked before save") # Save gs.save_game() await _wait() # Clear state manually to simulate fresh load gs._prestige_buff_unlocked.clear() TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost false after clear") TestUtils.assert_false(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost false after clear") # Load gs.load_game() await _wait() # Both should be restored TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"gold_boost"), "gold_boost restored after load") TestUtils.assert_true(gs.is_prestige_buff_unlocked(&"farm_boost"), "farm_boost restored after load") #endregion #region Helpers func _make_node(id: StringName, parent_ids: Array[StringName], tier: int, x_pos: float) -> PrestigeBuffNode: var n: PrestigeBuffNode = PrestigeBuffNode.new() n.id = id n.display_name = String(id) n.parent_ids = parent_ids n.tier = tier n.x_position = x_pos n.cost_mantissa = 1.0 return n func _wait() -> void: await get_tree().create_timer(0.3).timeout func _print_summary() -> void: TestUtils.print_result() passed = TestUtils.get_passed() failed = TestUtils.get_failed() func get_passed() -> int: return passed func get_failed() -> int: return failed