# test_soulbound_lock_security_parity.gd —— 40250 灵魂绑定与防丢安全锁 1:1 回归测试套件 extends SceneTree const SoulboundLockSecuritySystem = preload("res://soulbound_lock_security_system.gd") var _passed := 0 var _failed := 0 func _assert(cond: bool, msg: String) -> void: if cond: _passed += 1 print(" [PASS] %s" % msg) else: _failed += 1 printerr(" [FAIL] %s" % msg) func _init() -> void: print("\n=== Running test_soulbound_lock_security_parity (Direction 3: Soulbound Security 1:1) ===") _test_security_predicates() _test_soul_binding_flow() _test_unbinding_and_cancellation() _test_unbinding_completion() _finish() func _test_security_predicates() -> void: print("\n--- Test 1: Static Security Predicates (Death Drop, Sell, Stall) ---") var normal_item := {"vnum": 10, "is_soulbound": false} var bound_item := {"vnum": 189, "is_soulbound": true} # Normal item can drop, sell, stall _assert(SoulboundLockSecuritySystem.can_drop_on_death(normal_item) == true, "Normal item drops on red name death") _assert(SoulboundLockSecuritySystem.can_sell_to_shop(normal_item) == true, "Normal item can be sold to shop") _assert(SoulboundLockSecuritySystem.can_drop_to_ground(normal_item) == true, "Normal item can be dropped to ground") _assert(SoulboundLockSecuritySystem.can_trade_or_stall(normal_item) == true, "Normal item can be traded or stalled") # Soulbound item is 100% protected _assert(SoulboundLockSecuritySystem.can_drop_on_death(bound_item) == false, "Soulbound item IMMUNE to death drop") _assert(SoulboundLockSecuritySystem.can_sell_to_shop(bound_item) == false, "Soulbound item CANNOT be sold to shop") _assert(SoulboundLockSecuritySystem.can_drop_to_ground(bound_item) == false, "Soulbound item CANNOT be dropped to ground") _assert(SoulboundLockSecuritySystem.can_trade_or_stall(bound_item) == false, "Soulbound item CANNOT be put on bazaar stalls") func _test_soul_binding_flow() -> void: print("\n--- Test 2: Applying Soul Binding (72051) ---") var sys := SoulboundLockSecuritySystem.new() var inv: Array = [ {"vnum": SoulboundLockSecuritySystem.VNUM_SOULBIND_SCROLL, "name": "灵魂绑定卷轴", "count": 2}, {"vnum": 189, "name": "毒剑+9", "is_soulbound": false} ] var bind_sig := {"fired": false, "slot": -1, "name": ""} sys.item_soulbound.connect(func(s: int, iname: String): bind_sig["fired"] = true bind_sig["slot"] = s bind_sig["name"] = iname ) var res = sys.bind_item(0, 1, inv) _assert(res["ok"] == true, "Item soulbound") _assert(inv[1]["is_soulbound"] == true, "is_soulbound is true") _assert(inv[0]["count"] == 1, "Scroll count decreased (2 -> 1)") _assert(bind_sig["fired"] == true and bind_sig["slot"] == 1 and bind_sig["name"] == "毒剑+9", "item_soulbound emitted") # Duplicate binding rejected var fail_dup = sys.bind_item(0, 1, inv) _assert(fail_dup["ok"] == false and fail_dup["reason"] == "ALREADY_SOULBOUND", "Duplicate binding rejected") _assert(inv[0]["count"] == 1, "Scroll not consumed on failure") func _test_unbinding_and_cancellation() -> void: print("\n--- Test 3: Unbinding Initiation (72052) & Cancellation ---") var sys := SoulboundLockSecuritySystem.new() var inv: Array = [ {"vnum": SoulboundLockSecuritySystem.VNUM_SOULUNBIND_SCROLL, "name": "灵魂解绑卷轴", "count": 1}, {"vnum": 189, "name": "毒剑+9", "is_soulbound": true} ] var unbind_start_sig := {"fired": false, "time": 0.0} sys.unbinding_started.connect(func(_s: int, _n: String, t: float): unbind_start_sig["fired"] = true unbind_start_sig["time"] = t ) # Start unbinding (72 hours) var res = sys.start_unbinding(0, 1, inv, 72.0) _assert(res["ok"] == true, "Unbinding started") _assert(inv[0] == null, "Unbind scroll consumed") _assert(unbind_start_sig["fired"] == true and unbind_start_sig["time"] == 72.0 * 3600.0, "unbinding_started emitted with 72h") _assert(inv[1]["unbind_remaining_time"] == 72.0 * 3600.0, "Cooldown active") # Cancel unbinding var cancel_sig := {"fired": false} sys.unbinding_cancelled.connect(func(_s: int, _n: String): cancel_sig["fired"] = true) var cancel_res = sys.cancel_unbinding(1, inv) _assert(cancel_res["ok"] == true, "Unbinding cancelled") _assert(cancel_sig["fired"] == true, "unbinding_cancelled emitted") _assert(inv[1]["unbind_remaining_time"] == 0.0, "Timer cleared") _assert(inv[1]["is_soulbound"] == true, "Item remains safely soulbound") func _test_unbinding_completion() -> void: print("\n--- Test 4: Finalizing Unbinding After Cooldown ---") var sys := SoulboundLockSecuritySystem.new() var inv: Array = [ {"vnum": SoulboundLockSecuritySystem.VNUM_SOULUNBIND_SCROLL, "count": 1}, {"vnum": 189, "name": "毒剑+9", "is_soulbound": true} ] # Start short 1-second unbind for testing sys.start_unbinding(0, 1, inv, 1.0 / 3600.0) # 1 second # Immediate finalize attempt should fail var fail_early = sys.finalize_unbinding(1, inv) _assert(fail_early["ok"] == false and fail_early["reason"] == "COOLDOWN_NOT_FINISHED", "Cannot finalize before timer") # Update 2 seconds sys.update(2.0, inv) _assert(inv[1]["unbind_remaining_time"] == 0.0, "Timer expired") # Finalize unbind var unbound_sig := {"fired": false} sys.item_unbound.connect(func(_s: int, _n: String): unbound_sig["fired"] = true) var done_res = sys.finalize_unbinding(1, inv) _assert(done_res["ok"] == true, "Item unbound") _assert(unbound_sig["fired"] == true, "item_unbound emitted") _assert(inv[1]["is_soulbound"] == false, "Item is no longer soulbound") _assert(SoulboundLockSecuritySystem.can_drop_on_death(inv[1]) == true, "Now subject to normal drop rules") func _finish() -> void: print("\n==========================================") print("Soulbound Security Parity Tests Finished:") print("Passed: %d, Failed: %d" % [_passed, _failed]) print("==========================================") if _failed > 0: printerr("FAILED: Some tests did not pass.") quit(1) else: print("SUCCESS: All Soulbound Security parity tests passed!") quit(0)