259 lines
7.7 KiB
TypeScript
259 lines
7.7 KiB
TypeScript
import { beforeEach, expect, test, vi } from "vitest";
|
|
|
|
import { GET, POST } from "../../../app/api/libraries/[libraryType]/route";
|
|
import { RouteError } from "@/lib/http/response";
|
|
|
|
const { backendRequestMock } = vi.hoisted(() => ({
|
|
backendRequestMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock("@/lib/http/backend-client", () => ({
|
|
backendRequest: backendRequestMock,
|
|
}));
|
|
|
|
beforeEach(() => {
|
|
backendRequestMock.mockReset();
|
|
});
|
|
|
|
test("proxies backend library resources into the existing frontend view-model shape", async () => {
|
|
backendRequestMock.mockResolvedValue({
|
|
status: 200,
|
|
data: {
|
|
total: 1,
|
|
items: [
|
|
{
|
|
id: 12,
|
|
resource_type: "model",
|
|
name: "Ava Studio",
|
|
description: "棚拍女模特",
|
|
tags: ["女装", "棚拍"],
|
|
status: "active",
|
|
gender: "female",
|
|
age_group: "adult",
|
|
environment: null,
|
|
category: null,
|
|
files: [
|
|
{
|
|
id: 1,
|
|
file_role: "thumbnail",
|
|
storage_key: "library/models/ava/thumb.png",
|
|
public_url: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
bucket: "images",
|
|
mime_type: "image/png",
|
|
size_bytes: 2345,
|
|
sort_order: 0,
|
|
width: 480,
|
|
height: 600,
|
|
created_at: "2026-03-28T10:00:00Z",
|
|
},
|
|
],
|
|
cover_url: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
original_url: "https://images.marcusd.me/library/models/ava/original.png",
|
|
created_at: "2026-03-28T10:00:00Z",
|
|
updated_at: "2026-03-28T10:00:00Z",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
const response = await GET(new Request("http://localhost/api/libraries/models"), {
|
|
params: Promise.resolve({ libraryType: "models" }),
|
|
});
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(200);
|
|
expect(payload).toMatchObject({
|
|
mode: "proxy",
|
|
data: {
|
|
items: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
id: "12",
|
|
libraryType: "models",
|
|
name: "Ava Studio",
|
|
previewUri: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
isMock: false,
|
|
}),
|
|
]),
|
|
},
|
|
message: "资源库当前显示真实后端数据。",
|
|
});
|
|
expect(backendRequestMock).toHaveBeenCalledWith(
|
|
"/library/resources?resource_type=model&limit=100",
|
|
);
|
|
});
|
|
|
|
test("rejects unsupported placeholder library types with a normalized error", async () => {
|
|
const response = await GET(new Request("http://localhost/api/libraries/unknown"), {
|
|
params: Promise.resolve({ libraryType: "unknown" }),
|
|
});
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(payload).toEqual({
|
|
error: "NOT_FOUND",
|
|
message: "不支持的资源库类型。",
|
|
});
|
|
});
|
|
|
|
test("rejects inherited object keys instead of treating them as valid library types", async () => {
|
|
const response = await GET(new Request("http://localhost/api/libraries/toString"), {
|
|
params: Promise.resolve({ libraryType: "toString" }),
|
|
});
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(404);
|
|
expect(payload).toEqual({
|
|
error: "NOT_FOUND",
|
|
message: "不支持的资源库类型。",
|
|
});
|
|
});
|
|
|
|
test("normalizes backend errors while proxying library resources", async () => {
|
|
backendRequestMock.mockRejectedValue(
|
|
new RouteError(502, "BACKEND_UNAVAILABLE", "后端暂时不可用,请稍后重试。"),
|
|
);
|
|
|
|
const response = await GET(new Request("http://localhost/api/libraries/models"), {
|
|
params: Promise.resolve({ libraryType: "models" }),
|
|
});
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(502);
|
|
expect(payload).toEqual({
|
|
error: "BACKEND_UNAVAILABLE",
|
|
message: "后端暂时不可用,请稍后重试。",
|
|
});
|
|
});
|
|
|
|
test("proxies library resource creation and adapts the created item into the existing view-model shape", async () => {
|
|
backendRequestMock.mockResolvedValue({
|
|
status: 201,
|
|
data: {
|
|
id: 12,
|
|
resource_type: "model",
|
|
name: "Ava Studio",
|
|
description: "棚拍女模特",
|
|
tags: ["女装", "棚拍"],
|
|
status: "active",
|
|
gender: "female",
|
|
age_group: "adult",
|
|
pose_id: null,
|
|
environment: null,
|
|
category: null,
|
|
files: [
|
|
{
|
|
id: 1,
|
|
file_role: "thumbnail",
|
|
storage_key: "library/models/ava/thumb.png",
|
|
public_url: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
bucket: "images",
|
|
mime_type: "image/png",
|
|
size_bytes: 2345,
|
|
sort_order: 0,
|
|
width: 480,
|
|
height: 600,
|
|
created_at: "2026-03-28T10:00:00Z",
|
|
},
|
|
],
|
|
cover_url: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
original_url: "https://images.marcusd.me/library/models/ava/original.png",
|
|
created_at: "2026-03-28T10:00:00Z",
|
|
updated_at: "2026-03-28T10:00:00Z",
|
|
},
|
|
});
|
|
|
|
const response = await POST(
|
|
new Request("http://localhost/api/libraries/models", {
|
|
method: "POST",
|
|
headers: {
|
|
"content-type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
name: "Ava Studio",
|
|
description: "棚拍女模特",
|
|
tags: ["女装", "棚拍"],
|
|
gender: "female",
|
|
ageGroup: "adult",
|
|
files: [
|
|
{
|
|
fileRole: "original",
|
|
storageKey: "library/models/2026/ava-original.png",
|
|
publicUrl: "https://images.marcusd.me/library/models/ava/original.png",
|
|
mimeType: "image/png",
|
|
sizeBytes: 12345,
|
|
sortOrder: 0,
|
|
},
|
|
{
|
|
fileRole: "thumbnail",
|
|
storageKey: "library/models/2026/ava-thumb.png",
|
|
publicUrl: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
mimeType: "image/png",
|
|
sizeBytes: 2345,
|
|
sortOrder: 0,
|
|
},
|
|
],
|
|
}),
|
|
}),
|
|
{
|
|
params: Promise.resolve({ libraryType: "models" }),
|
|
},
|
|
);
|
|
const payload = await response.json();
|
|
|
|
expect(response.status).toBe(201);
|
|
expect(payload).toEqual({
|
|
mode: "proxy",
|
|
data: {
|
|
item: {
|
|
id: "12",
|
|
libraryType: "models",
|
|
name: "Ava Studio",
|
|
description: "棚拍女模特",
|
|
previewUri: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
originalUri: "https://images.marcusd.me/library/models/ava/original.png",
|
|
tags: ["女装", "棚拍"],
|
|
files: [
|
|
{
|
|
id: 1,
|
|
role: "thumbnail",
|
|
url: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
},
|
|
],
|
|
isMock: false,
|
|
backendId: 12,
|
|
poseId: null,
|
|
},
|
|
},
|
|
message: "资源创建成功。",
|
|
});
|
|
expect(backendRequestMock).toHaveBeenCalledWith("/library/resources", {
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
resource_type: "model",
|
|
name: "Ava Studio",
|
|
description: "棚拍女模特",
|
|
tags: ["女装", "棚拍"],
|
|
gender: "female",
|
|
age_group: "adult",
|
|
files: [
|
|
{
|
|
file_role: "original",
|
|
storage_key: "library/models/2026/ava-original.png",
|
|
public_url: "https://images.marcusd.me/library/models/ava/original.png",
|
|
mime_type: "image/png",
|
|
size_bytes: 12345,
|
|
sort_order: 0,
|
|
},
|
|
{
|
|
file_role: "thumbnail",
|
|
storage_key: "library/models/2026/ava-thumb.png",
|
|
public_url: "https://images.marcusd.me/library/models/ava/thumb.png",
|
|
mime_type: "image/png",
|
|
size_bytes: 2345,
|
|
sort_order: 0,
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
});
|