aboutsummaryrefslogtreecommitdiff
path: root/internal/game/casino_commands_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/casino_commands_test.go')
-rw-r--r--internal/game/casino_commands_test.go118
1 files changed, 118 insertions, 0 deletions
diff --git a/internal/game/casino_commands_test.go b/internal/game/casino_commands_test.go
new file mode 100644
index 0000000..7820650
--- /dev/null
+++ b/internal/game/casino_commands_test.go
@@ -0,0 +1,118 @@
+package game
+
+import (
+ "strings"
+ "testing"
+
+ "thehouseoficarus/internal/casino"
+ "thehouseoficarus/internal/color"
+ "thehouseoficarus/internal/net"
+ "thehouseoficarus/internal/player"
+)
+
+func TestCasinoCommandsWrapBeforeSessionWithoutLosingIndent(t *testing.T) {
+ commands := []casino.MenuCommand{
+ {Command: "bet <amount> on <target>", Desc: "Place bets until the dice roll; table rules lists targets"},
+ {Command: "roll / autoroll / stop", Desc: "Shoot / repeat on your turn / return to manual play"},
+ {Command: "unbet <target> / unbet all", Desc: "Remove one bet / every optional bet"},
+ {Command: "table", Desc: "View the felt"},
+ {Command: "insurance", Desc: "Buy insurance; a deliberately long description which requires a hanging continuation line"},
+ {Command: "decline", Desc: "Continue without insurance (also the timeout default)"},
+ {Command: "bet <amount> on player/banker/tie/player_pair/banker_pair", Desc: "One wager per round; 'on' is optional"},
+ {Command: "option casino_display compact", Desc: "Compact action hints (detailed for full command menus)"},
+ {Command: "spin", Desc: "Spin using your current wager"},
+ {Command: "autospin", Desc: "Start automatic betting"},
+ {Command: "unicode", Desc: strings.Repeat("é", 100)},
+ }
+ for _, wrap := range []int{0, 40, 60, 80, 120} {
+ p := player.New("a")
+ p.Options = map[string]any{"wrap_width": wrap}
+ conn := &casinoTestConn{}
+ sess := &net.Session{Player: p, Conn: conn}
+ g := &Game{}
+ text := g.casinoMenuCommands(sess, commands)
+ sess.WriteLine(text)
+ limit := 80
+ if wrap > 0 {
+ limit = min(limit, wrap)
+ }
+ for _, line := range strings.Split(strings.ReplaceAll(plainRoulette(string(conn.data)), "\r\n", "\n"), "\n") {
+ if line == "" {
+ continue
+ }
+ if color.VisibleLen(line) > limit {
+ t.Fatalf("wrap %d overlong %d: %q", wrap, color.VisibleLen(line), line)
+ }
+ if !strings.HasPrefix(line, " ") {
+ t.Fatalf("wrap %d lost indent: %q", wrap, line)
+ }
+ }
+ expected := strings.ReplaceAll(plainRoulette(text), "\r\n", "\n")
+ actual := strings.TrimRight(strings.ReplaceAll(plainRoulette(string(conn.data)), "\r\n", "\n"), "\n")
+ if expected != actual {
+ t.Fatalf("session reflowed prewrapped menu at %d", wrap)
+ }
+ }
+}
+func TestCasinoCommandsHangingIndentAndShortDescription(t *testing.T) {
+ sess := &net.Session{Player: player.New("a")}
+ g := &Game{}
+ commands := []casino.MenuCommand{
+ {Command: "bet <amount> on <target>", Desc: "Place a wager; targets: table rules"},
+ {Command: "table", Desc: "View the felt"},
+ }
+ text := plainRoulette(g.casinoMenuCommands(sess, commands))
+ lines := strings.Split(text, "\n")
+ if len(lines) != 2 {
+ t.Fatal("short commands should not wrap", text)
+ }
+ col := strings.Index(lines[0], "Place")
+ if col != strings.Index(lines[1], "View") {
+ t.Fatal("descriptions not aligned", text)
+ }
+ commands[0].Desc = strings.Repeat("long description ", 10)
+ text = plainRoulette(g.casinoMenuCommands(sess, commands))
+ lines = strings.Split(text, "\n")
+ for _, line := range lines[1 : len(lines)-1] {
+ if !strings.HasPrefix(line, strings.Repeat(" ", col)) {
+ t.Fatal("missing hanging indent", text)
+ }
+ }
+}
+func TestCasinoCompactHintsUseOnlyAvailableCommands(t *testing.T) {
+ p := player.New("a")
+ p.Options = map[string]any{"casino_display": "compact"}
+ p.Credits = 10
+ sess := &net.Session{Player: p}
+ g := &Game{}
+ menu := &casino.MenuView{Heading: "Your turn", Commands: []casino.MenuCommand{
+ {Command: "hit", Desc: "Take a card"}, {Command: "stand", Desc: "Hold"}, {Command: "double", Cost: 20},
+ }}
+ text := plainRoulette(g.casinoPrivateMessage(sess, "casino_menu", menu, ""))
+ if strings.Contains(text, "double") || !strings.Contains(text, " h:hit") || !strings.Contains(text, " st:stand") {
+ t.Fatal(text)
+ }
+ menu.ShowBalance = true
+ menu.Repeat = true
+ menu.Commands = []casino.MenuCommand{{Command: "table", Desc: "View the felt"}}
+ text = plainRoulette(g.casinoPrivateMessage(sess, "casino_menu", menu, ""))
+ if strings.Contains(text, "bet") || strings.Contains(text, "sitout") {
+ t.Fatal("compact mode invented actions", text)
+ }
+}
+func TestSimplifiedFeltSpacingAndNoIdlePrompt(t *testing.T) {
+ text := renderCrapsSnapshot(&casino.CrapsSnapshot{Variant: "simplified"}, "none", true)
+ if strings.Contains(text, "Place a bet, then roll when ready.") {
+ t.Fatal(text)
+ }
+ rows := strings.Split(text, "\n")
+ for i, row := range rows {
+ if strings.Contains(row, "PASS ONLY") {
+ if i+2 >= len(rows) || strings.Trim(rows[i+1], "│ ") != "" || !strings.HasPrefix(rows[i+2], "└") {
+ t.Fatal("expected exactly one blank felt row", text)
+ }
+ return
+ }
+ }
+ t.Fatal("missing pass label")
+}