Skip to article

Test without API keys

Exercise your application's failure paths with deterministic mock outcomes.

On this page

Assert fallback behavior

fallback.test.ts · Node
import assert from 'node:assert/strict';
import { createSearch } from '@belweave/search-sdk';
import { createMockProvider } from '@belweave/search-sdk/testing';

const primary = createMockProvider({
  id: 'primary',
  outcomes: [{ type: 'error', code: 'rate_limit', retryAfterMs: 2_000 }],
});
const backup = createMockProvider({ id: 'backup' });
const client = createSearch({ provider: primary, fallback: [backup] });
const response = await client.web.search({ query: 'search interfaces' });

assert.equal(response.provider, 'backup');
assert.equal(response.attempts.length, 2);
assert.equal(response.warnings[0]?.code, 'fallback_used');
assert.equal(primary.calls.length, 1);
assert.equal(backup.calls.length, 1);

Each mock has an independent outcome queue and call history. Outcomes are reserved in invocation order, even if concurrent calls complete in a different order. An exhausted explicit queue returns configuration.

Understand fixture behavior

  • Without an outcome queue, the default success fixture repeats.
  • A success outcome accepts results, usage, and delayMs. Omit provenance; the mock adds it.
  • An error outcome accepts code, optional status, retryAfterMs, and delayMs.
  • The mock implements hostname filtering and output limits. Timestamps reflect the actual test clock.

Keep evidence levels separate

CheckWhat it can establish
Application mockYour consumer's state and error handling.
Injected HTTP fixtureExpected request mapping and response validation.
Authenticated live requestBehavior of a particular provider request at a particular time.
Quality evaluationUsefulness against a dated query set and explicit rubric.

Passing fixture tests does not establish live key validity, production availability, billing correctness, or a provider quality ranking.