Add bot to play against.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/coder/websocket"
|
||||
|
||||
"github.com/greyson/super-auto-pets-board-game/internal/game"
|
||||
"github.com/greyson/super-auto-pets-board-game/internal/store"
|
||||
)
|
||||
|
||||
// TestE2EBotGame creates a vs-computer game over the API, plays the human's
|
||||
// shop turns over the wire, and verifies the scheduled bot actually takes
|
||||
// its own turns (spends coins) without any second client connected.
|
||||
func TestE2EBotGame(t *testing.T) {
|
||||
st, err := store.Open(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer st.Close()
|
||||
srv := New(st, "", false)
|
||||
ts := httptest.NewServer(srv.Handler())
|
||||
defer ts.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := http.Post(ts.URL+"/api/games", "application/json",
|
||||
bytes.NewBufferString(`{"name":"Human","bot":"easy"}`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var join joinResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&join); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
resp.Body.Close()
|
||||
|
||||
wsBase := "ws" + strings.TrimPrefix(ts.URL, "http")
|
||||
ws, _, err := websocket.Dial(ctx,
|
||||
wsBase+"/api/ws?game="+join.GameID+"&player="+join.PlayerID+"&token="+join.Token, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer ws.Close(websocket.StatusNormalClosure, "")
|
||||
|
||||
v := readState(t, ctx, ws)
|
||||
if v.Phase != game.PhaseShop {
|
||||
t.Fatalf("phase = %s, want shop (bot fills the lobby instantly)", v.Phase)
|
||||
}
|
||||
var bot *game.PlayerView
|
||||
for i := range v.Players {
|
||||
if v.Players[i].IsBot {
|
||||
bot = &v.Players[i]
|
||||
}
|
||||
}
|
||||
if bot == nil {
|
||||
t.Fatal("no bot seat in the game")
|
||||
}
|
||||
|
||||
// Play the human side: pass whenever it's our turn. The game can only
|
||||
// reach the arrange phase if the bot spends its own three coins too.
|
||||
deadline := time.Now().Add(25 * time.Second)
|
||||
for v.Phase == game.PhaseShop && time.Now().Before(deadline) {
|
||||
if v.Turn == v.YouSeat && v.Players[v.YouSeat].Coins > 0 && v.Pending == nil {
|
||||
send(t, ctx, ws, map[string]any{"type": "pass"})
|
||||
}
|
||||
rctx, rcancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
v = readState(t, rctx, ws)
|
||||
rcancel()
|
||||
}
|
||||
if v.Phase == game.PhaseShop {
|
||||
t.Fatalf("shop never ended; bot coins=%d", v.Players[bot.Seat].Coins)
|
||||
}
|
||||
t.Logf("reached phase %s; bot played its shop turns", v.Phase)
|
||||
}
|
||||
Reference in New Issue
Block a user