> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mouth.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Testing

> Smart contract and backend API test suites for the Mouth protocol.

## Smart contract tests

177 Foundry tests across 4 files, organized by domain:

```bash theme={null}
cd contracts && forge test
```

### Test structure

| File                      | Tests | What it covers                                                                                                                      |
| ------------------------- | ----- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `MouthBetPvP.t.sol`       | 64    | 1v1 PvP flow: deposit, withdraw, resolve, finalize, claim, resolverClaim, nonDisputeable                                            |
| `MouthBetOpenPvP.t.sol`   | 35    | Open PvP flow: multi-opponent deposits, partial fill, activatePartial, max opponents, withdrawAfterDeadline                         |
| `MouthBetEdgeCases.t.sol` | 32    | Cross-cutting scenarios: re-resolve / dispute window, mutual cancel, asymmetric odds, fee snapshot, resolver change, full lifecycle |
| `MouthBetFactory.t.sol`   | 46    | Factory: bet creation with various odds, admin functions (fee, resolver, treasury), upgrades, initialization                        |

All test contracts inherit from `BaseTest.sol`, which sets up a fork-like environment with a mock USDC token, deploys the factory via UUPS proxy, and provides shared helpers (`_createPvPBet`, `_createOpenBet`, `_opponentDeposit`, `_resolve`, etc.).

### Coverage

| File                | Lines            | Statements       | Functions    |
| ------------------- | ---------------- | ---------------- | ------------ |
| MouthBet.sol        | 98.09% (205/209) | 97.37% (222/228) | 100% (21/21) |
| MouthBetFactory.sol | 96.15% (50/52)   | 95.45% (42/44)   | 100% (10/10) |

```bash theme={null}
cd contracts && forge coverage --ir-minimum
```

<Note>
  Branch coverage numbers appear low (\~47% / \~12%) but this is an artifact of `--ir-minimum` generating imprecise source mappings. The `--ir-minimum` flag is required because the contracts hit "stack too deep" errors with standard coverage compilation.
</Note>

***

## Backend tests

The backend uses **integration tests** that exercise the full HTTP request lifecycle: routing, middleware, input validation, permission checks, state transitions, and database queries. Tests run against the **real PostgreSQL database** — only external services that cannot be automated (Privy OAuth, Base blockchain) are mocked.

```bash theme={null}
cd backend && pnpm test
```

56 tests across 4 files. Runs in \~60 seconds.

### What the tests cover

#### Auth (`auth.test.ts` — 11 tests)

| Flow                  | What's verified                                                    |
| --------------------- | ------------------------------------------------------------------ |
| `POST /auth/login`    | First login creates user in DB, returns profile                    |
| `POST /auth/login`    | Missing or invalid token returns 401                               |
| `GET /auth/me`        | Authenticated user gets their profile                              |
| `GET /auth/me`        | No token → 401, valid token but user deleted → 403                 |
| `POST /auth/withdraw` | Valid withdrawal returns txHash                                    |
| `POST /auth/withdraw` | Invalid address, zero amount, missing wallet, missing fields → 400 |

#### Bets (`bets.test.ts` — 33 tests)

| Flow               | What's verified                                                                                                                      |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| **Listing**        | `GET /bets` returns bets, supports `status` and `category` filters, pagination                                                       |
| **Detail**         | `GET /bets/:id` returns bet with challenger handle; 404 for non-existent                                                             |
| **Creation**       | `POST /bets` creates PvP and open\_pvp bets; validates required fields, odds range (1–99), minimum amount (5 USDC); 401 without auth |
| **Acceptance**     | Cannot accept own bet (400), non-pending bet (400), wrong opponent for PvP (403), expired deadline (400)                             |
| **Cancellation**   | Only challenger can cancel (403 for others), only pending bets (400)                                                                 |
| **Resolution**     | Only resolver wallet can resolve (403), must be active bet (400), validates outcome values                                           |
| **Finalization**   | Only resolver can finalize (403), must be resolved bet (400)                                                                         |
| **Claim**          | Only winner can claim (403 for loser/non-participant), must be finalized (400), no double-claim (400)                                |
| **Full lifecycle** | End-to-end: create → accept → resolve → finalize → claim                                                                             |

#### Users (`users.test.ts` — 8 tests)

| Flow                       | What's verified                                                                 |
| -------------------------- | ------------------------------------------------------------------------------- |
| `GET /users/:handle`       | Returns public profile (no private fields like `privyId`); 404 for non-existent |
| `GET /users/:handle/bets`  | Returns bet history (as challenger and as opponent); pagination; 404            |
| `GET /users/:handle/stats` | Returns stats array; 404 for non-existent                                       |

#### Leaderboard (`leaderboard.test.ts` — 4 tests)

| Flow               | What's verified                                              |
| ------------------ | ------------------------------------------------------------ |
| `GET /leaderboard` | Returns leaderboard array with activity data                 |
| `GET /leaderboard` | Supports `sort` parameter (wins, volume, profit) and `limit` |

### What is mocked

External services that require real credentials, OAuth flows, or cost real money are replaced with mock functions:

| Module              | Mock                                                                                 | Why                                               |
| ------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------- |
| **Privy auth**      | `verifyAccessToken`, `getUser`, `create`                                             | OAuth with X cannot be automated in tests         |
| **tx-relayer**      | `relayCreateBet`, `relayDeposit`, `relayWithdraw`, `relayClaim`, `relayTransferUSDC` | Would send real USDC transactions on Base mainnet |
| **blockchain**      | `resolveBetOnChain`, `finalizeBetOnChain`, `publicClient`                            | Would interact with real smart contracts          |
| **wallet-resolver** | `resolveXHandleToWallet`                                                             | Depends on Privy user lookup                      |

Mocks are defined in `src/__tests__/setup.ts` and reset before each test via `vi.clearAllMocks()`.

### What is real

* **PostgreSQL database** — all INSERT, SELECT, UPDATE, DELETE queries hit the real Neon DB
* **Hono app** — full middleware chain (CORS, logger, auth middleware)
* **Route handlers** — all validation logic, permission checks, SQL queries, state transitions
* **Drizzle ORM** — real query building and execution

### What the tests do NOT cover

| Area                                       | Reason                                                                                                        |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------- |
| **Privy ↔ backend integration**            | Privy is mocked — if Privy changes their SDK response format, tests won't catch it                            |
| **Smart contract interactions**            | Blockchain is mocked — contract bugs or ABI mismatches won't surface                                          |
| **Cron jobs** (bet expiration, auto-claim) | These run as scheduled processes, not HTTP routes                                                             |
| **Disputes**                               | Not yet implemented in tests                                                                                  |
| **Frontend ↔ backend**                     | Tests only cover the API layer, not the React frontend                                                        |
| **activity\_log population**               | No route writes to `activity_log` (populated by crons), so stats/leaderboard tests use manually inserted data |

### How test data is isolated

Tests **never delete existing data**. Each test helper (`createTestUser`, `createTestBet`, `createTestActivity`) tracks the IDs of records it creates. At the end of each test file, `cleanupTestData()` deletes **only** those tracked records:

```typescript theme={null}
// Tracked during test execution
const createdUserIds: string[] = [];
const createdBetIds: string[] = [];

// afterAll — only deletes what tests created
await db.delete(bets).where(inArray(bets.id, createdBetIds));
await db.delete(users).where(inArray(users.id, createdUserIds));
```

Test files run **sequentially** (`fileParallelism: false`) to avoid race conditions on the shared database.

### File structure

```
backend/
  vitest.config.ts              # Vitest config (sequential execution, path aliases)
  src/
    app.ts                      # Hono app (extracted from index.ts for testability)
    index.ts                    # Server startup + cron (not imported by tests)
    __tests__/
      setup.ts                  # Global mocks (Privy, blockchain, tx-relayer)
      helpers.ts                # createTestUser, createTestBet, makeRequest, cleanup
      auth.test.ts              # Auth route tests
      bets.test.ts              # Bet lifecycle tests
      users.test.ts             # User profile/stats tests
      leaderboard.test.ts       # Leaderboard tests
```

<Note>
  The `app.ts` / `index.ts` split exists specifically for testing. `app.ts` exports the Hono app without calling `serve()` or starting cron jobs, so tests can import it cleanly via `app.request()`.
</Note>
