task-99: 全链路压测记录 CPU、内存、GC、DB、Redis、RustFS、网络结果
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
import { test } from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import { createLoadTestRecorder, type LoadMetric, type LoadSample } from '../src/shared/load-test-recorder.ts'
|
||||
|
||||
const sample = (over: Partial<LoadSample> = {}): LoadSample => ({
|
||||
cpuPercent: 35,
|
||||
heapBytes: 1_200_000_000,
|
||||
gcCount: 12,
|
||||
dbQps: 500,
|
||||
redisQps: 900,
|
||||
rustfsQps: 60,
|
||||
networkBytesPerSec: 8_000_000,
|
||||
...over,
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_normal_default_path', async () => {
|
||||
const recorder = createLoadTestRecorder({
|
||||
metric: async () => ({ ok: true, output: '200 OK', latencyMs: 120 }),
|
||||
sample: async () => sample(),
|
||||
})
|
||||
const result = await recorder.run()
|
||||
assert.equal(result.ok, true)
|
||||
assert.equal(result.summary.requests, 100)
|
||||
assert.equal(result.summary.failures, 0)
|
||||
assert.equal(result.summary.ok, true)
|
||||
assert.equal(result.metrics.length, 100)
|
||||
assert.equal(result.metrics[0].latencyMs, 120)
|
||||
const s = result.summary.samples
|
||||
assert.equal(s.cpuPercent.avg, 35)
|
||||
assert.equal(s.heapBytes.avg, 1_200_000_000)
|
||||
assert.equal(s.dbQps.avg, 500)
|
||||
assert.equal(s.redisQps.avg, 900)
|
||||
assert.equal(s.rustfsQps.avg, 60)
|
||||
assert.equal(s.networkBytesPerSec.avg, 8_000_000)
|
||||
assert.equal(s.gcCount.avg, 12)
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_normal_multiple_items', async () => {
|
||||
// 批量压测:并发 N 路,结果不丢失且顺序稳定
|
||||
let seq = 0
|
||||
const recorder = createLoadTestRecorder({
|
||||
metric: async (i: number) => ({ ok: true, output: 'ok', latencyMs: 100 + (i % 5) }),
|
||||
sample: async () => {
|
||||
const cpu = 30 + (seq % 3) * 10
|
||||
seq += 1
|
||||
return sample({ cpuPercent: cpu })
|
||||
},
|
||||
concurrency: 4,
|
||||
})
|
||||
const result = await recorder.run()
|
||||
assert.equal(result.metrics.length, 100)
|
||||
const latencies = result.metrics.map((m) => m.latencyMs)
|
||||
assert.ok(latencies.every((l) => l >= 100 && l <= 104))
|
||||
assert.equal(result.summary.requests, 100)
|
||||
assert.equal(result.summary.ok, true)
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_normal_repeated_operation_is_idempotent', async () => {
|
||||
let n = 0
|
||||
const recorder = createLoadTestRecorder({
|
||||
metric: async () => {
|
||||
n += 1
|
||||
return { ok: true, output: 'ok', latencyMs: 50 }
|
||||
},
|
||||
sample: async () => sample(),
|
||||
})
|
||||
const first = await recorder.run()
|
||||
const second = await recorder.run()
|
||||
assert.equal(first.summary.requests, 100)
|
||||
assert.equal(second.summary.requests, 100)
|
||||
assert.equal(first.summary.ok, second.summary.ok)
|
||||
assert.deepEqual(first.summary.samples.heapBytes, second.summary.samples.heapBytes)
|
||||
assert.equal(n, 200, '重复执行线性增长,无残留状态')
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_boundary_empty_input', async () => {
|
||||
const recorder = createLoadTestRecorder({
|
||||
requests: 0,
|
||||
metric: async () => ({ ok: true, output: 'ok', latencyMs: 1 }),
|
||||
sample: async () => sample(),
|
||||
})
|
||||
const result = await recorder.run()
|
||||
assert.equal(result.summary.requests, 0)
|
||||
assert.equal(result.metrics.length, 0)
|
||||
assert.equal(result.summary.ok, true)
|
||||
assert.equal(result.summary.failures, 0)
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_boundary_single_item', async () => {
|
||||
const recorder = createLoadTestRecorder({
|
||||
requests: 1,
|
||||
metric: async () => ({ ok: true, output: 'ok', latencyMs: 77 }),
|
||||
sample: async () => sample(),
|
||||
})
|
||||
const result = await recorder.run()
|
||||
assert.equal(result.metrics.length, 1)
|
||||
assert.equal(result.metrics[0].latencyMs, 77)
|
||||
assert.equal(result.summary.requests, 1)
|
||||
assert.equal(result.summary.samples.cpuPercent.avg, 35)
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_boundary_limit_and_overflow', async () => {
|
||||
// 部分失败:失败率超阈值 → ok=false;失败计数准确
|
||||
const recorder = createLoadTestRecorder({
|
||||
requests: 10,
|
||||
maxFailures: 2,
|
||||
metric: async (i: number) =>
|
||||
i % 3 === 0
|
||||
? { ok: false, output: 'timeout', latencyMs: 1000 }
|
||||
: { ok: true, output: 'ok', latencyMs: 10 },
|
||||
sample: async () => sample(),
|
||||
})
|
||||
const result = await recorder.run()
|
||||
assert.equal(result.summary.requests, 10)
|
||||
assert.equal(result.summary.failures, 4, 'i%3===0 共 4 次')
|
||||
assert.equal(result.summary.ok, false)
|
||||
// 失败未超阈值:仍判定 ok
|
||||
const okRecorder = createLoadTestRecorder({
|
||||
requests: 10,
|
||||
maxFailures: 5,
|
||||
metric: async (i: number) =>
|
||||
i % 3 === 0
|
||||
? { ok: false, output: 'timeout', latencyMs: 1000 }
|
||||
: { ok: true, output: 'ok', latencyMs: 10 },
|
||||
sample: async () => sample(),
|
||||
})
|
||||
assert.equal((await okRecorder.run()).summary.ok, true)
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_invalid_input_rejected', async () => {
|
||||
assert.throws(() => createLoadTestRecorder({} as never), /metric 必须是函数/)
|
||||
assert.throws(
|
||||
() => createLoadTestRecorder({ metric: async () => ({ ok: true, output: '', latencyMs: 1 }), sample: undefined as never }),
|
||||
/sample 必须是函数/,
|
||||
)
|
||||
assert.throws(
|
||||
() => createLoadTestRecorder({ metric: async () => ({ ok: true, output: '', latencyMs: 1 }), sample: async () => sample(), requests: -1 }),
|
||||
/requests 必须是非负整数/,
|
||||
)
|
||||
assert.throws(
|
||||
() => createLoadTestRecorder({ metric: async () => ({ ok: true, output: '', latencyMs: 1 }), sample: async () => sample(), concurrency: 0 }),
|
||||
/concurrency 必须为正数/,
|
||||
)
|
||||
assert.throws(
|
||||
() => createLoadTestRecorder({ metric: async () => ({ ok: true, output: '', latencyMs: 1 }), sample: async () => sample(), maxFailures: -1 }),
|
||||
/maxFailures 必须是非负整数/,
|
||||
)
|
||||
const recorder = createLoadTestRecorder({
|
||||
metric: async () => ({ ok: true, output: 'ok', latencyMs: NaN }),
|
||||
sample: async () => sample(),
|
||||
})
|
||||
await assert.rejects(() => recorder.run(), /latencyMs 必须为非负数值/)
|
||||
})
|
||||
|
||||
test('test_task_099_rustfs_dependency_failure_releases_resources', async () => {
|
||||
// 采样依赖抛错:单次采样失败不中断压测,统计从有效采样计算;恢复后正常
|
||||
let broken = true
|
||||
const recorder = createLoadTestRecorder({
|
||||
requests: 10,
|
||||
metric: async () => ({ ok: true, output: 'ok', latencyMs: 10 }),
|
||||
sample: async () => {
|
||||
if (broken) throw new Error('prometheus down')
|
||||
return sample()
|
||||
},
|
||||
})
|
||||
const result = await recorder.run()
|
||||
assert.equal(result.summary.requests, 10, '采样失败不影响请求执行')
|
||||
assert.equal(result.sampleFailures, 10)
|
||||
assert.equal(result.summary.samples.avgCount, 0, '无有效采样时统计为空')
|
||||
broken = false
|
||||
const recovered = await recorder.run()
|
||||
assert.equal(recovered.sampleFailures, 0)
|
||||
assert.equal(recovered.summary.samples.avgCount, 10)
|
||||
assert.equal(recovered.summary.samples.cpuPercent.avg, 35)
|
||||
})
|
||||
Reference in New Issue
Block a user