mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 11:28:38 +00:00
test: dedupe lifecycle oauth and prompt-limit fixtures
This commit is contained in:
@@ -52,6 +52,25 @@ function createPromptRequest(
|
|||||||
} as unknown as PromptRequest;
|
} as unknown as PromptRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function expectOversizedPromptRejected(params: { sessionId: string; text: string }) {
|
||||||
|
const request = vi.fn(async () => ({ ok: true })) as GatewayClient["request"];
|
||||||
|
const sessionStore = createInMemorySessionStore();
|
||||||
|
const agent = new AcpGatewayAgent(createConnection(), createGateway(request), {
|
||||||
|
sessionStore,
|
||||||
|
});
|
||||||
|
await agent.loadSession(createLoadSessionRequest(params.sessionId));
|
||||||
|
|
||||||
|
await expect(agent.prompt(createPromptRequest(params.sessionId, params.text))).rejects.toThrow(
|
||||||
|
/maximum allowed size/i,
|
||||||
|
);
|
||||||
|
expect(request).not.toHaveBeenCalledWith("chat.send", expect.anything(), expect.anything());
|
||||||
|
const session = sessionStore.getSession(params.sessionId);
|
||||||
|
expect(session?.activeRunId).toBeNull();
|
||||||
|
expect(session?.abortController).toBeNull();
|
||||||
|
|
||||||
|
sessionStore.clearAllSessionsForTest();
|
||||||
|
}
|
||||||
|
|
||||||
describe("acp session creation rate limit", () => {
|
describe("acp session creation rate limit", () => {
|
||||||
it("rate limits excessive newSession bursts", async () => {
|
it("rate limits excessive newSession bursts", async () => {
|
||||||
const sessionStore = createInMemorySessionStore();
|
const sessionStore = createInMemorySessionStore();
|
||||||
@@ -94,42 +113,16 @@ describe("acp session creation rate limit", () => {
|
|||||||
|
|
||||||
describe("acp prompt size hardening", () => {
|
describe("acp prompt size hardening", () => {
|
||||||
it("rejects oversized prompt blocks without leaking active runs", async () => {
|
it("rejects oversized prompt blocks without leaking active runs", async () => {
|
||||||
const request = vi.fn(async () => ({ ok: true })) as GatewayClient["request"];
|
await expectOversizedPromptRejected({
|
||||||
const sessionStore = createInMemorySessionStore();
|
sessionId: "prompt-limit-oversize",
|
||||||
const agent = new AcpGatewayAgent(createConnection(), createGateway(request), {
|
text: "a".repeat(2 * 1024 * 1024 + 1),
|
||||||
sessionStore,
|
|
||||||
});
|
});
|
||||||
const sessionId = "prompt-limit-oversize";
|
|
||||||
await agent.loadSession(createLoadSessionRequest(sessionId));
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
agent.prompt(createPromptRequest(sessionId, "a".repeat(2 * 1024 * 1024 + 1))),
|
|
||||||
).rejects.toThrow(/maximum allowed size/i);
|
|
||||||
expect(request).not.toHaveBeenCalledWith("chat.send", expect.anything(), expect.anything());
|
|
||||||
const session = sessionStore.getSession(sessionId);
|
|
||||||
expect(session?.activeRunId).toBeNull();
|
|
||||||
expect(session?.abortController).toBeNull();
|
|
||||||
|
|
||||||
sessionStore.clearAllSessionsForTest();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("rejects oversize final messages from cwd prefix without leaking active runs", async () => {
|
it("rejects oversize final messages from cwd prefix without leaking active runs", async () => {
|
||||||
const request = vi.fn(async () => ({ ok: true })) as GatewayClient["request"];
|
await expectOversizedPromptRejected({
|
||||||
const sessionStore = createInMemorySessionStore();
|
sessionId: "prompt-limit-prefix",
|
||||||
const agent = new AcpGatewayAgent(createConnection(), createGateway(request), {
|
text: "a".repeat(2 * 1024 * 1024),
|
||||||
sessionStore,
|
|
||||||
});
|
});
|
||||||
const sessionId = "prompt-limit-prefix";
|
|
||||||
await agent.loadSession(createLoadSessionRequest(sessionId));
|
|
||||||
|
|
||||||
await expect(
|
|
||||||
agent.prompt(createPromptRequest(sessionId, "a".repeat(2 * 1024 * 1024))),
|
|
||||||
).rejects.toThrow(/maximum allowed size/i);
|
|
||||||
expect(request).not.toHaveBeenCalledWith("chat.send", expect.anything(), expect.anything());
|
|
||||||
const session = sessionStore.getSession(sessionId);
|
|
||||||
expect(session?.activeRunId).toBeNull();
|
|
||||||
expect(session?.abortController).toBeNull();
|
|
||||||
|
|
||||||
sessionStore.clearAllSessionsForTest();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,6 +14,27 @@ const urlToString = (url: Request | URL | string): string => {
|
|||||||
return "url" in url ? url.url : String(url);
|
return "url" in url ? url.url : String(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function createStoredCredential(
|
||||||
|
now: number,
|
||||||
|
): Parameters<typeof refreshChutesTokens>[0]["credential"] {
|
||||||
|
return {
|
||||||
|
access: "at_old",
|
||||||
|
refresh: "rt_old",
|
||||||
|
expires: now - 10_000,
|
||||||
|
email: "fred",
|
||||||
|
clientId: "cid_test",
|
||||||
|
} as unknown as Parameters<typeof refreshChutesTokens>[0]["credential"];
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectRefreshedCredential(
|
||||||
|
refreshed: Awaited<ReturnType<typeof refreshChutesTokens>>,
|
||||||
|
now: number,
|
||||||
|
) {
|
||||||
|
expect(refreshed.access).toBe("at_new");
|
||||||
|
expect(refreshed.refresh).toBe("rt_old");
|
||||||
|
expect(refreshed.expires).toBe(now + 1800 * 1000 - 5 * 60 * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
describe("chutes-oauth", () => {
|
describe("chutes-oauth", () => {
|
||||||
it("exchanges code for tokens and stores username as email", async () => {
|
it("exchanges code for tokens and stores username as email", async () => {
|
||||||
const fetchFn = withFetchPreconnect(async (input: RequestInfo | URL, init?: RequestInit) => {
|
const fetchFn = withFetchPreconnect(async (input: RequestInfo | URL, init?: RequestInit) => {
|
||||||
@@ -87,20 +108,12 @@ describe("chutes-oauth", () => {
|
|||||||
|
|
||||||
const now = 2_000_000;
|
const now = 2_000_000;
|
||||||
const refreshed = await refreshChutesTokens({
|
const refreshed = await refreshChutesTokens({
|
||||||
credential: {
|
credential: createStoredCredential(now),
|
||||||
access: "at_old",
|
|
||||||
refresh: "rt_old",
|
|
||||||
expires: now - 10_000,
|
|
||||||
email: "fred",
|
|
||||||
clientId: "cid_test",
|
|
||||||
} as unknown as Parameters<typeof refreshChutesTokens>[0]["credential"],
|
|
||||||
fetchFn,
|
fetchFn,
|
||||||
now,
|
now,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(refreshed.access).toBe("at_new");
|
expectRefreshedCredential(refreshed, now);
|
||||||
expect(refreshed.refresh).toBe("rt_old");
|
|
||||||
expect(refreshed.expires).toBe(now + 1800 * 1000 - 5 * 60 * 1000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("refreshes tokens and ignores empty refresh_token values", async () => {
|
it("refreshes tokens and ignores empty refresh_token values", async () => {
|
||||||
@@ -122,19 +135,11 @@ describe("chutes-oauth", () => {
|
|||||||
|
|
||||||
const now = 3_000_000;
|
const now = 3_000_000;
|
||||||
const refreshed = await refreshChutesTokens({
|
const refreshed = await refreshChutesTokens({
|
||||||
credential: {
|
credential: createStoredCredential(now),
|
||||||
access: "at_old",
|
|
||||||
refresh: "rt_old",
|
|
||||||
expires: now - 10_000,
|
|
||||||
email: "fred",
|
|
||||||
clientId: "cid_test",
|
|
||||||
} as unknown as Parameters<typeof refreshChutesTokens>[0]["credential"],
|
|
||||||
fetchFn,
|
fetchFn,
|
||||||
now,
|
now,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(refreshed.access).toBe("at_new");
|
expectRefreshedCredential(refreshed, now);
|
||||||
expect(refreshed.refresh).toBe("rt_old");
|
|
||||||
expect(refreshed.expires).toBe(now + 1800 * 1000 - 5 * 60 * 1000);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -32,6 +32,20 @@ async function getSessionsSpawnTool(opts: CreateOpenClawToolsOpts) {
|
|||||||
type GatewayRequest = { method?: string; params?: unknown };
|
type GatewayRequest = { method?: string; params?: unknown };
|
||||||
type AgentWaitCall = { runId?: string; timeoutMs?: number };
|
type AgentWaitCall = { runId?: string; timeoutMs?: number };
|
||||||
|
|
||||||
|
function buildDiscordCleanupHooks(onDelete: (key: string | undefined) => void) {
|
||||||
|
return {
|
||||||
|
onAgentSubagentSpawn: (params: unknown) => {
|
||||||
|
const rec = params as { channel?: string; timeout?: number } | undefined;
|
||||||
|
expect(rec?.channel).toBe("discord");
|
||||||
|
expect(rec?.timeout).toBe(1);
|
||||||
|
},
|
||||||
|
onSessionsDelete: (params: unknown) => {
|
||||||
|
const rec = params as { key?: string } | undefined;
|
||||||
|
onDelete(rec?.key);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function setupSessionsSpawnGatewayMock(opts: {
|
function setupSessionsSpawnGatewayMock(opts: {
|
||||||
includeSessionsList?: boolean;
|
includeSessionsList?: boolean;
|
||||||
includeChatHistory?: boolean;
|
includeChatHistory?: boolean;
|
||||||
@@ -216,15 +230,9 @@ describe("openclaw-tools: subagents (sessions_spawn lifecycle)", () => {
|
|||||||
callGatewayMock.mockReset();
|
callGatewayMock.mockReset();
|
||||||
let deletedKey: string | undefined;
|
let deletedKey: string | undefined;
|
||||||
const ctx = setupSessionsSpawnGatewayMock({
|
const ctx = setupSessionsSpawnGatewayMock({
|
||||||
onAgentSubagentSpawn: (params) => {
|
...buildDiscordCleanupHooks((key) => {
|
||||||
const rec = params as { channel?: string; timeout?: number } | undefined;
|
deletedKey = key;
|
||||||
expect(rec?.channel).toBe("discord");
|
}),
|
||||||
expect(rec?.timeout).toBe(1);
|
|
||||||
},
|
|
||||||
onSessionsDelete: (params) => {
|
|
||||||
const rec = params as { key?: string } | undefined;
|
|
||||||
deletedKey = rec?.key;
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const tool = await getSessionsSpawnTool({
|
const tool = await getSessionsSpawnTool({
|
||||||
@@ -309,15 +317,9 @@ describe("openclaw-tools: subagents (sessions_spawn lifecycle)", () => {
|
|||||||
let deletedKey: string | undefined;
|
let deletedKey: string | undefined;
|
||||||
const ctx = setupSessionsSpawnGatewayMock({
|
const ctx = setupSessionsSpawnGatewayMock({
|
||||||
includeChatHistory: true,
|
includeChatHistory: true,
|
||||||
onAgentSubagentSpawn: (params) => {
|
...buildDiscordCleanupHooks((key) => {
|
||||||
const rec = params as { channel?: string; timeout?: number } | undefined;
|
deletedKey = key;
|
||||||
expect(rec?.channel).toBe("discord");
|
}),
|
||||||
expect(rec?.timeout).toBe(1);
|
|
||||||
},
|
|
||||||
onSessionsDelete: (params) => {
|
|
||||||
const rec = params as { key?: string } | undefined;
|
|
||||||
deletedKey = rec?.key;
|
|
||||||
},
|
|
||||||
agentWaitResult: { status: "ok", startedAt: 3000, endedAt: 4000 },
|
agentWaitResult: { status: "ok", startedAt: 3000, endedAt: 4000 },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user