Skip to content

Commit efe3752

Browse files
committed
refactor: divided tests into separate files, added tests for buy_card
1 parent 9de7f68 commit efe3752

13 files changed

+1288
-1136
lines changed

src/lua/api.lua

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -648,19 +648,11 @@ API.functions["shop"] = function(args)
648648
if args.index == nil then
649649
API.send_error_response(
650650
"Missing required field: index",
651-
ERROR_CODES.INVALID_PARAMETER,
651+
ERROR_CODES.MISSING_ARGUMENTS,
652652
{ field = "index" }
653653
)
654654
return
655655
end
656-
if type(args.index) ~= "number" or args.index < 0 then
657-
API.send_error_response(
658-
"Index must be a non-negative number",
659-
ERROR_CODES.INVALID_PARAMETER,
660-
{ index = args.index }
661-
)
662-
return
663-
end
664656

665657
-- Get card index (1-based) and shop area
666658
local card_pos = args.index + 1
@@ -671,7 +663,7 @@ API.functions["shop"] = function(args)
671663
API.send_error_response(
672664
"Card index out of range",
673665
ERROR_CODES.PARAMETER_OUT_OF_RANGE,
674-
{ index = args.index, max_index = #area.cards - 1 }
666+
{ index = args.index, valid_range = "0-" .. tostring(#area.cards - 1) }
675667
)
676668
return
677669
end
@@ -693,7 +685,7 @@ API.functions["shop"] = function(args)
693685
if not card.ability or not card.ability.set then
694686
API.send_error_response(
695687
"Card has no ability set, can't check consumable area",
696-
ERROR_CODES.INVALID_ACTION,
688+
ERROR_CODES.INVALID_GAME_STATE,
697689
{ index = args.index }
698690
)
699691
return
@@ -726,7 +718,7 @@ API.functions["shop"] = function(args)
726718
if not card_buy_button then
727719
API.send_error_response(
728720
"Card has no buy button",
729-
ERROR_CODES.INVALID_ACTION,
721+
ERROR_CODES.INVALID_GAME_STATE,
730722
{ index = args.index }
731723
)
732724
return

src/lua/utils.lua

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -365,17 +365,23 @@ function utils.get_game_state()
365365
}
366366
end
367367

368-
local jokers = {}
369-
if G.jokers and G.jokers.cards then
370-
for i, card in pairs(G.jokers.cards) do
371-
jokers[i] = {
372-
label = card.label,
373-
config = {
374-
center = card.config.center,
375-
card_limit = G.jokers.card_limit,
376-
card_count = G.jokers.card_count,
377-
},
378-
}
368+
local jokers = {
369+
cards = {}
370+
}
371+
if G.jokers then
372+
373+
jokers.card_limit = G.jokers.card_limit
374+
jokers.card_count = G.jokers.card_count
375+
if G.jokers.cards then
376+
for i, card in pairs(G.jokers.cards) do
377+
jokers.cards[i] = {
378+
label = card.label,
379+
cost = card.cost,
380+
config = {
381+
center = card.config.center,
382+
},
383+
}
384+
end
379385
end
380386
end
381387

tests/lua/__init__.py

Whitespace-only changes.

tests/lua/endpoints/__init__.py

Whitespace-only changes.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import socket
2+
from typing import Generator
3+
4+
import pytest
5+
6+
from balatrobot.enums import ErrorCode, State
7+
8+
from ..conftest import assert_error_response, send_and_receive_api_message
9+
10+
11+
class TestCashOut:
12+
"""Tests for the cash_out API endpoint."""
13+
14+
@pytest.fixture(autouse=True)
15+
def setup_and_teardown(
16+
self, tcp_client: socket.socket
17+
) -> Generator[None, None, None]:
18+
"""Set up and tear down each test method."""
19+
# Start a run
20+
start_run_args = {
21+
"deck": "Red Deck",
22+
"stake": 1,
23+
"challenge": None,
24+
"seed": "OOOO155", # four of a kind in first hand
25+
}
26+
send_and_receive_api_message(tcp_client, "start_run", start_run_args)
27+
28+
# Select blind
29+
send_and_receive_api_message(
30+
tcp_client, "skip_or_select_blind", {"action": "select"}
31+
)
32+
33+
# Play a winning hand (four of a kind) to reach shop
34+
game_state = send_and_receive_api_message(
35+
tcp_client,
36+
"play_hand_or_discard",
37+
{"action": "play_hand", "cards": [0, 1, 2, 3]},
38+
)
39+
assert game_state["state"] == State.ROUND_EVAL.value
40+
yield
41+
send_and_receive_api_message(tcp_client, "go_to_menu", {})
42+
43+
def test_cash_out_success(self, tcp_client: socket.socket) -> None:
44+
"""Test successful cash out returns to shop state."""
45+
# Cash out should transition to shop state
46+
game_state = send_and_receive_api_message(tcp_client, "cash_out", {})
47+
48+
# Verify we're in shop state after cash out
49+
assert game_state["state"] == State.SHOP.value
50+
51+
def test_cash_out_invalid_state_error(self, tcp_client: socket.socket) -> None:
52+
"""Test cash out returns error when not in shop state."""
53+
# Go to menu first to ensure we're not in shop state
54+
send_and_receive_api_message(tcp_client, "go_to_menu", {})
55+
56+
# Try to cash out when not in shop - should return error
57+
response = send_and_receive_api_message(tcp_client, "cash_out", {})
58+
59+
# Verify error response
60+
assert_error_response(
61+
response,
62+
"Cannot cash out when not in round evaluation",
63+
["current_state"],
64+
ErrorCode.INVALID_GAME_STATE.value,
65+
)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import socket
2+
from typing import Generator
3+
4+
import pytest
5+
6+
from balatrobot.enums import State
7+
8+
from ..conftest import send_and_receive_api_message
9+
10+
11+
class TestGetGameState:
12+
"""Tests for the get_game_state API endpoint."""
13+
14+
@pytest.fixture(autouse=True)
15+
def setup_and_teardown(
16+
self, tcp_client: socket.socket
17+
) -> Generator[None, None, None]:
18+
"""Set up and tear down each test method."""
19+
yield
20+
send_and_receive_api_message(tcp_client, "go_to_menu", {})
21+
22+
def test_get_game_state_response(self, tcp_client: socket.socket) -> None:
23+
"""Test get_game_state message returns valid JSON game state."""
24+
game_state = send_and_receive_api_message(tcp_client, "get_game_state", {})
25+
assert isinstance(game_state, dict)
26+
27+
def test_game_state_structure(self, tcp_client: socket.socket) -> None:
28+
"""Test that game state contains expected top-level fields."""
29+
game_state = send_and_receive_api_message(tcp_client, "get_game_state", {})
30+
31+
assert isinstance(game_state, dict)
32+
33+
expected_keys = {"state", "game"}
34+
assert expected_keys.issubset(game_state.keys())
35+
assert isinstance(game_state["state"], int)
36+
assert isinstance(game_state["game"], (dict, type(None)))
37+
38+
def test_game_state_during_run(self, tcp_client: socket.socket) -> None:
39+
"""Test getting game state at different points during a run."""
40+
# Start a run
41+
start_run_args = {
42+
"deck": "Red Deck",
43+
"stake": 1,
44+
"challenge": None,
45+
"seed": "EXAMPLE",
46+
}
47+
initial_state = send_and_receive_api_message(
48+
tcp_client, "start_run", start_run_args
49+
)
50+
assert initial_state["state"] == State.BLIND_SELECT.value
51+
52+
# Get game state again to ensure it's consistent
53+
current_state = send_and_receive_api_message(tcp_client, "get_game_state", {})
54+
55+
assert current_state["state"] == State.BLIND_SELECT.value
56+
assert current_state["state"] == initial_state["state"]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import socket
2+
3+
from balatrobot.enums import State
4+
5+
from ..conftest import send_and_receive_api_message
6+
7+
8+
class TestGoToMenu:
9+
"""Tests for the go_to_menu API endpoint."""
10+
11+
def test_go_to_menu(self, tcp_client: socket.socket) -> None:
12+
"""Test going to the main menu."""
13+
game_state = send_and_receive_api_message(tcp_client, "go_to_menu", {})
14+
assert game_state["state"] == State.MENU.value
15+
16+
def test_go_to_menu_from_run(self, tcp_client: socket.socket) -> None:
17+
"""Test going to menu from within a run."""
18+
# First start a run
19+
start_run_args = {
20+
"deck": "Red Deck",
21+
"stake": 1,
22+
"challenge": None,
23+
"seed": "EXAMPLE",
24+
}
25+
initial_state = send_and_receive_api_message(
26+
tcp_client, "start_run", start_run_args
27+
)
28+
assert initial_state["state"] == State.BLIND_SELECT.value
29+
30+
# Now go to menu
31+
menu_state = send_and_receive_api_message(tcp_client, "go_to_menu", {})
32+
33+
assert menu_state["state"] == State.MENU.value

0 commit comments

Comments
 (0)