test: integration test suite (fake-satellite protocol + dashboard) on CI - #139
Merged
Conversation
Adds instrumented integration tests that drive the real connection stack (SatelliteConnectionManager, SatelliteHttpClient with TOFU pinning, SessionCrypto, the native UDP path) against an in-process FakeSatellite implementing the protocol-1 contract: PIN pairing with hmacProof validation, the declarative session PUT/GET, HTTP keep-alive, self-unpair, catalog/capabilities, and the encrypted ChaCha20-Poly1305 UDP data plane with heartbeat acks and session-close notify. Each FakeSatellite mints its own self-signed cert at runtime via okhttp-tls (no keys committed), so two instances present different certs, exercising cert pinning for real: pin one, reject the other on the same identity. SatelliteProtocolTest covers pair-to-Live, wrong-PIN, heartbeat decrypt, TOFU mismatch rejection, terminal 401 key-drop, epoch-driven reconcile, self-unpair, and kick. DashboardIntegrationTest asserts a live session reaches MainActivity's own views. Tests reach the app's real singletons through MainActivity injection fields (no production seams), matching the screenshot harness pattern. Run under Android Test Orchestrator (execution ANDROIDX_TEST_ORCHESTRATOR + clearPackageData): each test gets a fresh process, so one test's session, retry jobs, and sockets can't leak into the next. A pixel6Api34 managed device is added for local runs.
Adds an `instrumented` job that runs the integration package (fake- satellite protocol + dashboard) on the same reactivecircus emulator path the screenshots workflow uses (google_apis api 35, KVM), a known-good CI image for this app's GameActivity. Scoped to com.tinkernorth.dish.integration: the screenshots capture harness has its own per-locale workflow, and the ad-hoc launch tests predate the guided-setup redirect.
scripts/ci_local.sh runs every Android CI gate in the same order against the local tree (clang-format, metadata lint, ktlint+detekt, lint, unit tests, native C++ tests, assemble, and the integration suite when a device or --gmd is available), so a green run locally means a green run in CI. Requires JAVA_HOME; finds adb via the SDK when it is off PATH.
emir-hasanbegovic
force-pushed
the
feat/integration-tests
branch
from
July 9, 2026 00:19
bff9d1e to
e284abe
Compare
Grows the suite from 10 to 18 tests, all on the emulator + in-process fake, and migrates singleton access to a reflection helper (drops the per-test MainActivity launch for the protocol/catalog/input suites). New protocol flows: - Path B reverse pairing: client shows a PIN, operator approves, the status poll hands the key back and the session opens. - Session-close notify split: kicked re-enters the bounded backoff (a reconnect PUT follows), unpaired is terminal (drops the key). - Zero-controller session (a user in menus) PUTs an empty controllers array. Catalog (new CatalogIntegrationTest): GET /api/catalog returns the type list and publishes host features; a second fetch revalidates with the ETag and the 304 path serves the cache (both fetches hit the fake). Input data plane (new InputWireTest): bind the virtual slot, reach Live, and stream each message type the overlays send. The encrypted UDP packets must decrypt at the fake: INPUT (0x0001), MOTION (0x000A), BATTERY (0x000B), TOUCHPAD (0x000C). Proves the native encode + ChaCha20-Poly1305 send path against a session key from the real handshake. The fake now honours HTTP catalog ETag/304 and the Path B pair/status poll, and mints a fresh cert per instance (unchanged).
…ycle controller The input-wire tests registered the virtual slot through the lifecycle-scoped SlotTopologyController, which does not run on a headless CI emulator (no foreground window), so the slot never registered and the three stream tests failed on CI while passing locally. Declare the slot straight on the live connection with applyDesired instead: it drives the same declare -> syncSlot -> controller PUT -> registered path without the lifecycle dependency.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
The repo had ~1475 unit tests and 159 native tests but nothing exercising the integrated app on CI. This adds a real integration layer.
Fake-satellite protocol suite
An in-process
FakeSatelliteimplements the protocol-1 contract (satellite/docs/contract.md) over real TLS + UDP, so tests drive the production connection stack end to end:SatelliteConnectionManager,SatelliteHttpClient(TOFU cert pinning),SessionCrypto(HMAC proof + HKDF session key), and the native ChaCha20-Poly1305 UDP path. No mocks of the code under test. EachFakeSatellitemints its own self-signed cert at runtime viaokhttp-tls(no keys committed to the repo), so two instances present different certs and TOFU rejection is exercised for real.SatelliteProtocolTest(8): pair-to-Live with hmacProof validation and the declarative session PUT; wrong-PIN never stores a key; encrypted heartbeats decrypt with the derived session key and acks keep the session alive; TOFU rejects a different cert on the same identity; terminal 401 drops the key and stops retrying; an epoch-mismatched heartbeat ack triggers the reconcile GET; forget self-unpairs; a kick close-notify takes the session down.DashboardIntegrationTest(2): a live session is reflected inMainActivity's own views; the empty state shows the add prompt.Isolation
Runs under Android Test Orchestrator (
execution = "ANDROIDX_TEST_ORCHESTRATOR"+clearPackageData): each test gets a fresh process, so one test's session, retry jobs, and native sockets can't leak into the next. This was necessary because the suite drives real process-wide singletons; without it, later tests flaked deterministically. Verified green across repeated back-to-back runs locally.CI + local parity
instrumentedjob runs the integration package on the same emulator path the screenshots workflow uses (reactivecircus/android-emulator-runner, google_apis api 35, KVM), scoped tocom.tinkernorth.dish.integration. (A GMDaosp-atddevice was tried first but hung on CI with this app's GameActivity; the GMD device is kept for local--gmdruns.)scripts/ci_local.shruns every CI gate locally in the same order (clang-format, metadata lint, ktlint+detekt, lint, unit tests, native C++ tests, assemble, integration on an attached device or--gmd), so local green == CI green.Formatting
Confirmed: ktlint + detekt already lint all source sets including
testandandroidTest, so unit tests and these new integration tests are auto-formatted like the rest of the code. No formatting gap to close.🤖 Generated with Claude Code