Files
EvoScientist-WebUI/src/app/api/image-generation/export/route.test.ts
T

103 lines
3.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { NextRequest } from "next/server";
const mocks = vi.hoisted(() => ({
requireActor: vi.fn(),
requireAdmin: vi.fn(),
configApiFetch: vi.fn(),
isCrossOrigin: vi.fn(),
}));
vi.mock("server-only", () => ({}));
vi.mock("@/lib/server/actor", () => ({
requireActor: mocks.requireActor,
requireAdmin: mocks.requireAdmin,
ActorError: class ActorError extends Error {
constructor(
message: string,
readonly status: 401 | 403 = 401
) {
super(message);
}
},
}));
vi.mock("@/lib/server/evoscientistConfigClient", () => ({
configApiFetch: mocks.configApiFetch,
ConfigApiError: class ConfigApiError extends Error {
constructor(
message: string,
readonly status: number,
readonly code: string | null = null,
readonly details: unknown[] = []
) {
super(message);
}
},
}));
vi.mock("@/lib/server/errorLogStore", () => ({
appendErrorLog: vi.fn(),
}));
vi.mock("@/lib/server/workspace", () => ({
isCrossOrigin: mocks.isCrossOrigin,
}));
const routes = await import("./route");
function request(): NextRequest {
return new NextRequest("http://localhost/api/image-generation/export", {
method: "GET",
});
}
describe("image-generation export route", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.isCrossOrigin.mockReturnValue(false);
mocks.requireActor.mockReturnValue({ sub: "alice", role: "admin" });
mocks.requireAdmin.mockReturnValue(undefined);
mocks.configApiFetch.mockResolvedValue({
body: { kind: "evoscientist.image-generation", format_version: 1 },
});
});
it("GET proxies the backend export for an admin", async () => {
const response = await routes.GET(request());
expect(response.status).toBe(200);
expect(mocks.configApiFetch).toHaveBeenCalledWith(
{ sub: "alice", role: "admin" },
"/api/image-generation/export"
);
expect(await response.json()).toEqual({
kind: "evoscientist.image-generation",
format_version: 1,
});
expect(response.headers.get("cache-control")).toBe("no-store");
});
it("GET returns 403 for a non-admin", async () => {
const { ActorError } = await import("@/lib/server/actor");
mocks.requireAdmin.mockImplementation(() => {
throw new ActorError("Admin role required.", 403);
});
const response = await routes.GET(request());
expect(response.status).toBe(403);
expect(mocks.configApiFetch).not.toHaveBeenCalled();
});
it("GET returns 401 when unauthenticated", async () => {
const { ActorError } = await import("@/lib/server/actor");
mocks.requireActor.mockImplementation(() => {
throw new ActorError("Authentication required.", 401);
});
const response = await routes.GET(request());
expect(response.status).toBe(401);
});
it("GET returns 403 for cross-origin requests", async () => {
mocks.isCrossOrigin.mockReturnValue(true);
const response = await routes.GET(request());
expect(response.status).toBe(403);
expect(mocks.configApiFetch).not.toHaveBeenCalled();
});
});