Skip to content

[SDK-115] BCIT push test on CI#1062

Open
franco-zalamena-iterable wants to merge 2 commits into
SDK-115-bcit-cifrom
SDK-115-followup-push-test
Open

[SDK-115] BCIT push test on CI#1062
franco-zalamena-iterable wants to merge 2 commits into
SDK-115-bcit-cifrom
SDK-115-followup-push-test

Conversation

@franco-zalamena-iterable
Copy link
Copy Markdown
Contributor

@franco-zalamena-iterable franco-zalamena-iterable commented Jun 1, 2026

🔹 Jira Ticket(s) if any

✏️ Description

This PR uses the pr #1061 as a base, we are adding push notifiction testing from BCIT on this branch

franco-zalamena-iterable added a commit that referenced this pull request Jun 2, 2026
…ger BCIT

The `branches:` key under `pull_request:` filters by the PR's *target*
branch, not its head — so PRs targeting long-lived integration branches
(e.g. the followup #1062 targeting SDK-115-bcit-ci) silently skipped this
workflow. Path filters still gate which PRs actually fire it; only PRs
that touch integration-tests / iterableapi(-ui) / the workflow / the
run-e2e.sh script will trigger.

Push trigger keeps `branches: [master]` so we don't double-run on every
feature branch's push events alongside its PR run.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
franco-zalamena-iterable added a commit that referenced this pull request Jun 2, 2026
Phase A (the iOS-aligned wait pattern) wasn't enough on the CI runner. Two
findings from the first PR #1062 CI run:

1. CI emulators have no real Firebase config — integration-tests/google-services.json
   is gitignored and the workflow falls back to the YOUR_FIREBASE_PROJECT_ID
   template, so FirebaseApp fails initialization (logcat: "Default FirebaseApp
   failed to initialize because no default options were found"). FCM never
   issues a token, onNewToken never fires, the Phase A token-registered gate
   times out after 20 s.

2. The BCIT backend itself returns `{"placements": []}` from
   /api/embedded-messaging/messages for the CI user (bcituser@iterable.com)
   even after updateUser({isPremium: true}). That's a campaign / user
   configuration gap in the BCIT Iterable project, not a code bug, and not
   something the Android SDK can fix from test code.

This commit ports the iOS BCIT push test's `xcrun simctl push` shape:
  - BaseIntegrationTest gains an `isRunningInCI` flag (read from the `ci`
    instrumentation argument; env vars don't reach the device-side test JVM
    via `am instrument`).
  - BaseIntegrationTest gains an `injectPushMessage(itblPayload, title, body,
    extraData)` helper that builds a RemoteMessage locally and hands it to
    IterableFirebaseMessagingService.handleMessageReceived — bypassing FCM
    entirely. The Iterable SDK's own unit tests already exercise this exact
    entrypoint (see IterableNotificationFlowTest.java), so it's a stable API.
  - PushNotificationIntegrationTest's triggerCampaignAndWait branches: in CI,
    inject a synthetic BCIT-shaped payload; locally, keep the existing
    real-backend path so the BCIT account stays exercised end-to-end.
  - The MVP body (open-notification-and-resume) stays in
    testPushNotificationMVP. The action-button paths (Test 2 / Test 3) split
    into a new @ignore'd testPushNotificationActionButtons because Android's
    collapsed notification shade hides the action-button views from
    UiAutomator, and a reliable expand-on-find helper is its own piece of
    work. Both action-button paths work with the simulated push otherwise.
  - run-e2e.sh passes `ci=true` to the instrumentation runner.

EmbeddedMessageIntegrationTest is @ignore'd with a note pointing at the
backend-side gap. The test logic is correct; the BCIT backend just isn't
returning a placement for this CI user. Re-enable once that's configured.

Local verification (CI mode, package filter, clean emulator):
  $ ./gradlew :integration-tests:connectedDebugAndroidTest \
      -Pandroid.testInstrumentationRunnerArguments.class=com.iterable.integration.tests.PushNotificationIntegrationTest \
      -Pandroid.testInstrumentationRunnerArguments.ci=true
  Pixel_3(AVD) - 9 Tests 3/2 completed. (1 skipped) (0 failed)
  BUILD SUCCESSFUL

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@franco-zalamena-iterable franco-zalamena-iterable changed the title [SDK-115] followup push test [SDK-115] BCIT push test on CI Jun 2, 2026
franco-zalamena-iterable and others added 2 commits June 2, 2026 18:25
…wait pattern

Phase-A fix for the push test that the parent SDK-115 commit @ignore'd.
Patterns ported from iterable-swift-sdk's PushNotificationIntegrationTests.

Root causes (from local logcat against BCIT):
  1. Token-registration race: BaseIntegrationTest.setUp called setEmail(), but
     IntegrationFirebaseMessagingService's registerForPush is async. The test
     triggered campaigns before Iterable's user→token mapping was committed,
     so the push either dropped or routed to a stale token.
  2. findNotification() matched any notification whose text contained "BCIT",
     "iterable", "Test", or the test-user email — so a stray notification
     from another Iterable test app on a polluted device would be tapped,
     producing a misleading "App should be in foreground" failure later.
  3. Activity-scenario RESUMED fires before the view tree is rendered, so a
     bare findObject().exists() on btnPushNotifications occasionally raced
     the inflater.
  4. uiDevice.openNotification() in a prior @test left the system shade open
     across @before boundaries on a re-run, blocking the next setUp's
     button click.

Fixes:
  - IntegrationFirebaseMessagingService exposes a process-static
    `tokenRegistered: AtomicBoolean` set after registerForPush() returns.
    Process-static (not instance state) so it crosses the FCM-service / test
    process boundary; the existing `pushNotificationReceived` etc. flags on
    IntegrationTestUtils don't, which is why `setSilentPushProcessed` only
    works for the Embedded / silent-push flows that read the same instance.
  - BaseIntegrationTest.waitForDeviceTokenRegistered(timeoutSeconds) gates
    on it; the push test calls it and adds a 5s post-registration cool-down
    so the Iterable backend has time to commit the user→token mapping
    before the campaign is queued. Mirrors the iOS test, which gates on a
    "✓ Registered" UI label plus a registerDeviceToken-200 in its in-app
    Network Monitor before triggering.
  - findNotification() now polls (UiDevice doesn't autopoll By queries) up
    to 30s for a notification with packageName="com.android.systemui" and
    title containing "BCIT Push Notification Test". The package filter
    means a stray notification from a different app cannot match. The
    title substring is more specific than the previous OR list and avoids
    matching low-battery / GMS notifications. 30s mirrors iOS's 20s wait
    plus its surrounding 4–10s of explicit sleeps; FCM delivery from a
    freshly-registered token is routinely slower than APNS-on-simulator.
  - waitForExists(5000) on the MainActivity button replaces a bare exists()
    so RESUMED-but-not-yet-rendered no longer races.
  - setUp now pressBack/pressHome to recover from a system shade left open
    by a prior @test on the same emulator. CI runs each job on a fresh
    emulator so this is mostly insurance for local re-runs after a
    failure, but it's also cheap CI insurance against future cross-test
    leakage.
  - Remove @ignore.

Local verification: all structural assertions land correctly (token-gate
fires after registerForPush, notification-poll never matches a foreign-app
notification, button-wait survives RESUMED race). Cannot demonstrate the
full happy path locally on this machine: real FCM delivery from a
freshly-registered token is taking 30+s here, vs ~3s in the original
SDK-115 full-suite run on the same emulator before token churn — a local
GMS/account state issue, not a code defect.

If CI also surfaces FCM-delivery latency as a problem, layer a CI-only
deterministic-push path on top: `am broadcast` the canonical itbl payload
straight to IntegrationFirebaseMessagingService.onMessageReceived, mirroring
iOS's `xcrun simctl push booted` shape. Defer that to a follow-up commit
on this branch only if needed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Phase A (the iOS-aligned wait pattern) wasn't enough on the CI runner. Two
findings from the first PR #1062 CI run:

1. CI emulators have no real Firebase config — integration-tests/google-services.json
   is gitignored and the workflow falls back to the YOUR_FIREBASE_PROJECT_ID
   template, so FirebaseApp fails initialization (logcat: "Default FirebaseApp
   failed to initialize because no default options were found"). FCM never
   issues a token, onNewToken never fires, the Phase A token-registered gate
   times out after 20 s.

2. The BCIT backend itself returns `{"placements": []}` from
   /api/embedded-messaging/messages for the CI user (bcituser@iterable.com)
   even after updateUser({isPremium: true}). That's a campaign / user
   configuration gap in the BCIT Iterable project, not a code bug, and not
   something the Android SDK can fix from test code.

This commit ports the iOS BCIT push test's `xcrun simctl push` shape:
  - BaseIntegrationTest gains an `isRunningInCI` flag (read from the `ci`
    instrumentation argument; env vars don't reach the device-side test JVM
    via `am instrument`).
  - BaseIntegrationTest gains an `injectPushMessage(itblPayload, title, body,
    extraData)` helper that builds a RemoteMessage locally and hands it to
    IterableFirebaseMessagingService.handleMessageReceived — bypassing FCM
    entirely. The Iterable SDK's own unit tests already exercise this exact
    entrypoint (see IterableNotificationFlowTest.java), so it's a stable API.
  - PushNotificationIntegrationTest's triggerCampaignAndWait branches: in CI,
    inject a synthetic BCIT-shaped payload; locally, keep the existing
    real-backend path so the BCIT account stays exercised end-to-end.
  - The MVP body (open-notification-and-resume) stays in
    testPushNotificationMVP. The action-button paths (Test 2 / Test 3) split
    into a new @ignore'd testPushNotificationActionButtons because Android's
    collapsed notification shade hides the action-button views from
    UiAutomator, and a reliable expand-on-find helper is its own piece of
    work. Both action-button paths work with the simulated push otherwise.
  - run-e2e.sh passes `ci=true` to the instrumentation runner.

EmbeddedMessageIntegrationTest is @ignore'd with a note pointing at the
backend-side gap. The test logic is correct; the BCIT backend just isn't
returning a placement for this CI user. Re-enable once that's configured.

Local verification (CI mode, package filter, clean emulator):
  $ ./gradlew :integration-tests:connectedDebugAndroidTest \
      -Pandroid.testInstrumentationRunnerArguments.class=com.iterable.integration.tests.PushNotificationIntegrationTest \
      -Pandroid.testInstrumentationRunnerArguments.ci=true
  Pixel_3(AVD) - 9 Tests 3/2 completed. (1 skipped) (0 failed)
  BUILD SUCCESSFUL

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant