diff --git a/src/auto-reply/reply/session-resets.test.ts b/src/auto-reply/reply/session-resets.test.ts index 3c4810388..16c44172f 100644 --- a/src/auto-reply/reply/session-resets.test.ts +++ b/src/auto-reply/reply/session-resets.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import { buildModelAliasIndex } from "../../agents/model-selection.js"; import { formatZonedTimestamp } from "../../infra/format-time/format-datetime.ts"; @@ -17,12 +17,26 @@ vi.mock("../../agents/model-catalog.js", () => ({ ]), })); -describe("initSessionState reset triggers in WhatsApp groups", () => { - async function createStorePath(prefix: string): Promise { - const root = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); - return path.join(root, "sessions.json"); - } +let suiteRoot = ""; +let suiteCase = 0; +beforeAll(async () => { + suiteRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-resets-suite-")); +}); + +afterAll(async () => { + await fs.rm(suiteRoot, { recursive: true, force: true }); + suiteRoot = ""; + suiteCase = 0; +}); + +async function createStorePath(prefix: string): Promise { + const root = path.join(suiteRoot, `${prefix}${++suiteCase}`); + await fs.mkdir(root, { recursive: true }); + return path.join(root, "sessions.json"); +} + +describe("initSessionState reset triggers in WhatsApp groups", () => { async function seedSessionStore(params: { storePath: string; sessionKey: string; @@ -257,11 +271,6 @@ describe("initSessionState reset triggers in WhatsApp groups", () => { }); describe("initSessionState reset triggers in Slack channels", () => { - async function createStorePath(prefix: string): Promise { - const root = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); - return path.join(root, "sessions.json"); - } - async function seedSessionStore(params: { storePath: string; sessionKey: string; @@ -453,11 +462,6 @@ describe("applyResetModelOverride", () => { }); describe("initSessionState preserves behavior overrides across /new and /reset", () => { - async function createStorePath(prefix: string): Promise { - const root = await fs.mkdtemp(path.join(os.tmpdir(), prefix)); - return path.join(root, "sessions.json"); - } - async function seedSessionStoreWithOverrides(params: { storePath: string; sessionKey: string; diff --git a/src/auto-reply/reply/session.test.ts b/src/auto-reply/reply/session.test.ts index 100251cd9..ba239f610 100644 --- a/src/auto-reply/reply/session.test.ts +++ b/src/auto-reply/reply/session.test.ts @@ -1,14 +1,33 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; -import { describe, expect, it, vi } from "vitest"; +import { afterAll, beforeAll, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import { saveSessionStore } from "../../config/sessions.js"; import { initSessionState } from "./session.js"; +let suiteRoot = ""; +let suiteCase = 0; + +beforeAll(async () => { + suiteRoot = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-suite-")); +}); + +afterAll(async () => { + await fs.rm(suiteRoot, { recursive: true, force: true }); + suiteRoot = ""; + suiteCase = 0; +}); + +async function makeCaseDir(prefix: string): Promise { + const dir = path.join(suiteRoot, `${prefix}${++suiteCase}`); + await fs.mkdir(dir, { recursive: true }); + return dir; +} + describe("initSessionState thread forking", () => { it("forks a new session from the parent session file", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-thread-session-")); + const root = await makeCaseDir("openclaw-thread-session-"); const sessionsDir = path.join(root, "sessions"); await fs.mkdir(sessionsDir, { recursive: true }); @@ -80,7 +99,7 @@ describe("initSessionState thread forking", () => { }); it("records topic-specific session files when MessageThreadId is present", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-topic-session-")); + const root = await makeCaseDir("openclaw-topic-session-"); const storePath = path.join(root, "sessions.json"); const cfg = { @@ -107,7 +126,7 @@ describe("initSessionState thread forking", () => { describe("initSessionState RawBody", () => { it("triggerBodyNormalized correctly extracts commands when Body contains context but RawBody is clean", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rawbody-")); + const root = await makeCaseDir("openclaw-rawbody-"); const storePath = path.join(root, "sessions.json"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -128,7 +147,7 @@ describe("initSessionState RawBody", () => { }); it("Reset triggers (/new, /reset) work with RawBody", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rawbody-reset-")); + const root = await makeCaseDir("openclaw-rawbody-reset-"); const storePath = path.join(root, "sessions.json"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -150,7 +169,7 @@ describe("initSessionState RawBody", () => { }); it("preserves argument casing while still matching reset triggers case-insensitively", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rawbody-reset-case-")); + const root = await makeCaseDir("openclaw-rawbody-reset-case-"); const storePath = path.join(root, "sessions.json"); const cfg = { @@ -178,7 +197,7 @@ describe("initSessionState RawBody", () => { }); it("falls back to Body when RawBody is undefined", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rawbody-fallback-")); + const root = await makeCaseDir("openclaw-rawbody-fallback-"); const storePath = path.join(root, "sessions.json"); const cfg = { session: { store: storePath } } as OpenClawConfig; @@ -197,7 +216,7 @@ describe("initSessionState RawBody", () => { }); it("uses the default per-agent sessions store when config store is unset", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-session-store-default-")); + const root = await makeCaseDir("openclaw-session-store-default-"); const stateDir = path.join(root, ".openclaw"); const agentId = "worker1"; const sessionKey = `agent:${agentId}:telegram:12345`; @@ -243,7 +262,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 5, 0, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-daily-")); + const root = await makeCaseDir("openclaw-reset-daily-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:whatsapp:dm:s1"; const existingSessionId = "daily-session-id"; @@ -273,7 +292,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 3, 0, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-daily-edge-")); + const root = await makeCaseDir("openclaw-reset-daily-edge-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:whatsapp:dm:s-edge"; const existingSessionId = "daily-edge-session"; @@ -303,7 +322,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 5, 30, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-idle-")); + const root = await makeCaseDir("openclaw-reset-idle-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:whatsapp:dm:s2"; const existingSessionId = "idle-session-id"; @@ -338,7 +357,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 5, 0, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-thread-")); + const root = await makeCaseDir("openclaw-reset-thread-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:slack:channel:c1:thread:123"; const existingSessionId = "thread-session-id"; @@ -374,7 +393,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 5, 0, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-thread-nosuffix-")); + const root = await makeCaseDir("openclaw-reset-thread-nosuffix-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:discord:channel:c1"; const existingSessionId = "thread-nosuffix"; @@ -409,7 +428,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 5, 0, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-type-default-")); + const root = await makeCaseDir("openclaw-reset-type-default-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:whatsapp:dm:s4"; const existingSessionId = "type-default-session"; @@ -444,7 +463,7 @@ describe("initSessionState reset policy", () => { vi.useFakeTimers(); vi.setSystemTime(new Date(2026, 0, 18, 5, 0, 0)); try { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-reset-legacy-")); + const root = await makeCaseDir("openclaw-reset-legacy-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:whatsapp:dm:s3"; const existingSessionId = "legacy-session-id"; @@ -478,7 +497,7 @@ describe("initSessionState reset policy", () => { describe("initSessionState channel reset overrides", () => { it("uses channel-specific reset policy when configured", async () => { - const root = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-channel-idle-")); + const root = await makeCaseDir("openclaw-channel-idle-"); const storePath = path.join(root, "sessions.json"); const sessionKey = "agent:main:discord:dm:123"; const sessionId = "session-override";