103 lines
3.1 KiB
TypeScript
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();
|
|
});
|
|
});
|