mirror of https://github.com/openclaw/openclaw.git
test: expand presence and maintenance warning coverage
This commit is contained in:
parent
96c48f5566
commit
6bbf2d486c
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { randomUUID } from "node:crypto";
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
const mocks = vi.hoisted(() => ({
|
const mocks = vi.hoisted(() => ({
|
||||||
|
|
@ -37,6 +38,26 @@ vi.mock("./system-events.js", () => ({
|
||||||
|
|
||||||
const { deliverSessionMaintenanceWarning } = await import("./session-maintenance-warning.js");
|
const { deliverSessionMaintenanceWarning } = await import("./session-maintenance-warning.js");
|
||||||
|
|
||||||
|
function createParams(
|
||||||
|
overrides: Partial<Parameters<typeof deliverSessionMaintenanceWarning>[0]> = {},
|
||||||
|
): Parameters<typeof deliverSessionMaintenanceWarning>[0] {
|
||||||
|
const sessionKey = overrides.sessionKey ?? `agent:${randomUUID()}:main`;
|
||||||
|
return {
|
||||||
|
cfg: {},
|
||||||
|
sessionKey,
|
||||||
|
entry: {} as never,
|
||||||
|
warning: {
|
||||||
|
activeSessionKey: sessionKey,
|
||||||
|
pruneAfterMs: 1_000,
|
||||||
|
maxEntries: 100,
|
||||||
|
wouldPrune: true,
|
||||||
|
wouldCap: false,
|
||||||
|
...(overrides.warning as object),
|
||||||
|
} as never,
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
describe("deliverSessionMaintenanceWarning", () => {
|
describe("deliverSessionMaintenanceWarning", () => {
|
||||||
let prevVitest: string | undefined;
|
let prevVitest: string | undefined;
|
||||||
let prevNodeEnv: string | undefined;
|
let prevNodeEnv: string | undefined;
|
||||||
|
|
@ -68,18 +89,9 @@ describe("deliverSessionMaintenanceWarning", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("forwards session context to outbound delivery", async () => {
|
it("forwards session context to outbound delivery", async () => {
|
||||||
await deliverSessionMaintenanceWarning({
|
const params = createParams({ sessionKey: "agent:main:main" });
|
||||||
cfg: {},
|
|
||||||
sessionKey: "agent:main:main",
|
await deliverSessionMaintenanceWarning(params);
|
||||||
entry: {} as never,
|
|
||||||
warning: {
|
|
||||||
activeSessionKey: "agent:main:main",
|
|
||||||
pruneAfterMs: 1_000,
|
|
||||||
maxEntries: 100,
|
|
||||||
wouldPrune: true,
|
|
||||||
wouldCap: false,
|
|
||||||
} as never,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith(
|
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
|
@ -90,4 +102,61 @@ describe("deliverSessionMaintenanceWarning", () => {
|
||||||
);
|
);
|
||||||
expect(mocks.enqueueSystemEvent).not.toHaveBeenCalled();
|
expect(mocks.enqueueSystemEvent).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("suppresses duplicate warning contexts for the same session", async () => {
|
||||||
|
const params = createParams();
|
||||||
|
|
||||||
|
await deliverSessionMaintenanceWarning(params);
|
||||||
|
await deliverSessionMaintenanceWarning(params);
|
||||||
|
|
||||||
|
expect(mocks.deliverOutboundPayloads).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to a system event when the last target is not deliverable", async () => {
|
||||||
|
mocks.resolveSessionDeliveryTarget.mockReturnValueOnce({
|
||||||
|
channel: "debug",
|
||||||
|
to: "+15550001",
|
||||||
|
accountId: "acct-1",
|
||||||
|
threadId: "thread-1",
|
||||||
|
});
|
||||||
|
mocks.isDeliverableMessageChannel.mockReturnValueOnce(false);
|
||||||
|
|
||||||
|
await deliverSessionMaintenanceWarning(
|
||||||
|
createParams({
|
||||||
|
warning: {
|
||||||
|
pruneAfterMs: 3_600_000,
|
||||||
|
maxEntries: 10,
|
||||||
|
wouldPrune: false,
|
||||||
|
wouldCap: true,
|
||||||
|
} as never,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(mocks.deliverOutboundPayloads).not.toHaveBeenCalled();
|
||||||
|
expect(mocks.enqueueSystemEvent).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("most recent 10 sessions"),
|
||||||
|
expect.objectContaining({ sessionKey: expect.stringContaining("agent:") }),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("skips warning delivery in test mode", async () => {
|
||||||
|
process.env.NODE_ENV = "test";
|
||||||
|
|
||||||
|
await deliverSessionMaintenanceWarning(createParams());
|
||||||
|
|
||||||
|
expect(mocks.resolveSessionDeliveryTarget).not.toHaveBeenCalled();
|
||||||
|
expect(mocks.deliverOutboundPayloads).not.toHaveBeenCalled();
|
||||||
|
expect(mocks.enqueueSystemEvent).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("enqueues a system event when outbound delivery fails", async () => {
|
||||||
|
mocks.deliverOutboundPayloads.mockRejectedValueOnce(new Error("boom"));
|
||||||
|
|
||||||
|
await deliverSessionMaintenanceWarning(createParams());
|
||||||
|
|
||||||
|
expect(mocks.enqueueSystemEvent).toHaveBeenCalledWith(
|
||||||
|
expect.stringContaining("older than 1 second"),
|
||||||
|
expect.objectContaining({ sessionKey: expect.stringContaining("agent:") }),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,42 @@ describe("system-presence", () => {
|
||||||
expect(entry?.scopes).toEqual(expect.arrayContaining(["operator.admin", "system.run"]));
|
expect(entry?.scopes).toEqual(expect.arrayContaining(["operator.admin", "system.run"]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("parses node presence text and normalizes the update key", () => {
|
||||||
|
const update = updateSystemPresence({
|
||||||
|
text: "Node: Relay-Host (10.0.0.9) · app 2.1.0 · last input 7s ago · mode ui · reason beacon",
|
||||||
|
instanceId: " Mixed-Case-Node ",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(update.key).toBe("mixed-case-node");
|
||||||
|
expect(update.changedKeys).toEqual(["host", "ip", "version", "mode", "reason"]);
|
||||||
|
expect(update.next).toMatchObject({
|
||||||
|
host: "Relay-Host",
|
||||||
|
ip: "10.0.0.9",
|
||||||
|
version: "2.1.0",
|
||||||
|
lastInputSeconds: 7,
|
||||||
|
mode: "ui",
|
||||||
|
reason: "beacon",
|
||||||
|
text: "Node: Relay-Host (10.0.0.9) · app 2.1.0 · last input 7s ago · mode ui · reason beacon",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("drops blank role and scope entries while keeping fallback text", () => {
|
||||||
|
const deviceId = randomUUID();
|
||||||
|
|
||||||
|
upsertPresence(deviceId, {
|
||||||
|
deviceId,
|
||||||
|
host: "relay-host",
|
||||||
|
mode: "operator",
|
||||||
|
roles: [" operator ", "", " "],
|
||||||
|
scopes: ["operator.admin", "", " "],
|
||||||
|
});
|
||||||
|
|
||||||
|
const entry = listSystemPresence().find((candidate) => candidate.deviceId === deviceId);
|
||||||
|
expect(entry?.roles).toEqual(["operator"]);
|
||||||
|
expect(entry?.scopes).toEqual(["operator.admin"]);
|
||||||
|
expect(entry?.text).toBe("Node: relay-host · mode operator");
|
||||||
|
});
|
||||||
|
|
||||||
it("prunes stale non-self entries after TTL", () => {
|
it("prunes stale non-self entries after TTL", () => {
|
||||||
vi.useFakeTimers();
|
vi.useFakeTimers();
|
||||||
vi.setSystemTime(Date.now());
|
vi.setSystemTime(Date.now());
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue