# §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.