test(gateway): consolidate deleted-agent session fixtures (#118549)

Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
This commit is contained in:
Peter Steinberger
2026-08-03 00:02:57 -07:00
committed by GitHub
parent c6bcd93561
commit 8edfa5ee52
3 changed files with 49 additions and 67 deletions
@@ -5,22 +5,20 @@ import { vi } from "vitest";
const deletedAgentSessionMocks = vi.hoisted(() => ({
loadSessionEntry: vi.fn(),
loadSessionEntryReadOnly: vi.fn(),
resolveDeletedAgentIdFromSessionKey: vi.fn(),
}));
vi.mock("../session-utils.js", async () => {
const actual = await vi.importActual<typeof import("../session-utils.js")>("../session-utils.js");
return {
...actual,
loadSessionEntry: deletedAgentSessionMocks.loadSessionEntry,
resolveDeletedAgentIdFromSessionKey:
deletedAgentSessionMocks.resolveDeletedAgentIdFromSessionKey,
};
});
vi.mock("../session-utils.js", () => ({
loadSessionEntry: deletedAgentSessionMocks.loadSessionEntry,
loadSessionEntryReadOnly: deletedAgentSessionMocks.loadSessionEntryReadOnly,
resolveDeletedAgentIdFromSessionKey: deletedAgentSessionMocks.resolveDeletedAgentIdFromSessionKey,
}));
/** Resets mocked deleted-agent session lookups between tests. */
export function resetDeletedAgentSessionMocks(): void {
deletedAgentSessionMocks.loadSessionEntry.mockReset();
deletedAgentSessionMocks.loadSessionEntryReadOnly.mockReset();
deletedAgentSessionMocks.resolveDeletedAgentIdFromSessionKey.mockReset();
}
@@ -1,50 +0,0 @@
/**
* Tests that session send rejects sessions whose configured agent was deleted.
*/
import { expectDefined } from "@openclaw/normalization-core";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { ErrorCodes } from "../../../packages/gateway-protocol/src/index.js";
import {
mockDeletedAgentSession,
resetDeletedAgentSessionMocks,
} from "./deleted-agent-guard.test-helpers.js";
import { sessionMessagingHandlers } from "./sessions-messaging.js";
import type { GatewayRequestContext, RespondFn } from "./types.js";
describe("sessions.send / sessions.steer deleted-agent guard", () => {
beforeEach(() => {
resetDeletedAgentSessionMocks();
});
for (const method of ["sessions.send", "sessions.steer"] as const) {
it(`${method} rejects keys belonging to a deleted agent`, async () => {
const orphanKey = mockDeletedAgentSession();
const respond = vi.fn() as unknown as RespondFn;
const context = {
chatAbortControllers: new Map(),
broadcastToConnIds: vi.fn(),
getSessionEventSubscriberConnIds: () => new Set<string>(),
getRuntimeConfig: () => ({}),
} as unknown as GatewayRequestContext;
await expectDefined(
sessionMessagingHandlers[method],
"sessionMessagingHandlers[method] test invariant",
)({
req: { id: "req-1" } as never,
params: { key: orphanKey, message: "hi" },
respond,
context,
client: null,
isWebchatConnect: () => false,
});
expect(respond).toHaveBeenCalledWith(false, undefined, {
code: ErrorCodes.INVALID_REQUEST,
message: 'Agent "deleted-agent" no longer exists in configuration',
});
});
}
});
@@ -11,8 +11,10 @@ import { expectSubagentFollowupReactivation } from "./subagent-followup.test-hel
import type { GatewayRequestContext, RespondFn } from "./types.js";
const loadSessionEntryMock = vi.fn();
const loadSessionEntryReadOnlyMock = vi.fn();
const readSessionMessageCountAsyncMock = vi.fn();
const loadGatewaySessionRowMock = vi.fn();
const resolveDeletedAgentIdFromSessionKeyMock = vi.fn();
const getLatestSubagentRunByChildSessionKeyMock = vi.fn();
const replaceSubagentRunAfterSteerMock = vi.fn();
const chatSendMock = vi.fn();
@@ -45,14 +47,13 @@ vi.mock("../../auto-reply/reply/queue/cleanup.js", async () => {
};
});
vi.mock("../session-utils.js", async () => {
const actual = await vi.importActual<typeof import("../session-utils.js")>("../session-utils.js");
return {
...actual,
loadSessionEntry: (...args: unknown[]) => loadSessionEntryMock(...args),
loadGatewaySessionRow: (...args: unknown[]) => loadGatewaySessionRowMock(...args),
};
});
vi.mock("../session-utils.js", () => ({
loadSessionEntry: (...args: unknown[]) => loadSessionEntryMock(...args),
loadSessionEntryReadOnly: (...args: unknown[]) => loadSessionEntryReadOnlyMock(...args),
loadGatewaySessionRow: (...args: unknown[]) => loadGatewaySessionRowMock(...args),
resolveDeletedAgentIdFromSessionKey: (...args: unknown[]) =>
resolveDeletedAgentIdFromSessionKeyMock(...args),
}));
vi.mock("../session-transcript-readers.js", async () => {
const actual = await vi.importActual<typeof import("../session-transcript-readers.js")>(
@@ -112,8 +113,10 @@ function createRequestContext(overrides: Record<string, unknown> = {}): GatewayR
describe("sessions.send completed subagent follow-up status", () => {
beforeEach(() => {
loadSessionEntryMock.mockReset();
loadSessionEntryReadOnlyMock.mockReset();
readSessionMessageCountAsyncMock.mockReset().mockResolvedValue(0);
loadGatewaySessionRowMock.mockReset();
resolveDeletedAgentIdFromSessionKeyMock.mockReset().mockReturnValue(null);
getLatestSubagentRunByChildSessionKeyMock.mockReset();
replaceSubagentRunAfterSteerMock.mockReset();
chatSendMock.mockReset();
@@ -137,6 +140,37 @@ describe("sessions.send completed subagent follow-up status", () => {
);
});
for (const method of ["sessions.send", "sessions.steer"] as const) {
it(`${method} rejects keys belonging to a deleted agent`, async () => {
const orphanKey = "agent:deleted-agent:main";
loadSessionEntryMock.mockReturnValue({
cfg: {},
canonicalKey: orphanKey,
storePath: "/tmp/sessions.json",
entry: { sessionId: "sess-orphan" },
});
resolveDeletedAgentIdFromSessionKeyMock.mockReturnValue("deleted-agent");
const respondMock = vi.fn();
await expectDefined(
sessionMessagingHandlers[method],
"sessionMessagingHandlers[method] test invariant",
)({
req: { id: "req-deleted-agent" } as never,
params: { key: orphanKey, message: "hi" },
respond: respondMock as unknown as RespondFn,
context: createRequestContext(),
client: null,
isWebchatConnect: () => false,
});
expect(respondMock).toHaveBeenCalledWith(false, undefined, {
code: ErrorCodes.INVALID_REQUEST,
message: 'Agent "deleted-agent" no longer exists in configuration',
});
});
}
it("reactivates completed subagent sessions before broadcasting sessions.changed", async () => {
const childSessionKey = "agent:main:subagent:followup";
const completedRun = {