- 全表新增 deleted_at 列,统一软删除替代硬删除+archived_at - models.model_id 重命名为 external_id,消除语义混淆 - conversations.model_id 改为可空(模型为建议而非绑定) - messages 新增 updated_at,移除 CASCADE 改为 DAO 层级联 - 移除 DB 层 UNIQUE 约束,改为应用层检查(配合软删除) - 新增 helpers.ts(baseColumns + 构造层防御)、ESLint 规则、契约测试 - 迁移 0004 补全 CHECK 约束(providers.type/materials.status/messages.role) - DAO 层全面重写:级联软删除、应用层唯一、provider 删除保护 - 路由/前端/测试全量适配 externalId 重命名及类型变更
153 lines
4.1 KiB
TypeScript
153 lines
4.1 KiB
TypeScript
import { screen, waitFor } from "@testing-library/react";
|
|
import { describe, expect, test } from "bun:test";
|
|
import { createElement } from "react";
|
|
|
|
import { App } from "../../../src/web/app";
|
|
import { renderWithProviders } from "../test-utils";
|
|
|
|
const MOCK_PROJECT = {
|
|
createdAt: "2024-01-01T00:00:00.000Z",
|
|
description: "测试项目",
|
|
id: "test-project-id",
|
|
name: "测试项目",
|
|
status: "active" as const,
|
|
updatedAt: "2024-01-01T00:00:00.000Z",
|
|
};
|
|
|
|
function createMockHandler(overrides?: { status?: "active" | "archived" }) {
|
|
const project = { ...MOCK_PROJECT, ...overrides };
|
|
const handler = (input: RequestInfo | URL) => {
|
|
const url = input instanceof Request ? input.url : typeof input === "string" ? input : input.toString();
|
|
if (url.includes("/api/meta")) {
|
|
return new Response(
|
|
JSON.stringify({ ok: true, service: "test-app", timestamp: new Date().toISOString(), version: "0.1.0" }),
|
|
{ headers: { "Content-Type": "application/json" }, status: 200 },
|
|
);
|
|
}
|
|
if (url.includes(`/api/projects/${project.id}`)) {
|
|
return new Response(JSON.stringify({ project }), {
|
|
headers: { "Content-Type": "application/json" },
|
|
status: 200,
|
|
});
|
|
}
|
|
if (url.includes("/api/projects/") && url.includes("/conversations")) {
|
|
return new Response(JSON.stringify({ items: [], page: 1, pageSize: 100, total: 0 }), {
|
|
headers: { "Content-Type": "application/json" },
|
|
status: 200,
|
|
});
|
|
}
|
|
return new Response(JSON.stringify({ error: "Not Found" }), { status: 404 });
|
|
};
|
|
const mocked = handler as unknown as typeof fetch;
|
|
globalThis.fetch = mocked;
|
|
window.fetch = mocked;
|
|
}
|
|
|
|
describe("Workbench 路由", () => {
|
|
test("active 项目可进入 Workbench", async () => {
|
|
createMockHandler();
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: `/workbench/${MOCK_PROJECT.id}`,
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
const body = document.body.textContent ?? "";
|
|
expect(body).toContain("工作台");
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("Workbench 显示返回管理台按钮", async () => {
|
|
createMockHandler();
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: `/workbench/${MOCK_PROJECT.id}`,
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText("返回管理台")).not.toBeNull();
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("不存在项目显示不可访问", async () => {
|
|
createMockHandler();
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: "/workbench/nonexistent-id",
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText("项目不存在或不可访问")).not.toBeNull();
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("archived 项目显示不可访问", async () => {
|
|
createMockHandler({ status: "archived" });
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: `/workbench/${MOCK_PROJECT.id}`,
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText("项目不存在或不可访问")).not.toBeNull();
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("Workbench 显示聊天室菜单", async () => {
|
|
createMockHandler();
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: `/workbench/${MOCK_PROJECT.id}`,
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText("聊天室")).not.toBeNull();
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("Workbench 收集箱路由可达", async () => {
|
|
createMockHandler();
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: `/workbench/${MOCK_PROJECT.id}/inbox`,
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText("新增素材")).not.toBeNull();
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
|
|
test("Workbench 显示收集箱菜单", async () => {
|
|
createMockHandler();
|
|
|
|
renderWithProviders(createElement(App), {
|
|
initialRoute: `/workbench/${MOCK_PROJECT.id}`,
|
|
});
|
|
|
|
await waitFor(
|
|
() => {
|
|
expect(screen.getByText("收集箱")).not.toBeNull();
|
|
},
|
|
{ timeout: 10000 },
|
|
);
|
|
});
|
|
});
|