test(reporting): ReportUpload testability seams + push IdleService E2E #283

Merged
JMR-dev merged 2 commits from test-257-reportupload-seams-e2e into main 2026-07-04 05:16:33 +00:00
JMR-dev commented 2026-07-04 04:58:28 +00:00 (Migrated from github.com)

Closes #257

Testability seams (minimal, behaviour-preserving)

  • ReportUploadScheduler — inject Provider<WorkManager> (exactly mirroring SyncScheduler) instead of calling the WorkManager.getInstance(context) static that MockK can't stub on the abstract WorkManager (AbstractMethodError). Resolves lazily at enqueue time, so no change to the on-demand-WorkManager init ordering.
  • ReportUploadWorker — take the ingest endpoint via a new @DebugReportEndpoint qualifier, provided from BuildConfig.DEBUG_REPORT_ENDPOINT in ReportingModule, rather than reading the BuildConfig static inline. Production value is identical (empty by default), so the "no endpoint configured → clean failure" behaviour is unchanged.
  • IdleService — extract the foreground-notification channel + push-mode→text logic (the service's only non-lifecycle logic) into PushStatusNotification. startAsForeground now just delegates; behaviour is byte-for-byte the same notification.

Coverage added

  • ReportUploadSchedulerTest (unit) — pins the per-report unique-work name and the REPLACE policy; statusFlow observes the right unique work.
  • ReportUploadWorkerHttpTest (unit) — drives the previously-unreachable transmit path against an in-process JDK HttpServer on loopback (no new dependency, same "real in-process server" approach as GreenMail): 2xx → success + report deleted (and asserts the JSON POST body/content-type), 4xx → permanent failure, 5xx → retry then failure at the attempt cap, network error → retry.
  • PushStatusNotificationInstrumentedTest (instrumented/E2E) — asserts the channel importance and the IDLE vs POLLING notification text using the real application Context (a ContextWrapper, never mockk<Context>()), following the #272 / BatteryOptimizationManagerIntentTest patterns.

CrashReporter.terminate (item 4) left as-is per the ticket.

Validation

  • Compile gate (JDK 21, no emulator): :app:testDebugUnitTest + :app:compileDebugAndroidTestKotlin + :app:ktlintCheck + :app:detekt + :app:assembleDebug — all green.
  • Unit: ReportUploadSchedulerTest 2/2, ReportUploadWorkerHttpTest 5/5, ReportUploadWorkerTest 3/3.
  • Instrumented: PushStatusNotificationInstrumentedTest 3/3 green on an API 36 emulator via the merged local helper (#269); teardown verified clean (no orphaned qemu). CI runs the full multi-API matrix.

🤖 Generated with Claude Code

Closes #257 ## Testability seams (minimal, behaviour-preserving) - **`ReportUploadScheduler`** — inject `Provider<WorkManager>` (exactly mirroring `SyncScheduler`) instead of calling the `WorkManager.getInstance(context)` static that MockK can't stub on the abstract `WorkManager` (`AbstractMethodError`). Resolves lazily at enqueue time, so no change to the on-demand-WorkManager init ordering. - **`ReportUploadWorker`** — take the ingest endpoint via a new `@DebugReportEndpoint` qualifier, provided from `BuildConfig.DEBUG_REPORT_ENDPOINT` in `ReportingModule`, rather than reading the `BuildConfig` static inline. Production value is identical (empty by default), so the "no endpoint configured → clean failure" behaviour is unchanged. - **`IdleService`** — extract the foreground-notification channel + push-mode→text logic (the service's only non-lifecycle logic) into `PushStatusNotification`. `startAsForeground` now just delegates; behaviour is byte-for-byte the same notification. ## Coverage added - **`ReportUploadSchedulerTest`** (unit) — pins the per-report unique-work name and the `REPLACE` policy; `statusFlow` observes the right unique work. - **`ReportUploadWorkerHttpTest`** (unit) — drives the previously-unreachable transmit path against an in-process JDK `HttpServer` on loopback (no new dependency, same "real in-process server" approach as GreenMail): 2xx → success + report deleted (and asserts the JSON POST body/content-type), 4xx → permanent failure, 5xx → retry then failure at the attempt cap, network error → retry. - **`PushStatusNotificationInstrumentedTest`** (instrumented/E2E) — asserts the channel importance and the IDLE vs POLLING notification text using the real application `Context` (a `ContextWrapper`, never `mockk<Context>()`), following the `#272` / `BatteryOptimizationManagerIntentTest` patterns. `CrashReporter.terminate` (item 4) left as-is per the ticket. ## Validation - Compile gate (JDK 21, no emulator): `:app:testDebugUnitTest` + `:app:compileDebugAndroidTestKotlin` + `:app:ktlintCheck` + `:app:detekt` + `:app:assembleDebug` — all green. - Unit: `ReportUploadSchedulerTest` 2/2, `ReportUploadWorkerHttpTest` 5/5, `ReportUploadWorkerTest` 3/3. - Instrumented: `PushStatusNotificationInstrumentedTest` 3/3 green on an API 36 emulator via the merged local helper (`#269`); teardown verified clean (no orphaned qemu). CI runs the full multi-API matrix. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.