Skip to documentation

Guide · Journal and assertions

Available

Last verified

Know exactly which fixture answered

Every LLM request is written to a short-lived journal: the normalized request, the matched fixture, chaos and VCR decisions, the rendered answer, and timing. Tests assert against the journal instead of guessing from response text.

In this guide

  • What an entry records
  • Read the journal from any surface
  • Assert matches in tests
  • Retention and redaction

Prerequisites

  • An LLM DotMock workspace, or a local dotmock.yaml served by dotmock serve
  • The workspace ID in $API_ID and its runtime URL in $MOCK_URL (SDK base URL $MOCK_URL/v1 for OpenAI-compatible clients)
On this page

What an entry records

Each entry has the request's id, timestamp (ms), method, path, provider, endpoint, model, matched flag, session, seed, redacted headers, and body. The response part records status, fixtureId, fixtureName, chaosAction, vcrProxied or vcrReplayed, durationMs, stream, and the rendered content, reasoning, toolCalls, finishReason, usage, or error.

Journal entry
{
  "id": "req_01J…",
  "timestamp": 1758787200000,
  "method": "POST",
  "path": "/v1/chat/completions",
  "provider": "openai",
  "model": "gpt-4o-mini",
  "matched": true,
  "session": "test-42",
  "seed": 1234,
  "headers": { "authorization": "[REDACTED]", "content-type": "application/json" },
  "body": { "model": "gpt-4o-mini", "messages": [{ "role": "user", "content": "hello" }] },
  "response": {
    "status": 200,
    "fixtureId": "greeting",
    "fixtureName": "greeting",
    "durationMs": 3,
    "content": "Hello!",
    "finishReason": "stop"
  }
}

Read the journal from any surface

The workspace's Inspect view shows the same entries with the full request and response. For automation, filter by fixture, match status, provider, session, or time.

  • Dashboard API — GET /mock-apis/$API_ID/llm-fixtures/journal?limit=50&fixtureId=&fixtureName=&matched=&provider=&session=&since=, and DELETE to clear it
  • CLI — dotmock llm journal $API_ID [--session id] [--fixture name] [--limit 50] [--follow] [--local]
  • MCP — get_llm_journal with apiId, limit (1–500), fixtureId, fixtureName, matched, provider, session, since, and includePayloads
  • Local server — GET /__dotmock/journal?api=<subdomain>&limit=100&session=<id> (limit up to 1000)
Follow live traffic
dotmock llm journal $API_ID --session test-42 --follow
dotmock llm journal assistant --local --fixture greeting

Assert fixture matches in tests

Assertions on the journal prove behavior the response alone cannot: that a tool round trip made two calls, that a retry happened after a 429, or that no request fell through to the catch-all. Reset between tests so each test sees only its own entries.

Vitest / Jest
import { expectFixtureMatched, getJournal, resetDotmock } from "@dotmock/cli/testing";

beforeEach(() => resetDotmock());

it("retries after a rate limit", async () => {
  await runAgent("summarize the ticket");
  await expectFixtureMatched("rate-limited-once", { times: 1 });
  await expectFixtureMatched("summary", { times: 1 });
  const unmatched = (await getJournal()).filter((e) => !e.matched);
  expect(unmatched).toHaveLength(0);
});

pytest
def test_tool_round_trip(dotmock):
    run_agent("weather in Paris?")
    dotmock.assert_fixture_matched("weather-tool-call", times=2)
    assert all(e["matched"] for e in dotmock.journal())

Retention and redaction

Hosted journals keep the most recent 1,000 entries per API for one hour in Redis. The local server keeps them in memory; POST /__dotmock/reset clears counters and the journal for the API, while a reset scoped to one session clears only that session's counters.

Authorization, proxy-authorization, x-api-key, api-key, x-goog-api-key, cookie, x-internal-token, and x-amz-security-token headers are redacted. Request bodies over 64 KB keep a 4 KB preview and rendered content is capped at 4 KB.