# §5 — Chat Room test plan

Spec: [requirement/flow_customer.mermaid.md §5](../../../requirement/flow_customer.mermaid.md)
plus the user-stated message-delivery contract:

> 1. WebSocket when app is foreground / WS connected.
> 2. FCM when app is background / WS disconnected.

The lifecycle observers that enforce (2) live at:
- Customer side: `client_app/lib/main.dart::_AppState.didChangeAppLifecycleState`
- Mitra side: `mitra_app/lib/main.dart::_AppState.didChangeAppLifecycleState` +
  `mitra_app/lib/core/chat/mitra_chat_notifier.dart` (`_connectedSessionId`
  + getter for the observer to read on `paused`).

A 15s `Timer.periodic` in `active_session_notifier.dart` would otherwise
re-open the customer WS right after the observer closed it; the
`_appPaused` gate in customer main.dart suppresses that race.

## Implemented

| File | Direction | Expected dispatch |
|---|---|---|
| `ts-customer-05-01-background_disconnects_ws_so_messages_fall_back_to_fcm.yaml` | mitra → customer, customer is backgrounded | `delivered_via=fcm` |
| `ts-customer-05-02-customer_message_to_backgrounded_mitra_falls_back_to_fcm.yaml` | customer → mitra, mitra is backgrounded | `delivered_via=fcm` |

Both flows drive the customer device only (Maestro can only target one
`--udid` per run). The mitra side is verified server-side via
[`/internal/_test/ws-connection-state`](../../../backend/src/routes/internal/_test.routes.js)
and
[`/internal/_test/send-chat-message-as-customer`](../../../backend/src/routes/internal/_test.routes.js)
(introduced 2026-05-18 with the lifecycle fix).

## Helper scripts (under `../scripts/`)

| Script | Purpose |
|---|---|
| `assert_ws_state.js` | Reads `/internal/_test/ws-connection-state?session_id=…`; throws on mismatch with `EXPECTED_CUSTOMER_CONNECTED` / `EXPECTED_MITRA_CONNECTED`. |
| `assert_delivered_via.js` | Sends a chat message via the test endpoint matching `SENDER` (`mitra` default, or `customer`); asserts the response's `delivered_via` equals `EXPECTED_DELIVERED_VIA`. |
| `inspect_ws_state.js` | Same data as `assert_ws_state.js` but exports the booleans into `output.CUSTOMER_CONNECTED` / `output.MITRA_CONNECTED` for downstream conditions without throwing. |
| `send_chat_message_as_mitra.js` | Mitra→customer one-shot send. Captures `output.MESSAGE_ID` + `output.DELIVERED_VIA`. |

## Pre-reqs for both flows

- Backend reachable; `NODE_ENV != 'production'`.
- ≥1 mitra online (the `seed_history_session.js` helper picks the
  most-recently-online mitra to act as the eventual acceptor).
- For 05-01: customer device is the one Maestro drives; `pressKey: HOME`
  inside the flow backgrounds it. No mitra-app interaction required.
- For 05-02: **the mitra app on `emulator-5556` must be backgrounded or
  force-stopped before the flow runs**. The `assert_ws_state.js` guard
  fails loudly if mitra is foreground (proving WS-over-FCM precedence —
  also a correct test outcome, just the wrong side of the matrix). To
  ensure FCM actually arrives on the device, the mitra account on
  `emulator-5556` must have a `fcm_token` in `mitras.fcm_token` (i.e.,
  the app has signed in and called `/api/shared/device-token` at some
  point) — verified by `seed_history_session.js`'s output mitra ID and
  the `MITRA_NAME_RE` it captures.

## Manual mitra-side verification

After 05-02 passes, optionally inspect the mitra device's notification
shade to confirm the FCM tray notification rendered:

```bash
export ADB_SERVER_SOCKET=tcp:172.22.240.1:5037
adb -s emulator-5556 shell cmd statusbar expand-notifications
adb -s emulator-5556 exec-out screencap -p > /tmp/mitra_shade.png
adb -s emulator-5556 shell cmd statusbar collapse
```

Expect to see a `Pesan baru dari Customer` notification within ~1s on an
API 28+ AVD (older AVDs queue FCM for 5-30 min — see the per-AVD
Play-Services notes in the project memory).

## Not yet automated

**Mitra-side lifecycle observer** end-to-end. To verify that the mitra
app's `didChangeAppLifecycleState(paused)` actually closes its chat WS,
the test would need to drive the mitra emulator (Maestro on
`--udid emulator-5556`) through login → chat-screen → `pressKey: HOME` →
assert backend WS state. This requires:

1. Pre-seeding an active chat session for the currently-signed-in mitra
   (no existing test endpoint for "seed customer + pair + active
   session"; the existing scripts assume the customer app drives it).
2. Driving the mitra OTP login + chat-request acceptance UI from
   Maestro.

Both are feasible but out of scope for the current iteration. The
mitra-side fix is covered indirectly: ts-customer-05-02 asserts the
backend correctly dispatches via FCM whenever the mitra WS is closed,
and the mitra app's lifecycle observer was verified manually
(2026-05-18) to be the mechanism that closes that WS.
