test: 测试体系全面优化,修复 Windows SQLite EBUSY 和前端产品缺陷

测试基础设施
- 统一 SQLite 测试 DB/临时目录 helper(tests/helpers.ts),支持 Windows EBUSY 重试清理
- 测试库使用 PRAGMA journal_mode=DELETE 避免 WAL 句柄延迟
- 路由 handler 测试改用 createMigratedMemoryTestDatabase 避免 File DB 锁
- SQLite 聚焦 --rerun-each=20 全部通过(720 pass)

后端测试补强
- 新增 tests/server/app.test.ts 真实 startServer 集成测试
- 覆盖 /api/meta、项目 CRUD、错误路径、静态 fallback、安全 header
- bootstrap/logger 测试捕获预期输出,消除测试噪音

前端测试补强
- 移除 .ant-* 内部类名依赖,改为角色/文本/导航/请求契约断言
- 项目页补充搜索、Tab 切换、表单、表格操作、错误反馈行为测试
- 新增 hooks(use-theme-preference、use-sidebar-collapsed、use-projects)纯逻辑测试
- 新增 ErrorBoundary 错误展示和刷新按钮测试
- 新增搜索清空行为测试
- 测试 setup 过滤 antd/rc-trigger NaN height warning

产品修复(测试暴露)
- 修复 ProjectToolbar 搜索框无法输入(新增 draftKeyword 状态)
- 加固 ProjectFormModal 表单字段同步(useEffect 替代不可靠的 afterOpenChange)
- 清理 ProjectFormModal 冗余 afterOpenChange 同步逻辑

重构与合规
- ProjectContext 拆分为三文件满足 React Fast Refresh 规则
- use-projects.ts 导出内部 helper 函数供测试验证
- scripts/build.ts 提取纯生成函数供测试使用,修复构建步骤日志编号
- 修复 build 测试覆盖真实生成逻辑

文档同步
- 更新后端/前端/开发文档测试规范、质量门禁和 helper 使用说明
This commit is contained in:
2026-05-29 00:45:21 +08:00
parent 6cb378d7cb
commit 2ea4bd4410
31 changed files with 1417 additions and 723 deletions

View File

@@ -1,48 +1,55 @@
import { describe, expect, test } from "bun:test";
import { validateVersion } from "../../scripts/bump-version-logic";
import { createMigrationsDataSource, createServerEntrySource, createStaticAssetsSource } from "../../scripts/build";
describe("build 版本注入", () => {
test("validateVersion 接受有效版本", () => {
expect(() => validateVersion("0.1.0")).not.toThrow();
expect(() => validateVersion("1.2.3")).not.toThrow();
});
test("validateVersion 拒绝无效版本", () => {
expect(() => validateVersion("invalid")).toThrow();
expect(() => validateVersion("1.0.0-beta.1")).toThrow();
});
test("生成的 server-entry 包含版本字面量", () => {
test("生成的 server-entry 包含真实 bootstrap 参数", () => {
const version = "0.1.0";
const serverEntryTs = [
`import { bootstrap } from "../src/server/bootstrap";`,
`import { parseRuntimeArgs } from "../src/server/config";`,
`import { staticAssets } from "./static-assets";`,
"",
`const APP_VERSION = "${version}" as const;`,
"",
`async function main() {`,
` const { configPath } = parseRuntimeArgs();`,
` await bootstrap({ configPath, mode: "production", staticAssets, version: APP_VERSION });`,
`}`,
"",
`void main().catch((error) => {`,
` console.error("启动失败:", error instanceof Error ? error.message : error);`,
` process.exit(1);`,
`});`,
"",
].join("\n");
const serverEntryTs = createServerEntrySource(version);
expect(serverEntryTs).toContain(`const APP_VERSION = "${version}"`);
expect(serverEntryTs).toContain("version: APP_VERSION");
expect(serverEntryTs).toContain(`import { MIGRATIONS } from "./migrations-data";`);
expect(serverEntryTs).toContain(`import { staticAssets } from "./static-assets";`);
expect(serverEntryTs).toContain(
`bootstrap({ configPath, migrations: MIGRATIONS, mode: "production", staticAssets, version: APP_VERSION })`,
);
expect(serverEntryTs).toContain("createConsoleFallback().fatal");
});
test("版本字面量不依赖外部 package.json", () => {
const serverEntryTs = [`const APP_VERSION = "0.1.0" as const;`].join("\n");
test("生成的 static-assets 使用 indexHtml 与文件映射", () => {
const staticAssetsTs = createStaticAssetsSource({
fileEntries: [` "/assets/app.js": Bun.file(f1),`],
importLines: [
`import f0 from "./../dist/web/index.html" with { type: "file" };`,
`import f1 from "./../dist/web/assets/app.js" with { type: "file" };`,
],
indexHtmlVar: "f0",
});
expect(serverEntryTs).not.toContain("package.json");
expect(serverEntryTs).not.toContain("Bun.file");
expect(serverEntryTs).toContain('"0.1.0"');
expect(staticAssetsTs).toContain(`export const staticAssets`);
expect(staticAssetsTs).toContain(`"/assets/app.js": Bun.file(f1)`);
expect(staticAssetsTs).toContain(`indexHtml: Bun.file(f0)`);
});
test("生成的 migrations-data 嵌入真实记录形状", () => {
const migrationsTs = createMigrationsDataSource([
{ checksum: "abc123", id: "0000_initial", sql: "CREATE TABLE projects (id TEXT);" },
]);
expect(migrationsTs).toContain(`import type { MigrationRecord }`);
expect(migrationsTs).toContain(`export const MIGRATIONS: MigrationRecord[]`);
expect(migrationsTs).toContain(`id: "0000_initial"`);
expect(migrationsTs).toContain(`checksum: "abc123"`);
expect(migrationsTs).toContain(`CREATE TABLE projects`);
});
test("构建步骤编号保持 1/4 到 4/4 一致", async () => {
const source = await Bun.file(new URL("../../scripts/build.ts", import.meta.url)).text();
expect(source).toContain(`Step 1/4: Vite build`);
expect(source).toContain(`Step 2/4: Code generation`);
expect(source).toContain(`Step 3/4: Generating static assets`);
expect(source).toContain(`Step 4/4: Bun compile`);
expect(source).not.toContain(`Step 1/3`);
});
});