diff --git a/doc/local-android-testing-ngrok.md b/doc/local-android-testing-ngrok.md index ad35cbe8..1da4d66b 100644 --- a/doc/local-android-testing-ngrok.md +++ b/doc/local-android-testing-ngrok.md @@ -291,7 +291,8 @@ The app normally calls `APP_SERVER` (from `VITE_APP_SERVER`). For local wakeup t | **Test Mode** | Sends `testMode: true` on register/refresh (default on when unset in storage) | | **Register Token Now** | `POST /notifications/register` with current FCM token and `platform: "android"` | | **Refresh Notifications** | `POST /notifications/refresh` (same as post-wakeup flow) | -| **Simulate WAKEUP_PING** | Calls refresh API directly (no FCM) — quick backend + ngrok test | +| **Simulate WAKEUP_PING (Local)** | Calls refresh API directly (no FCM) — quick backend + ngrok test | +| **Send Real WAKEUP_PING** | `POST /debug/send-wakeup` on the backend override URL; server sends a real FCM `WAKEUP_PING` to the panel’s current FCM token (see below) | | **Event Log** | Shared `[Notifications]` panel log (100 entries) | Persistence: `localStorage` keys `notificationDebug.backendBaseUrl` and `notificationDebug.testMode` (`NotificationDebugConfig.ts`). @@ -300,14 +301,59 @@ Persistence: `localStorage` keys `notificationDebug.backendBaseUrl` and `notific When **Test Mode** is on (default if never saved), register and refresh requests include `"testMode": true`. The backend can route dev traffic separately from production. Turn it off in the panel only if you intentionally want production-mode API behavior against your tunnel. -### Two “Simulate WAKEUP_PING” buttons +### WAKEUP_PING debug controls + +Three panel actions exercise different segments of the wakeup pipeline. Use them to bisect failures (see [Troubleshooting §14](#fcm-message-not-received)). | Button | Behavior | |--------|----------| -| **Backend Testing → Simulate WAKEUP_PING** | Skips FCM; calls refresh API only (ngrok path test) | -| **Wakeup Ping Simulator** (lower on panel) | Runs production handler with synthetic `WAKEUP_PING` payload | +| **Backend Testing → Simulate WAKEUP_PING (Local)** | Skips FCM; calls refresh API only (ngrok path test) | +| **Backend Testing → Send Real WAKEUP_PING** | Full pipeline: backend `/debug/send-wakeup` → FCM → Capacitor listener → refresh (see below) | +| **Wakeup Ping Simulator** (lower on panel) | Runs production handler with synthetic `WAKEUP_PING` payload (no FCM, no backend wakeup call) | -Use the backend button to verify ngrok + refresh; use the simulator to verify handler + refresh chaining without FCM. +Use **Simulate WAKEUP_PING (Local)** to verify ngrok + refresh; use **Wakeup Ping Simulator** to verify handler + refresh chaining without FCM; use **Send Real WAKEUP_PING** for end-to-end FCM delivery on device. + +### Send Real WAKEUP_PING + +**Send Real WAKEUP_PING** is the in-app equivalent of calling **`POST /debug/send-wakeup`** from the Mac (see [Verification Checklist step 7](#7-manual-wakeup-endpoint-sends-fcm-successfully)). It is intended for **local testing and diagnostics** on non-production builds only. + +**What it does:** + +1. Reads the **Current FCM Token** shown in the panel (same token used by **Register Token Now**). If no token is available, the action fails with a panel status message. +2. `POST`s to `{backend override}/debug/send-wakeup` with `deviceId`, `fcmToken`, `platform: "android"`, and `testMode` from the panel config (`NotificationDebugService.sendRealWakeupPing()`). +3. On HTTP success, the **notification-wakeup-service** enqueues a real FCM **data** message with `data.type = "WAKEUP_PING"` to that device. +4. When FCM delivers the message to the app, Capacitor fires `pushNotificationReceived` → `handleCapacitorPushNotificationReceived()` → `refreshNotificationsWithDiagnostics({ source: "WAKEUP_PING" })` → `POST /notifications/refresh` → `applyNotificationRefreshPayload()` (clear + reschedule via **daily-notification-plugin**). + +That exercises the full **backend → FCM → Capacitor push listener → refresh request → notification rescheduling** path on Android without manual `curl` on the Mac. + +**Prerequisites:** ngrok backend override saved, **Test Mode** as needed, notification permission granted, **Register Token Now** succeeded, app **backgrounded** (Home — not force-stop) before expecting FCM delivery ([§8](#8-android-platform-notes)). + +#### Expected Logcat output + +Filter logcat (prefix is always `[Notifications]`): + +```bash +adb logcat | grep -E '\[Notifications\].*(Real WAKEUP_PING|pushNotificationReceived|WAKEUP_PING|Refresh started|Refresh completed)' +``` + +On a **successful end-to-end** run (HTTP success from the panel, then FCM delivery within ~30–120s), expect these key lines in order: + +``` +[Notifications] Real WAKEUP_PING requested +[Notifications] Real WAKEUP_PING success +[Notifications] pushNotificationReceived type=WAKEUP_PING +[Notifications] WAKEUP_PING handler — invoking refresh +[Notifications] Refresh started (WAKEUP_PING) +[Notifications] Refresh completed (WAKEUP_PING) in …ms (scheduled N) +``` + +Intermediate lines (e.g. `WAKEUP_PING received — will trigger refresh`, `Schedule replacement: …`, auth bypass messages) are normal. ngrok should also show a new `POST /notifications/refresh` after the push is handled. + +#### Send Real WAKEUP_PING vs end-to-end success + +The panel status **“Real WAKEUP_PING sent via backend.”** and the Event Log line **`Real WAKEUP_PING success`** only confirm that the backend **accepted the request and attempted FCM delivery**. They do **not** prove the device received the push or ran refresh. + +**Successful end-to-end delivery** is confirmed only when the subsequent **`pushNotificationReceived`**, **`WAKEUP_PING handler`**, and **`Refresh started (WAKEUP_PING)`** / **`Refresh completed (WAKEUP_PING)`** lines appear in logcat (or Event Log) within the delivery window. If you see `Real WAKEUP_PING success` but not those lines, treat it as an FCM delivery problem ([FCM message not received](#fcm-message-not-received)), not a backend enqueue failure. ### Programmatic override (optional) @@ -408,7 +454,7 @@ Doze, App Standby, and OEM battery menus are weak or absent on many emulators. U adb logcat | grep -E '\[Notifications\]|\[FirebaseMessaging\]|\[NativeNotificationService\]' ``` -Expect `pushNotificationReceived type=WAKEUP_PING` and `WAKEUP_PING handler — invoking refresh` after a successful wake. +Expect `pushNotificationReceived type=WAKEUP_PING` and `WAKEUP_PING handler — invoking refresh` after a successful wake. For a panel-driven test, use **Send Real WAKEUP_PING** ([§6](#send-real-wakeup_ping)) instead of manual `curl`. --- @@ -453,7 +499,7 @@ If wakeup works on a **Pixel** but fails on an OEM phone, assume battery policy 1. Server accepts `/debug/send-wakeup` → FCM enqueue succeeds. 2. Device may **hold** the message until Doze maintenance or OEM policy allows delivery. 3. `pushNotificationReceived` runs → `refreshNotificationsWithDiagnostics()` → ngrok `POST /notifications/refresh`. -4. Any step can lag under battery savers; use **Simulate WAKEUP_PING** in the debug panel to separate FCM delay from refresh/API issues. +4. Any step can lag under battery savers; use **Simulate WAKEUP_PING (Local)** in the debug panel to separate FCM delay from refresh/API issues; use **Send Real WAKEUP_PING** for the full FCM path ([§6](#send-real-wakeup_ping)). --- @@ -466,9 +512,9 @@ If wakeup works on a **Pixel** but fails on an OEM phone, assume battery policy 5. Open **Notification Debug Panel** → paste ngrok URL → **Save Backend URL**; confirm **Test Mode** is on. 6. Tap **Register Token Now** → confirm ngrok `POST /notifications/register` and `[Notifications] Token registration success` in Event Log / logcat. 7. Tap **Refresh Notifications** → confirm `POST /notifications/refresh` and `Refresh completed in Nms (scheduled X)` in Event Log. -8. Optional: tap **Simulate WAKEUP_PING** (backend button) to verify ngrok + refresh without FCM. -9. From the Mac, call **`/debug/send-wakeup`** (see [curl examples](#13-sample-curl-commands)) with the registered `deviceId` / token as required by **notification-wakeup-service**. -10. Watch logcat for `WAKEUP_PING` and refresh timing lines. +8. Optional: tap **Simulate WAKEUP_PING (Local)** to verify ngrok + refresh without FCM. +9. Background the app (Home), then tap **Send Real WAKEUP_PING** (or from the Mac, call **`/debug/send-wakeup`** — see [curl examples](#13-sample-curl-commands)) with the registered `deviceId` / token as required by **notification-wakeup-service**. +10. Watch logcat for `WAKEUP_PING` and `Refresh completed (WAKEUP_PING)` lines ([expected output](#expected-logcat-output)). 11. Open **ngrok inspect UI** (`http://127.0.0.1:4040`) to correlate HTTP traffic. 12. Use **Pending Notification Inspector** on the panel to confirm locally scheduled fires after refresh. @@ -581,7 +627,9 @@ curl -sS -X POST "$BASE/notifications/refresh" \ **Actions:** 1. Use `deviceId` from step 4. -2. From the Mac: +2. Either: + - **On device:** background the app (Home), open **Notification Debug Panel**, tap **Send Real WAKEUP_PING**; or + - **From the Mac:** ```bash curl -sS -X POST "$BASE/debug/send-wakeup" \ @@ -591,7 +639,7 @@ curl -sS -X POST "$BASE/debug/send-wakeup" \ 3. Check **notification-wakeup-service** logs for FCM send success (no Admin SDK / token errors). -**Expected outcome:** HTTP **200** (or documented success code) from `/debug/send-wakeup`; server logs indicate FCM message enqueued/sent with `data.type = "WAKEUP_PING"`. This step alone does not prove device delivery (see step 8). +**Expected outcome:** HTTP **200** (or documented success code) from `/debug/send-wakeup`; Event Log / logcat: `Real WAKEUP_PING success` when using the panel button; server logs indicate FCM message enqueued/sent with `data.type = "WAKEUP_PING"`. This step alone does not prove device delivery (see step 8 and [Send Real WAKEUP_PING vs end-to-end success](#send-real-wakeup_ping-vs-end-to-end-success)). --- @@ -600,7 +648,7 @@ curl -sS -X POST "$BASE/debug/send-wakeup" \ **Actions:** 1. **Background** the app (Home — not force-stop). -2. Run step 7 again (or wait for a server-driven wakeup). +2. Run step 7 again (panel **Send Real WAKEUP_PING** or Mac `curl`), or wait for a server-driven wakeup. 3. Filter logcat: ```bash @@ -609,12 +657,13 @@ adb logcat | grep -E 'WAKEUP_PING|pushNotificationReceived|Refresh completed' **Expected outcome (within ~30–120s, longer under Doze/OEM):** -1. `pushNotificationReceived` / `WAKEUP_PING received` +1. `pushNotificationReceived type=WAKEUP_PING` / `WAKEUP_PING received` 2. `WAKEUP_PING handler — invoking refresh` -3. ngrok: new `POST /notifications/refresh` -4. `Refresh completed in …ms (scheduled N)` +3. `Refresh started (WAKEUP_PING)` +4. ngrok: new `POST /notifications/refresh` +5. `Refresh completed (WAKEUP_PING) in …ms (scheduled N)` -**Isolation:** **Wakeup Ping Simulator** on the panel should produce lines 2–4 without FCM. **Backend Testing → Simulate WAKEUP_PING** proves refresh only (no handler). +**Isolation:** **Wakeup Ping Simulator** on the panel should produce lines 2–5 without FCM. **Simulate WAKEUP_PING (Local)** proves refresh only (no handler, source `WAKEUP_PING simulation`). Full expected logcat for **Send Real WAKEUP_PING**: [§6](#expected-logcat-output). --- @@ -687,9 +736,9 @@ npm run dev 13. Background app (Home). -14. Mac: `curl -X POST "$BASE/debug/send-wakeup" -H "Content-Type: application/json" -d '{"deviceId":"…","testMode":true}'` → server FCM success. +14. Tap **Send Real WAKEUP_PING** in the panel (or Mac: `curl -X POST "$BASE/debug/send-wakeup" -H "Content-Type: application/json" -d '{"deviceId":"…","testMode":true}'`) → panel `Real WAKEUP_PING success` and server FCM enqueue success. -15. Within 30–120s (longer if unplugged/Doze): logcat shows `WAKEUP_PING` → refresh; ngrok shows second `POST /notifications/refresh`. +15. Within 30–120s (longer if unplugged/Doze): logcat shows `pushNotificationReceived` → `Refresh completed (WAKEUP_PING)`; ngrok shows second `POST /notifications/refresh`. 16. Pending Inspector **Refresh** → timestamps updated (replacement, not duplicate stack). @@ -697,7 +746,7 @@ npm run dev 17. If `testMode` returned a trigger within a few minutes, wait for wall-clock fire with app backgrounded; confirm notification appears (permission + channel + exact alarm rules). -18. If FCM step 15 failed but step 11 passed: run **Simulate WAKEUP_PING** (backend) then **Wakeup Ping Simulator** to bisect FCM vs handler; see [Troubleshooting](#14-troubleshooting). +18. If FCM step 15 failed but step 11 passed: run **Simulate WAKEUP_PING (Local)** then **Wakeup Ping Simulator** to bisect FCM vs handler; see [Troubleshooting](#14-troubleshooting) and [Send Real WAKEUP_PING vs end-to-end success](#send-real-wakeup_ping-vs-end-to-end-success). ### End-to-end pass criteria @@ -839,7 +888,7 @@ Compare with panel **Backend Status** and Event Log error text. **Verification:** -1. **Simulate WAKEUP_PING** (backend button) — if this fails, problem is ngrok/refresh API, not FCM. +1. **Simulate WAKEUP_PING (Local)** — if this fails, problem is ngrok/refresh API, not FCM. 2. ngrok inspect for refresh status code and response body. 3. Logcat: `refreshNotifications failed` or JWT errors from `configureNativeFetcherIfReady()`. @@ -849,15 +898,17 @@ Compare with panel **Backend Status** and Event Log error text. ### FCM message not received -**Symptoms:** `/debug/send-wakeup` returns success on Mac; no `pushNotificationReceived` / `WAKEUP_PING` in logcat within 2 minutes; refresh never triggered. +**Symptoms:** `/debug/send-wakeup` or panel **Send Real WAKEUP_PING** shows success (`Real WAKEUP_PING success` in Event Log); no `pushNotificationReceived` / `WAKEUP_PING` in logcat within 2 minutes; refresh never triggered. **Likely causes:** Force-stopped app; wrong FCM token on server; Doze/OEM delay; no Google Play services; payload missing `data.type = "WAKEUP_PING"`; device offline. +**Note:** `Real WAKEUP_PING success` only means the backend accepted and sent the FCM request. Missing downstream refresh logs indicates a **delivery** failure, not a failed wakeup API call ([§6](#send-real-wakeup_ping-vs-end-to-end-success)). + **Verification:** 1. App **backgrounded** (Home), not force-stopped. 2. Panel FCM token matches token used by server/register. -3. **Simulate WAKEUP_PING** (backend button) works → isolates FCM path. +3. **Simulate WAKEUP_PING (Local)** works → isolates FCM path from refresh/API. 4. Wait 30–120s (longer on Doze/OEM). 5. Battery **Unrestricted** and OEM autostart enabled for test device.