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

# Testing

> Test strategy, coverage summary, and how to run sync system tests.

The sync system has **575 tests** across three packages, all using **Vitest**.

| Package                | Test files | Tests | What's covered                                                        |
| ---------------------- | ---------- | ----- | --------------------------------------------------------------------- |
| `nu_pos_sync_protocol` | 1          | 55    | Zod validators for all 15 message types                               |
| `nu_pos_hub`           | 12         | 129   | Storage, leasing, handlers, server, forwarder, adapter                |
| `nu_pos_react`         | 24         | 391   | State machines, transports, reconcile, outbox, and existing POS tests |

## Running tests

<Warning>
  Always run from the correct package directory. Running from the repo root picks up test files from all packages, causing path alias resolution failures.
</Warning>

<Tabs>
  <Tab title="Protocol">
    ```bash theme={null}
    cd nu_pos_sync_protocol
    npx vitest run
    ```
  </Tab>

  <Tab title="Hub">
    ```bash theme={null}
    cd nu_pos_hub
    npx vitest run
    ```
  </Tab>

  <Tab title="Frontend">
    ```bash theme={null}
    cd nu_pos_react
    npx vitest run
    ```
  </Tab>
</Tabs>

### Single test file

```bash theme={null}
cd nu_pos_react && npx vitest run src/state/machines/__tests__/syncMachine.test.ts
cd nu_pos_hub && npx vitest run src/handlers/__tests__/orderHandler.test.ts
```

### Watch mode

```bash theme={null}
cd nu_pos_react && npm test
cd nu_pos_hub && npm test
```

## Test architecture

### Protocol tests

`nu_pos_sync_protocol/src/__tests__/validators.test.ts` (55 tests)

* Round-trip validation for all terminal and hub message types
* Rejection of malformed messages (missing fields, wrong types)
* `parseTerminalMessage()`, `parseHubMessage()`, `parseProtocolMessage()` helpers

### Hub tests

#### Storage layer (41 tests)

| File                 | Tests | Coverage                                                   |
| -------------------- | ----- | ---------------------------------------------------------- |
| `orderStore.test.ts` | 11    | Upsert with version auto-increment, get, getByTable, count |
| `leaseStore.test.ts` | 11    | Grant, renew, release, getExpired, getByTerminal           |
| `eventLog.test.ts`   | 11    | Append, getPending, markForwarded, getRecent, count        |
| `tableStore.test.ts` | 8     | Upsert, get, getAll, status transitions                    |

All storage tests use in-memory SQLite (`:memory:`) for speed and isolation.

#### Lease manager (25 tests)

* Acquire: new lease, re-entrant, denied (held by other), force override
* Release: by holder, by non-holder (rejected), with final snapshot
* Heartbeat: renewal, rejected for non-holder
* Tick: expired lease revocation, callback invocation
* Disconnect: grace period extension

#### Handlers (14 tests)

| File                   | Tests | Coverage                                                                                 |
| ---------------------- | ----- | ---------------------------------------------------------------------------------------- |
| `orderHandler.test.ts` | 7     | Snapshot without lease, with lease, version conflict, broadcast, event log, table status |
| `leaseHandler.test.ts` | 7     | Acquire, release, heartbeat, force override, event logging                               |

#### Server (25 tests)

| File                    | Tests | Coverage                                                       |
| ----------------------- | ----- | -------------------------------------------------------------- |
| `wsServer.test.ts`      | 3     | WebSocket connection, auth flow, message routing               |
| `messageRouter.test.ts` | 9     | JSON parsing, schema validation, auth gating, message dispatch |
| `httpServer.test.ts`    | 13    | Health, status, events, dashboard, OPTIONS, 404, CORS          |

#### Upstream (24 tests)

| File                  | Tests | Coverage                                                                    |
| --------------------- | ----- | --------------------------------------------------------------------------- |
| `forwarder.test.ts`   | 9     | Drain pending events, mark forwarded, skip non-order events, handle failure |
| `odooAdapter.test.ts` | 15    | Auth, forward create/update, payload transformation, voided line filtering  |

### Frontend tests

#### Sync-specific (67 tests)

| File                   | Tests | Coverage                                                             |
| ---------------------- | ----- | -------------------------------------------------------------------- |
| `syncMachine.test.ts`  | 35    | All 4 states, all 14 events, context updates, state transitions      |
| `hubTransport.test.ts` | 24    | Lease acquire/deny/timeout/revoke, message routing, offline fallback |
| `reconcile.test.ts`    | 8     | All reconcile strategies: accept\_remote, merge, default fallback    |

#### Transport infrastructure (31 tests)

| File                      | Tests | Coverage                                                    |
| ------------------------- | ----- | ----------------------------------------------------------- |
| `directTransport.test.ts` | 18    | Local locks, outbox integration, state lifecycle            |
| `createTransport.test.ts` | 4     | Factory: returns direct when hub disabled, hub when enabled |
| `hubDiscovery.test.ts`    | 9     | Config extraction, URL resolution                           |

## Testing patterns

### Hub: in-memory SQLite

```typescript theme={null}
beforeEach(() => {
  db = initDb(":memory:");
  orderStore = createOrderStore(db);
});
```

### Hub: mock WebSocket

```typescript theme={null}
function makeMockWs() {
  return {
    send: vi.fn(),
    readyState: 1,
    OPEN: 1,
  } as unknown as import("ws").WebSocket;
}
```

### Frontend: module mocking

```typescript theme={null}
vi.mock("../sync/outbox", () => ({
  queueOutbound: vi.fn(),
  flushOutbound: vi.fn().mockResolvedValue({ sent: 0, failed: 0 }),
  listOutbound: vi.fn().mockReturnValue([]),
}));
```

### Frontend: XState actor testing

```typescript theme={null}
const actor = createActor(syncMachine);
actor.start();
actor.send({ type: "TRANSPORT_RECONNECTING" });
expect(actor.getSnapshot().value).toBe("reconnecting");
```

## What's not tested

The following are intentionally untested (React components requiring DOM/render context):

* `SyncStatusIndicator` — Read-only badge, tested visually
* `ConflictDialog` — Radix UI dialog, tested visually
* `SyncOverlay` — Toast integration, tested visually
* `SyncUIProvider` — Simple React context with `useState`
* `RealtimeProvider` — Tested indirectly through transport and machine tests
