Error Injection
Test your application's error handling with one-shot errors, stream truncation, and timed disconnects. llmock provides three mechanisms for simulating failures.
One-Shot Errors
Queue an error that fires on the next request and auto-removes itself. Useful for testing retry logic.
const mock = new LLMock();
await mock.start();
mock.onMessage("hello", { content: "Hi!" });
// Queue a 429 rate limit error for the next request
mock.nextRequestError(429, {
message: "Rate limit exceeded",
type: "rate_limit_error",
});
// First request → 429 error
const res1 = await fetch(`${mock.url}/v1/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "gpt-4",
messages: [{ role: "user", content: "hello" }],
}),
});
expect(res1.status).toBe(429);
// Second request → normal response (error auto-removed)
const res2 = await fetch(`${mock.url}/v1/chat/completions`, { /* same */ });
expect(res2.status).toBe(200);
Stream Truncation
Abort a streaming response after a specific number of SSE chunks. Tests that your application handles partial streams gracefully.
mock.on(
{ userMessage: "long story" },
{ content: "This is a very long response that will be cut short" },
{ truncateAfterChunks: 3 } // Abort after 3 SSE chunks
);
Timed Disconnect
Disconnect after a specified number of milliseconds. Simulates network timeouts and connection drops.
mock.on(
{ userMessage: "slow" },
{ content: "This response will never complete" },
{ disconnectAfterMs: 100 } // Kill connection after 100ms
);
Error Fixtures in JSON
{
"fixtures": [
{
"match": { "userMessage": "error-test" },
"response": {
"error": {
"message": "Rate limited",
"type": "rate_limit_error"
},
"status": 429
}
},
{
"match": { "userMessage": "partial" },
"response": { "content": "This gets cut off" },
"truncateAfterChunks": 2
},
{
"match": { "userMessage": "timeout" },
"response": { "content": "Never finishes" },
"disconnectAfterMs": 50
}
]
}
Interruption Behavior
-
truncateAfterChunks— counts SSE data lines sent; aborts on the Nth chunk -
disconnectAfterMs— starts a timer when the response begins; kills the connection when it fires - If both are set, whichever fires first wins
-
Interrupted requests are recorded in the journal with
response.interrupted: trueandresponse.interruptReason
nextRequestError() is one-shot: it fires once and auto-removes itself. For
persistent error fixtures, use addFixture() with an error response.