mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 04:00:43 +00:00
Merged via squash.
Prepared head SHA: 3d6ee85993
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
155 lines
4.0 KiB
TypeScript
155 lines
4.0 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import {
|
|
onDiagnosticEvent,
|
|
resetDiagnosticEventsForTest,
|
|
type DiagnosticEventPayload,
|
|
} from "../infra/diagnostic-events.js";
|
|
import { emitDiagnosticMemorySample, resetDiagnosticMemoryForTest } from "./diagnostic-memory.js";
|
|
|
|
function memoryUsage(overrides: Partial<NodeJS.MemoryUsage>): NodeJS.MemoryUsage {
|
|
return {
|
|
rss: 100,
|
|
heapTotal: 80,
|
|
heapUsed: 40,
|
|
external: 10,
|
|
arrayBuffers: 5,
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("diagnostic memory", () => {
|
|
beforeEach(() => {
|
|
resetDiagnosticEventsForTest();
|
|
resetDiagnosticMemoryForTest();
|
|
});
|
|
|
|
afterEach(() => {
|
|
resetDiagnosticEventsForTest();
|
|
resetDiagnosticMemoryForTest();
|
|
});
|
|
|
|
it("emits memory samples with byte counts", () => {
|
|
const events: DiagnosticEventPayload[] = [];
|
|
const stop = onDiagnosticEvent((event) => events.push(event));
|
|
|
|
emitDiagnosticMemorySample({
|
|
now: 1000,
|
|
uptimeMs: 123,
|
|
memoryUsage: memoryUsage({ rss: 4096, heapUsed: 1024 }),
|
|
});
|
|
stop();
|
|
|
|
expect(events).toMatchObject([
|
|
{
|
|
type: "diagnostic.memory.sample",
|
|
uptimeMs: 123,
|
|
memory: {
|
|
rssBytes: 4096,
|
|
heapUsedBytes: 1024,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("emits pressure when RSS crosses a threshold", () => {
|
|
const events: DiagnosticEventPayload[] = [];
|
|
const stop = onDiagnosticEvent((event) => events.push(event));
|
|
|
|
emitDiagnosticMemorySample({
|
|
now: 1000,
|
|
memoryUsage: memoryUsage({ rss: 2000 }),
|
|
thresholds: {
|
|
rssWarningBytes: 1000,
|
|
rssCriticalBytes: 3000,
|
|
pressureRepeatMs: 60_000,
|
|
},
|
|
});
|
|
stop();
|
|
|
|
expect(events).toContainEqual(
|
|
expect.objectContaining({
|
|
type: "diagnostic.memory.pressure",
|
|
level: "warning",
|
|
reason: "rss_threshold",
|
|
thresholdBytes: 1000,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("can check pressure without recording an idle memory sample", () => {
|
|
const events: DiagnosticEventPayload[] = [];
|
|
const stop = onDiagnosticEvent((event) => events.push(event));
|
|
|
|
emitDiagnosticMemorySample({
|
|
now: 1000,
|
|
emitSample: false,
|
|
memoryUsage: memoryUsage({ rss: 2000 }),
|
|
thresholds: {
|
|
rssWarningBytes: 1000,
|
|
rssCriticalBytes: 3000,
|
|
pressureRepeatMs: 60_000,
|
|
},
|
|
});
|
|
stop();
|
|
|
|
expect(events.map((event) => event.type)).toEqual(["diagnostic.memory.pressure"]);
|
|
});
|
|
|
|
it("emits pressure when RSS grows quickly", () => {
|
|
const events: DiagnosticEventPayload[] = [];
|
|
const stop = onDiagnosticEvent((event) => events.push(event));
|
|
|
|
emitDiagnosticMemorySample({
|
|
now: 1000,
|
|
memoryUsage: memoryUsage({ rss: 1000 }),
|
|
thresholds: {
|
|
rssWarningBytes: 10_000,
|
|
heapUsedWarningBytes: 10_000,
|
|
rssGrowthWarningBytes: 500,
|
|
growthWindowMs: 10_000,
|
|
},
|
|
});
|
|
emitDiagnosticMemorySample({
|
|
now: 2000,
|
|
memoryUsage: memoryUsage({ rss: 1700 }),
|
|
thresholds: {
|
|
rssWarningBytes: 10_000,
|
|
heapUsedWarningBytes: 10_000,
|
|
rssGrowthWarningBytes: 500,
|
|
growthWindowMs: 10_000,
|
|
},
|
|
});
|
|
stop();
|
|
|
|
expect(events).toContainEqual(
|
|
expect.objectContaining({
|
|
type: "diagnostic.memory.pressure",
|
|
level: "warning",
|
|
reason: "rss_growth",
|
|
rssGrowthBytes: 700,
|
|
windowMs: 1000,
|
|
}),
|
|
);
|
|
});
|
|
|
|
it("throttles repeated pressure events by reason and level", () => {
|
|
const events: DiagnosticEventPayload[] = [];
|
|
const stop = onDiagnosticEvent((event) => events.push(event));
|
|
|
|
for (const now of [1000, 2000]) {
|
|
emitDiagnosticMemorySample({
|
|
now,
|
|
memoryUsage: memoryUsage({ rss: 2000 }),
|
|
thresholds: {
|
|
rssWarningBytes: 1000,
|
|
rssCriticalBytes: 3000,
|
|
pressureRepeatMs: 60_000,
|
|
},
|
|
});
|
|
}
|
|
stop();
|
|
|
|
expect(events.filter((event) => event.type === "diagnostic.memory.pressure")).toHaveLength(1);
|
|
});
|
|
});
|