mirror of https://github.com/openclaw/openclaw.git
test: add json file and canvas host helper coverage
This commit is contained in:
parent
7817eb0117
commit
cda9eacada
|
|
@ -0,0 +1,64 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { resolveCanvasHostUrl } from "./canvas-host-url.js";
|
||||
|
||||
describe("resolveCanvasHostUrl", () => {
|
||||
it("returns undefined when no canvas port or usable host is available", () => {
|
||||
expect(resolveCanvasHostUrl({})).toBeUndefined();
|
||||
expect(resolveCanvasHostUrl({ canvasPort: 3000, hostOverride: "127.0.0.1" })).toBeUndefined();
|
||||
});
|
||||
|
||||
it("prefers non-loopback host overrides and preserves explicit ports", () => {
|
||||
expect(
|
||||
resolveCanvasHostUrl({
|
||||
canvasPort: 3000,
|
||||
hostOverride: " canvas.openclaw.ai ",
|
||||
requestHost: "gateway.local:9000",
|
||||
localAddress: "192.168.1.10",
|
||||
}),
|
||||
).toBe("http://canvas.openclaw.ai:3000");
|
||||
});
|
||||
|
||||
it("falls back from rejected loopback overrides to request hosts", () => {
|
||||
expect(
|
||||
resolveCanvasHostUrl({
|
||||
canvasPort: 3000,
|
||||
hostOverride: "127.0.0.1",
|
||||
requestHost: "example.com:8443",
|
||||
}),
|
||||
).toBe("http://example.com:3000");
|
||||
});
|
||||
|
||||
it("maps proxied default gateway ports to request-host ports or scheme defaults", () => {
|
||||
expect(
|
||||
resolveCanvasHostUrl({
|
||||
canvasPort: 18789,
|
||||
requestHost: "gateway.example.com:9443",
|
||||
forwardedProto: "https",
|
||||
}),
|
||||
).toBe("https://gateway.example.com:9443");
|
||||
expect(
|
||||
resolveCanvasHostUrl({
|
||||
canvasPort: 18789,
|
||||
requestHost: "gateway.example.com",
|
||||
forwardedProto: ["https", "http"],
|
||||
}),
|
||||
).toBe("https://gateway.example.com:443");
|
||||
expect(
|
||||
resolveCanvasHostUrl({
|
||||
canvasPort: 18789,
|
||||
requestHost: "gateway.example.com",
|
||||
}),
|
||||
).toBe("http://gateway.example.com:80");
|
||||
});
|
||||
|
||||
it("brackets ipv6 hosts and can fall back to local addresses", () => {
|
||||
expect(
|
||||
resolveCanvasHostUrl({
|
||||
canvasPort: 3000,
|
||||
requestHost: "not a host",
|
||||
localAddress: "2001:db8::1",
|
||||
scheme: "https",
|
||||
}),
|
||||
).toBe("https://[2001:db8::1]:3000");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createAsyncLock, readJsonFile, writeJsonAtomic, writeTextAtomic } from "./json-files.js";
|
||||
|
||||
describe("json file helpers", () => {
|
||||
it("reads valid json and returns null for missing or invalid files", async () => {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-json-files-"));
|
||||
const validPath = path.join(base, "valid.json");
|
||||
const invalidPath = path.join(base, "invalid.json");
|
||||
|
||||
await fs.writeFile(validPath, '{"ok":true}', "utf8");
|
||||
await fs.writeFile(invalidPath, "{not-json}", "utf8");
|
||||
|
||||
await expect(readJsonFile<{ ok: boolean }>(validPath)).resolves.toEqual({ ok: true });
|
||||
await expect(readJsonFile(invalidPath)).resolves.toBeNull();
|
||||
await expect(readJsonFile(path.join(base, "missing.json"))).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it("writes json atomically with pretty formatting and optional trailing newline", async () => {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-json-files-"));
|
||||
const filePath = path.join(base, "nested", "config.json");
|
||||
|
||||
await writeJsonAtomic(
|
||||
filePath,
|
||||
{ ok: true, nested: { value: 1 } },
|
||||
{ trailingNewline: true, ensureDirMode: 0o755 },
|
||||
);
|
||||
|
||||
await expect(fs.readFile(filePath, "utf8")).resolves.toBe(
|
||||
'{\n "ok": true,\n "nested": {\n "value": 1\n }\n}\n',
|
||||
);
|
||||
});
|
||||
|
||||
it("writes text atomically and avoids duplicate trailing newlines", async () => {
|
||||
const base = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-json-files-"));
|
||||
const filePath = path.join(base, "nested", "note.txt");
|
||||
|
||||
await writeTextAtomic(filePath, "hello", { appendTrailingNewline: true });
|
||||
await expect(fs.readFile(filePath, "utf8")).resolves.toBe("hello\n");
|
||||
|
||||
await writeTextAtomic(filePath, "hello\n", { appendTrailingNewline: true });
|
||||
await expect(fs.readFile(filePath, "utf8")).resolves.toBe("hello\n");
|
||||
});
|
||||
|
||||
it("serializes async lock callers even across rejections", async () => {
|
||||
const withLock = createAsyncLock();
|
||||
const events: string[] = [];
|
||||
|
||||
const first = withLock(async () => {
|
||||
events.push("first:start");
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
events.push("first:end");
|
||||
throw new Error("boom");
|
||||
});
|
||||
|
||||
const second = withLock(async () => {
|
||||
events.push("second:start");
|
||||
events.push("second:end");
|
||||
return "ok";
|
||||
});
|
||||
|
||||
await expect(first).rejects.toThrow("boom");
|
||||
await expect(second).resolves.toBe("ok");
|
||||
expect(events).toEqual(["first:start", "first:end", "second:start", "second:end"]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue