From beffb6fe48baac0f920bcb908b1f691e8047ffb4 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 15:09:26 +0000 Subject: [PATCH] refactor(test): dedupe session-memory hook setup --- .../bundled/session-memory/handler.test.ts | 292 ++++-------------- 1 file changed, 64 insertions(+), 228 deletions(-) diff --git a/src/hooks/bundled/session-memory/handler.test.ts b/src/hooks/bundled/session-memory/handler.test.ts index a8723093f..3ccaae032 100644 --- a/src/hooks/bundled/session-memory/handler.test.ts +++ b/src/hooks/bundled/session-memory/handler.test.ts @@ -40,6 +40,44 @@ function createMockSessionContent( .join("\n"); } +async function runNewWithPreviousSession(params: { + sessionContent: string; + cfg?: (tempDir: string) => OpenClawConfig; +}): Promise<{ tempDir: string; files: string[]; memoryContent: string }> { + const tempDir = await makeTempWorkspace("openclaw-session-memory-"); + const sessionsDir = path.join(tempDir, "sessions"); + await fs.mkdir(sessionsDir, { recursive: true }); + + const sessionFile = await writeWorkspaceFile({ + dir: sessionsDir, + name: "test-session.jsonl", + content: params.sessionContent, + }); + + const cfg = + params.cfg?.(tempDir) ?? + ({ + agents: { defaults: { workspace: tempDir } }, + } satisfies OpenClawConfig); + + const event = createHookEvent("command", "new", "agent:main:main", { + cfg, + previousSessionEntry: { + sessionId: "test-123", + sessionFile, + }, + }); + + await handler(event); + + const memoryDir = path.join(tempDir, "memory"); + const files = await fs.readdir(memoryDir); + const memoryContent = + files.length > 0 ? await fs.readFile(path.join(memoryDir, files[0]), "utf-8") : ""; + + return { tempDir, files, memoryContent }; +} + describe("session-memory hook", () => { it("skips non-command events", async () => { const tempDir = await makeTempWorkspace("openclaw-session-memory-"); @@ -70,10 +108,6 @@ describe("session-memory hook", () => { }); it("creates memory file with session content on /new command", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - // Create a mock session file with user/assistant messages const sessionContent = createMockSessionContent([ { role: "user", content: "Hello there" }, @@ -81,33 +115,10 @@ describe("session-memory hook", () => { { role: "user", content: "What is 2+2?" }, { role: "assistant", content: "2+2 equals 4" }, ]); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, - }); - - await handler(event); - - // Memory file should be created - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); + const { files, memoryContent } = await runNewWithPreviousSession({ sessionContent }); expect(files.length).toBe(1); // Read the memory file and verify content - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); expect(memoryContent).toContain("user: Hello there"); expect(memoryContent).toContain("assistant: Hi! How can I help?"); expect(memoryContent).toContain("user: What is 2+2?"); @@ -115,10 +126,6 @@ describe("session-memory hook", () => { }); it("filters out non-message entries (tool calls, system)", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - // Create session with mixed entry types const sessionContent = createMockSessionContent([ { role: "user", content: "Hello" }, @@ -127,29 +134,7 @@ describe("session-memory hook", () => { { type: "tool_result", result: "found it" }, { role: "user", content: "Thanks" }, ]); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, - }); - - await handler(event); - - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); + const { memoryContent } = await runNewWithPreviousSession({ sessionContent }); // Only user/assistant messages should be present expect(memoryContent).toContain("user: Hello"); @@ -162,10 +147,6 @@ describe("session-memory hook", () => { }); it("filters out inter-session user messages", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - const sessionContent = [ JSON.stringify({ type: "message", @@ -184,29 +165,7 @@ describe("session-memory hook", () => { message: { role: "user", content: "External follow-up" }, }), ].join("\n"); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, - }); - - await handler(event); - - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); + const { memoryContent } = await runNewWithPreviousSession({ sessionContent }); expect(memoryContent).not.toContain("Forwarded internal instruction"); expect(memoryContent).toContain("assistant: Acknowledged"); @@ -214,39 +173,13 @@ describe("session-memory hook", () => { }); it("filters out command messages starting with /", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - const sessionContent = createMockSessionContent([ { role: "user", content: "/help" }, { role: "assistant", content: "Here is help info" }, { role: "user", content: "Normal message" }, { role: "user", content: "/new" }, ]); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, - }); - - await handler(event); - - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); + const { memoryContent } = await runNewWithPreviousSession({ sessionContent }); // Command messages should be filtered out expect(memoryContent).not.toContain("/help"); @@ -257,48 +190,26 @@ describe("session-memory hook", () => { }); it("respects custom messages config (limits to N messages)", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - // Create 10 messages const entries = []; for (let i = 1; i <= 10; i++) { entries.push({ role: "user", content: `Message ${i}` }); } const sessionContent = createMockSessionContent(entries); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - // Configure to only include last 3 messages - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - hooks: { - internal: { - entries: { - "session-memory": { enabled: true, messages: 3 }, + const { memoryContent } = await runNewWithPreviousSession({ + sessionContent, + cfg: (tempDir) => ({ + agents: { defaults: { workspace: tempDir } }, + hooks: { + internal: { + entries: { + "session-memory": { enabled: true, messages: 3 }, + }, }, }, - }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, + }), }); - await handler(event); - - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); - // Only last 3 messages should be present expect(memoryContent).not.toContain("user: Message 1\n"); expect(memoryContent).not.toContain("user: Message 7\n"); @@ -308,10 +219,6 @@ describe("session-memory hook", () => { }); it("filters messages before slicing (fix for #2681)", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - // Create session with many tool entries interspersed with messages // This tests that we filter FIRST, then slice - not the other way around const entries = [ @@ -327,39 +234,20 @@ describe("session-memory hook", () => { { role: "assistant", content: "Fourth message" }, ]; const sessionContent = createMockSessionContent(entries); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - // Request 3 messages - if we sliced first, we'd only get 1-2 messages - // because the last 3 lines include tool entries - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - hooks: { - internal: { - entries: { - "session-memory": { enabled: true, messages: 3 }, + const { memoryContent } = await runNewWithPreviousSession({ + sessionContent, + cfg: (tempDir) => ({ + agents: { defaults: { workspace: tempDir } }, + hooks: { + internal: { + entries: { + "session-memory": { enabled: true, messages: 3 }, + }, }, }, - }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, + }), }); - await handler(event); - - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); - // Should have exactly 3 user/assistant messages (the last 3) expect(memoryContent).not.toContain("First message"); expect(memoryContent).toContain("user: Third message"); @@ -368,70 +256,18 @@ describe("session-memory hook", () => { }); it("handles empty session files gracefully", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: "", - }); - - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, - }); - // Should not throw - await handler(event); - - // Memory file should still be created with metadata - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); + const { files } = await runNewWithPreviousSession({ sessionContent: "" }); expect(files.length).toBe(1); }); it("handles session files with fewer messages than requested", async () => { - const tempDir = await makeTempWorkspace("openclaw-session-memory-"); - const sessionsDir = path.join(tempDir, "sessions"); - await fs.mkdir(sessionsDir, { recursive: true }); - // Only 2 messages but requesting 15 (default) const sessionContent = createMockSessionContent([ { role: "user", content: "Only message 1" }, { role: "assistant", content: "Only message 2" }, ]); - const sessionFile = await writeWorkspaceFile({ - dir: sessionsDir, - name: "test-session.jsonl", - content: sessionContent, - }); - - const cfg: OpenClawConfig = { - agents: { defaults: { workspace: tempDir } }, - }; - - const event = createHookEvent("command", "new", "agent:main:main", { - cfg, - previousSessionEntry: { - sessionId: "test-123", - sessionFile, - }, - }); - - await handler(event); - - const memoryDir = path.join(tempDir, "memory"); - const files = await fs.readdir(memoryDir); - const memoryContent = await fs.readFile(path.join(memoryDir, files[0]), "utf-8"); + const { memoryContent } = await runNewWithPreviousSession({ sessionContent }); // Both messages should be included expect(memoryContent).toContain("user: Only message 1");