package game import ( "strings" "testing" "thehouseoficarus/internal/casino" "thehouseoficarus/internal/color" ) func TestCryptoEntryIsBoardAndSharedHintsNotRules(t *testing.T) { g, s, conn := cryptoFixture(t) g.CryptoClient = cryptoMockClient(t, "100") g.doUse(s, "crypto") out := string(conn.data) for _, wanted := range []string{"CRYPTO EXCHANGE", "Currency", "Buy", "Sell", "BTC", "view SYMBOL", "fund CREDITS", "stop"} { if !strings.Contains(out, wanted) { t.Fatalf("missing %q: %s", wanted, out) } } for _, unwanted := range []string{"HOW TO TRADE", "Paper trading only", "HOUSE-MODEL", "volatility", "collateral", "Black-Scholes", "0.5%", "10-credit", "1,000,000"} { if strings.Contains(out, unwanted) { t.Fatalf("entry dumped rules %q: %s", unwanted, out) } } for _, line := range strings.Split(out, "\n") { if color.VisibleLen(strings.TrimSuffix(line, "\r")) > 80 { t.Fatalf("wide entry line: %q", line) } } } func TestCryptoHintPreferencesAndExplicitWhat(t *testing.T) { for _, mode := range []string{"off", "once", "on"} { t.Run(mode, func(t *testing.T) { g, s, conn := cryptoFixture(t) g.CryptoClient = cryptoMockClient(t, "100") s.Player.Options["casino_hints"] = mode g.doUse(s, "crypto") if got := strings.Contains(string(conn.data), "view SYMBOL"); got != (mode != "off") { t.Fatalf("entry hints %s: %s", mode, conn.data) } conn.data = nil g.executeCrypto(s, "refresh", nil) if got := strings.Contains(string(conn.data), "view SYMBOL"); got != (mode == "on") { t.Fatalf("repeat hints %s: %s", mode, conn.data) } conn.data = nil // Look marks the same ShowHints flag the prediction counter uses. s.CryptoShowHints = true g.executeCrypto(s, "refresh", nil) if got := strings.Contains(string(conn.data), "view SYMBOL"); got != (mode != "off") { t.Fatalf("look hints %s: %s", mode, conn.data) } conn.data = nil g.handleGameCommand(s, "what") if !strings.Contains(string(conn.data), "view SYMBOL") || strings.Contains(string(conn.data), "HOW TO TRADE") { t.Fatalf("what did not show command menu: %s", conn.data) } if g.cryptoInputOwned(s, "what") || g.cryptoInputOwned(s, "verbs") || g.cryptoInputOwned(s, "north") { t.Fatal("counter stole a world command") } }) } } func TestCryptoContextMenusAndRefresh(t *testing.T) { g, s, conn := cryptoFixture(t) g.CryptoClient = cryptoMockClient(t, "100") s.Player.Options["casino_hints"] = "on" g.executeCrypto(s, "view", []string{"BTC"}) if !strings.Contains(string(conn.data), "buy BTC AMOUNT") || !strings.Contains(string(conn.data), "contracts") { t.Fatalf("missing currency hints: %s", conn.data) } conn.data = nil g.executeCrypto(s, "contracts", nil) if !strings.Contains(string(conn.data), "option TYPE BTC") || !strings.Contains(string(conn.data), "quote TYPE BTC") { t.Fatalf("missing option hints: %s", conn.data) } conn.data = nil g.executeCrypto(s, "active", nil) g.executeCrypto(s, "refresh", nil) if s.CryptoScreen != "active" || strings.Contains(string(conn.data), "DAILY CANDLES") || !strings.Contains(string(conn.data), "close ID") { t.Fatalf("refresh left portfolio: %s", conn.data) } conn.data = nil g.executeCrypto(s, "closed", nil) g.executeCrypto(s, "refresh", nil) if s.CryptoScreen != "closed" || strings.Contains(string(conn.data), "close ID") { t.Fatalf("closed hints or refresh wrong: %s", conn.data) } conn.data = nil g.executeCrypto(s, "back", nil) if s.CryptoScreen != "markets" || !strings.Contains(string(conn.data), "view SYMBOL") { t.Fatalf("back failed: %s", conn.data) } s.CryptoBusy = true commands := g.cryptoCommands(s) if len(commands) != 1 || commands[0] != (casino.MenuCommand{Command: "stop", Desc: "Leave and cancel the update"}) { t.Fatal(commands) } } func TestCryptoGlobalOptionCommandsStayReachable(t *testing.T) { g, s, _ := cryptoFixture(t) g.CryptoClient = cryptoMockClient(t, "100") // The settings command is not claimed by the counter... if g.cryptoInputOwned(s, "options") { t.Fatal("counter stole the options settings command") } if g.handleCryptoInput(s, "option", nil) { t.Fatal("bare option should fall through to settings") } // ...but a real option trade is claimed. if !g.cryptoInputOwned(s, "option") { t.Fatal("option trade should be owned") } if !g.handleCryptoInput(s, "option", []string{"call", "BTC", "0.01", "100", "7"}) { t.Fatal("option trade was not handled") } } func TestCryptoCommandAbbreviations(t *testing.T) { g, s, conn := cryptoFixture(t) g.CryptoClient = cryptoMockClient(t, "100") // Unique prefixes resolve like every other counter command. if got, ok := cryptoVerb("mar"); !ok || got != "markets" { t.Fatalf("mar -> %q, %v", got, ok) } if got, ok := cryptoVerb("opt"); !ok || got != "option" { t.Fatalf("opt -> %q, %v", got, ok) } if !g.cryptoInputOwned(s, "mar") { t.Fatal("mar should be owned") } // Ambiguous shortcuts are claimed and explained instead of running. if got, ok := cryptoVerb("b"); !ok || got != "" { t.Fatalf("b -> %q, %v", got, ok) } conn.data = nil if !g.handleCryptoInput(s, "b", nil) || !strings.Contains(string(conn.data), "ambiguous") { t.Fatalf("ambiguous shortcut: %s", conn.data) } // Real world commands still win over counter prefixes. for _, cmd := range []string{"look", "north", "score", "inventory"} { if got, ok := cryptoVerb(cmd); ok { t.Fatalf("%s was claimed as %q", cmd, got) } } }