Files
Pete/internal/web/devcasino_test.go
prosolis 2d653bf439 games: the ladder gets played, and the rack learns where to stand
Trivia had every Go test passing and had never been in a browser, which
this plan's own rule says means nothing. So: play it.

The game itself holds up. The clock drains honestly and does not restart
on a reload, the multiple compounds, walking pays exactly what the felt
quoted, the reveal marks the right answer, and the auto-submit at zero
lands as a timeout rather than an illegal move. The next question's
answer never crosses the wire.

Two bugs only the browser could show:

- The spot printed double the stake after every settled game. standing()
  set spot.amount and *then* poured the chips on, and pour grows the pile
  from what it is told is already there. The money was always right; the
  number under the chips was not, which is the one rule the felt is built
  on.

- The house rack sat on top of the multiplier at 390px. Its 5.75rem inset
  is not a margin, it is the width of blackjack's shoe — so on a phone the
  rack sits in the middle of the felt. It now shrinks on small screens and
  pulls into the corner where the corner is empty; data-at says which rack
  is which, because pulling blackjack's to the edge slides it under the
  deck.

The dev rig seeds its own question bank now (one real OpenTDB batch per
difficulty), because a fresh dev database 503s every start otherwise.
2026-07-14 02:33:28 -07:00

112 lines
3.3 KiB
Go

package web
import (
"context"
"encoding/json"
"fmt"
"io/fs"
"net"
"net/http"
"os"
"testing"
"time"
"pete/internal/games/trivia"
"pete/internal/opentdb"
"pete/internal/storage"
)
// TestDevCasino is not a test. It is the casino, running, on a port, with one
// signed-in player who has chips — so the table can be driven in a real browser,
// which is the only honest way to review an animation.
//
// Skipped unless you ask for it:
//
// PETE_DEV_CASINO=:7788 go test ./internal/web -run TestDevCasino -timeout 0
//
// It prints the session cookie to plant. The routes are wired here rather than
// taken from New(), because New() decides whether the casino exists at the
// moment it builds the mux, and the test rig only signs the player in afterwards.
func TestDevCasino(t *testing.T) {
addr := os.Getenv("PETE_DEV_CASINO")
if addr == "" {
t.Skip("set PETE_DEV_CASINO=:port to run the casino for a browser")
}
s := newCasino(t)
fund(t, 5000)
seedTriviaBank(t)
payload, _ := json.Marshal(SessionUser{
Sub: "sub-1", Username: "reala", Name: "Reala",
Exp: time.Now().Add(24 * time.Hour).Unix(),
})
cookie := s.auth.sign(payload)
staticSub, err := fs.Sub(staticFS, "static")
if err != nil {
t.Fatal(err)
}
mux := http.NewServeMux()
mux.Handle("GET /static/", http.StripPrefix("/static/", http.FileServer(http.FS(staticSub))))
s.casinoRoutes(mux)
ln, err := net.Listen("tcp", addr)
if err != nil {
t.Fatal(err)
}
// Written to a file, not printed: `go test` buffers stdout, and the browser
// driver needs the cookie while the server is still running.
if out := os.Getenv("PETE_DEV_COOKIE_FILE"); out != "" {
if err := os.WriteFile(out, []byte(cookie), 0o600); err != nil {
t.Fatal(err)
}
}
fmt.Printf("\nCASINO http://localhost%s/games\nCOOKIE %s=%s\n\n", addr, sessionCookie, cookie)
srv := &http.Server{Handler: mux, ReadHeaderTimeout: 5 * time.Second}
t.Cleanup(func() { _ = srv.Close() })
_ = srv.Serve(ln)
}
// seedTriviaBank puts enough questions in the bank to deal a ladder of each
// difficulty.
//
// The rig does not run StartTriviaBank — a dev casino that spends its first two
// minutes dripping four hundred questions per difficulty out of a free API is a
// dev casino you cannot use. But a fresh database has an empty bank, and an
// empty bank means every start 503s, so the rig would be unable to show you the
// one game it exists to show you.
//
// One real batch per difficulty, through the real client: fifty questions is
// four ladders' worth, and it means what the browser renders came out of OpenTDB
// and through the same decode-and-store path production uses, entities and all.
func seedTriviaBank(t *testing.T) {
t.Helper()
ctx := context.Background()
client := opentdb.New()
for i, tier := range trivia.Tiers {
have, err := storage.CountTrivia(tier.Difficulty)
if err != nil {
t.Fatal(err)
}
if have >= trivia.Rungs {
continue
}
if i > 0 {
time.Sleep(opentdb.Politeness) // the API asks; asking faster earns nothing
}
qs, err := client.Fetch(ctx, tier.Difficulty, opentdb.Batch)
if err != nil {
t.Fatalf("seeding the trivia bank (%s): %v", tier.Difficulty, err)
}
added, err := storage.AddTriviaQuestions(tier.Difficulty, qs)
if err != nil {
t.Fatal(err)
}
fmt.Printf("BANK %-6s %d questions\n", tier.Difficulty, added)
}
}