186 lines
5.6 KiB
GDScript
186 lines
5.6 KiB
GDScript
extends SceneTree
|
||
|
||
# Auth/BaseLevel 回归测试(无需 GUT)
|
||
#
|
||
# 运行方式:
|
||
# "D:\\technology\\biancheng\\Godot\\Godot_v4.5.1-stable_win64.exe" --headless --path . --script tests/unit/test_auth_baselevel_regressions.gd
|
||
|
||
var _failures: Array[String] = []
|
||
|
||
class TestBaseLevel:
|
||
extends BaseLevel
|
||
|
||
func _ready() -> void:
|
||
# 测试场景中跳过原始 _ready,避免网络/场景副作用
|
||
pass
|
||
|
||
func _init() -> void:
|
||
call_deferred("_run")
|
||
|
||
func _run() -> void:
|
||
await _test_forgot_password_response_handler()
|
||
await _test_auth_request_tracking_cleanup()
|
||
await _test_verification_code_timestamp_and_remaining_time()
|
||
await _test_send_code_cooldown_starts_on_success_only()
|
||
await _test_join_session_wait_has_timeout_guards()
|
||
|
||
if _failures.is_empty():
|
||
print("PASS: test_auth_baselevel_regressions")
|
||
quit(0)
|
||
return
|
||
|
||
for failure in _failures:
|
||
push_error(failure)
|
||
print("FAIL: test_auth_baselevel_regressions (%d)" % _failures.size())
|
||
quit(1)
|
||
|
||
func _test_forgot_password_response_handler() -> void:
|
||
var manager := AuthManager.new()
|
||
var toast_message: String = ""
|
||
|
||
manager.show_toast_message.connect(func(message: String, _is_success: bool): toast_message = message)
|
||
|
||
manager._on_forgot_password_response(true, {}, {})
|
||
_assert(
|
||
"重置验证码" in toast_message,
|
||
"忘记密码成功响应应使用 forgot_password 处理器文案"
|
||
)
|
||
|
||
toast_message = ""
|
||
manager._on_forgot_password_response(false, {"error_code": "USER_NOT_FOUND", "message": "用户不存在"}, {})
|
||
_assert(
|
||
("用户不存在" in toast_message) and ("请检查邮箱或手机号" in toast_message),
|
||
"忘记密码失败响应应使用 forgot_password 错误分支"
|
||
)
|
||
|
||
manager.cleanup()
|
||
|
||
func _test_auth_request_tracking_cleanup() -> void:
|
||
var manager := AuthManager.new()
|
||
manager.active_request_ids = ["req_a", "req_b"]
|
||
|
||
manager._on_network_request_completed("req_a", true, {})
|
||
_assert(
|
||
manager.active_request_ids.size() == 1 and manager.active_request_ids[0] == "req_b",
|
||
"请求完成后应从 active_request_ids 中移除对应 request_id"
|
||
)
|
||
|
||
manager._on_network_request_failed("req_b", "NETWORK_ERROR", "x")
|
||
_assert(
|
||
manager.active_request_ids.is_empty(),
|
||
"请求失败后也应从 active_request_ids 中移除对应 request_id"
|
||
)
|
||
|
||
manager.cleanup()
|
||
|
||
func _test_verification_code_timestamp_and_remaining_time() -> void:
|
||
var manager := AuthManager.new()
|
||
var email := "cooldown_regression@example.com"
|
||
|
||
manager._record_verification_code_sent(email)
|
||
|
||
var sent_timestamp: int = int(manager.verification_codes_sent[email].get("time", 0))
|
||
_assert(
|
||
sent_timestamp > 86400,
|
||
"验证码发送时间应记录 Unix 时间戳,避免跨天计算异常"
|
||
)
|
||
|
||
manager.verification_codes_sent[email].time = int(Time.get_unix_time_from_system()) - 1000
|
||
_assert(
|
||
manager.get_remaining_cooldown_time(email) == 0,
|
||
"冷却已过期时,剩余时间应为 0"
|
||
)
|
||
|
||
manager.cleanup()
|
||
|
||
func _test_send_code_cooldown_starts_on_success_only() -> void:
|
||
var packed_scene: PackedScene = load("res://scenes/ui/AuthScene.tscn")
|
||
_assert(packed_scene != null, "应能加载 AuthScene.tscn")
|
||
if packed_scene == null:
|
||
return
|
||
|
||
var auth_scene: Control = packed_scene.instantiate()
|
||
root.add_child(auth_scene)
|
||
await process_frame
|
||
|
||
auth_scene.register_email.text = "invalid_email"
|
||
auth_scene._on_send_code_pressed()
|
||
await process_frame
|
||
|
||
_assert(
|
||
auth_scene.cooldown_timer == null,
|
||
"邮箱不合法时不应启动验证码冷却计时器"
|
||
)
|
||
_assert(
|
||
not auth_scene.send_code_btn.disabled,
|
||
"邮箱不合法时发送按钮不应被锁定"
|
||
)
|
||
|
||
# 模拟发送成功回调触发冷却(并补齐 manager 内部冷却记录)
|
||
auth_scene.auth_manager._record_verification_code_sent("regression@example.com")
|
||
auth_scene._on_controller_verification_code_sent("ok")
|
||
|
||
_assert(
|
||
auth_scene.cooldown_timer != null,
|
||
"验证码发送成功后应启动冷却计时器"
|
||
)
|
||
_assert(
|
||
auth_scene.send_code_btn.disabled,
|
||
"验证码发送成功后应禁用发送按钮"
|
||
)
|
||
|
||
auth_scene.queue_free()
|
||
await process_frame
|
||
|
||
func _test_join_session_wait_has_timeout_guards() -> void:
|
||
var level := TestBaseLevel.new()
|
||
root.add_child(level)
|
||
await process_frame
|
||
|
||
var original_token: String = LocationManager._auth_token
|
||
var original_poll_interval: float = level._join_token_poll_interval
|
||
var original_token_timeout: float = level._join_token_wait_timeout
|
||
var original_player_timeout_frames: int = level._join_player_wait_timeout_frames
|
||
|
||
level._join_token_poll_interval = 0.01
|
||
level._join_token_wait_timeout = 0.03
|
||
level._join_player_wait_timeout_frames = 2
|
||
|
||
# 场景 1: Token 一直未就绪,函数应在超时后退出,而不是无限递归等待
|
||
LocationManager._auth_token = ""
|
||
level._join_session_with_player("test_session")
|
||
await create_timer(0.15).timeout
|
||
|
||
_assert(
|
||
not level._is_joining_session,
|
||
"Token 长时间未就绪时应超时退出 join 流程"
|
||
)
|
||
|
||
# 场景 2: Token 已就绪但玩家一直未生成,也应在上限后退出
|
||
LocationManager._auth_token = "dummy_token"
|
||
level._player_spawned = false
|
||
level._local_player = null
|
||
level._join_session_with_player("test_session")
|
||
await process_frame
|
||
await process_frame
|
||
await process_frame
|
||
await process_frame
|
||
|
||
_assert(
|
||
not level._is_joining_session,
|
||
"玩家长时间未就绪时应在帧上限后退出 join 流程"
|
||
)
|
||
|
||
# 恢复现场
|
||
LocationManager._auth_token = original_token
|
||
level._join_token_poll_interval = original_poll_interval
|
||
level._join_token_wait_timeout = original_token_timeout
|
||
level._join_player_wait_timeout_frames = original_player_timeout_frames
|
||
|
||
level.queue_free()
|
||
await process_frame
|
||
|
||
func _assert(condition: bool, message: String) -> void:
|
||
if not condition:
|
||
_failures.append(message)
|