When you need to test streaming responses insomnia is a practical choice because it renders Server-Sent Events as they arrive and lets you script assertions against the aggregated payload. This guide walks through configuring an OpenAI-compatible chat completion request with stream: true, validating the SSE chunks inside Insomnia’s test runner, and running the same checks from the Inso CLI so they fit a CI pipeline.
Prerequisites
You need Insomnia (desktop or CLI via Inso) 2023.5 or later, a valid API key for an OpenAI-compatible gateway, and a workspace with at least one environment. No local server is required. If you haven’t already, create an environment named Default with base_url and api_key variables:
{
"base_url": "https://api.openai.com/v1",
"api_key": "sk-..."
}
Swap base_url to your own gateway. For example, n4n.ai exposes a single OpenAI-compatible endpoint that fronts 240+ models and applies automatic fallback when a provider is rate-limited or degraded, so you can keep one base URL and change only the model field.
Step 1: Configure the streaming request
Create a new POST request named “Stream Chat”. Set the URL to {{ base_url }}/chat/completions. Add headers:
{
"Authorization": "Bearer {{ api_key }}",
"Content-Type": "application/json"
}
Use this body to force token streaming:
{
"model": "gpt-4o-mini",
"messages": [
{"role": "user", "content": "Count to three slowly."}
],
"stream": true,
"temperature": 0
}
Save. Insomnia tags the response as “Event Stream” once it sees text/event-stream. If you test streaming responses insomnia against multiple models, replace the hard-coded model with {{ model }} and define that variable per environment.
Step 2: Send and watch the stream
Click Send. The response pane opens a live view. Each data: frame appends as it arrives. A correct stream looks like:
data: {"choices":[{"delta":{"role":"assistant"}}]}
data: {"choices":[{"delta":{"content":"One"}}]}
data: {"choices":[{"delta":{"content":" Two"}}]}
data: [DONE]
Check the Headers subtab: Content-Type must be text/event-stream, and Transfer-Encoding should be chunked. If you receive a single JSON object with choices[0].message.content, the server ignored stream. To test streaming responses insomnia reliably, confirm manually that chunks appear incrementally and the [DONE] frame closes the connection.
Step 3: Write a basic test suite
Insomnia’s Tests tab runs Chai assertions after the response finishes. The global response object holds the raw stream text. Start with smoke checks:
const raw = response.body.toString();
expect(raw, 'must be event-stream').to.include('data:');
expect(raw, 'must terminate').to.include('[DONE]');
expect(response.status, 'status 200').to.equal(200);
Run the request. The sidebar shows pass/fail. A green check means the stream opened, emitted events, and closed properly.
Step 4: Parse SSE frames and assert on deltas
The raw body concatenates frames with blank lines. Split on \n\n or \r\n\r\n, strip the data: prefix, and skip [DONE]:
const raw = response.body.toString();
const frames = raw
.split(/\r?\n\r?\n/)
.map(s => s.trim())
.filter(s => s.startsWith('data:'))
.map(s => s.replace(/^data:\s*/, ''))
.filter(s => s !== '[DONE]');
expect(frames.length, 'at least one delta').to.be.greaterThan(0);
const parsed = frames.map(f => JSON.parse(f));
const text = parsed
.map(j => j.choices?.[0]?.delta?.content || '')
.join('');
expect(text, 'reply mentions numbers').to.match(/One|Two|Three/i);
This catches providers that silently batch the whole reply into one frame (still technically SSE but defeats the purpose). When you test streaming responses insomnia with this parser, you enforce true incremental delivery.
Step 5: Verify usage metering and routing hints
Add stream_options to request body to get token counts in the final frame:
{
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": "Hi"}],
"stream": true,
"stream_options": {"include_usage": true}
}
Assert the usage object exists:
const last = parsed[parsed.length - 1];
expect(last, 'last frame has usage').to.have.property('usage');
expect(last.usage.completion_tokens).to.be.a('number');
Gateways may also echo cache directives. n4n.ai honors client routing directives and forwards provider cache-control hints, so checking x-cache is meaningful there. In Insomnia tests:
const cache = response.headers['x-cache'];
if (cache) {
expect(cache, 'cache hint valid').to.match(/HIT|MISS|SKIP/);
}
Step 6: Automate with Inso CLI
GUI runs don’t guard main. Install Inso and run the suite headlessly:
npm install -g inso
inso run test "Stream Chat" --env "Default"
Inso executes the same JavaScript against the streamed body. Drop it into CI:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npx inso run test "Stream Chat" --env "CI"
A non-streaming mock or a broken proxy fails the [DONE] assertion, blocking merge.
Step 7: Handle fallback and partial streams
Networks drop. Write a test that fails on incomplete streams:
const done = frames.some(f => f === '[DONE]');
expect(done, 'stream closed cleanly').to.equal(true);
If your gateway does automatic fallback mid-stream, the chunk sequence may resume from a backup provider. Your parser already tolerates arbitrary frame counts. Keep the system_fingerprint stable across frames when present:
const fps = parsed.map(j => j.system_fingerprint).filter(Boolean);
if (fps.length) {
expect(new Set(fps).size, 'single fingerprint').to.equal(1);
}
To test streaming responses insomnia across models, loop environments:
for m in gpt-4o-mini claude-3-haiku; do
inso run test "Stream Chat" --env "$m"
done
Step 8: Debug timing and ordering
Insomnia’s Timeline tab stamps each chunk. If you suspect reordering, log delta indices:
parsed.forEach((j, i) => {
if (j.choices?.[0]?.delta?.content) {
console.log(i, j.choices[0].delta.content);
}
});
Run from Inso with --verbose to see console output. This surfaces proxies that buffer SSE.
Troubleshooting
- No incremental render: Ensure
stream: trueis at top level, not insidemessages. - Tests can’t see
response.body: Insomnia provides it only after completion; streamed bodies are fully buffered for scripts. - CORS errors: Insomnia bypasses browser CORS; if you see them, it’s a proxy issue, not the client.
Verify success
Your verification loop is complete when:
- The Insomnia Event Stream pane fills chunk by chunk, ending with
data: [DONE]. - The Tests tab is green for frame count, content regex, usage, and termination.
inso run testexits zero in terminal and CI.- Simulating a kill (e.g.,
tcpkill) makes the incomplete-stream test fail.
Following these steps lets you test streaming responses insomnia without writing a custom WebSocket or fetch client for every regression. The SSE contract is simple; the valuable checks are delta granularity, clean termination, and stable metadata across fallback.