Files
FusionAGI/tests/load/k6_prompt.js
Devin AI 96c32aed21
Some checks failed
CI / lint (pull_request) Failing after 42s
CI / test (3.10) (pull_request) Failing after 37s
CI / test (3.11) (pull_request) Failing after 36s
CI / test (3.12) (pull_request) Successful in 1m10s
CI / docker (pull_request) Has been skipped
Wire all integrations + production hardening: 15 recommendations
Integration & Wiring:
- useStore/useAppState wired into App.tsx (replaces 8 useState calls)
- React Router wired at app root (URL-based navigation)
- SparklineChart/MetricCard/BarChart integrated into Admin + Ethics pages
- useNotifications.handleWSEvent wired into WebSocket handler
- Notification center dropdown in header with unread badge
- Locale selector added to Settings page (6 languages)
- Dashboard data fetching with 10s polling into MetricCards
- File drag-and-drop support on chat area

Production Hardening:
- PostgresStateBackend with connection pooling (psycopg2)
- App lifespan wires backend from FUSIONAGI_DB_BACKEND env (memory|sqlite|postgres)
- Redis cache wired from FUSIONAGI_REDIS_URL env at startup
- Multi-process uvicorn config for horizontal scaling

Testing:
- Playwright visual regression tests (12 stories x 2 viewports)
- k6 load test script with ramp/spike/ramp-down stages
- 7 new Python tests (postgres fallback, app wiring)

575 Python tests + 45 frontend tests = 620 total, 0 ruff errors.

Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
2026-05-02 03:49:14 +00:00

125 lines
3.4 KiB
JavaScript

/**
* k6 load test for FusionAGI prompt endpoint.
*
* Run:
* k6 run tests/load/k6_prompt.js
*
* Options:
* k6 run --vus 10 --duration 30s tests/load/k6_prompt.js
* k6 run --vus 50 --duration 2m tests/load/k6_prompt.js
*
* Requires:
* - FusionAGI API running at http://localhost:8000
* - k6 installed (https://k6.io/docs/getting-started/installation/)
*/
import http from 'k6/http'
import { check, sleep } from 'k6'
import { Rate, Trend } from 'k6/metrics'
// Custom metrics
const errorRate = new Rate('errors')
const promptDuration = new Trend('prompt_duration', true)
const sessionDuration = new Trend('session_duration', true)
// Test configuration
export const options = {
stages: [
{ duration: '10s', target: 5 }, // ramp up
{ duration: '30s', target: 10 }, // steady
{ duration: '10s', target: 20 }, // spike
{ duration: '10s', target: 0 }, // ramp down
],
thresholds: {
http_req_duration: ['p(95)<5000'], // 95% under 5s
errors: ['rate<0.1'], // <10% error rate
},
}
const BASE_URL = __ENV.API_URL || 'http://localhost:8000'
const API_KEY = __ENV.API_KEY || ''
const PROMPTS = [
'Explain the concept of recursion',
'What are the benefits of microservices?',
'Design a rate limiter',
'Compare SQL and NoSQL databases',
'Explain the CAP theorem',
'What is eventual consistency?',
'How does garbage collection work?',
'Explain WebSocket vs HTTP polling',
]
function getHeaders() {
const headers = { 'Content-Type': 'application/json' }
if (API_KEY) {
headers['Authorization'] = `Bearer ${API_KEY}`
}
return headers
}
export default function () {
const headers = getHeaders()
// 1. Create session
const sessionStart = Date.now()
const sessionRes = http.post(`${BASE_URL}/v1/sessions`, null, { headers })
sessionDuration.add(Date.now() - sessionStart)
const sessionOk = check(sessionRes, {
'session created': (r) => r.status === 200 || r.status === 201,
'session has id': (r) => {
try { return !!JSON.parse(r.body).session_id } catch { return false }
},
})
if (!sessionOk) {
errorRate.add(1)
sleep(1)
return
}
const sessionId = JSON.parse(sessionRes.body).session_id
const prompt = PROMPTS[Math.floor(Math.random() * PROMPTS.length)]
// 2. Send prompt
const promptStart = Date.now()
const promptRes = http.post(
`${BASE_URL}/v1/sessions/${sessionId}/prompt`,
JSON.stringify({ prompt }),
{ headers, timeout: '30s' },
)
promptDuration.add(Date.now() - promptStart)
const promptOk = check(promptRes, {
'prompt success': (r) => r.status === 200,
'has final_answer': (r) => {
try { return !!JSON.parse(r.body).final_answer } catch { return false }
},
})
if (!promptOk) {
errorRate.add(1)
}
// 3. Health check
const healthRes = http.get(`${BASE_URL}/health`, { headers })
check(healthRes, {
'health ok': (r) => r.status === 200,
})
sleep(0.5 + Math.random())
}
export function handleSummary(data) {
return {
stdout: JSON.stringify({
total_requests: data.metrics.http_reqs.values.count,
avg_duration_ms: Math.round(data.metrics.http_req_duration.values.avg),
p95_duration_ms: Math.round(data.metrics.http_req_duration.values['p(95)']),
error_rate: data.metrics.errors ? data.metrics.errors.values.rate : 0,
avg_prompt_ms: data.metrics.prompt_duration ? Math.round(data.metrics.prompt_duration.values.avg) : 0,
}, null, 2),
}
}