Wallet test suite
Run the whole wallet contract against your wallet in the sandbox, and read what each case sent, expected and got.
Give the suite a test player of yours with at least 10.00 and its currency. It calls your wallet_url exactly as the aggregator will, signed with your key, with ids that start with cg-test-. Money cases read the balance back with getbalance rather than trusting the answer; a bet your wallet should have refused but took is rolled back. A full run leaves the player 2.00 up.
/v1/test/wallet-validation
Start a run (sandbox only)
{ "player_id": "qa-player-1", "currency": "EUR" }/v1/test/wallet-validation/{id}
The run and every case so far
Poll until status is passed, failed or error. Every case carries outcome (pass, warn, fail, skipped), expected, actual, the request sent, response_status, response_body and docs_path, the page of this guide the rule is on. A run passes with warnings: a warning is a refusal under another code, or answers slower than a second. One run at a time, 10 an hour. The same suite runs from the Operator Portal under Validation.
The cases
| Case | What it checks |
|---|---|
signature_refused | A request with a wrong signature is refused with 401 |
getbalance | getbalance answers the balance as a string |
bet | A bet takes the amount |
bet_repeated | The same bet again moves nothing and gets the first answer |
win | A win credits the amount |
win_repeated | The same win again moves nothing |
win_zero | A win of 0.00 is accepted |
rollback | A rollback returns the bet |
rollback_repeated | The same rollback again moves nothing |
rollback_unknown | A rollback of a bet never seen answers the balance and moves nothing |
bet_after_rollback | A bet that arrives after its rollback is refused |
insufficient_funds | A bet over the balance is refused with INSUFFICIENT_FUNDS |
currency_mismatch | A bet in another currency is refused with CURRENCY_MISMATCH |
unknown_player | An unknown player is refused with PLAYER_NOT_FOUND |
win_without_bet | A win for a round without a bet is credited |
free_spins_bet | A bet with free_spins_id takes nothing |
malformed | A body without player_id is refused with a 4xx, not a 5xx |
response_time | 95% of answers come back in under a second |
bonus_amount | bonus_amount, where reported, is a string |
In your CI
agg-wallet-suite.mjs starts a run, waits for it, prints every case and writes a JUnit XML report your CI reads. It is a client of the two calls above: the cases run on our side, the same ones as in the Operator Portal. Node 18 or later, no dependencies. It runs against the wallet_url registered for the key, so deploy the wallet under test there first.
export AGG_API_URL=<sandbox base URL from the Operator Portal>
export AGG_KEY_ID=ak_5f2c9e01a34b7d8e2f0c1b6a
export AGG_SECRET=<sandbox secret>
node agg-wallet-suite.mjs --player qa-player-1 --currency EUR --junit wallet-suite.xml
# the same without Node on the machine
docker run --rm -e AGG_API_URL -e AGG_KEY_ID -e AGG_SECRET -v "$PWD:/w" -w /w node:22-alpine node agg-wallet-suite.mjs --player qa-player-1 --currency EUR| Exit code | Meaning |
|---|---|
| 0 | Every case passed; warnings do not fail the build |
| 1 | A case failed: the report names it, what was expected and the page of this guide |
| 2 | No verdict: the run was refused (one at a time, 10 an hour), the options are incomplete, or it did not finish within --timeout (300 s) |
Reference wallet
reference-wallet.mjs is a wallet written to this contract in one file: signature check, idempotency by transaction_id, rollback before the bet, money as strings. It passes every case. When a case fails for you, compare your answer with its answer to the same request.
AGG_SECRET=<sandbox secret> PLAYER_ID=qa-player-1 CURRENCY=EUR BALANCE=1000.00 PORT=8080 node reference-wallet.mjs
# wallet_url: https://<your host>/wallet