doatlas-2 / artifacts /api-server /src /llm /__tests__ /sample-persistence.test.ts
Iostream-Li's picture
Add files using upload-large-folder tool
6d1fe92 verified
Raw
History Blame Contribute Delete
3.67 kB
/**
* Unit coverage for the gateway's sample-restore + dirty-hook plumbing
* that backs the Postgres persistence layer (Task #130). The actual
* upsert is exercised by the route integration tests; here we just
* verify the in-memory contract the persistence module relies on.
*
* Run with: pnpm --filter @workspace/api-server test:sample-persistence
*/
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";
// Default for anthropic@replit-integration in ADAPTER_DEFAULTS.
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);
// Restored samples must keep chronological order.
for (let i = 1; i < snap!.recentSamples.length; i++) {
assert.ok(snap!.recentSamples[i]!.at >= snap!.recentSamples[i - 1]!.at);
}
// Persistence cap mirrors the in-memory RECENT_DURATION_LIMIT (50).
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"), []);
});