aboutsummaryrefslogtreecommitdiff
path: root/internal/game/crypto_ux_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/crypto_ux_test.go')
-rw-r--r--internal/game/crypto_ux_test.go147
1 files changed, 147 insertions, 0 deletions
diff --git a/internal/game/crypto_ux_test.go b/internal/game/crypto_ux_test.go
new file mode 100644
index 0000000..6ac4a57
--- /dev/null
+++ b/internal/game/crypto_ux_test.go
@@ -0,0 +1,147 @@
+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)
+ }
+ }
+}