Files
NianAIGC/tests/image-templates.test.ts
2026-07-03 11:25:25 +08:00

123 lines
4.9 KiB
TypeScript

import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
createImageTemplate,
deleteImageTemplate,
listImageTemplates,
updateImageTemplate
} from "@/lib/server/data-store";
import { normalizeImageTemplateCreate } from "@/lib/server/image-template-input";
import { extractMaterialPlaceholders } from "@/lib/prompt/material-placeholders";
let runtimeDir = "";
let previousRuntimeDir: string | undefined;
let previousSupabaseUrl: string | undefined;
let previousSupabaseKey: string | undefined;
describe("image templates", () => {
beforeEach(async () => {
runtimeDir = await mkdtemp(join(tmpdir(), "zhinian-templates-"));
previousRuntimeDir = process.env.ZHINIAN_RUNTIME_DIR;
previousSupabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
previousSupabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
process.env.ZHINIAN_RUNTIME_DIR = runtimeDir;
delete process.env.NEXT_PUBLIC_SUPABASE_URL;
delete process.env.SUPABASE_SERVICE_ROLE_KEY;
});
afterEach(async () => {
restoreEnv("ZHINIAN_RUNTIME_DIR", previousRuntimeDir);
restoreEnv("NEXT_PUBLIC_SUPABASE_URL", previousSupabaseUrl);
restoreEnv("SUPABASE_SERVICE_ROLE_KEY", previousSupabaseKey);
await rm(runtimeDir, { force: true, recursive: true });
});
it("stores templates inside the account owner boundary", async () => {
const first = await createImageTemplate({
ownerId: "auth:app:user-a",
name: "商品主图",
description: "适合电商主视觉",
prompt: "生成干净高级的商品主图",
previewImageUrl: "/mock/preview-a.png",
settings: { width: 2048, height: 2048, forceSingle: true },
sortOrder: 2
});
await createImageTemplate({
ownerId: "auth:app:user-b",
name: "社媒海报",
prompt: "生成适合社媒传播的海报",
settings: {},
sortOrder: 1
});
const owned = await listImageTemplates("auth:app:user-a");
expect(owned).toHaveLength(1);
expect(owned[0]).toMatchObject({
id: first.id,
ownerId: "auth:app:user-a",
description: "适合电商主视觉",
prompt: "生成干净高级的商品主图",
settings: { width: 2048, height: 2048, forceSingle: true }
});
expect(await listImageTemplates("auth:app:user-b")).toHaveLength(1);
});
it("does not update or delete another account's template", async () => {
const template = await createImageTemplate({
ownerId: "auth:app:user-a",
name: "A",
prompt: "提示词 A",
settings: {},
sortOrder: 0
});
expect(await updateImageTemplate(template.id, "auth:app:user-b", { name: "B" })).toBeNull();
expect(await deleteImageTemplate(template.id, "auth:app:user-b")).toBeNull();
expect((await listImageTemplates("auth:app:user-a"))[0].name).toBe("A");
const updated = await updateImageTemplate(template.id, "auth:app:user-a", { name: "A+", sortOrder: 3 });
expect(updated).toMatchObject({ name: "A+", sortOrder: 3 });
expect(await deleteImageTemplate(template.id, "auth:app:user-a")).toMatchObject({ id: template.id });
expect(await listImageTemplates("auth:app:user-a")).toHaveLength(0);
});
it("normalizes template creation input", () => {
expect(normalizeImageTemplateCreate({
name: " 品牌氛围图 ",
description: " 品牌视觉模板 ",
prompt: " 生成品牌氛围视觉 ",
previewImageUrl: "https://example.com/preview.png",
settings: { engine: "evolink", width: "1440", height: "2560", forceSingle: false, quality: "high", scale: "999" },
sortOrder: "5"
})).toMatchObject({
name: "品牌氛围图",
description: "品牌视觉模板",
prompt: "生成品牌氛围视觉",
previewImageUrl: "https://example.com/preview.png",
settings: { engine: "evolink", width: 1440, height: 2560, forceSingle: false, quality: "high", scale: 100 },
sortOrder: 5
});
expect(() => normalizeImageTemplateCreate({ name: "", prompt: "x" })).toThrow("模板名称不能为空");
expect(() => normalizeImageTemplateCreate({ name: "x", prompt: "x", previewImageUrl: "javascript:alert(1)" })).toThrow("效果预览图地址");
});
it("extracts prompt material placeholders for template upload slots", () => {
expect(extractMaterialPlaceholders("以 @图片1 为主体,参考 @图2 的色调,再参考 @视频1 的运动感。")).toEqual([
{ token: "@图片1", type: "image", index: 1 },
{ token: "@图片2", type: "image", index: 2 },
{ token: "@视频1", type: "video", index: 1 }
]);
expect(extractMaterialPlaceholders("以 @图片 为主体,@图片这种普通文字不应变成占位。")).toEqual([]);
});
});
function restoreEnv(name: string, value: string | undefined) {
if (value === undefined) {
delete process.env[name];
return;
}
process.env[name] = value;
}