Files
antigravity-claude-proxy/tests/stress-test-streaming.cjs
Badri Narayanan S b72aa0e056 fix: handle thinking-only responses in stress test
Count responses with thinking content (but no text) as successful,
and validate actual response status instead of hardcoding 200.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 20:31:10 +05:30

142 lines
5.2 KiB
JavaScript

/**
* Stress Test (Streaming) - Send multiple concurrent streaming requests to test rate limit handling
*
* Usage: node tests/stress-test-streaming.cjs [count] [model]
* Example: node tests/stress-test-streaming.cjs 10 gemini-3-flash
*/
const BASE_URL = process.env.ANTHROPIC_BASE_URL || 'http://localhost:8080';
const count = parseInt(process.argv[2]) || 8;
const model = process.argv[3] || 'gemini-3-flash';
async function sendStreamingRequest(id) {
const startTime = Date.now();
try {
const response = await fetch(`${BASE_URL}/v1/messages`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'test',
'anthropic-version': '2023-06-01'
},
body: JSON.stringify({
model: model,
max_tokens: 100,
stream: true,
messages: [
{ role: 'user', content: `Request ${id}: Say "Hello ${id}" and nothing else.` }
]
})
});
const elapsed = Date.now() - startTime;
if (!response.ok) {
const errorText = await response.text();
console.log(`[${id}] ❌ ${response.status} after ${elapsed}ms: ${errorText.substring(0, 100)}`);
return { id, success: false, status: response.status, elapsed };
}
// Read the SSE stream
const reader = response.body.getReader();
const decoder = new TextDecoder();
let fullText = '';
let hasThinking = false;
let eventCount = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value, { stream: true });
const lines = chunk.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data === '[DONE]') continue;
try {
const event = JSON.parse(data);
eventCount++;
// Extract text or thinking from content_block_delta events
if (event.type === 'content_block_delta') {
if (event.delta?.text) {
fullText += event.delta.text;
} else if (event.delta?.thinking) {
hasThinking = true;
}
}
} catch (e) {
// Ignore parse errors for partial chunks
}
}
}
}
const totalElapsed = Date.now() - startTime;
const hasContent = fullText.length > 0 || hasThinking;
if (!hasContent) {
console.log(`[${id}] ⚠️ ${response.status} after ${totalElapsed}ms (${eventCount} events): No content received`);
return { id, success: false, status: response.status, elapsed: totalElapsed, eventCount };
}
const textPreview = fullText.substring(0, 50) || '(thinking only)';
console.log(`[${id}] ✅ ${response.status} after ${totalElapsed}ms (${eventCount} events): "${textPreview}..."`);
return { id, success: true, status: response.status, elapsed: totalElapsed, eventCount };
} catch (error) {
const elapsed = Date.now() - startTime;
console.log(`[${id}] ❌ Error after ${elapsed}ms: ${error.message}`);
return { id, success: false, error: error.message, elapsed };
}
}
async function runStressTest() {
console.log(`\n🚀 Stress Test (STREAMING): Sending ${count} concurrent requests to ${model}\n`);
console.log(`Target: ${BASE_URL}/v1/messages (stream=true)\n`);
console.log('─'.repeat(70));
const startTime = Date.now();
// Send all requests concurrently
const promises = [];
for (let i = 1; i <= count; i++) {
promises.push(sendStreamingRequest(i));
}
const results = await Promise.all(promises);
const totalElapsed = Date.now() - startTime;
console.log('─'.repeat(70));
// Summary
const successful = results.filter(r => r.success).length;
const failed = results.filter(r => !r.success).length;
const avgElapsed = Math.round(results.reduce((sum, r) => sum + r.elapsed, 0) / results.length);
const totalEvents = results.filter(r => r.success).reduce((sum, r) => sum + (r.eventCount || 0), 0);
console.log(`\n📊 Summary:`);
console.log(` Total time: ${totalElapsed}ms`);
console.log(` Successful: ${successful}/${count}`);
console.log(` Failed: ${failed}/${count}`);
console.log(` Avg response time: ${avgElapsed}ms`);
console.log(` Total SSE events: ${totalEvents}`);
if (failed > 0) {
const errors = results.filter(r => !r.success);
const statusCounts = {};
errors.forEach(e => {
const key = e.status || 'network';
statusCounts[key] = (statusCounts[key] || 0) + 1;
});
console.log(` Error breakdown: ${JSON.stringify(statusCounts)}`);
}
console.log('');
}
runStressTest().catch(console.error);