diff --git a/extensions/canvas/src/host/file-resolver.test.ts b/extensions/canvas/src/host/file-resolver.test.ts index 800a189b318..819d62306ff 100644 --- a/extensions/canvas/src/host/file-resolver.test.ts +++ b/extensions/canvas/src/host/file-resolver.test.ts @@ -4,6 +4,8 @@ import { resolvePreferredOpenClawTmpDir, withTempWorkspace } from "openclaw/plug import { describe, expect, it } from "vitest"; import { normalizeUrlPath, resolveFileWithinRoot } from "./file-resolver.js"; +type ResolvedFile = NonNullable>>; + async function withCanvasTemp(prefix: string, run: (dir: string) => Promise): Promise { return await withTempWorkspace( { rootDir: resolvePreferredOpenClawTmpDir(), prefix }, @@ -11,6 +13,16 @@ async function withCanvasTemp(prefix: string, run: (dir: string) => Promise>, +): ResolvedFile { + expect(result).toEqual(expect.objectContaining({ handle: expect.any(Object) })); + if (result === null) { + throw new Error("Expected resolved file within root"); + } + return result; +} + describe("resolveFileWithinRoot", () => { it("normalizes URL paths", () => { expect(normalizeUrlPath("/nested/../file.txt")).toBe("/file.txt"); @@ -23,11 +35,11 @@ describe("resolveFileWithinRoot", () => { await fs.writeFile(path.join(root, "docs", "index.html"), "

docs

"); const result = await resolveFileWithinRoot(root, "/docs"); - expect(result).not.toBeNull(); + const resolved = expectResolvedFile(result); try { - await expect(result?.handle.readFile({ encoding: "utf8" })).resolves.toBe("

docs

"); + await expect(resolved.handle.readFile({ encoding: "utf8" })).resolves.toBe("

docs

"); } finally { - await result?.handle.close().catch(() => {}); + await resolved.handle.close().catch(() => {}); } }); }); diff --git a/extensions/codex/src/app-server/trajectory.test.ts b/extensions/codex/src/app-server/trajectory.test.ts index 91b0e3a3076..ab611db3a73 100644 --- a/extensions/codex/src/app-server/trajectory.test.ts +++ b/extensions/codex/src/app-server/trajectory.test.ts @@ -8,6 +8,8 @@ import { resolveCodexTrajectoryPointerFlags, } from "./trajectory.js"; +type CodexTrajectoryRecorder = NonNullable>; + const tempDirs: string[] = []; function makeTempDir(): string { @@ -22,6 +24,16 @@ afterEach(() => { } }); +function expectTrajectoryRecorder( + recorder: ReturnType, +): CodexTrajectoryRecorder { + expect(recorder).toEqual(expect.objectContaining({ recordEvent: expect.any(Function) })); + if (recorder === null) { + throw new Error("Expected Codex trajectory recorder"); + } + return recorder; +} + describe("Codex trajectory recorder", () => { it("keeps write flags usable when O_NOFOLLOW is unavailable", () => { const constants = { @@ -52,13 +64,13 @@ describe("Codex trajectory recorder", () => { env: {}, }); - expect(recorder).not.toBeNull(); - recorder?.recordEvent("session.started", { + const trajectoryRecorder = expectTrajectoryRecorder(recorder); + trajectoryRecorder.recordEvent("session.started", { apiKey: "secret", headers: [{ name: "Authorization", value: "Bearer sk-test-secret-token" }], command: "curl -H 'Authorization: Bearer sk-other-secret-token'", }); - await recorder?.flush(); + await trajectoryRecorder.flush(); const filePath = path.join(tmpDir, "session.trajectory.jsonl"); const content = fs.readFileSync(filePath, "utf8"); @@ -82,8 +94,9 @@ describe("Codex trajectory recorder", () => { env: { OPENCLAW_TRAJECTORY_DIR: tmpDir }, }); - recorder?.recordEvent("session.started"); - await recorder?.flush(); + const trajectoryRecorder = expectTrajectoryRecorder(recorder); + trajectoryRecorder.recordEvent("session.started"); + await trajectoryRecorder.flush(); expect(fs.existsSync(path.join(tmpDir, "___evil_session.jsonl"))).toBe(true); }); @@ -119,8 +132,9 @@ describe("Codex trajectory recorder", () => { env: {}, }); - recorder?.recordEvent("session.started"); - await recorder?.flush(); + const trajectoryRecorder = expectTrajectoryRecorder(recorder); + trajectoryRecorder.recordEvent("session.started"); + await trajectoryRecorder.flush(); expect(fs.existsSync(path.join(targetDir, "session.trajectory.jsonl"))).toBe(false); }); @@ -137,12 +151,13 @@ describe("Codex trajectory recorder", () => { env: {}, }); - recorder?.recordEvent("context.compiled", { + const trajectoryRecorder = expectTrajectoryRecorder(recorder); + trajectoryRecorder.recordEvent("context.compiled", { fields: Object.fromEntries( Array.from({ length: 100 }, (_, index) => [`field-${index}`, "x".repeat(3_000)]), ), }); - await recorder?.flush(); + await trajectoryRecorder.flush(); const parsed = JSON.parse( fs.readFileSync(path.join(tmpDir, "session.trajectory.jsonl"), "utf8"), diff --git a/extensions/discord/src/monitor/presence.test.ts b/extensions/discord/src/monitor/presence.test.ts index 1ea06f9dc28..6c1d117105b 100644 --- a/extensions/discord/src/monitor/presence.test.ts +++ b/extensions/discord/src/monitor/presence.test.ts @@ -1,44 +1,61 @@ import { describe, expect, it } from "vitest"; import { resolveDiscordPresenceUpdate } from "./presence.js"; +type DiscordPresenceUpdate = NonNullable>; + +function expectPresenceUpdate( + result: ReturnType, +): DiscordPresenceUpdate { + expect(result).toEqual(expect.objectContaining({ activities: expect.any(Array) })); + if (result === null) { + throw new Error("Expected Discord presence update"); + } + return result; +} + describe("resolveDiscordPresenceUpdate", () => { it("returns online presence when no config is provided", () => { - const result = resolveDiscordPresenceUpdate({}); - expect(result).not.toBeNull(); - expect(result!.status).toBe("online"); - expect(result!.activities).toEqual([]); + const result = expectPresenceUpdate(resolveDiscordPresenceUpdate({})); + expect(result.status).toBe("online"); + expect(result.activities).toEqual([]); }); it("uses configured status", () => { - const result = resolveDiscordPresenceUpdate({ status: "dnd" }); - expect(result!.status).toBe("dnd"); + const result = expectPresenceUpdate(resolveDiscordPresenceUpdate({ status: "dnd" })); + expect(result.status).toBe("dnd"); }); it("includes activity when configured", () => { - const result = resolveDiscordPresenceUpdate({ activity: "Helping humans" }); - expect(result!.status).toBe("online"); - expect(result!.activities).toHaveLength(1); - expect(result!.activities[0].state).toBe("Helping humans"); + const result = expectPresenceUpdate( + resolveDiscordPresenceUpdate({ activity: "Helping humans" }), + ); + expect(result.status).toBe("online"); + expect(result.activities).toHaveLength(1); + expect(result.activities[0].state).toBe("Helping humans"); }); it("uses custom activity type by default", () => { - const result = resolveDiscordPresenceUpdate({ activity: "test" }); - expect(result!.activities[0].type).toBe(4); - expect(result!.activities[0].name).toBe("Custom Status"); + const result = expectPresenceUpdate(resolveDiscordPresenceUpdate({ activity: "test" })); + expect(result.activities[0].type).toBe(4); + expect(result.activities[0].name).toBe("Custom Status"); }); it("respects explicit activityType", () => { - const result = resolveDiscordPresenceUpdate({ activity: "test", activityType: 3 }); - expect(result!.activities[0].type).toBe(3); - expect(result!.activities[0].name).toBe("test"); + const result = expectPresenceUpdate( + resolveDiscordPresenceUpdate({ activity: "test", activityType: 3 }), + ); + expect(result.activities[0].type).toBe(3); + expect(result.activities[0].name).toBe("test"); }); it("sets streaming URL for type 1", () => { - const result = resolveDiscordPresenceUpdate({ - activity: "Live", - activityType: 1, - activityUrl: "https://twitch.tv/test", - }); - expect(result!.activities[0].url).toBe("https://twitch.tv/test"); + const result = expectPresenceUpdate( + resolveDiscordPresenceUpdate({ + activity: "Live", + activityType: 1, + activityUrl: "https://twitch.tv/test", + }), + ); + expect(result.activities[0].url).toBe("https://twitch.tv/test"); }); }); diff --git a/extensions/memory-wiki/src/tool.test.ts b/extensions/memory-wiki/src/tool.test.ts index 9fd0acf780e..5c0888862b0 100644 --- a/extensions/memory-wiki/src/tool.test.ts +++ b/extensions/memory-wiki/src/tool.test.ts @@ -3,9 +3,11 @@ import type { ResolvedMemoryWikiConfig } from "./config.js"; import { createWikiApplyTool } from "./tool.js"; function asSchemaObject(value: unknown): Record { - expect(typeof value).toBe("object"); - expect(value).not.toBeNull(); + expect(value).toEqual(expect.any(Object)); expect(Array.isArray(value)).toBe(false); + if (typeof value !== "object" || value === null || Array.isArray(value)) { + throw new Error("Expected JSON schema object"); + } return value as Record; }