import { describe, expect, it } from "vitest"; import { METRIC_COUNTER } from "@/lib/constants/metrics"; import { computeP95, computeRate, computeRates, type MetricsCounters, } from "@/modules/metrics/collector"; function emptyCounters(): MetricsCounters { return { [METRIC_COUNTER.POST_TOTAL]: 0, [METRIC_COUNTER.POST_DONE]: 0, [METRIC_COUNTER.CACHE_HIT_L1]: 0, [METRIC_COUNTER.CACHE_HIT_L2]: 0, [METRIC_COUNTER.RPA_TRIGGERED]: 0, [METRIC_COUNTER.RPA_DONE]: 0, [METRIC_COUNTER.DONE_TOTAL]: 0, [METRIC_COUNTER.DONE_REALTIME]: 0, }; } describe("computeRates (GD-4)", () => { it("100 次 mock:80 done / 100 post → api 80%", () => { const counters: MetricsCounters = { ...emptyCounters(), [METRIC_COUNTER.POST_TOTAL]: 100, [METRIC_COUNTER.POST_DONE]: 80, }; expect(computeRates(counters).api_success_rate).toBe(80); }); it("RPA 85/100 → rpa_success_rate 85%", () => { const counters: MetricsCounters = { ...emptyCounters(), [METRIC_COUNTER.RPA_TRIGGERED]: 100, [METRIC_COUNTER.RPA_DONE]: 85, }; expect(computeRates(counters).rpa_success_rate).toBe(85); }); it("realtime 90/100 done → realtime_rate 90%", () => { const counters: MetricsCounters = { ...emptyCounters(), [METRIC_COUNTER.DONE_TOTAL]: 100, [METRIC_COUNTER.DONE_REALTIME]: 90, }; expect(computeRates(counters).realtime_rate).toBe(90); }); it("cache (L1+L2) 90/100 post → cache_hit_rate 90%", () => { const counters: MetricsCounters = { ...emptyCounters(), [METRIC_COUNTER.POST_TOTAL]: 100, [METRIC_COUNTER.CACHE_HIT_L1]: 40, [METRIC_COUNTER.CACHE_HIT_L2]: 50, }; expect(computeRates(counters).cache_hit_rate).toBe(90); }); it("分母为 0 → 返回 0", () => { expect(computeRate(10, 0)).toBe(0); expect(computeRates(emptyCounters()).api_success_rate).toBe(0); }); }); describe("computeP95", () => { it("100 样本 P95 取第 95 百分位", () => { const samples = Array.from({ length: 100 }, (_, index) => index + 1); expect(computeP95(samples)).toBe(95); }); it("空样本 → 0", () => { expect(computeP95([])).toBe(0); }); });