Example Fixtures

Ready-to-use fixture examples for every mock type. Copy any example to get started quickly, or use fixtures/examples/full-suite.json as a complete config template.

LLM Fixtures

Embeddings

Matching on inputText and returning a vector.

fixtures/examples/llm/embeddings.json json
{
  "fixtures": [
    {
      "match": { "inputText": "hello world" },
      "response": {
        "embedding": [0.0023064255, -0.009327292, 0.015797347]
      }
    }
  ]
}

Streaming Physics

Realistic streaming timing with ttft, tps, and jitter.

fixtures/examples/llm/streaming-physics.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "explain gravity" },
      "response": {
        "content": "Gravity is a fundamental force of nature that attracts objects with mass toward one another. It keeps planets in orbit around the sun and holds galaxies together."
      },
      "streamingProfile": {
        "ttft": 200,
        "tps": 40,
        "jitter": 0.1
      }
    }
  ]
}

Error Injection

Rate limit error response for any message.

fixtures/examples/llm/error-injection.json json
{
  "fixtures": [
    {
      "match": { "userMessage": ".*" },
      "response": {
        "error": {
          "message": "Rate limit exceeded. Please retry after 30 seconds.",
          "type": "rate_limit_error",
          "code": "rate_limit_exceeded"
        },
        "status": 429
      }
    }
  ]
}

Sequential Responses

Stateful multi-turn responses using sequenceIndex.

fixtures/examples/llm/sequential-responses.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "tell me a joke", "sequenceIndex": 0 },
      "response": {
        "content": "Why did the programmer quit his job? Because he didn't get arrays!"
      }
    },
    {
      "match": { "userMessage": "tell me a joke", "sequenceIndex": 1 },
      "response": { "content": "Why do Java developers wear glasses? Because they can't C#!" }
    },
    {
      "match": { "userMessage": "tell me a joke", "sequenceIndex": 2 },
      "response": {
        "content": "A SQL query walks into a bar, sees two tables, and asks: 'Can I join you?'"
      }
    }
  ]
}

Protocol Configs

MCP

Tools and resources for the Model Context Protocol mock.

fixtures/examples/mcp/mcp-config.json json
{
  "mcp": {
    "tools": [
      {
        "name": "search",
        "description": "Search the web",
        "inputSchema": {
          "type": "object",
          "properties": {
            "query": { "type": "string" }
          },
          "required": ["query"]
        },
        "result": "No results found for the given query."
      }
    ],
    "resources": [
      {
        "uri": "file:///readme",
        "name": "README",
        "mimeType": "text/plain",
        "text": "# My Project\n\nThis is the project README."
      }
    ]
  }
}

A2A

Agent registration and streaming tasks for the Agent-to-Agent protocol mock.

fixtures/examples/a2a/a2a-config.json json
{
  "a2a": {
    "agents": [
      {
        "name": "research-agent",
        "description": "An agent that researches topics and returns summaries",
        "version": "1.0.0",
        "skills": [
          {
            "id": "web-research",
            "name": "Web Research",
            "description": "Search the web and summarize findings",
            "tags": ["research", "search"]
          }
        ],
        "capabilities": { "streaming": true },
        "messages": [
          {
            "pattern": "research",
            "parts": [{ "text": "Here is a summary of my research findings on the topic." }]
          }
        ],
        "streamingTasks": [
          {
            "pattern": "deep-research",
            "events": [
              { "type": "status", "state": "TASK_STATE_WORKING" },
              {
                "type": "artifact",
                "name": "research-report",
                "parts": [{ "text": "## Research Report\n\nFindings from deep research..." }],
                "lastChunk": true
              },
              { "type": "status", "state": "TASK_STATE_COMPLETED" }
            ],
            "delayMs": 100
          }
        ]
      }
    ]
  }
}

AG-UI

Text response and tool call event stream for the AG-UI protocol mock.

fixtures/examples/agui/agui-text-response.json json
{
  "agui": {
    "fixtures": [
      {
        "match": { "message": "hello" },
        "text": "Hi! How can I help you today?"
      },
      {
        "match": { "message": "search" },
        "events": [
          { "type": "RUN_STARTED", "threadId": "t1", "runId": "r1" },
          { "type": "TOOL_CALL_START", "toolCallId": "tc1", "toolCallName": "web_search" },
          {
            "type": "TOOL_CALL_ARGS",
            "toolCallId": "tc1",
            "delta": "{\"query\": \"latest news\"}"
          },
          { "type": "TOOL_CALL_END", "toolCallId": "tc1" },
          {
            "type": "TEXT_MESSAGE_START",
            "messageId": "m1",
            "role": "assistant"
          },
          {
            "type": "TEXT_MESSAGE_CONTENT",
            "messageId": "m1",
            "delta": "Here are the latest results..."
          },
          { "type": "TEXT_MESSAGE_END", "messageId": "m1" },
          { "type": "RUN_FINISHED", "threadId": "t1", "runId": "r1" }
        ]
      }
    ]
  }
}

Vector

Collection with vectors and query results for the vector database mock.

fixtures/examples/vector/vector-config.json json
{
  "vector": {
    "collections": [
      {
        "name": "documents",
        "dimension": 3,
        "vectors": [
          {
            "id": "doc-1",
            "values": [0.1, 0.2, 0.3],
            "metadata": { "title": "Getting Started", "category": "tutorial" }
          },
          {
            "id": "doc-2",
            "values": [0.4, 0.5, 0.6],
            "metadata": { "title": "API Reference", "category": "reference" }
          }
        ],
        "queryResults": [
          {
            "id": "doc-1",
            "score": 0.95,
            "metadata": { "title": "Getting Started", "category": "tutorial" }
          },
          {
            "id": "doc-2",
            "score": 0.82,
            "metadata": { "title": "API Reference", "category": "reference" }
          }
        ]
      }
    ]
  }
}

Testing & Operations

Chaos Testing

Configure drop, malformed, and disconnect rates for chaos testing.

fixtures/examples/chaos/chaos-config.json json
{
  "llm": {
    "fixtures": "fixtures/example-greeting.json",
    "chaos": {
      "dropRate": 0.1,
      "malformedRate": 0.05,
      "disconnectRate": 0.02
    }
  }
}

Record & Replay

Provider URLs for proxy mode to record live API traffic.

fixtures/examples/record-replay/record-config.json json
{
  "llm": {
    "record": {
      "providers": {
        "openai": "https://api.openai.com/v1",
        "anthropic": "https://api.anthropic.com"
      },
      "fixturePath": "./recorded-fixtures"
    }
  }
}

Snapshot Recording (per-test fixtures)

Send X-Test-Id from your test runner to organize recorded fixtures into per-test directories. Here is a Playwright example:

playwright/setup.ts ts
import { test } from "@playwright/test";

test.beforeEach(async ({ page }, testInfo) => {
  // Route all LLM traffic through aimock with a test ID header
  await page.setExtraHTTPHeaders({
    "X-Test-Id": testInfo.title,
  });
});

The resulting fixture directory layout groups each test’s recordings by provider:

Recorded fixture tree text
fixtures/recorded/
  should-greet-the-user/
    openai.json
    anthropic.json
  should-handle-tool-calls/
    openai.json

See Snapshot-Style Recording for the full workflow, including replay and drift detection.

Full Suite

Complete Config

A complete configuration running all mocks on one port.

fixtures/examples/full-suite.json json
{
  "port": 4000,
  "host": "127.0.0.1",
  "metrics": true,
  "strict": true,

  "llm": {
    "fixtures": "fixtures/example-greeting.json",
    "chaos": {
      "dropRate": 0.01,
      "malformedRate": 0.005,
      "disconnectRate": 0.002
    }
  },

  "mcp": {
    "serverInfo": { "name": "full-suite-mcp", "version": "1.0.0" },
    "tools": [
      {
        "name": "search",
        "description": "Search the knowledge base",
        "inputSchema": {
          "type": "object",
          "properties": {
            "query": { "type": "string" },
            "limit": { "type": "number" }
          },
          "required": ["query"]
        },
        "result": "Found 3 results for your query."
      }
    ],
    "resources": [
      {
        "uri": "file:///config",
        "name": "Configuration",
        "mimeType": "application/json",
        "text": "{\"version\": \"1.0\", \"environment\": \"test\"}"
      }
    ],
    "prompts": [
      {
        "name": "summarize",
        "description": "Summarize a document",
        "arguments": [{ "name": "text", "description": "The text to summarize", "required": true }],
        "result": {
          "messages": [
            {
              "role": "assistant",
              "content": { "type": "text", "text": "Here is a summary of the provided text." }
            }
          ]
        }
      }
    ]
  },

  "a2a": {
    "agents": [
      {
        "name": "assistant",
        "description": "A general-purpose assistant agent",
        "version": "1.0.0",
        "skills": [{ "id": "qa", "name": "Q&A", "description": "Answer questions" }],
        "capabilities": { "streaming": true },
        "messages": [
          {
            "pattern": ".*",
            "parts": [{ "text": "I can help you with that." }]
          }
        ]
      }
    ]
  },

  "agui": {
    "fixtures": [
      {
        "match": { "message": "hello" },
        "text": "Hello from the full-suite mock!"
      },
      {
        "match": { "toolName": "get_data" },
        "events": [
          { "type": "RUN_STARTED", "threadId": "t1", "runId": "r1" },
          { "type": "TOOL_CALL_START", "toolCallId": "tc1", "toolCallName": "get_data" },
          { "type": "TOOL_CALL_ARGS", "toolCallId": "tc1", "delta": "{}" },
          { "type": "TOOL_CALL_END", "toolCallId": "tc1" },
          { "type": "RUN_FINISHED", "threadId": "t1", "runId": "r1" }
        ]
      }
    ]
  },

  "vector": {
    "collections": [
      {
        "name": "knowledge-base",
        "dimension": 384,
        "queryResults": [
          {
            "id": "kb-001",
            "score": 0.97,
            "metadata": { "source": "docs", "title": "Quick Start Guide" }
          }
        ]
      }
    ]
  },

  "services": {
    "search": true,
    "rerank": true,
    "moderate": true
  }
}

Multi-Turn Conversations

Choosing a multi-turn matcher

aimock provides four match fields for multi-turn fixtures, each suited to a different scenario. turnIndex matches by conversation depth (how many assistant replies precede this request) and is stateless, so it works with concurrent clients. hasToolResult is a boolean that distinguishes the initial request from the follow-up after a tool executes — ideal for simple two-step tool rounds. toolCallId matches a specific tool call by its ID, useful when the agent calls multiple tools and you need to respond to exactly one. sequenceIndex is a stateful counter (“first request gets A, second gets B”) but is not safe for concurrent requests to the same fixture. See the Multi-Turn Conversations reference for the full details.

Multi-turn conversation with turnIndex

Use turnIndex to give different responses at each conversational turn. Combine with hasToolResult to handle the post-tool-execution follow-up.

fixtures/examples/llm/multi-turn-turnindex.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "plan a trip", "turnIndex": 0 },
      "response": { "content": "I'd love to help plan your trip! Where would you like to go?" }
    },
    {
      "match": { "userMessage": "plan a trip", "turnIndex": 1 },
      "response": {
        "content": "Great choice! Let me search for flights.",
        "toolCalls": [{ "name": "search_flights", "arguments": "{\"destination\": \"Tokyo\"}" }]
      }
    },
    {
      "match": { "userMessage": "plan a trip", "turnIndex": 2, "hasToolResult": true },
      "response": { "content": "I found several flights to Tokyo. The best option is..." }
    }
  ]
}

Tool-call cycle with hasToolResult

For a simple tool round-trip, hasToolResult is the most concise matcher: false on the initial request, true after the tool result comes back.

fixtures/examples/llm/tool-cycle-hastoolresult.json json
{
  "fixtures": [
    {
      "match": { "userMessage": "what's the weather?", "hasToolResult": false },
      "response": {
        "content": null,
        "toolCalls": [{ "name": "get_weather", "arguments": "{\"city\": \"San Francisco\"}" }]
      }
    },
    {
      "match": { "userMessage": "what's the weather?", "hasToolResult": true },
      "response": { "content": "It's 72°F and sunny in San Francisco!" }
    }
  ]
}

Dynamic / Async Responses

Fixture responses can be functions — sync or async — that receive the request and return the response dynamically. Use this when you need to await side effects, compute responses based on request content, or inject runtime data into fixtures.

Async response with side-effect

Wait for an external operation to complete before constructing the fixture response. Eliminates race conditions in multi-turn E2E tests where entity creation happens out-of-band.

async-side-effect.ts ts
mock.on(
  { toolCallId: "call_create_entity" },
  async (req) => {
    const entity = await createEntityPromise;
    return {
      content: `Entity "${entity.name}" created!`,
      toolCalls: [{
        name: "next_step",
        arguments: JSON.stringify({ entityId: entity.id }),
      }],
    };
  },
);

Request-aware response

Compute the response from the incoming request content. Useful for echo-style fixtures, transformations, or conditional logic that goes beyond what match fields can express.

request-aware.ts ts
mock.onMessage("translate", (req) => {
  const text = req.messages.at(-1)?.content ?? "";
  return { content: `Translated: ${text.toUpperCase()}` };
});