# 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, 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 `. 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 GUI is a style spike until Phase 4: the table and wheel are painted from a demo `RouletteView`, and no network connection is made. Run it on Linux and on Windows 10 before approving the look. 1. `cargo run -p zagan-bet-gui` at 1440x900, then at 1920x1080. The header keeps its right margin at both sizes and shows ONLINE, host, ping, account and balance; the table is centred and the sidebar is full height. 2. The felt shows 0, the 3x12 numbers, dozens, columns and the six outside cells. Chips sit on their targets: a straight in the cell, a corner on the lattice crossing, an outside bet in its box. 3. Click a pocket: a chip of the selected denomination appears and the slip gains a row. Chips are 10/25/50/100; the selected one has a cyan ring. 4. Arrow keys move the cursor. Over the numbers the cyan marker sits on the cell or lattice point and the covered cells tint; leaving the table walks dozens, columns and outside. 5. Space or SPIN animates the wheel for about 2.4 s and stops with the winning pocket under the pointer. The winning cell gets a gold outline, RESULT names the number and colour, and LAST gains a dot. SPIN is disabled during the spin. 6. Clear bets empties the slip and the total. Resize to 1100x720: the sidebar scrolls, nothing overlaps the header, and the table keeps its aspect ratio. 7. Compare the 1080p window against the approved reference screenshot. ## 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 with no network traffic. 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 is absent from the `CRASH_START` line (it must only appear once the round is over). ## 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 4.x client to a 3.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 ` without asking for a password. 3. `hosts` must list both addresses on one line under one fingerprint. 4. `forget-host ` must say the credentials were kept and name the remaining address. Connecting through the remaining address still authenticates automatically. 5. `forget-host ` 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`.