test: scope subagent sqlite state env

This commit is contained in:
Shakker
2026-06-05 16:48:32 +01:00
parent 1e683ff245
commit c85b0ee3db

View File

@@ -8,7 +8,7 @@ import {
closeOpenClawStateDatabaseForTest,
openOpenClawStateDatabase,
} from "../state/openclaw-state-db.js";
import { captureEnv } from "../test-utils/env.js";
import { withEnvAsync } from "../test-utils/env.js";
import {
loadSubagentRegistryFromSqlite,
saveSubagentRegistryToSqlite,
@@ -56,12 +56,10 @@ function createRun(overrides: Partial<SubagentRunRecord> = {}): SubagentRunRecor
}
describe("subagent registry sqlite store", () => {
const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]);
let tempStateDir: string | null = null;
beforeEach(async () => {
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-subagent-sqlite-"));
process.env.OPENCLAW_STATE_DIR = tempStateDir;
});
afterEach(async () => {
@@ -70,69 +68,81 @@ describe("subagent registry sqlite store", () => {
await fs.rm(tempStateDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 });
tempStateDir = null;
}
envSnapshot.restore();
});
async function withTempStateEnv<T>(fn: () => Promise<T>): Promise<T> {
if (!tempStateDir) {
throw new Error("expected temp state dir");
}
return await withEnvAsync({ OPENCLAW_STATE_DIR: tempStateDir }, fn);
}
it("persists subagent runs in the shared sqlite state database", async () => {
const run = createRun();
await withTempStateEnv(async () => {
const run = createRun();
saveSubagentRegistryToSqlite(new Map([[run.runId, run]]));
saveSubagentRegistryToSqlite(new Map([[run.runId, run]]));
const restored = loadSubagentRegistryFromSqlite();
expect(restored.get(run.runId)).toMatchObject({
runId: run.runId,
childSessionKey: run.childSessionKey,
requesterSessionKey: run.requesterSessionKey,
task: run.task,
endedAt: run.endedAt,
outcome: run.outcome,
completion: run.completion,
delivery: run.delivery,
const restored = loadSubagentRegistryFromSqlite();
expect(restored.get(run.runId)).toMatchObject({
runId: run.runId,
childSessionKey: run.childSessionKey,
requesterSessionKey: run.requesterSessionKey,
task: run.task,
endedAt: run.endedAt,
outcome: run.outcome,
completion: run.completion,
delivery: run.delivery,
});
expect(await fs.stat(path.join(tempStateDir!, "state", "openclaw.sqlite"))).toBeTruthy();
await expect(fs.stat(path.join(tempStateDir!, "subagents", "runs.json"))).rejects.toThrow();
});
expect(await fs.stat(path.join(tempStateDir!, "state", "openclaw.sqlite"))).toBeTruthy();
await expect(fs.stat(path.join(tempStateDir!, "subagents", "runs.json"))).rejects.toThrow();
});
it("uses save calls as whole-registry snapshots", () => {
const first = createRun({ runId: "run-one", childSessionKey: "agent:main:subagent:one" });
const second = createRun({ runId: "run-two", childSessionKey: "agent:main:subagent:two" });
it("uses save calls as whole-registry snapshots", async () => {
await withTempStateEnv(async () => {
const first = createRun({ runId: "run-one", childSessionKey: "agent:main:subagent:one" });
const second = createRun({ runId: "run-two", childSessionKey: "agent:main:subagent:two" });
saveSubagentRegistryToSqlite(
new Map([
[first.runId, first],
[second.runId, second],
]),
);
saveSubagentRegistryToSqlite(new Map([[second.runId, second]]));
saveSubagentRegistryToSqlite(
new Map([
[first.runId, first],
[second.runId, second],
]),
);
saveSubagentRegistryToSqlite(new Map([[second.runId, second]]));
expect([...loadSubagentRegistryFromSqlite().keys()]).toEqual(["run-two"]);
expect([...loadSubagentRegistryFromSqlite().keys()]).toEqual(["run-two"]);
});
});
it("imports the legacy json registry when sqlite has no runs", async () => {
// Import deletes the JSON source after the first successful migration so
// later loads treat SQLite as canonical state.
const legacyRun = createRun({
runId: "legacy-run",
childSessionKey: "agent:main:subagent:legacy",
task: "import legacy registry",
await withTempStateEnv(async () => {
// Import deletes the JSON source after the first successful migration so
// later loads treat SQLite as canonical state.
const legacyRun = createRun({
runId: "legacy-run",
childSessionKey: "agent:main:subagent:legacy",
task: "import legacy registry",
});
const registryPath = path.join(tempStateDir!, "subagents", "runs.json");
await fs.mkdir(path.dirname(registryPath), { recursive: true });
await fs.writeFile(
registryPath,
`${JSON.stringify({ version: 2, runs: { [legacyRun.runId]: legacyRun } })}\n`,
"utf8",
);
const imported = loadSubagentRegistryFromSqlite();
expect(imported.get(legacyRun.runId)?.task).toBe("import legacy registry");
await expect(fs.stat(registryPath)).rejects.toThrow();
expect(loadSubagentRegistryFromSqlite().get(legacyRun.runId)?.task).toBe(
"import legacy registry",
);
expect(
openOpenClawStateDatabase().db.prepare("SELECT COUNT(*) AS count FROM subagent_runs").get(),
).toEqual({ count: 1 });
});
const registryPath = path.join(tempStateDir!, "subagents", "runs.json");
await fs.mkdir(path.dirname(registryPath), { recursive: true });
await fs.writeFile(
registryPath,
`${JSON.stringify({ version: 2, runs: { [legacyRun.runId]: legacyRun } })}\n`,
"utf8",
);
const imported = loadSubagentRegistryFromSqlite();
expect(imported.get(legacyRun.runId)?.task).toBe("import legacy registry");
await expect(fs.stat(registryPath)).rejects.toThrow();
expect(loadSubagentRegistryFromSqlite().get(legacyRun.runId)?.task).toBe(
"import legacy registry",
);
expect(
openOpenClawStateDatabase().db.prepare("SELECT COUNT(*) AS count FROM subagent_runs").get(),
).toEqual({ count: 1 });
});
});