147 lines
6.6 KiB
GDScript
147 lines
6.6 KiB
GDScript
extends Node
|
||
## Headless E2E — gerçek backend'e karşı istemci akışlarını doğrular.
|
||
## Çalıştırma (RP5): tests/run_e2e.sh (token üretir + tea_mint seed eder + bunu çağırır)
|
||
## godot --headless --path . res://tests/e2e_test.tscn
|
||
## Env: SS_TOKEN_A (ana karakter), SS_TOKEN_B (wstestchar), SS_API_BASE (ops. override)
|
||
|
||
var _fails := 0
|
||
var _ws_connected := false
|
||
var _got_online_list := false
|
||
var _got_chat_history := false
|
||
var _got_local_history := false
|
||
var _moved_payload: Dictionary = {}
|
||
var _chat_payload: Dictionary = {}
|
||
|
||
func _ready() -> void:
|
||
await _run()
|
||
print("== SONUÇ: %s (%d hata) ==" % ["BAŞARILI" if _fails == 0 else "BAŞARISIZ", _fails])
|
||
get_tree().quit(0 if _fails == 0 else 1)
|
||
|
||
func _run() -> void:
|
||
print("== Stepstead headless E2E ==")
|
||
var base := OS.get_environment("SS_API_BASE")
|
||
if base != "":
|
||
Config.override_base = base
|
||
print(" API base: ", base)
|
||
var token_a := OS.get_environment("SS_TOKEN_A")
|
||
var token_b := OS.get_environment("SS_TOKEN_B")
|
||
if token_a == "" or token_b == "":
|
||
_check(false, "SS_TOKEN_A / SS_TOKEN_B env değişkenleri gerekli")
|
||
return
|
||
|
||
Auth.token = token_a
|
||
|
||
# 1) Karakter yükle
|
||
var res: Dictionary = await GameState.load_character()
|
||
_check(res.ok and str(GameState.character.get("name", "")) != "", "load_character")
|
||
print(" karakter: %s, adım: %d" % [str(GameState.character.get("name", "")), int(GameState.character.get("step_balance", 0))])
|
||
|
||
# 2) Envanter yükle
|
||
res = await GameState.load_inventory()
|
||
_check(res.ok, "load_inventory (%d item)" % GameState.inventory.size())
|
||
|
||
# 3) Consumable kullan (wrapper tea_mint seed etti)
|
||
var bal_before: int = int(GameState.character.get("step_balance", 0))
|
||
res = await GameState.use_item("tea_mint", 1)
|
||
_check(res.ok, "use_item HTTP")
|
||
var bal_after: int = int(GameState.character.get("step_balance", 0))
|
||
_check(bal_after == bal_before + 50, "use_item +50 adım (önce %d, sonra %d)" % [bal_before, bal_after])
|
||
|
||
# 4) WebSocket bağlan
|
||
WsClient.connection_changed.connect(func(c: bool) -> void: _ws_connected = c)
|
||
WsClient.online_list.connect(func(_players: Array) -> void: _got_online_list = true)
|
||
WsClient.chat_history.connect(func(_messages: Array) -> void: _got_chat_history = true)
|
||
WsClient.player_moved.connect(func(p: Dictionary) -> void: _moved_payload = p)
|
||
WsClient.chat_received.connect(func(m: Dictionary) -> void: _chat_payload = m)
|
||
WsClient.start()
|
||
_check(await _wait_for(func() -> bool: return _ws_connected, 10.0), "WS bağlantısı açıldı")
|
||
_check(await _wait_for(func() -> bool: return _got_online_list, 5.0), "online_list alındı")
|
||
_check(await _wait_for(func() -> bool: return _got_chat_history, 5.0), "chat_history alındı")
|
||
|
||
# 4b) Chat: gönder → echo geri gelmeli
|
||
var chat_msg := "e2e test %d" % (Time.get_ticks_msec())
|
||
WsClient.send_chat(chat_msg)
|
||
_check(await _wait_for(func() -> bool: return str(_chat_payload.get("text", "")) == chat_msg, 10.0),
|
||
"chat gönderildi ve echo alındı")
|
||
|
||
# 4c) Yerel chat: kanal local echo + yerel geçmiş
|
||
await get_tree().create_timer(1.2).timeout # flood koruması
|
||
var local_msg := "e2e yerel %d" % (Time.get_ticks_msec())
|
||
WsClient.send_chat(local_msg, "local")
|
||
_check(await _wait_for(func() -> bool:
|
||
return str(_chat_payload.get("text", "")) == local_msg \
|
||
and str(_chat_payload.get("channel", "")) == "local", 10.0),
|
||
"yerel chat echo alındı (channel=local)")
|
||
WsClient.local_chat_history.connect(func(_m: Array) -> void: _got_local_history = true)
|
||
WsClient.request_local_history()
|
||
_check(await _wait_for(func() -> bool: return _got_local_history, 5.0), "yerel chat geçmişi alındı")
|
||
|
||
# 4d) Günlük hedef + market vitrini
|
||
res = await GameState.daily_status()
|
||
_check(res.ok and res.body is Dictionary and res.body.has("goal"),
|
||
"daily_status (hedef %d, bugün %d)" % [int(res.body.get("goal", 0)), int(res.body.get("today_steps", 0))] if res.ok else "daily_status")
|
||
var market_rows: Array = await GameState.market_listings()
|
||
_check(market_rows is Array, "market_listings (%d ilan)" % market_rows.size())
|
||
var vendor_rows: Array = await GameState.vendor_prices()
|
||
_check(not vendor_rows.is_empty(), "vendor_prices (%d çeşit item)" % vendor_rows.size())
|
||
var stock_rows: Array = await GameState.vendor_stock()
|
||
_check(not stock_rows.is_empty(), "vendor_stock (%d mal)" % stock_rows.size())
|
||
res = await GameState.leaderboard("steps")
|
||
_check(res.ok and res.body is Dictionary and not (res.body.get("rows", []) as Array).is_empty(),
|
||
"leaderboard (sıram #%d)" % int(res.body.get("my_rank", 0)) if res.ok else "leaderboard")
|
||
|
||
# 5) Harita + seyahat (lokasyon-graf dünya)
|
||
res = await GameState.load_map()
|
||
var locs: Array = GameState.map_data.get("locations", [])
|
||
var my_loc := str(GameState.map_data.get("my_location", ""))
|
||
_check(res.ok and locs.size() >= 10 and my_loc != "", "load_map (%d lokasyon, buradayım: %s)" % [locs.size(), my_loc])
|
||
|
||
# Bulunduğum yerden giden ilk yolu seç, oraya git
|
||
var dest := ""
|
||
for r in GameState.map_data.get("roads", []):
|
||
if str(r["a"]) == my_loc:
|
||
dest = str(r["b"])
|
||
break
|
||
if str(r["b"]) == my_loc:
|
||
dest = str(r["a"])
|
||
break
|
||
_check(dest != "", "gidilecek komşu lokasyon bulundu (%s)" % dest)
|
||
res = await GameState.travel(dest)
|
||
_check(res.ok and str(GameState.character.get("location_code", "")) == dest, "travel → %s" % dest)
|
||
# Yolda olay çıktıysa çöz (0. seçim)
|
||
if res.ok and res.body is Dictionary and res.body.get("event") is Dictionary:
|
||
var ev_res: Dictionary = await GameState.resolve_event(0)
|
||
_check(ev_res.ok, "yol olayı çözüldü (%s)" % str(res.body["event"].get("name", "")))
|
||
|
||
# 6) Lokasyon detayı
|
||
res = await GameState.load_location()
|
||
_check(res.ok and res.body is Dictionary and res.body.has("resources"),
|
||
"load_location (%d kaynak)" % (res.body.get("resources", []) as Array).size() if res.ok else "load_location")
|
||
|
||
# 7) B karakteri REST ile seyahat etsin → bize location_code'lu player_moved düşmeli
|
||
Auth.token = token_b
|
||
var mv: Dictionary = await Api.request("POST", "/world/travel", {"to_code": "fisilti_ormani"}, true)
|
||
if not mv.ok:
|
||
mv = await Api.request("POST", "/world/travel", {"to_code": "koy"}, true)
|
||
Auth.token = token_a
|
||
_check(mv.ok, "B /world/travel")
|
||
_check(await _wait_for(func() -> bool: return str(_moved_payload.get("name", "")) == "wstestchar", 10.0),
|
||
"player_moved eventi WS'ten alındı (lokasyon: %s)" % str(_moved_payload.get("location_code", "")))
|
||
|
||
WsClient.stop()
|
||
|
||
func _wait_for(cond: Callable, timeout: float) -> bool:
|
||
var elapsed := 0.0
|
||
while elapsed < timeout:
|
||
if cond.call():
|
||
return true
|
||
await get_tree().process_frame
|
||
elapsed += get_process_delta_time()
|
||
return bool(cond.call())
|
||
|
||
func _check(ok: bool, what: String) -> void:
|
||
if ok:
|
||
print(" ✅ ", what)
|
||
else:
|
||
_fails += 1
|
||
printerr(" ❌ ", what)
|