Files
diegosouzapw--omniroute/tests/unit/chatcore-memory-pressure.test.ts
2026-07-13 13:39:12 +08:00

101 lines
4.3 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
// ── estimateSizeFast (truncateForLog dependency) ───────────────────────
test("estimateSizeFast estimates small objects correctly", async () => {
const { estimateSizeFast } = await import("../../open-sse/utils/estimateSize.ts");
const small = { model: "gpt-4", messages: [{ role: "user", content: "hello" }] };
const size = estimateSizeFast(small);
assert.ok(size > 0, "Size should be positive");
assert.ok(size < 1024, `Small object should be < 1KB, got ${size}`);
});
test("estimateSizeFast estimates large objects correctly", async () => {
const { estimateSizeFast } = await import("../../open-sse/utils/estimateSize.ts");
const largeContent = "x".repeat(100 * 1024);
const large = { model: "gpt-4", messages: [{ role: "user", content: largeContent }] };
const size = estimateSizeFast(large);
assert.ok(size > 100 * 1024, `Large object should be > 100KB, got ${size}`);
});
test("estimateSizeFast handles null and primitives", async () => {
const { estimateSizeFast } = await import("../../open-sse/utils/estimateSize.ts");
assert.equal(estimateSizeFast(null), 0);
assert.equal(estimateSizeFast(undefined), 0);
assert.equal(estimateSizeFast("hello"), 5);
assert.equal(estimateSizeFast(42), 8);
assert.equal(estimateSizeFast(true), 4);
});
test("estimateSizeFast handles circular references", async () => {
const { estimateSizeFast } = await import("../../open-sse/utils/estimateSize.ts");
const obj: Record<string, unknown> = { a: 1 };
obj.self = obj;
const size = estimateSizeFast(obj);
assert.ok(size > 0, "Should handle circular refs");
assert.ok(size < 1000, `Simple circular ref should be small, got ${size}`);
});
// ── HEAP_PRESSURE_THRESHOLD_MB auto-calibration ────────────────────────
// Regression: the old fixed 200MB default sat below the app's ~260MB working set,
// so the chatCore heap guard 503'd every request ("resource pressure" outage).
// The live constant must now auto-calibrate from the real V8 heap ceiling.
test("HEAP_PRESSURE_THRESHOLD_MB auto-calibrates from the live V8 heap ceiling (no fixed 200)", async () => {
const { getHeapStatistics } = await import("node:v8");
const { HEAP_PRESSURE_THRESHOLD_MB, computeHeapPressureThresholdMb } = await import(
"../../open-sse/utils/heapPressure.ts"
);
const limitMb = getHeapStatistics().heap_size_limit / (1024 * 1024);
// The live constant must equal the pure helper applied to this process's
// actual ceiling — no drift between the resolved value and the formula.
assert.equal(
HEAP_PRESSURE_THRESHOLD_MB,
computeHeapPressureThresholdMb(limitMb, process.env.HEAP_PRESSURE_THRESHOLD_MB)
);
// With no operator override it must clear the ~260MB baseline, otherwise the
// guard would reject every request at idle (the bug we are fixing).
if (!process.env.HEAP_PRESSURE_THRESHOLD_MB) {
assert.ok(
HEAP_PRESSURE_THRESHOLD_MB >= 400,
`live threshold ${HEAP_PRESSURE_THRESHOLD_MB}MB must clear the ~260MB app baseline`
);
}
});
// ── estimateSizeFast vs MAX_LOG_BODY_CHARS threshold ───────────────────
// This validates the logic that truncateForLog uses: if estimateSizeFast
// returns <= 8KB, the object is kept as-is; otherwise it's summarized.
test("estimateSizeFast distinguishes small vs large for 8KB threshold", async () => {
const { estimateSizeFast } = await import("../../open-sse/utils/estimateSize.ts");
const MAX_LOG_BODY_CHARS = 8 * 1024;
// Small: 50 messages with short content
const small = {
model: "gpt-4",
messages: Array.from({ length: 50 }, (_, i) => ({
role: "user",
content: `message ${i}`,
})),
};
assert.ok(
estimateSizeFast(small) <= MAX_LOG_BODY_CHARS,
`Small payload (${estimateSizeFast(small)}B) should be <= ${MAX_LOG_BODY_CHARS}`
);
// Large: 500 messages with long content
const large = {
model: "gpt-4",
messages: Array.from({ length: 500 }, (_, i) => ({
role: "user",
content: `x`.repeat(500),
})),
};
assert.ok(
estimateSizeFast(large) > MAX_LOG_BODY_CHARS,
`Large payload (${estimateSizeFast(large)}B) should be > ${MAX_LOG_BODY_CHARS}`
);
});