# 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()}` };
    });