| |
| |
| |
| |
| |
| |
| |
| |
| import { test } from "node:test"; |
| import assert from "node:assert/strict"; |
|
|
| process.env.DATABASE_URL ??= "postgres://test:test@127.0.0.1:5432/test"; |
|
|
| const gateway = await import("../gateway"); |
|
|
| const ADAPTER = "anthropic"; |
| const SOURCE = "replit-integration"; |
| |
| const TIMEOUT_MS = 90_000; |
|
|
| function snapshotFor(adapter: string, source: string) { |
| return gateway |
| .circuitSnapshot() |
| .find((e) => e.adapter === adapter && e.source === source); |
| } |
|
|
| test("restoreRecentSamples seeds the in-memory window so the sparkline is populated immediately after a restart", () => { |
| gateway._resetAllCircuitsForTesting(); |
|
|
| const now = Date.now(); |
| const samples = Array.from({ length: 12 }, (_, i) => ({ |
| ms: 200 + i * 10, |
| at: now - (12 - i) * 1000, |
| })); |
| gateway.restoreRecentSamples(ADAPTER, SOURCE, samples); |
|
|
| const snap = snapshotFor(ADAPTER, SOURCE); |
| assert.ok(snap, "provider state should exist after restore"); |
| assert.equal(snap!.recentCallCount, 12); |
| assert.equal(snap!.recentSamples.length, 12); |
| |
| for (let i = 1; i < snap!.recentSamples.length; i++) { |
| assert.ok(snap!.recentSamples[i]!.at >= snap!.recentSamples[i - 1]!.at); |
| } |
| |
| const big = Array.from({ length: 200 }, (_, i) => ({ ms: i, at: now + i })); |
| gateway.restoreRecentSamples(ADAPTER, SOURCE, big); |
| const snap2 = snapshotFor(ADAPTER, SOURCE); |
| assert.equal(snap2!.recentSamples.length, 50); |
| }); |
|
|
| test("restoreRecentSamples re-arms the near-ceiling latch so latency-alert evaluation survives a restart", () => { |
| gateway._resetAllCircuitsForTesting(); |
|
|
| const now = Date.now(); |
| const samples = Array.from( |
| { length: gateway.LATENCY_ALERT_MIN_SAMPLES + 2 }, |
| (_, i) => ({ ms: TIMEOUT_MS, at: now - (10 - i) * 1000 }), |
| ); |
| gateway.restoreRecentSamples(ADAPTER, SOURCE, samples); |
|
|
| const snap = snapshotFor(ADAPTER, SOURCE); |
| assert.ok( |
| (snap?.p95NearCeilingSinceMs ?? 0) > 0, |
| "p95 should be flagged near-ceiling once enough hot samples are restored", |
| ); |
| }); |
|
|
| test("dirty hook fires on every new sample so the persistence flush picks it up", () => { |
| gateway._resetAllCircuitsForTesting(); |
| const dirty: string[] = []; |
| gateway._setSampleDirtyHook((adapter, source) => { |
| dirty.push(`${adapter}@${source}`); |
| }); |
| try { |
| gateway._recordDurationForTesting(ADAPTER, SOURCE, 123); |
| gateway._recordDurationForTesting(ADAPTER, SOURCE, 456); |
| gateway._recordDurationForTesting("openai", "byok-direct", 789); |
| assert.deepEqual(dirty, [ |
| `${ADAPTER}@${SOURCE}`, |
| `${ADAPTER}@${SOURCE}`, |
| "openai@byok-direct", |
| ]); |
| } finally { |
| gateway._setSampleDirtyHook(null); |
| } |
| }); |
|
|
| test("getRecentSamplesFor returns the live window the persistence layer flushes", () => { |
| gateway._resetAllCircuitsForTesting(); |
| gateway._recordDurationForTesting(ADAPTER, SOURCE, 111); |
| gateway._recordDurationForTesting(ADAPTER, SOURCE, 222); |
| const samples = gateway.getRecentSamplesFor(ADAPTER, SOURCE); |
| assert.equal(samples.length, 2); |
| assert.equal(samples[0]!.ms, 111); |
| assert.equal(samples[1]!.ms, 222); |
| assert.deepEqual(gateway.getRecentSamplesFor("nope", "nope"), []); |
| }); |
|
|