Skip to main content

Gateway response fixtures

This page tells a contributor how a Gateway response change reaches the CLI tests. A fixture is one recorded Gateway response under packages/php-sdk/fixtures/<family>/<command>/<case>.json. The Gateway test suite records it and the API reference validates it. The CLI test suite replays it through a Saloon mock and compares the complete command output with a stored expectation. Task group fixtures include nullable vm_power, separately from task status. CLI tests cover the VM power label for known states and omit the label and JSON field when power is null. The SDK accepts a missing power field and ignores unknown optional response fields, so a field rename does not make an older client reject the task group. Tool scan and adoption fixtures distinguish formula, cask, and Vite+ global identities, registered and informational packages, dependencies, unsupported artifacts, per-manager scan states, inspection time, and failed scans. Scan fixtures also record authorization and unmanaged-node refusals. Adoption fixtures record a created Tool, the same intent, a repaired failure, constraint conflicts and violations, a busy scope, a missing or unsupported package, and authorization failure. The CLI replays those responses for tool:adopt. Doctor fixtures include informational-only healthy reports and mixed reports with drift or unverifiable findings. Node fixtures include an enrolled Mac with empty roles. Web fixtures preserve registered Tools while a live inventory request fails. Node key fixtures under nodes/node-wireguard-key record an updated Node, a Gateway-role refusal, and a sanitized hub projection failure. The node:wireguard-key CLI tests replay each response in human and JSON modes. Keep the projection failure’s error code and step, but never record remote process output or private key material. WireGuard key recovery owns the stored-key and identical-replay behavior; subprocess feature tests cover interrupted publication separately from response fixtures. Task fixtures include a GitHub feedback fixup with fixup_problem: review:{reviewer_id} and an immutable findings packet in brief. Record that response from the Gateway fixture test with example identities and findings. Its review-findings deliverable and optional project-check use the existing response schema. Replaying the fixture must preserve source provenance without interpreting review text as an action. The Tasks contract owns the meaning of those fields. Task response fixtures include a topology array on each subtask. Default cases record []; the declared case records app-dev. Re-record all responses that contain subtasks when this field changes, regenerate OpenAPI and the web API types, and update the matching CLI expectations. Human subtask details show Topology only for a nonempty declaration; JSON keeps the array in both cases. Compute drivers defines the accepted workload roles and readiness gate. Process response fixtures include user, which is null when the Process uses its derived account. When an explicit account is selected, the response includes that name both in user and in runtime_config.user; see Node accounts. Instance create, list, and show fixtures include routes, every Route of the Instance, and each Route includes web_root. Instance create, list, and show fixtures include annotator_port and annotator_url. Both are null without an assigned annotator port; an Instance with a port but no Route still has a null URL. Re-record these fixtures when either field changes, regenerate the OpenAPI schema, and replay the Instance CLI contracts. Human detail output shows the annotator properties only when a port is assigned, while JSON retains the nullable fields. The web API types must also be regenerated from the same OpenAPI schema. Gateway fixtures record the desired fleet state with an available, a fallback, and a pending CLI release, and gateway:status as an active peer sees it. The Gateway test serves the release-shaped GitHub responses under apps/gateway/tests/Fixtures/GitHub/CliRelease. The self-update contract downloads the stand-in binaries under apps/cli/tests/Fixtures/SelfUpdate, whose checksums those fixtures name. An Agent pin change updates the Gateway and Fleet fixtures with the released version, asset URLs, and verified checksums. Update the SDK checksum expectations, CLI download stand-ins, and matching CLI output together. The Agent release checksums remain separate from the local stand-in checksums used by self-update tests. Gateway release fixtures under gateway-releases record one release record at each point the CLI can see it: queued, running, live, switched back, paused, and rolled back. gateway:release:deploy and gateway:release:rollback replay the queued response and then a sequence of record reads, so their expected output shows how the progress tree follows a release. The automatic release fixtures record the state disabled, enabled, paused, and resumed. Project Document fixtures record a complete file lifecycle, reads of earlier versions, revision conflicts, and redacted storage status. The SDK and CLI replay the same envelopes, preserving IDs, revisions, checksums, cursors, and request IDs. Human show, list, and version-history expectations live under apps/cli/tests/Expected/project-documents; raw read and download tests check exact bytes separately. Gateway rollback fixtures record a queued code switch with force: false and no force member in the rollback phase. The request is bodyless. The crossing-refusal fixture is removed because Laravel owns migration discovery; Gateway recovery explains the decision.

Record a fixture

A Gateway fixture test sends a request with deterministic data and calls record_fixture() from apps/gateway/tests/Support/ResponseFixtures.php with the fixture name, the SDK request class that sends the route, and the route.
A normal test run asserts that the response still equals the recorded file. A run with ORBIT_FIXTURES=record rewrites the file. Use the request id fixture_request_id() so recorded and expected output stay stable.
Instance fixtures include nullable seed_path and seed_commit fields. These name the default’s checkout and its last deployed commit, or the seed selected for a new workspace. Re-record create, list, and show responses together, then update the CLI’s JSON expectations; human tables keep their existing columns. The recorded file holds the request class, the route, the status, and the body. It holds no secrets, because fixture tests use example values. Project fixtures include source defaults and task settings such as task_check; update the corresponding CLI expectations when those response fields change. Task fixtures share one id sequence with their subtasks. A subtask id is greater than its task id, and creating another task does not reuse a subtask id. The tasks/tasks-show/watched fixture records a running task whose branch pull request was opened outside Orbit. It has watched_pr_url, watched_pr_number, and watched_pr_state, while pr_url remains null. The default task fixtures keep the watched fields null. The SDK and CLI replay both cases so a watched pull request cannot become the reviewed pull request by accident. When these fields change, re-record every response that includes a task and update its CLI JSON expectation. The branch watch owns their meaning. The tasks/tasks-question-close fixtures record a question closed as superseded and the tasks.question_closed refusal of a second close with another status. The SDK and the CLI replay both.

Validate a fixture

bin/api-fixtures --check validates every fixture body against the response schema for its route and status in docs/openapi.json. The apps/docs checks run it, so a fixture whose body differs from the API reference fails continuous integration. Regenerate the reference with composer docs-openapi when a response shape changes on purpose. The inputs that command reads are on API reference generation.

Replay a fixture in the CLI

A CLI contract test loads a fixture by name with gateway_fixture_mock() from apps/cli/tests/Helpers/GatewayFixtures.php, runs the command, and compares the output with apps/cli/tests/Expected/<family>/<command>/<case>.human.txt or .json through expect_output().
A normal run asserts the exact output. A run with ORBIT_EXPECTED=update rewrites the expected files after a reviewed change.

Watch a fixture in a terminal

A development checkout of the CLI can replay recorded responses under a real command, so you can watch the rendering that the contract tests hold. Set ORBIT_DESIGN=1, name the fixtures in ORBIT_GATEWAY_FIXTURES, and point ORBIT_HOME at an empty directory. The replay seeds a Gateway profile there. The CLI binary does not include the replay. apps/cli/design/README.md also covers the design sketches that ORBIT_DESIGN=1 loads.

Find the commands a change reaches

bin/cli-contract runs only the CLI tests that replay the named fixtures. With --changed, it takes the fixtures that differ from origin/main. With --preview, it renders every contract case into a temporary directory and lists the commands whose output differs from tests/Expected, with a diff per file. That list is the map of the commands a rendering change reaches. With --coverage, it lists the product commands that have no expected output yet.
The change cycle is: change the Gateway, re-record the fixtures, review the fixture diff, run bin/cli-contract --changed, fix or accept each command’s output, and update the expected files.

Web app fixtures

The web app’s demo mode and its tests run against the files under apps/web/fixtures/fleet. These files use the same format without a request class. They are written by hand as one coherent fleet, because a recorded fixture covers one route and the web app needs records that refer to each other. bin/api-fixtures --check validates them together with the recorded fixtures. The demo fleet has one tool inventory for each active Node. Node 1 is an empty Linux read: brew completed with no packages, brew-cask unsupported, and vp absent. Node 2 is a partial Linux read: brew incomplete, brew-cask unsupported, and vp conflicting, so none of those empty arrays is an inventory. Node 3 is unreachable and has no inventory fixture; the demo refuses a scan with tool.node_inactive. Node 4 is the macOS read: registered packages with a newer observed version, the openssl@3 dependency, unsupported casks, a formula and a cask both named visual-studio-code, and ghost, which the demo lists as supported and then refuses on adopt. The demo adopt, update, and remove handlers change only that in-memory fleet.

Families with fixtures

The directories under packages/php-sdk/fixtures list the families that have recorded fixtures and contract tests. Add a family by recording from its Gateway tests with record_fixture() and writing its CLI contract test in the same change. Extension responses are replayed by the extension:list, extension:enable, and extension:disable command contracts; do not leave a changed extension fixture without a CLI test that names it. bin/cli-contract --coverage shows the commands that still have no expected output.