Files
Alfred/tests/server/db/materials.test.ts
lanyuanxiaoyao 12edf0b545 feat: 实现阶段二实体体系——AI预处理真实化+实体CRUD+审核归一化
- 新增 entities 数据表(含迁移)、Entity 类型、DAO 层完整 CRUD
- AI 预处理管道接入真实模型(generateText),输出结构化 JSON(摘要+规范化内容+候选实体)
- 模板接口重构为 {systemPrompt, buildUserPrompt, parseOutput},general/meeting 模板真实化
- 新增 5 个实体路由端点 + 实体管理前端页面(列表/详情/编辑弹窗)
- 审核面板增强:展示 AI 预处理结构化结果+候选实体归一化面板(合并/新建/选择/放弃)
- 素材通过时根据用户确认的候选实体写入 entities 表
- 工作台菜单新增"实体"入口
- 新增 entities DAO 测试(16)、processor 测试(11)、路由测试(8),服务端 367 测试全部通过
- TypeScript 0 错误
2026-06-08 18:49:30 +08:00

278 lines
9.1 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type Database from "bun:sqlite";
import { describe, expect, test } from "bun:test";
import {
approveMaterial,
createMaterial,
deleteMaterial,
discardMaterial,
getMaterial,
retryMaterial,
} from "../../../src/server/db/materials";
import { createProject } from "../../../src/server/db/projects";
import { createNoopLogger } from "../../../src/server/logger";
import { createMigratedTestDatabase } from "../../helpers";
const LOG = createNoopLogger();
function withMaterialsDb(callback: (db: Database) => void): void {
const handle = createMigratedTestDatabase("materials-dao-test");
try {
callback(handle.db);
handle.close();
} finally {
handle.cleanup();
}
}
function setupProject(db: Database, name = "测试项目"): string {
const result = createProject(db, { name }, LOG);
if ("error" in result) throw new Error(result.error);
return result.project.id;
}
function setupMaterial(
db: Database,
projectId: string,
overrides: Partial<{
associatedDate: string;
description: string;
materialType: "general" | "meeting";
}> = {},
): string {
const result = createMaterial(
db,
projectId,
{
associatedDate: overrides.associatedDate ?? "2024-01-15",
description: overrides.description ?? "测试素材",
materialType: overrides.materialType,
},
LOG,
);
if ("error" in result) throw new Error(result.error);
return result.material.id;
}
function setMaterialStatus(
db: Database,
materialId: string,
status: "approved" | "discarded" | "failed" | "pending" | "processing" | "review",
processedContent: string | null = null,
): void {
db.prepare("UPDATE materials SET status = ?, processed_content = ?, updated_at = ? WHERE id = ?").run(
status,
processedContent,
new Date().toISOString(),
materialId,
);
}
describe("素材数据访问层", () => {
describe("createMaterial", () => {
test("默认 materialType 为 general", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const result = createMaterial(db, projectId, { associatedDate: "2024-01-15", description: "测试" }, LOG);
expect("error" in result).toBe(false);
const material = (result as { material: { materialType: string } }).material;
expect(material.materialType).toBe("general");
});
});
test("显式指定 materialType 为 meeting", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const result = createMaterial(
db,
projectId,
{ associatedDate: "2024-01-15", description: "会议素材", materialType: "meeting" },
LOG,
);
expect("error" in result).toBe(false);
const material = (result as { material: { materialType: string } }).material;
expect(material.materialType).toBe("meeting");
});
});
test("非法 materialType 返回 400", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const result = createMaterial(
db,
projectId,
{
associatedDate: "2024-01-15",
description: "测试",
materialType: "invalid" as "general",
},
LOG,
);
expect("error" in result).toBe(true);
expect((result as { status: number }).status).toBe(400);
});
});
});
describe("getMaterial", () => {
test("返回包含 materialType 和 processedContent 字段", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
const result = getMaterial(db, projectId, materialId);
expect("error" in result).toBe(false);
const material = (result as { material: { materialType: string; processedContent: null | string } }).material;
expect(material.materialType).toBe("general");
expect(material.processedContent).toBeNull();
});
});
});
describe("approveMaterial", () => {
test("review 状态通过成功", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "review");
const result = approveMaterial(db, projectId, materialId, [], LOG);
expect("error" in result).toBe(false);
const material = (result as { material: { status: string } }).material;
expect(material.status).toBe("approved");
});
});
test("非 review 状态拒绝pending", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
const result = approveMaterial(db, projectId, materialId, [], LOG);
expect("error" in result).toBe(true);
expect((result as { status: number }).status).toBe(409);
});
});
test("素材不存在返回 404", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const result = approveMaterial(db, projectId, "nonexistent", [], LOG);
expect("error" in result).toBe(true);
expect((result as { status: number }).status).toBe(404);
});
});
});
describe("discardMaterial", () => {
test("review 状态放弃成功", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "review");
const result = discardMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(false);
const material = (result as { material: { status: string } }).material;
expect(material.status).toBe("discarded");
});
});
test("非 review 状态拒绝", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
const result = discardMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(true);
expect((result as { status: number }).status).toBe(409);
});
});
});
describe("retryMaterial", () => {
test("failed 状态重试成功并清空 processedContent", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "failed", "之前的内容");
const result = retryMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(false);
const material = (result as { material: { processedContent: null | string; status: string } }).material;
expect(material.status).toBe("pending");
expect(material.processedContent).toBeNull();
});
});
test("非 failed 状态拒绝", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "review");
const result = retryMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(true);
expect((result as { status: number }).status).toBe(409);
});
});
});
describe("deleteMaterial", () => {
test("processing 状态禁止删除返回 409", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "processing");
const result = deleteMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(true);
expect((result as { status: number }).status).toBe(409);
});
});
test("pending 状态可正常删除", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
const result = deleteMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(false);
});
});
test("review 状态可正常删除", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "review");
const result = deleteMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(false);
});
});
test("failed 状态可正常删除", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "failed");
const result = deleteMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(false);
});
});
test("approved 状态可正常删除", () => {
withMaterialsDb((db) => {
const projectId = setupProject(db);
const materialId = setupMaterial(db, projectId);
setMaterialStatus(db, materialId, "approved");
const result = deleteMaterial(db, projectId, materialId, LOG);
expect("error" in result).toBe(false);
});
});
});
});