test(qa-lab): release cached databases before removing fixture temp dirs (#128335)

Five qa-lab fixtures point a state directory at a temporary workspace and then
remove it while the shared and per-agent SQLite handles opened underneath are
still cached, so Windows fails the removal with EBUSY while Linux unlinks the
open files and stays green.

Release both before each removal, in the ordering merged for this class: the
agent close releases its leases through shared state and reopens it, so the
shared store is released second.
This commit is contained in:
Yiğit ERDOĞAN
2026-08-23 23:36:49 +03:00
committed by GitHub
parent 1be9b56d57
commit 40c88ebefd
5 changed files with 40 additions and 2 deletions
@@ -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();
});
@@ -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();
});
@@ -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();
});
@@ -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 })),
);
@@ -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();
});