# contracts/fixtures — golden request/response pairs Every method has at least one success fixture. A method with no fixture is not done. These files are replayed by `tests/conformance/` against **both** the generated C++ and a live server, which is what lets four lanes build in parallel and still be compatible: green fixtures mean the C++ daemon and the TypeScript extension agree, without either having ever run against the other. `tools/mockd` also answers from them, so the GUI and extension are developed against the same bytes conformance asserts. ## Layout ``` fixtures/ ├── *.json one success fixture per method ├── errors/ error cases: auth, transport, not-found, bad path, timeout └── events/ one fixture per server-to-client notification ``` ## Shape ```jsonc { "name": "download.add — add an ISO for later, into the Programs category", "description": "Why this case is worth pinning.", "transport": "uds", // optional: replay only on this transport "requires": "...", // optional: a condition a plain server cannot produce "kind": "timeout", // optional: the correct behaviour is *no reply* "request": { "jsonrpc": "2.0", "id": 11, "method": "download.add", "params": { } }, "response": { "jsonrpc": "2.0", "id": 11, "result": { } }, "assertions": [ "things a runner or a reviewer should check" ] } ``` An event fixture carries `notification` instead of `request`/`response`. `assertions` are prose, for the human writing the implementation. The runners check the machine-checkable parts: schema validity, error codes, shape, and the timeout. ## Placeholders Some values cannot be pinned in a golden file. These stand in for them, and the runners treat them as "any value of the right shape": | Placeholder | Means | |---|---| | `$uuid` | any UUID | | `$isoDate` | any RFC 3339 date-time | | `$opaque` | a credential-shaped string (a token) | | `$any` | any value | | `$taskId`, `$taskId2` | a task the runner creates during setup, and binds before replaying | `$taskId` exists so a fixture never depends on a task id that only happens to exist in a seeded mock. The same fixture then runs against an empty `veloxd` and a populated `mockd`. ## Values are matched by shape, not by equality A live daemon returns its own task ids and its own clock. Demanding byte-identical results would only teach the suite to lie, so the runners assert: * the payload passes the **generated validator** — this is the real cross-language check; * the **key structure** matches the golden file, with no extra and no missing fields; * **error codes** match exactly. A `null` where the golden shows a value is accepted: the validator has already ruled on whether null is legal there, and a golden file shows one plausible value, not the only one. ## `requires`: fixtures a mock cannot produce Most error fixtures are *intrinsic* — a path outside the allowed roots, an out-of-range parameter, an unknown task id — and any correct server produces them from the request alone. Those are replayed everywhere. Four are environmental: a 403 from an origin server, a full disk, a pairing lockout, a wedged daemon. They carry `requires`, are skipped by default, and are exercised where the condition can actually be arranged — `run.sh` starts a deliberately slow `mockd` to prove `capture.offer` fails open, and lane PKG/QA's `tools/testserver` covers the hostile-server cases in `tests/integration/`. `errors/capture.offer.timeout.json` is the most important file in this directory. Its correct response is *no response*: past 750 ms the extension must abandon the offer and let Firefox download normally. A download manager that eats downloads when its daemon is down is worse than no download manager. ## No fixture may pair a real external URL with `startMode: "now"` This suite replays every fixture against a real, live `veloxd` (`tests/conformance/run.sh`), not just `mockd`. `mockd` never actually fetches anything, so it hid this for a while: a fixture with `startMode: "now"` (or `"queue"` into a running queue — anything that gets admitted to the scheduler right away) and a real, resolvable URL makes a **real** daemon actually start downloading it, for real, onto whatever machine runs the suite. This happened — twice, with `download.add.json` pointed at a ~6 GB Ubuntu ISO, straight into the developer's real `~/Downloads`. The fix in each case is one of: - `startMode: "later"` — exercises the add path (validation, category assignment, the event) without ever handing the task to the engine; - a URL under `example.org`/`example.com` (IANA-reserved for exactly this, RFC 2606) — resolvable enough to validate as a URL, never a real download source; - `requires`, if the fixture's entire point needs a real transfer to fail in a specific way (see `errors/download.add.disk-full.json`) — skipped by default, so it only ever runs where the condition has actually been arranged. A real `saveDir` gets the same treatment for the same reason: an absolute path like `/home/sami/Downloads/...` only means anything on the machine that fixture was written on. Omit `saveDir` and let `saveTo.defaultDir` apply, or use a relative-feeling path under a root the runner controls.