test: tighten fetch helper and package root coverage

This commit is contained in:
Peter Steinberger 2026-03-13 19:04:41 +00:00
parent 6bbf2d486c
commit e6213b2fc7
2 changed files with 80 additions and 1 deletions

View File

@ -141,6 +141,19 @@ describe("resolveOpenClawPackageRoot", () => {
expect(resolveOpenClawPackageRootSync({ moduleUrl })).toBe(pkgRoot); expect(resolveOpenClawPackageRootSync({ moduleUrl })).toBe(pkgRoot);
}); });
it("falls through from a non-openclaw moduleUrl candidate to cwd", async () => {
const wrongPkgRoot = fx("moduleurl-fallthrough", "wrong");
const cwdPkgRoot = fx("moduleurl-fallthrough", "cwd");
setFile(path.join(wrongPkgRoot, "package.json"), JSON.stringify({ name: "not-openclaw" }));
setFile(path.join(cwdPkgRoot, "package.json"), JSON.stringify({ name: "openclaw" }));
const moduleUrl = pathToFileURL(path.join(wrongPkgRoot, "dist", "index.js")).toString();
expect(resolveOpenClawPackageRootSync({ moduleUrl, cwd: cwdPkgRoot })).toBe(cwdPkgRoot);
await expect(resolveOpenClawPackageRoot({ moduleUrl, cwd: cwdPkgRoot })).resolves.toBe(
cwdPkgRoot,
);
});
it("ignores invalid moduleUrl values and falls back to cwd", async () => { it("ignores invalid moduleUrl values and falls back to cwd", async () => {
const pkgRoot = fx("invalid-moduleurl"); const pkgRoot = fx("invalid-moduleurl");
setFile(path.join(pkgRoot, "package.json"), JSON.stringify({ name: "openclaw" })); setFile(path.join(pkgRoot, "package.json"), JSON.stringify({ name: "openclaw" }));
@ -160,6 +173,16 @@ describe("resolveOpenClawPackageRoot", () => {
expect(resolveOpenClawPackageRootSync({ cwd: pkgRoot })).toBeNull(); expect(resolveOpenClawPackageRootSync({ cwd: pkgRoot })).toBeNull();
}); });
it("falls back from a symlinked argv1 to the node_modules package root", () => {
const project = fx("symlink-node-modules-fallback");
const argv1 = path.join(project, "node_modules", ".bin", "openclaw");
state.realpaths.set(abs(argv1), abs(path.join(project, "versions", "current", "openclaw.mjs")));
const pkgRoot = path.join(project, "node_modules", "openclaw");
setFile(path.join(pkgRoot, "package.json"), JSON.stringify({ name: "openclaw" }));
expect(resolveOpenClawPackageRootSync({ argv1 })).toBe(pkgRoot);
});
it("async resolver matches sync behavior", async () => { it("async resolver matches sync behavior", async () => {
const pkgRoot = fx("async"); const pkgRoot = fx("async");
setFile(path.join(pkgRoot, "package.json"), JSON.stringify({ name: "openclaw" })); setFile(path.join(pkgRoot, "package.json"), JSON.stringify({ name: "openclaw" }));

View File

@ -1,11 +1,17 @@
import { describe, expect, it } from "vitest"; import { afterEach, describe, expect, it, vi } from "vitest";
import { import {
buildUsageErrorSnapshot, buildUsageErrorSnapshot,
buildUsageHttpErrorSnapshot, buildUsageHttpErrorSnapshot,
fetchJson,
parseFiniteNumber, parseFiniteNumber,
} from "./provider-usage.fetch.shared.js"; } from "./provider-usage.fetch.shared.js";
describe("provider usage fetch shared helpers", () => { describe("provider usage fetch shared helpers", () => {
afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});
it("builds a provider error snapshot", () => { it("builds a provider error snapshot", () => {
expect(buildUsageErrorSnapshot("zai", "API error")).toEqual({ expect(buildUsageErrorSnapshot("zai", "API error")).toEqual({
provider: "zai", provider: "zai",
@ -23,6 +29,56 @@ describe("provider usage fetch shared helpers", () => {
expect(parseFiniteNumber(value)).toBe(expected); expect(parseFiniteNumber(value)).toBe(expected);
}); });
it("forwards request init and clears the timeout on success", async () => {
vi.useFakeTimers();
const clearTimeoutSpy = vi.spyOn(globalThis, "clearTimeout");
const fetchFn = vi.fn(
async (_url: string, init?: RequestInit) =>
new Response(JSON.stringify({ aborted: init?.signal?.aborted ?? false }), { status: 200 }),
);
const response = await fetchJson(
"https://example.com/usage",
{
method: "POST",
headers: { authorization: "Bearer test" },
},
1_000,
fetchFn,
);
expect(fetchFn).toHaveBeenCalledWith(
"https://example.com/usage",
expect.objectContaining({
method: "POST",
headers: { authorization: "Bearer test" },
signal: expect.any(AbortSignal),
}),
);
await expect(response.json()).resolves.toEqual({ aborted: false });
expect(clearTimeoutSpy).toHaveBeenCalledTimes(1);
});
it("aborts timed out requests and clears the timer on rejection", async () => {
vi.useFakeTimers();
const clearTimeoutSpy = vi.spyOn(globalThis, "clearTimeout");
const fetchFn = vi.fn(
(_url: string, init?: RequestInit) =>
new Promise<Response>((_, reject) => {
init?.signal?.addEventListener("abort", () => reject(new Error("aborted by timeout")), {
once: true,
});
}),
);
const request = fetchJson("https://example.com/usage", {}, 50, fetchFn);
const rejection = expect(request).rejects.toThrow("aborted by timeout");
await vi.advanceTimersByTimeAsync(50);
await rejection;
expect(clearTimeoutSpy).toHaveBeenCalledTimes(1);
});
it("maps configured status codes to token expired", () => { it("maps configured status codes to token expired", () => {
const snapshot = buildUsageHttpErrorSnapshot({ const snapshot = buildUsageHttpErrorSnapshot({
provider: "openai-codex", provider: "openai-codex",