generation API contract fixtures: test clients without creating renders
A generation client can be tested against the public response contract without contacting a paid model. Build small synthetic fixtures from the serializer and route behavior, then exercise your parsing, persistence, and interface decisions offline. Fixtures are especially valuable for distinctions that an occasional successful render does not cover.
Separate fixture families
Create one family for submission responses, one for HTTP errors, and one for job snapshots. Do not reuse a single success object everywhere. A successful POST has job_id, a known submission-failure envelope can have error.job_id, and a GET snapshot has id.
export const fixtures = {
accepted: { status: 202, body: {
job_id: 'fixture-job', state: 'running', model: 'seedance-2.0-fast',
created_at: '2026-10-24T00:00:00.000Z', credits_held: 25,
} },
submissionFailed: { status: 502, body: { error: {
type: 'upstream_error', code: 'UPSTREAM_SUBMISSION_FAILED',
message: 'Upstream submission failed. Check the job for credit reconciliation.',
job_id: 'fixture-job',
} } },
pendingFields: { state: 'running', outputs: null, error: null,
progress: null, credits_actual: null },
};
The pendingFields object is a partial test building block, not a complete public snapshot. Add the snapshot identifier, model, modality, timestamps, and held credits when your validator expects the full response. Label partial fixtures clearly so they cannot accidentally redefine the API contract.
Cover meaningful state and accounting combinations
Include queued, running, succeeded, failed, and canceled. A failed submission marker with credits_actual: null represents unsettled accounting; a failed job with zero records settled cost. A successful HTTP read can describe either failed case. Allow null outputs until validated success rather than indexing the first result immediately.
Add fixtures for an unexpected media type, empty output array, missing optional upstream identifier, and a successful render followed by a local download error. The last case belongs to application delivery state and should not rewrite the remote job as failed.
Version the evidence behind fixtures
Store a fixture version and the source implementation date in test metadata. Use synthetic identifiers and harmless example URLs rather than copying real keys, prompts, or signed asset references. When a serializer changes, update the fixtures deliberately and review which client assumptions changed with them.
Do not generate fixtures by requesting a new paid render during every test run. Use a mocked HTTP boundary or local server, and fail tests if an unexpected external request escapes. Keep the production API base and credentials absent from the test environment wherever possible.
Acceptance checks
Assert that accepted and recoverable error IDs are both persisted correctly, malformed envelopes preserve HTTP status, and a pending snapshot cannot trigger publication. Verify a failed job returned with HTTP 200 reaches the failure handler. Exercise repeated terminal snapshots and confirm only one local delivery operation is scheduled.
Finally, test fixture completeness separately from business behavior. A parser test that always accepts your own partial object can hide missing required fields instead of revealing integration drift.
See error validation, fault injection, and failed job responses.
Continue with production operations
Where these facts come from
- Editorial keyword hypothesis; search volume not measured
- codebase: src/ai/jobs/http.ts
- codebase: src/ai/api/errors.ts
- codebase: src/ai/api/submission-errors.test.ts
- codebase: src/ai/jobs/http.test.ts