Merged via squash. Prepared head SHA: 567b3ed68434220bb319a940fa1b834a2f520ff5 Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com> Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com> Reviewed-by: @velvet-shark
102 lines
3.9 KiB
TypeScript
102 lines
3.9 KiB
TypeScript
import fs from "node:fs/promises";
|
|
import type { IncomingMessage } from "node:http";
|
|
import os from "node:os";
|
|
import path from "node:path";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const { resolveControlUiRootSyncMock, isPackageProvenControlUiRootSyncMock } = vi.hoisted(() => ({
|
|
resolveControlUiRootSyncMock: vi.fn(),
|
|
isPackageProvenControlUiRootSyncMock: vi.fn().mockReturnValue(true),
|
|
}));
|
|
|
|
vi.mock("../infra/control-ui-assets.js", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../infra/control-ui-assets.js")>();
|
|
return {
|
|
...actual,
|
|
resolveControlUiRootSync: resolveControlUiRootSyncMock,
|
|
isPackageProvenControlUiRootSync: isPackageProvenControlUiRootSyncMock,
|
|
};
|
|
});
|
|
|
|
const { handleControlUiHttpRequest } = await import("./control-ui.js");
|
|
const { makeMockHttpResponse } = await import("./test-http-response.js");
|
|
|
|
async function withControlUiRoot<T>(fn: (tmp: string) => Promise<T>) {
|
|
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-ui-auto-root-"));
|
|
try {
|
|
await fs.writeFile(path.join(tmp, "index.html"), "<html>fallback</html>\n");
|
|
return await fn(tmp);
|
|
} finally {
|
|
await fs.rm(tmp, { recursive: true, force: true });
|
|
}
|
|
}
|
|
|
|
afterEach(() => {
|
|
resolveControlUiRootSyncMock.mockReset();
|
|
isPackageProvenControlUiRootSyncMock.mockReset();
|
|
isPackageProvenControlUiRootSyncMock.mockReturnValue(true);
|
|
});
|
|
|
|
describe("handleControlUiHttpRequest auto-detected root", () => {
|
|
it("serves hardlinked asset files for bundled auto-detected roots", async () => {
|
|
await withControlUiRoot(async (tmp) => {
|
|
const assetsDir = path.join(tmp, "assets");
|
|
await fs.mkdir(assetsDir, { recursive: true });
|
|
await fs.writeFile(path.join(assetsDir, "app.js"), "console.log('hi');");
|
|
await fs.link(path.join(assetsDir, "app.js"), path.join(assetsDir, "app.hl.js"));
|
|
resolveControlUiRootSyncMock.mockReturnValue(tmp);
|
|
|
|
const { res, end } = makeMockHttpResponse();
|
|
const handled = handleControlUiHttpRequest(
|
|
{ url: "/assets/app.hl.js", method: "GET" } as IncomingMessage,
|
|
res,
|
|
);
|
|
|
|
expect(handled).toBe(true);
|
|
expect(res.statusCode).toBe(200);
|
|
expect(String(end.mock.calls[0]?.[0] ?? "")).toBe("console.log('hi');");
|
|
});
|
|
});
|
|
|
|
it("serves hardlinked SPA fallback index.html for bundled auto-detected roots", async () => {
|
|
await withControlUiRoot(async (tmp) => {
|
|
const sourceIndex = path.join(tmp, "index.source.html");
|
|
const indexPath = path.join(tmp, "index.html");
|
|
await fs.writeFile(sourceIndex, "<html>fallback-hardlink</html>\n");
|
|
await fs.rm(indexPath);
|
|
await fs.link(sourceIndex, indexPath);
|
|
resolveControlUiRootSyncMock.mockReturnValue(tmp);
|
|
|
|
const { res, end } = makeMockHttpResponse();
|
|
const handled = handleControlUiHttpRequest(
|
|
{ url: "/dashboard", method: "GET" } as IncomingMessage,
|
|
res,
|
|
);
|
|
|
|
expect(handled).toBe(true);
|
|
expect(res.statusCode).toBe(200);
|
|
expect(String(end.mock.calls[0]?.[0] ?? "")).toBe("<html>fallback-hardlink</html>\n");
|
|
});
|
|
});
|
|
|
|
it("rejects hardlinked assets for non-package-proven auto-detected roots", async () => {
|
|
isPackageProvenControlUiRootSyncMock.mockReturnValue(false);
|
|
await withControlUiRoot(async (tmp) => {
|
|
const assetsDir = path.join(tmp, "assets");
|
|
await fs.mkdir(assetsDir, { recursive: true });
|
|
await fs.writeFile(path.join(assetsDir, "app.js"), "console.log('hi');");
|
|
await fs.link(path.join(assetsDir, "app.js"), path.join(assetsDir, "app.hl.js"));
|
|
resolveControlUiRootSyncMock.mockReturnValue(tmp);
|
|
|
|
const { res } = makeMockHttpResponse();
|
|
const handled = handleControlUiHttpRequest(
|
|
{ url: "/assets/app.hl.js", method: "GET" } as IncomingMessage,
|
|
res,
|
|
);
|
|
|
|
expect(handled).toBe(true);
|
|
expect(res.statusCode).toBe(404);
|
|
});
|
|
});
|
|
});
|