fix production issues
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 51s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 51s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Optional k6 scenario for authenticated booking smoke under load.
|
||||
* Install k6 separately: https://k6.io/docs/get-started/installation/
|
||||
*
|
||||
* k6 run -e API_BASE=https://staging.example.com -e TOKEN=... scripts/load/booking-smoke.k6.js
|
||||
*/
|
||||
import http from 'k6/http'
|
||||
import { check, sleep } from 'k6'
|
||||
|
||||
export const options = {
|
||||
vus: Number(__ENV.VUS || 5),
|
||||
duration: __ENV.DURATION || '2m',
|
||||
thresholds: {
|
||||
http_req_failed: ['rate<0.05'],
|
||||
http_req_duration: ['p(95)<2000'],
|
||||
},
|
||||
}
|
||||
|
||||
const base = (__ENV.API_BASE || 'http://127.0.0.1:4000').replace(/\/$/, '')
|
||||
const token = __ENV.TOKEN || ''
|
||||
|
||||
export default function () {
|
||||
const health = http.get(`${base}/health`)
|
||||
check(health, { 'health 200': (r) => r.status === 200 })
|
||||
|
||||
const ready = http.get(`${base}/ready`)
|
||||
check(ready, { 'ready 200': (r) => r.status === 200 })
|
||||
|
||||
if (token) {
|
||||
const vehicles = http.get(`${base}/api/v1/vehicles?page=1&pageSize=10`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
check(vehicles, { 'vehicles ok': (r) => r.status === 200 || r.status === 401 })
|
||||
}
|
||||
|
||||
sleep(1)
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Lightweight soak/load probe for staging (Phase 3).
|
||||
* Does not require k6 — uses Node fetch against /health, /ready, /metrics.
|
||||
*
|
||||
* Usage:
|
||||
* API_BASE=https://staging.example.com node scripts/load/soak-probe.mjs
|
||||
* DURATION_SEC=60 CONCURRENCY=8 node scripts/load/soak-probe.mjs
|
||||
*/
|
||||
const base = (process.env.API_BASE ?? 'http://127.0.0.1:4000').replace(/\/$/, '')
|
||||
const durationSec = Number(process.env.DURATION_SEC ?? 30)
|
||||
const concurrency = Number(process.env.CONCURRENCY ?? 4)
|
||||
const paths = (process.env.SOAK_PATHS ?? '/health,/ready,/metrics').split(',').map((p) => p.trim())
|
||||
|
||||
const stats = {
|
||||
ok: 0,
|
||||
fail: 0,
|
||||
statuses: /** @type {Record<string, number>} */ ({}),
|
||||
latencies: /** @type {number[]} */ ([]),
|
||||
}
|
||||
|
||||
async function oneRequest(path) {
|
||||
const started = Date.now()
|
||||
try {
|
||||
const res = await fetch(`${base}${path}`, { method: 'GET' })
|
||||
const ms = Date.now() - started
|
||||
stats.latencies.push(ms)
|
||||
const key = String(res.status)
|
||||
stats.statuses[key] = (stats.statuses[key] ?? 0) + 1
|
||||
if (res.status >= 200 && res.status < 400) stats.ok += 1
|
||||
else stats.fail += 1
|
||||
} catch {
|
||||
stats.fail += 1
|
||||
stats.statuses.error = (stats.statuses.error ?? 0) + 1
|
||||
}
|
||||
}
|
||||
|
||||
async function worker(deadline) {
|
||||
let i = 0
|
||||
while (Date.now() < deadline) {
|
||||
const path = paths[i % paths.length]
|
||||
i += 1
|
||||
await oneRequest(path)
|
||||
}
|
||||
}
|
||||
|
||||
function percentile(sorted, p) {
|
||||
if (!sorted.length) return 0
|
||||
const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1))
|
||||
return sorted[idx]
|
||||
}
|
||||
|
||||
const deadline = Date.now() + durationSec * 1000
|
||||
console.log(`Soak probe ${base} duration=${durationSec}s concurrency=${concurrency} paths=${paths.join(',')}`)
|
||||
|
||||
await Promise.all(Array.from({ length: concurrency }, () => worker(deadline)))
|
||||
|
||||
const sorted = [...stats.latencies].sort((a, b) => a - b)
|
||||
const total = stats.ok + stats.fail
|
||||
console.log(JSON.stringify({
|
||||
total,
|
||||
ok: stats.ok,
|
||||
fail: stats.fail,
|
||||
errorRate: total ? Number((stats.fail / total).toFixed(4)) : 0,
|
||||
p50_ms: percentile(sorted, 50),
|
||||
p95_ms: percentile(sorted, 95),
|
||||
p99_ms: percentile(sorted, 99),
|
||||
statuses: stats.statuses,
|
||||
}, null, 2))
|
||||
|
||||
if (stats.fail / Math.max(total, 1) > Number(process.env.MAX_ERROR_RATE ?? 0.05)) {
|
||||
console.error('Soak probe FAILED: error rate above MAX_ERROR_RATE')
|
||||
process.exit(1)
|
||||
}
|
||||
console.log('Soak probe PASSED')
|
||||
Reference in New Issue
Block a user