test: cover sqlite persisted enum parsing

This commit is contained in:
Peter Steinberger
2026-05-09 02:32:53 +01:00
parent a2d7af0132
commit bef71f76da
4 changed files with 99 additions and 2 deletions

View File

@@ -1,9 +1,11 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { afterEach, describe, expect, expectTypeOf, it } from "vitest";
import { closeOpenClawAgentDatabasesForTest } from "../../state/openclaw-agent-db.js";
import { closeOpenClawStateDatabaseForTest } from "../../state/openclaw-state-db.js";
import type { VirtualAgentFsEntry } from "./agent-filesystem.js";
import { parseVirtualAgentFsEntryKind } from "./agent-filesystem.js";
import { createSqliteVirtualAgentFs } from "./virtual-agent-fs.sqlite.js";
function createTempStateDir(): string {
@@ -16,6 +18,21 @@ afterEach(() => {
});
describe("SqliteVirtualAgentFs", () => {
it("types public results and rejects invalid persisted entry kinds", () => {
const scratch = createSqliteVirtualAgentFs({
agentId: "main",
namespace: "scratch",
env: { OPENCLAW_STATE_DIR: createTempStateDir() },
});
expectTypeOf(scratch.stat("/tmp")).toEqualTypeOf<VirtualAgentFsEntry | null>();
expect(parseVirtualAgentFsEntryKind("file")).toBe("file");
expect(parseVirtualAgentFsEntryKind("directory")).toBe("directory");
expect(() => parseVirtualAgentFsEntryKind("socket")).toThrow(
"Invalid persisted VFS entry kind",
);
});
it("stores scratch files by agent and namespace", () => {
const env = { OPENCLAW_STATE_DIR: createTempStateDir() };
const mainScratch = createSqliteVirtualAgentFs({

View File

@@ -196,4 +196,39 @@ describe("DebugProxyCaptureStore", () => {
expect(store.readBlob(sharedPayload.dataBlobId ?? "")).toContain('"shared":true');
expect(store.listSessions(10).map((session) => session.id)).toEqual(["session-b"]);
});
it("purges sessions, events, and blobs in one store mutation", () => {
const store = makeStore();
store.upsertSession({
id: "session-purge",
startedAt: Date.now(),
mode: "proxy-run",
sourceScope: "openclaw",
sourceProcess: "openclaw",
dbPath: store.dbPath,
blobDir: store.blobDir,
});
const payload = persistEventPayload(store, {
data: "purge me",
contentType: "text/plain",
});
store.recordEvent({
sessionId: "session-purge",
ts: Date.now(),
sourceScope: "openclaw",
sourceProcess: "openclaw",
protocol: "https",
direction: "outbound",
kind: "request",
flowId: "flow-purge",
method: "POST",
host: "api.example.com",
path: "/v1/purge",
...payload,
});
expect(store.purgeAll()).toEqual({ sessions: 1, events: 1, blobs: 1 });
expect(store.listSessions(10)).toEqual([]);
expect(store.readBlob(payload.dataBlobId ?? "")).toBeNull();
});
});

View File

@@ -14,7 +14,12 @@ import {
resolveTaskFlowRegistrySqlitePath,
} from "./task-flow-registry.paths.js";
import { configureTaskFlowRegistryRuntime } from "./task-flow-registry.store.js";
import type { TaskFlowRecord } from "./task-flow-registry.types.js";
import {
parseOptionalTaskFlowSyncMode,
parseTaskFlowStatus,
type TaskFlowRecord,
} from "./task-flow-registry.types.js";
import { parseTaskNotifyPolicy } from "./task-registry.types.js";
function createStoredFlow(): TaskFlowRecord {
return {
@@ -125,6 +130,19 @@ describe("task-flow-registry store runtime", () => {
expect(restoredFlow.goal).toBe("Restored flow");
});
it("rejects invalid persisted flow enum values", () => {
expect(parseOptionalTaskFlowSyncMode("managed")).toBe("managed");
expect(parseOptionalTaskFlowSyncMode(null)).toBeUndefined();
expect(parseTaskFlowStatus("waiting")).toBe("waiting");
expect(parseTaskNotifyPolicy("state_changes")).toBe("state_changes");
expect(() => parseOptionalTaskFlowSyncMode("legacy")).toThrow(
"Invalid persisted task flow sync mode",
);
expect(() => parseTaskFlowStatus("done")).toThrow("Invalid persisted task flow status");
expect(() => parseTaskNotifyPolicy("verbose")).toThrow("Invalid persisted task notify policy");
});
it("restores persisted wait-state, revision, and cancel intent from sqlite", async () => {
await withFlowRegistryTempDir(async (root) => {
process.env.OPENCLAW_STATE_DIR = root;

View File

@@ -16,6 +16,14 @@ import {
type TaskRegistryObserverEvent,
} from "./task-registry.store.js";
import type { TaskRecord } from "./task-registry.types.js";
import {
parseOptionalTaskTerminalOutcome,
parseTaskDeliveryStatus,
parseTaskNotifyPolicy,
parseTaskRuntime,
parseTaskScopeKind,
parseTaskStatus,
} from "./task-registry.types.js";
const ORIGINAL_STATE_DIR = process.env.OPENCLAW_STATE_DIR;
@@ -96,6 +104,25 @@ describe("task-registry store runtime", () => {
expect(latestSnapshot.tasks.get("task-restored")?.task).toBe("Restored task");
});
it("rejects invalid persisted task enum values", () => {
expect(parseTaskRuntime("cron")).toBe("cron");
expect(parseTaskScopeKind("system")).toBe("system");
expect(parseTaskStatus("running")).toBe("running");
expect(parseTaskDeliveryStatus("pending")).toBe("pending");
expect(parseTaskNotifyPolicy("done_only")).toBe("done_only");
expect(parseOptionalTaskTerminalOutcome("blocked")).toBe("blocked");
expect(parseOptionalTaskTerminalOutcome(null)).toBeUndefined();
expect(() => parseTaskRuntime("timer")).toThrow("Invalid persisted task runtime");
expect(() => parseTaskScopeKind("workspace")).toThrow("Invalid persisted task scope kind");
expect(() => parseTaskStatus("done")).toThrow("Invalid persisted task status");
expect(() => parseTaskDeliveryStatus("ok")).toThrow("Invalid persisted task delivery status");
expect(() => parseTaskNotifyPolicy("verbose")).toThrow("Invalid persisted task notify policy");
expect(() => parseOptionalTaskTerminalOutcome("failed")).toThrow(
"Invalid persisted task terminal outcome",
);
});
it("emits incremental observer events for restore, mutation, and delete", () => {
const events: TaskRegistryObserverEvent[] = [];
configureTaskRegistryRuntime({