1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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)
}
}
}
|