Skip to content

Consume octi-web's published wire-format fixtures#318

Merged
d4rken merged 1 commit into
mainfrom
feat/consume-web-fixtures
May 21, 2026
Merged

Consume octi-web's published wire-format fixtures#318
d4rken merged 1 commit into
mainfrom
feat/consume-web-fixtures

Conversation

@d4rken
Copy link
Copy Markdown
Member

@d4rken d4rken commented May 21, 2026

What changed

No user-facing behavior change. Internal: app-main now consumes octi-web's published wire-format fixtures and asserts every payload decodes to the canonical shape octi-web emits. If a future octi-web change drops a required field, retypes a value, or renames an enum entry, app-main's CI catches it here — same gate the existing Tink fixture verify provides for the encryption layer, applied to the per-module JSON payloads.

This is the first consumer half of Phase B in the cross-repo wire-format gate. The matching upstream-gating workflow on octi-web (which would run app-main + octi-desktop tests against an octi-web PR's HEAD via INTEROP_FIXTURE_OVERRIDES) lands in a follow-up once octi-desktop is also consuming these same fixtures.

Technical Context

  • Multi-source v2 lockfile from day one. fixture-lock.json at repo root uses a schemaVersion: 2 + sources: { "<owner>/<repo>": { ref, manifest_sha256 } } schema. App-main is starting fresh on this — it had no prior v1 lockfile to migrate. octi-web and octi-desktop already ship single-source v1 lockfiles; they migrate to v2 in their own follow-ups (B3/B4) so the cross-repo trust shape doesn't have to converge in one atomic step.
  • Shared sync infrastructure landed in app-common-test/src/main/java/testhelpers/interop/. modules-meta, modules-clipboard, modules-files all depend on sync-core — consumer tests can't live in sync-core because they need to import the per-module *Info model classes. The shared helpers (InteropFixtures schemas, SyncRefResolver validation, InteropFixtureSync fetcher) sit one layer above so each module's src/test can call into them through the existing testImplementation(project(":app-common-test")) wiring.
  • HttpURLConnection, not java.net.http.HttpClient. This object lives in src/main of an Android library module; java.net.http is API 33+ on Android even though tests run on the JVM. HttpURLConnection skips the NewApi gate cleanly. Streaming read with a hard byte cap (64 KiB manifest, 256 KiB per fixture file, 32 files max per manifest) so a malformed or hostile upstream can't OOM the test JVM.
  • INTEROP_FIXTURE_OVERRIDES env var is declared as a Gradle task input. Otherwise an overridden run could be UP-TO-DATE skipped against a prior cached result. Same reasoning as fixture-lock.json itself being an input.
  • Override targeting an unknown source fails loudly. A Phase C workflow that accidentally sends {"d4rken-org/octi-desktop": "<sha>"} before app-main's lock actually contains that source gets rejected at parse time — silent fallback would let the gate report green against a lock that doesn't actually verify the implied wire.
  • Producer-side sha256 + byteLength on every PublishedVector are re-verified at read time. octi-web's B1 self-check pins them at generate time; this consumer side double-checks against the actual payloadJson bytes so a hand-edited fixture (with manifest untouched) fails here, not silently as a green decode.
  • Warm-cache reads use Files.size() pre-checks to mirror the on-fetch size caps. A locally-tampered cache file can't bypass them on the second run.

Review guidance

  • Per-module consumer tests are the bulk of the diff. Each asserts every field of the canonical input declared in octi-web's tools/generate-fixtures.ts — adding a new field there without updating the matching mirror here makes the test fail with a useful expected/actual diff.
  • Multi-source resolver lives at app-common-test/.../SyncRefResolver.kt; cross-reference with the same-named files at octi-web/src/__interop__/sync-ref-resolver.ts and octi-desktop/.../SyncRefResolver.kt. SOURCE_PATHS is the per-repo cross-repo trust boundary — adding a fourth source requires a code change in each repo (deliberate friction).

Test plan

  • ./gradlew :sync-core:testDebugUnitTest :modules-meta:testDebugUnitTest :modules-clipboard:testDebugUnitTest :modules-files:testDebugUnitTest green on a cold cache (fetched fixtures from octi-web@84b9e57)
  • Same command green on a warm cache (logs "interop fixtures cache hit")
  • 16 new tests in SyncRefResolverTest covering validateLock, parseOverrides, resolveAll (incl. the new "override targets a source not present in the lock" check)
  • 4 new tests in WebMetaInteropTest, 4 in WebClipboardInteropTest, 7 in WebFilesInteropTest — every field of every vector asserted
  • Live cross-repo verify: covered by the symmetric upstream-gating workflow that lands on octi-web in B4 (after octi-desktop ships matching consumer support in B3).

Sister PRs (the trio of producer↔consumer pairs):

Introduces a v2 multi-source fixture-lock.json at repo root pinning d4rken-org/octi-web at a known commit + manifest sha256. Per-module consumer tests under modules-{meta,clipboard,files}/src/test parse each octi-web payloadJson through the production decoder and assert field-by-field against octi-web's canonical inputs.

Shared sync infrastructure lives in app-common-test/src/main/java/testhelpers/interop/ — InteropFixtureSync (HttpURLConnection-based fetch+verify with size caps, idempotent across test classes), SyncRefResolver (SOURCE_PATHS allowlist + INTEROP_FIXTURE_OVERRIDES env merge with strict validation), and the wire schemas.

Pairs with octi-web#26 (the producer).
@d4rken d4rken added Build/Deploy c: sync Component: Synchronization c: module Component: Info Modules c: module/meta Related to meta module (`module-meta`) that contains the basic device infos. c: module/clipboard Related to the clipboard module (`module-clipboard`). c: module/files Related to the file sharing module (`module-files`). labels May 21, 2026
@d4rken d4rken merged commit d6f2afd into main May 21, 2026
17 checks passed
@d4rken d4rken deleted the feat/consume-web-fixtures branch May 21, 2026 13:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build/Deploy c: module/clipboard Related to the clipboard module (`module-clipboard`). c: module/files Related to the file sharing module (`module-files`). c: module/meta Related to meta module (`module-meta`) that contains the basic device infos. c: module Component: Info Modules c: sync Component: Synchronization

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant