Skip to content

test(runway): end-to-end coverage for the merge queue round trip - #540

Open
behinddwalls wants to merge 2 commits into
mainfrom
preetam/runway
Open

test(runway): end-to-end coverage for the merge queue round trip#540
behinddwalls wants to merge 2 commits into
mainfrom
preetam/runway

Conversation

@behinddwalls

@behinddwalls behinddwalls commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Why?

Runway had no integration or e2e coverage at all — test/integration/ has no runway/ subtree, and test/e2e/ covered only submitqueue and stovepipe. Everything Runway does was proven in isolation: git_merger_test.go drives a real git binary against a real bare remote across every merge strategy, and the controllers are covered with mocks.

What nothing proved is the service. A client publishes a MergeRequest to a real queue and a MergeResult has to come back on the signal topic under the client's correlation id. Topic-registry wiring, the protojson round trip over the wire, partition-key propagation, the ack-and-publish-FAILED path for expected outcomes, and DLQ reconciliation were all untested. The submitqueue e2e does boot the Runway container, but with the noop merger and only as a dependency of submitqueue's own flow.

What?

test/e2e/runway/ is a compose-backed suite (queue MySQL + the Runway service) that drives Runway the way its real client does. Runway is consumer-only, so the suite publishes to an inbound merge topic and listens on the corresponding signal topic. Five cases:

  • Merge happy path — SUCCEEDED on merge-signal with the id echoed, every step attributable by step id in application order, a produced revision per step, and the partition key carried from request to result.
  • Conflict-check happy path — SUCCEEDED on merge-conflict-check-signal with no outputs, since a dry run commits nothing.
  • Terminal failures — conflict and invalid request, on both topic pairs: FAILED with a reason, answered by the controller rather than dead-lettered.
  • DLQ reconcile — an unexpected fault is rejected to the dead-letter topic and the reconciler resolves the correlation id, so the client never waits forever.
  • Undecodable payload — bytes that carry no correlation id signal nothing. Asserted with a same-partition sentinel rather than a sleep.

Outcomes have to be steerable from the payload, so the service runs a new runway/extension/merger/fake whose result is driven by an sq-fake=<token> marker in a change URI (merge-conflict, merge-invalid, merge-error), matching the convention the submitqueue fakes already use. It is selected by MERGER=fake; the git and noop paths are untouched. What a merge does stays out of the e2e — that is the git merger's own test.

The marker helper moved from submitqueue/core/fakemarker to platform/fakemarker. Runway needs it and {domain}/core/ is domain-internal; four importers were updated.

One assertion is worth calling out. Checking that a request landed on the dead-letter topic races the queue's message GC, which reclaims the row once the reconciler acks it. The suite discriminates on the dead-lettered: reason prefix instead — deterministic, and also the only thing on the wire that tells a client which component answered.

Test Plan

  • bazel test //test/e2e/runway:go_default_test — 5 tests, ~13s. Verified with -test.v that every case and subtest actually runs.
  • bazel test //test/e2e/submitqueue:go_default_test and //test/e2e/stovepipe:go_default_test — confirms the fakemarker move.
  • bazel test //runway/... — including the new fake's unit tests.
  • make test, make fmt, license headers, gazelle/tidy idempotency.

//submitqueue/orchestrator/controller/{batch,cancel}:go_default_test fail, but they fail identically on unmodified main and bazel query shows neither depends on anything this touches.

behinddwalls and others added 2 commits August 6, 2026 15:51
## Summary

### Why?

Runway had no integration or e2e coverage at all — `test/integration/` has no `runway/` subtree, and `test/e2e/` covered only `submitqueue` and `stovepipe`. Everything Runway does was proven in isolation: `git_merger_test.go` drives a real `git` binary against a real bare remote across every merge strategy, and the controllers are covered with mocks.

What nothing proved is the service. A client publishes a `MergeRequest` to a real queue and a `MergeResult` has to come back on the signal topic under the client's correlation id. Topic-registry wiring, the protojson round trip over the wire, partition-key propagation, the ack-and-publish-FAILED path for expected outcomes, and DLQ reconciliation were all untested. The submitqueue e2e does boot the Runway container, but with the noop merger and only as a dependency of submitqueue's own flow.

### What?

`test/e2e/runway/` is a compose-backed suite (queue MySQL + the Runway service) that drives Runway the way its real client does. Runway is consumer-only, so the suite publishes to an inbound merge topic and listens on the corresponding signal topic. Five cases:

- **Merge happy path** — SUCCEEDED on `merge-signal` with the id echoed, every step attributable by step id in application order, a produced revision per step, and the partition key carried from request to result.
- **Conflict-check happy path** — SUCCEEDED on `merge-conflict-check-signal` with no outputs, since a dry run commits nothing.
- **Terminal failures** — conflict and invalid request, on both topic pairs: FAILED with a reason, answered by the controller rather than dead-lettered.
- **DLQ reconcile** — an unexpected fault is rejected to the dead-letter topic and the reconciler resolves the correlation id, so the client never waits forever.
- **Undecodable payload** — bytes that carry no correlation id signal nothing. Asserted with a same-partition sentinel rather than a sleep.

Outcomes have to be steerable from the payload, so the service runs a new `runway/extension/merger/fake` whose result is driven by an `sq-fake=<token>` marker in a change URI (`merge-conflict`, `merge-invalid`, `merge-error`), matching the convention the submitqueue fakes already use. It is selected by `MERGER=fake`; the git and noop paths are untouched. What a merge *does* stays out of the e2e — that is the git merger's own test.

The marker helper moved from `submitqueue/core/fakemarker` to `platform/fakemarker`. Runway needs it and `{domain}/core/` is domain-internal; four importers were updated.

One assertion is worth calling out. Checking that a request landed on the dead-letter topic races the queue's message GC, which reclaims the row once the reconciler acks it. The suite discriminates on the `dead-lettered: ` reason prefix instead — deterministic, and also the only thing on the wire that tells a client which component answered.

## Test Plan

- ✅ `bazel test //test/e2e/runway:go_default_test` — 5 tests, ~13s. Verified with `-test.v` that every case and subtest actually runs.
- ✅ `bazel test //test/e2e/submitqueue:go_default_test` and `//test/e2e/stovepipe:go_default_test` — confirms the `fakemarker` move.
- ✅ `bazel test //runway/...` — including the new fake's unit tests.
- ✅ `make test`, `make fmt`, license headers, gazelle/tidy idempotency.

`//submitqueue/orchestrator/controller/{batch,cancel}:go_default_test` fail, but they fail identically on unmodified `main` and `bazel query` shows neither depends on anything this touches.
@behinddwalls
behinddwalls marked this pull request as ready for review August 7, 2026 00:40
@behinddwalls
behinddwalls requested review from a team and sbalabanov as code owners August 7, 2026 00:40
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