test: tighten extension helper assertions

This commit is contained in:
Peter Steinberger
2026-05-08 16:51:10 +01:00
parent 15ad70356c
commit ddaf9178c5
4 changed files with 82 additions and 36 deletions

View File

@@ -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<Awaited<ReturnType<typeof resolveFileWithinRoot>>>;
async function withCanvasTemp<T>(prefix: string, run: (dir: string) => Promise<T>): Promise<T> {
return await withTempWorkspace(
{ rootDir: resolvePreferredOpenClawTmpDir(), prefix },
@@ -11,6 +13,16 @@ async function withCanvasTemp<T>(prefix: string, run: (dir: string) => Promise<T
);
}
function expectResolvedFile(
result: Awaited<ReturnType<typeof resolveFileWithinRoot>>,
): 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"), "<h1>docs</h1>");
const result = await resolveFileWithinRoot(root, "/docs");
expect(result).not.toBeNull();
const resolved = expectResolvedFile(result);
try {
await expect(result?.handle.readFile({ encoding: "utf8" })).resolves.toBe("<h1>docs</h1>");
await expect(resolved.handle.readFile({ encoding: "utf8" })).resolves.toBe("<h1>docs</h1>");
} finally {
await result?.handle.close().catch(() => {});
await resolved.handle.close().catch(() => {});
}
});
});

View File

@@ -8,6 +8,8 @@ import {
resolveCodexTrajectoryPointerFlags,
} from "./trajectory.js";
type CodexTrajectoryRecorder = NonNullable<ReturnType<typeof createCodexTrajectoryRecorder>>;
const tempDirs: string[] = [];
function makeTempDir(): string {
@@ -22,6 +24,16 @@ afterEach(() => {
}
});
function expectTrajectoryRecorder(
recorder: ReturnType<typeof createCodexTrajectoryRecorder>,
): 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"),

View File

@@ -1,44 +1,61 @@
import { describe, expect, it } from "vitest";
import { resolveDiscordPresenceUpdate } from "./presence.js";
type DiscordPresenceUpdate = NonNullable<ReturnType<typeof resolveDiscordPresenceUpdate>>;
function expectPresenceUpdate(
result: ReturnType<typeof resolveDiscordPresenceUpdate>,
): 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");
});
});

View File

@@ -3,9 +3,11 @@ import type { ResolvedMemoryWikiConfig } from "./config.js";
import { createWikiApplyTool } from "./tool.js";
function asSchemaObject(value: unknown): Record<string, unknown> {
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<string, unknown>;
}