summaryrefslogtreecommitdiffstats
path: root/docs/TESTING.md
blob: 141ea758e3e3363f4fe5d4a262942be4949faed6 (plain) (blame)
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# Testing

## Automated suite

```sh
cargo test --workspace
```

The suite is deterministic and headless: no network, no PTY, no sleeps, no
shared global database. Each test owns its state.

## Cross-compiling the client

The client is rustls-only. The server is the only thing that links OpenSSL via
`native-tls`, so it is behind the default `server` feature. Building the client
for another target does not need that toolchain at all:

```sh
rustup target add i686-unknown-linux-gnu
cargo build --release --bin client --no-default-features \
  --target i686-unknown-linux-gnu
```

`--no-default-features` drops `native-tls`, which is what fails with
`Could not find OpenSSL` when no 32-bit OpenSSL is installed. `rusqlite` is
bundled and `ring` (rustls) only needs `gcc -m32`, which a normal multilib
setup provides. The binary can be checked with
`file target/i686-unknown-linux-gnu/release/client`; it must say `ELF 32-bit`
and `Intel i386`.

Building the server for i686 is a different problem: it needs 32-bit OpenSSL
headers and libraries (`libssl-dev:i386` plus
`PKG_CONFIG_ALLOW_CROSS=1 PKG_CONFIG_LIBDIR=/usr/lib/i386-linux-gnu/pkgconfig`),
or a vendored OpenSSL.

| File | Covers |
| --- | --- |
| `tests/client_core.rs` | Client command/game flows via a headless screen applying logical effects. |
| `tests/client_ui.rs` | Help, history, completion, bet parsing, display width, cursor math. |
| `tests/blackjack.rs` | Card totals, naturals, hit/stand/double, dealer bust, S17, state parsing. |
| `tests/crash.rs` | Multiplier curve, payout floor, crash-point distribution, house edge, cap. |
| `tests/protocol.rs` | Newline buffering and timeout/error classification. |
| `tests/roulette.rs` | Wheel, every bet type and payout, zero handling, cursor grid (straight/split/corner), result parser. |
| `tests/server_core.rs` | Handler, bcrypt migration, SQL injection, transaction rollback, `plays` audit, sessions. |
| `tests/server_setup.rs` | `--init` identity generation, PKCS#12 loadability, overwrite refusal. |
| `tests/slots.rs` | Server slots payout rules and overflow handling. |
| `tests/storage.rs` | Client/server SQLite storage helpers. |
| `tests/tofu.rs` | SPKI fingerprint and TOFU decisions. |
| `gui/tests/layout.rs` | GUI presentation maths: money grouping, wheel angle, table aspect, lattice snapping (straight/split/corner/outside), bet names. |

`tests/tofu.rs` and `tests/server_setup.rs` shell out to `openssl`, so run them
on a machine where the `openssl` CLI is available.

The GUI has its own integration tests because the root package cannot depend on
the GUI (`gui/tests/`); `cargo test --workspace` runs both halves.

## Development resets

The server creates `server.db` in its working directory. The client keeps
`client.db` (host trust, stored credentials) in the XDG data directory:
`$XDG_DATA_HOME/zagan-bet/client.db`, usually
`~/.local/share/zagan-bet/client.db`. Override it with `client -d <dir>`.

While the software is in testing, delete both after a schema or trust change:

```sh
rm -f server.db
rm -rf ~/.local/share/zagan-bet
```

The audit schema is not auto-migrated from the pre-2.0 slots-only `plays`
table. Starting against an old database will fail until it is removed.

## Building the GUI

The GUI is a workspace member, so it never enters the TUI or server build graph:

```sh
cargo build -p zagan-bet-gui                                  # debug
cargo build -p zagan-bet-gui --profile gui-release            # release
cargo build -p zagan-bet-gui --target x86_64-pc-windows-gnu \
  --profile gui-release                                       # Windows
```

`gui-release` is declared at the workspace root; a profile inside a member's
manifest would be ignored. Fonts are embedded, so there is no runtime asset
directory to ship.

## Manual TUI checklist

There is no CI. Run this on Linux and on a Windows VM before a release.
Use an 80-column terminal because several output lines are width-sensitive.

1. Run `cargo run --bin server -- --init` once, then start the server with a
   valid `identity.p12` and start the client.
2. First contact prints three lines and the 64-character fingerprint does not
   wrap at 80 columns. `Connected to ...` shows a one-decimal ping.
3. `register` / `login`: backspace removes one `*` per key, Esc cancels the
   prompt, Ctrl+C exits and restores the terminal.
4. `help` fits 80 columns. Tab completion wraps within 76 columns and the
   cursor stays visible.
5. `play slots 50` shows a 5x3 grid, the `Pays bet x symbol x shape` lines, and
   the fetched balance.
6. Space shows `Spinning...`, then the 3 rows, win text, winning-line list, and
   new balance inside the same 10-line board. Winning symbols appear as
   yellow/bold `{ X }` cells; the `Winning lines:` list names the payline
   (e.g. `4 (V)`). The controls line is never overwritten. Slots rules and RTP:
   `docs/SLOTS.md`.
7. `+` / `=` / `-` after a spin changes only the bet; no stale grid or result
   lines appear.
8. `q` clears the board. Ctrl+C in a game clears the board and exits.
9. `info`, `ping`, `send hello`, and `hosts` print readable output; the host
   fingerprint fits 80 columns.
10. Connect to the same server through two addresses (for example `127.0.0.1`
    and the machine's LAN IP). `hosts` must list both addresses on one line
    under a single fingerprint, because the certificate key is the same.
11. Windows: repeat 2-10 and confirm keys are not duplicated or printed twice.

## Manual GUI checklist

The desktop frontend shares `ClientCore` and the data directory with the
terminal client; a saved login from one signs in the other.

1. `cargo run -p zagan-bet-gui` (or `-d <dir>` / `--unsafe`). The connect
   screen lists remembered hosts with short fingerprints.
2. First contact: the fingerprint modal appears before any credentials. Cancel
   leaves the connect screen; Trust continues to the login screen.
3. Register, then log in with "Remember this login". The lobby shows the host,
   ping, account, balance and the four games; the balance fills in immediately
   without entering a game. Quit and reconnect: the stored login signs in
   without asking.
4. Each game: place a bet or deal, play a round, and confirm the balance and the
   header update. Slots shows reels and win lines; roulette spins the wheel and
   reports the payout; blackjack draws suits and settles; crash draws a smooth
   curve and cashes out. While the reels or wheel turn, the balance shows the
   stake already deducted; the payout appears only when they stop. A spin or
   deal the server refuses (for example an insufficient balance) shows the
   error and must not animate the reels, the wheel or the curve.
5. Sidebar actions never move while the slip grows. CLEAR empties the slip;
   right or middle click on the felt takes one chip back; C clears and space
   spins in roulette.
6. Leaving a live blackjack hand or crash round asks for confirmation, then
   forfeits. Leaving an idle table returns to the lobby.
7. HISTORY shows the last plays; SHOW changes the limit. LOG OUT returns to
   the login screen but stays connected; Disconnect drops the session (the
   saved login survives).
8. Run the client against a server of another MAJOR.MINOR version: the
   mismatch screen names both versions and the download page. A changed host
   key stops the connection and offers forget-and-reconnect.
9. Resize to 1100x720: the sidebar scrolls, the actions stay reachable, and
   the board keeps its aspect ratio.

## Manual roulette checklist

1. `play roulette` draws the full felt: 0, the 3x12 numbers, dozens, columns,
   and the six outside cells, with the cursor on number 1.
2. Arrows move the cursor. On the numbers the highlighted cells show the bet:
   one number (straight), two (split), four (corner). Every number and every
   adjacent pair is reachable: from 1, Right is the 1/4 split and Right again is
   number 4; Up is the 1/2 split and Up again is number 2. The status line names
   the bet and its odds. Down leaves the felt into the dozens, then columns,
   then outside; Up walks back.
3. ENTER places the chip where the cursor is; `C` clears; `+`/`=`/`-` change the
   chip size. The bets line lists every chip and the total.
4. Space scrolls the wheel under a fixed ball marker and ends on the winning
   number.
5. Check the payouts:
   - a straight pays 36x, a split 18x, a corner 9x;
   - a dozen or column pays 3x;
   - an outside bet pays 2x;
   - `0` loses every outside bet, both dozens and every column, and pays a
     straight 0.
6. The result line names the number, color and parity, the total returned and
   the profit. The next spin reuses the placed chips.

## Manual history check

1. Play a few rounds across games, then run `history`.
2. The table is newest first and shows the time, game, bet, winnings, balance
   and a one-line result (`17 black`, `cashed 1.12x`, `none`, `win`).
3. `history 2` shows two rows; `history 0` and `history nope` are rejected
   without sending anything. A fresh account says `No plays recorded yet.`

## Manual terminal size check

1. Start the client in a terminal smaller than 80x24: it must print
   `zagan.bet needs a terminal of at least 80x24 cells` and exit without
   switching to the alternate screen or raw mode.
2. At 80x24 every game board (slots 10 lines, roulette 12, blackjack 6, crash 6)
   must fit without wrapping.
3. While running, shrink the terminal below 80x24: a `WARNING:` line appears in
   the transcript, or under the board when a game is open.
4. Shrink during a live blackjack hand or a running crash round: nothing
   interrupts the round. The warning appears once the round settles.

## Manual blackjack checklist

1. `play blackjack` shows the ready board; Space deals.
2. The dealer's hole card is hidden while it is the player's turn. H/S/D work;
   D is only offered on the first two cards and reserves the extra bet.
3. Stand and check the dealer stands on all 17s. Check a dealer bust pays the
   player, and a natural blackjack pays 3:2.
4. Q during a hand forfeits the reserved bet (it is **not** refunded); Q after
   a hand just leaves. Disconnecting mid-hand also forfeits it.
5. Check the audit row: `game_type = blackjack`, the total bet (including a
   double), winnings, and the final cards/result in `details`. A forfeit has
   `result = forfeit`, `winnings = 0`.

## Manual crash checklist

1. `play crash` shows the ready board with a 1.00x multiplier; Space starts and
   reserves the bet.
2. The multiplier rises smoothly from the server-clock anchor and is refreshed
   by 10 Hz ticks. Cash out with C and confirm the board shows the cashout
   multiplier, the payout and the new balance.
3. On another round, press nothing. The board must switch to `CRASHED at Xx`
   by itself when the crash point is reached, and the bet must be lost.
4. Press C just as a round crashes. Either it pays or the crashed result wins;
   a late cashout must never show an error over a finished round.
5. Q during a live round forfeits the bet; Q when idle just leaves.
6. Check the audit rows: one per round, `game_type = crash`, the reserved bet,
   the payout, and `crash_point`/`multiplier`/`result` in `details`.
7. Confirm the crash point and seed are absent from the `CRASH_START` message;
   only `commit` and `started_at` appear. When the round ends, `seed` appears
   and `sha256(seed) == commit`; recompute the crash point from the seed.
8. Confirm `CRASH_BUSTED` is an `EVT`, not a `RES`, and that a second poll
   produces no repeated settlement.
9. The board's fairness line shows the short commit while running, then the
   short seed and `verified` after the reveal (TUI and GUI).

## Manual restart check

1. Start a blackjack hand (or a crash round) and confirm `pending_plays` has one
   row while the round is live.
2. `kill -9` the server, then start it again on the same database. The log must
   say `Settled 1 orphaned reservation(s) as abandoned after a restart`.
3. Confirm the balance did not change, `pending_plays` is empty, and `plays` has
   one row with `winnings = 0`, `details.result = "abandoned"` and
   `details.reason = "server_restart"`.

## Manual version handshake check

1. Connect a client and server from the same release: the client connects and
   the server log shows the `HELLO` exchange.
2. Connect a 5.x client to a 4.x server (or the reverse): the client must print
   `Version mismatch: client ..., server ...` and never reach the login prompt.
3. With a deliberately skew-built server (PATCH differs) the connection is
   accepted and, when the server's version is newer, the client prints
   `A newer version is available`.
4. Before the handshake, `openssl s_client -quiet -connect host:port` followed
   by `PING` must answer `ERROR HELLO required`.

## Manual identity / credential checklist

1. Connect through one address, `register` and `login`. `hosts` shows the
   fingerprint.
2. `disconnect`, then `connect` through another address that reaches the same
   server (for example `10.0.0.100` when you first used `127.0.0.1`). The client
   must print `Authenticated as <user>` without asking for a password.
3. `hosts` must list both addresses on one line under one fingerprint.
4. `forget-host <first address>` must say the credentials were kept and name
   the remaining address. Connecting through the remaining address still
   authenticates automatically.
5. `forget-host <last address>` must say trust and credentials were cleared.
   The next connect is a first contact and does not authenticate.

## Manual server checks

- Register, log out, log in again (bcrypt path).
- Log in with a pre-0.3 user row (legacy SHA-256) and confirm it still works;
  the row is upgraded to bcrypt.
- Spin several times and confirm `plays` grows by one row per spin while
  `users.balance` changes by `winnings - bet`.

## Packaging checklist

1. Set the version in `Cargo.toml`, `gui/Cargo.toml` and `webd/Cargo.toml`
   (same MAJOR.MINOR), run `cargo test --workspace`, build the browser app
   (`cd web/play && npm ci && npm run build`), and commit. `publish.sh` refuses
   a dirty tree because the site links the committed git history as the GPL
   source.
2. `./scripts/publish.sh` (add `DEST=` to stage elsewhere). It builds the GUI
   with `gui-release` and the terminal clients and servers. The installers are
   stable site files and are not regenerated.
3. `php -S 127.0.0.1:8899 -t <staging>` and
   `./scripts/deploy.sh <staging> http://127.0.0.1:8899/` to upload and verify
   every app, client and server against the page's checksums, the stable
   installers, and the newest-version feed at `latest.php`.
4. On a clean user account: run the stable installer
   (`ZAGAN_SITE=http://127.0.0.1:8899 ./web/install.sh`) and launch the new
   menu entry. It must install the newest version even when an older app sits
   next to it, and refuse a corrupted download.
5. Repeat on Windows for the `.ps1` installer and the Start-menu shortcut.
6. Start `webd` against the staging server with a pinned fingerprint and open
   `/play/` through nginx. Run the manual web checklist below.

## Manual web checklist

1. Open `/play/` in a fresh browser. The auth card must appear; the top bar
   must not claim a balance until login.
2. Register, then log in with the password. The lobby must show 1 000 Kr and
   every game. Refresh the page; the session cookie must reattach and the
   balance must survive (within `ZAGAN_GRACE_SECONDS`).
3. Open the same URL in a second tab. The first tab must show "Opened in
   another tab" and stop reconnecting; the second must continue.
4. Slots: spin and watch the reels settle on the server result; check the
   payout and balance. Roulette: place straight, dozen, column and outside
   chips, right-click one back, clear, spin, and confirm the result.
5. Blackjack: deal, hit/stand/double, then deal again; forfeiting must warn
   and lose the reserved bet.
6. Crash: start a round, watch the curve and ticks, cash out; on another
   round let it bust. Confirm the reveal line shows `sha256(seed) == commit`
   and the recomputed crash point matches.
7. Refresh in the middle of a live crash round. The current view must return
   with the same `commit` and a rising multiplier; the round must still settle.
8. Close the tab mid-round and wait past the grace window. The next visit must
   log in again, and the server must have forfeited the round.
9. Stop the gateway. The browser must show the reconnect overlay, then recover
   when `webd` returns. Stop it for longer than the grace window: the round is
   forfeited and a login is required.
10. Check the browser console: no credential is stored, the cookie is HttpOnly,
    and the only network target is `/ws`.