8.6 KiB
Shared Test Environments
NomadCode milestones can reference this document when a verification step needs a shared machine or platform SDK that is not expected to exist in every agent workspace.
Android SDK Remote Runner
Use the Toki Labs host for Android SDK dependent checks.
Access
ssh toki@toki-labs.com
After connecting, use the NomadCode checkout prepared on that host. If the checkout is under the same workspace convention as local agent workspaces, start here:
cd /config/workspace/nomadcode
If that path is absent, use the active NomadCode checkout on the host and make sure it is on the branch or commit that contains the change being verified.
Environment Check
Run these before Android-specific verification:
flutter doctor -v
echo "$ANDROID_HOME"
echo "$ANDROID_SDK_ROOT"
flutter devices
flutter doctor -v should show an Android toolchain with an installed SDK. flutter devices is allowed to show no physical device when the check only needs a build. A device or emulator is required only for an interactive smoke run.
Standard Checks
For Android SDK dependent client milestones, run from the Flutter host:
cd apps/client
flutter pub get
flutter build apk --debug
flutter build apk --debug is the default Android SDK validation for roadmap completion when no device smoke is explicitly required. It verifies Gradle, Android SDK, plugin registration, Firebase Android config wiring, and Dart bootstrap compilation for Android.
Generic Dart/widget checks such as flutter test and flutter analyze --no-fatal-infos may be run locally or on the remote host when the milestone explicitly asks for them. Do not treat the remote host as the source of truth for non-Android SDK checks unless its Flutter SDK version is part of the milestone evidence.
Optional Device Smoke
Run this only when the milestone asks for an Android runtime smoke or notification/device behavior:
cd apps/client
flutter devices
flutter run -d <device-id>
For Mattermost push work, confirm the app boots, Firebase initialization does not crash, and notification navigation callbacks remain observable. Do not record tokens, credentials, signing keys, or private server values in roadmap or docs.
Mattermost Server-Generated Push Smoke
Use this when NomadCode needs to reproduce the same signed FCM path that was verified from the sibling ../nexo messaging plugin environment.
Roadmap Target
The current matching roadmap target is:
- Phase:
agent-roadmap/phase/external-integration/PHASE.md - Milestone:
agent-roadmap/phase/external-integration/milestones/external-integration.md - Task fit:
[mattermost-adapter]and Mattermost notification evidence for external integration validation.
Private Inputs
Before running this smoke, read agent-ops/rules/private/rules.md and use the local-only secret file referenced there. Do not print or commit raw bot tokens, passwords, FCM tokens, signing keys, or push signatures.
Create the ignored credential asset used by apps/client:
{
"serverUrl": "<mattermost-base-url-from-private-rules>",
"loginId": "<receiver-test-account-email-from-private-rules>",
"password": "<receiver-test-account-password-from-private-rules>"
}
Write it to:
apps/client/assets/mattermost_credentials.json
This asset is intentionally ignored by apps/client/.gitignore. It lets MattermostAuthService login, call setAuthToken, fetch the server signing public key, call setSigningKey, and register the current FCM device token.
If the private environment provides a Mattermost server identifier that appears in FCM payloads as server_id, add it as serverId or serverIdentifier in the ignored credential asset. The host passes it through setAuthToken(..., identifier:) so nexo_messaging can resolve server_url even when more than one server is registered.
Workspace Shape
The remote Android runner needs the same sibling workspace layout as the local agent workspace:
<workspace>/
nomadcode/
nexo/
proto-socket/
apps/client/pubspec.yaml depends on:
nexo_messaging:
path: ../../../nexo/packages/messaging_flutter
proto_socket:
path: ../../../proto-socket/dart
Make sure the remote ../nexo checkout contains the current packages/messaging_flutter changes, especially the Android opened-intent fix that converts nested Bundle extras into Flutter codec-safe values. Without that fix, tapping a notification can crash with Unsupported value: 'Bundle[EMPTY_PARCEL]'.
NomadCode already uses Android applicationId = "com.tokilabs.mattermost" in apps/client/android/app/build.gradle.kts; unlike the standalone Nexo test harness, no Gradle application-id override is expected.
Run App
On the Android remote runner:
cd <workspace>/nomadcode/apps/client
flutter pub get
flutter analyze --no-fatal-infos
flutter test
flutter run -d emulator-5554 --no-resident
Expected app-side setup logs include:
FirebaseApp initialization successful
[MattermostAuth] Logging in to ...
[MattermostAuth] Login OK ...
[MattermostAuth] Signing key stored.
[MattermostAuth] FCM device token registered successfully.
If login or signing-key storage is skipped, fix the ignored credential asset or private environment before triggering the push. A valid server-generated push should not require manually injecting server URL rows or signing keys.
Trigger Signed Push
Use the bot token from private rules to send a normal Mattermost DM or mention to the receiver test account through the Mattermost REST API. The important property is that the message is created by the server, then delivered through push-proxy to FCM; do not use a hand-built placeholder signature as the positive test.
The generic API flow is:
# 1. Resolve bot user and receiver user.
GET /api/v4/users/me
GET /api/v4/users/email/<receiver-email>
# 2. Open or create a direct channel.
POST /api/v4/channels/direct
# body: ["<bot-user-id>", "<receiver-user-id>"]
# 3. Post the smoke message.
POST /api/v4/posts
# body: {"channel_id":"<direct-channel-id>","message":"nomadcode signed push smoke <timestamp>"}
All calls use:
Authorization: Bearer <bot-access-token-from-private-rules>
Expected Evidence
Collect redacted evidence only. Good evidence is logcat snippets, sanitized UI text, or dumpsys notification excerpts that omit raw payload signatures and tokens.
The positive signed path should show:
NexoFCM : >>> onMessageReceived ENTER
NexoFCM : onMessageReceived type=message ... serverUrl=<mattermost-base-url>
ReceiptDelivery: Send ACK=...
NexoFCM : Sending notification event to Flutter: type=message
It should not show:
Cannot verify
No signing key
Notification skipped: signature verification failed
Then verify:
- System notification displays the server-generated message and has a
Replyaction. - Tapping the notification invokes the host channel navigation callback.
- Inline reply sends successfully. NomadCode should be able to test this fully because
MattermostAuthServicecallssetAuthToken; if reply fails, inspect auth-token handoff before treating plugin behavior as failed. - Dismiss/clear path logs the dismiss or cancel event.
- Device registration uses the
android_rn-v2:prefix before the raw FCM token is registered with Mattermost.
Negative Controls
If direct push-proxy access is available, send one placeholder-signature payload and one unsigned payload as negative controls. Both may reach onMessageReceived and ACK, but must stop before notification display with:
Notification skipped: signature verification failed
Evidence Checklist
Record these outcomes in the relevant agent-task review stub or milestone evidence. Use PASS, PARTIAL, BLOCKED, or N/A rather than overstating results.
| 항목 | 기대 |
|---|---|
| FCM payload wakes / receipt | PASS |
| valid server-generated signature 처리 | PASS |
| invalid / unsigned drop | PASS |
| notification display | PASS |
| ACK request | PASS |
| opened event | PASS |
| channel navigation | PASS |
| thread navigation | N/A unless the payload has root_id |
| inline reply | PASS expected for NomadCode host auth wiring |
| dismiss / clear | PASS |
| device token prefix | PASS |
Roadmap Evidence
When a milestone depends on this environment, leave only a short evidence note in the milestone, for example:
Android SDK check: see docs/test-environments.md#android-sdk-remote-runner; flutter build apk --debug passed on toki@toki-labs.com.
Keep detailed reusable environment instructions in this document, not in individual milestone files.