diff --git a/extensions/qa-lab/src/runtime-parity-cache-diagnostics.test.ts b/extensions/qa-lab/src/runtime-parity-cache-diagnostics.test.ts index c3092eed9511..97c72be0ff2d 100644 --- a/extensions/qa-lab/src/runtime-parity-cache-diagnostics.test.ts +++ b/extensions/qa-lab/src/runtime-parity-cache-diagnostics.test.ts @@ -1,7 +1,11 @@ import path from "node:path"; +import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime"; import { resolveStorePath, upsertSessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; import { appendSessionTranscriptMessageByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime"; -import { formatSqliteSessionFileMarker } from "openclaw/plugin-sdk/sqlite-runtime-testing"; +import { + closeOpenClawAgentDatabasesForTest, + formatSqliteSessionFileMarker, +} from "openclaw/plugin-sdk/sqlite-runtime-testing"; import { afterEach, describe, expect, it } from "vitest"; import { buildRuntimeParityCacheDiagnostics } from "./runtime-parity-cache-diagnostics.js"; import { captureRuntimeParityCell, type RuntimeParityUsage } from "./runtime-parity.js"; @@ -10,6 +14,11 @@ import { createTempDirHarness } from "./temp-dir.test-helper.js"; const tempDirs = createTempDirHarness(); afterEach(async () => { + // Fixtures point a state dir at these temp workspaces, so the shared and per-agent + // SQLite handles stay cached and Windows fails the removal with EBUSY. The agent close + // releases its leases through shared state and reopens it, so the store is released second. + closeOpenClawAgentDatabasesForTest(); + resetPluginStateStoreForTests(); await tempDirs.cleanup(); }); diff --git a/extensions/qa-lab/src/runtime-parity-session-selection.test.ts b/extensions/qa-lab/src/runtime-parity-session-selection.test.ts index 833e5a45d037..92b6e85aaae1 100644 --- a/extensions/qa-lab/src/runtime-parity-session-selection.test.ts +++ b/extensions/qa-lab/src/runtime-parity-session-selection.test.ts @@ -1,8 +1,10 @@ import path from "node:path"; +import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime"; import { resolveStorePath, upsertSessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; import { appendSessionTranscriptMessageByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime"; import { appendSqliteTrajectoryRuntimeEvents, + closeOpenClawAgentDatabasesForTest, formatSqliteSessionFileMarker, } from "openclaw/plugin-sdk/sqlite-runtime-testing"; import { afterEach, describe, expect, it } from "vitest"; @@ -12,6 +14,11 @@ import { createTempDirHarness } from "./temp-dir.test-helper.js"; const tempDirs = createTempDirHarness(); afterEach(async () => { + // Fixtures point a state dir at these temp workspaces, so the shared and per-agent + // SQLite handles stay cached and Windows fails the removal with EBUSY. The agent close + // releases its leases through shared state and reopens it, so the store is released second. + closeOpenClawAgentDatabasesForTest(); + resetPluginStateStoreForTests(); await tempDirs.cleanup(); }); diff --git a/extensions/qa-lab/src/runtime-parity.test.ts b/extensions/qa-lab/src/runtime-parity.test.ts index f8257d0d61f3..3ae9c198e132 100644 --- a/extensions/qa-lab/src/runtime-parity.test.ts +++ b/extensions/qa-lab/src/runtime-parity.test.ts @@ -2,10 +2,12 @@ import { createServer } from "node:http"; import type { AddressInfo } from "node:net"; import path from "node:path"; +import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime"; import { resolveStorePath, upsertSessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; import { appendSessionTranscriptMessageByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime"; import { appendSqliteTrajectoryRuntimeEvents, + closeOpenClawAgentDatabasesForTest, formatSqliteSessionFileMarker, } from "openclaw/plugin-sdk/sqlite-runtime-testing"; import { afterEach, describe, expect, it, vi } from "vitest"; @@ -25,6 +27,11 @@ const tempDirs = createTempDirHarness(); afterEach(async () => { vi.unstubAllGlobals(); + // Fixtures point a state dir at these temp workspaces, so the shared and per-agent + // SQLite handles stay cached and Windows fails the removal with EBUSY. The agent close + // releases its leases through shared state and reopens it, so the store is released second. + closeOpenClawAgentDatabasesForTest(); + resetPluginStateStoreForTests(); await tempDirs.cleanup(); }); diff --git a/extensions/qa-lab/src/runtime-tool-fixture.test.ts b/extensions/qa-lab/src/runtime-tool-fixture.test.ts index 4a8c1ad9502e..fc32e153569a 100644 --- a/extensions/qa-lab/src/runtime-tool-fixture.test.ts +++ b/extensions/qa-lab/src/runtime-tool-fixture.test.ts @@ -2,8 +2,10 @@ import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime"; import { upsertSessionEntry } from "openclaw/plugin-sdk/session-store-runtime"; import { appendSessionTranscriptMessageByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime"; +import { closeOpenClawAgentDatabasesForTest } from "openclaw/plugin-sdk/sqlite-runtime-testing"; import { afterEach, describe, expect, it, vi } from "vitest"; import { QaSuiteInfraError } from "./errors.js"; import { runRuntimeToolFixture } from "./runtime-tool-fixture.js"; @@ -437,6 +439,10 @@ async function runMockRuntimeToolFixtureWithOutputs(params: { } afterEach(async () => { + // The session store keeps the state database open under the temporary root, so + // Windows fails the removal with EBUSY unless the cached handle is released first. + closeOpenClawAgentDatabasesForTest(); + resetPluginStateStoreForTests(); await Promise.all( tempRoots.splice(0).map((tempRoot) => fs.rm(tempRoot, { recursive: true, force: true })), ); diff --git a/extensions/qa-lab/src/suite-runtime-agent-session.test.ts b/extensions/qa-lab/src/suite-runtime-agent-session.test.ts index 04d7740541ac..dd75bc7b1380 100644 --- a/extensions/qa-lab/src/suite-runtime-agent-session.test.ts +++ b/extensions/qa-lab/src/suite-runtime-agent-session.test.ts @@ -1,12 +1,16 @@ // Qa Lab tests cover suite runtime agent session plugin behavior. import fs from "node:fs/promises"; import path from "node:path"; +import { resetPluginStateStoreForTests } from "openclaw/plugin-sdk/plugin-state-test-runtime"; import { loadTranscriptEventsSync, upsertSessionEntry, } from "openclaw/plugin-sdk/session-store-runtime"; import { appendSessionTranscriptMessageByIdentity } from "openclaw/plugin-sdk/session-transcript-runtime"; -import { appendSqliteSessionTranscriptEventForTest } from "openclaw/plugin-sdk/sqlite-runtime-testing"; +import { + closeOpenClawAgentDatabasesForTest, + appendSqliteSessionTranscriptEventForTest, +} from "openclaw/plugin-sdk/sqlite-runtime-testing"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createSession, @@ -22,6 +26,11 @@ const { cleanup, makeTempDir } = createTempDirHarness(); afterEach(async () => { vi.useRealTimers(); + // Fixtures point a state dir at these temp workspaces, so the shared and per-agent + // SQLite handles stay cached and Windows fails the removal with EBUSY. The agent close + // releases its leases through shared state and reopens it, so the store is released second. + closeOpenClawAgentDatabasesForTest(); + resetPluginStateStoreForTests(); await cleanup(); });