Forráskód Böngészése

docs(architecture): 重构测试策略文档,拆分为各包专用测试规范

将原有的统一测试策略文档拆分为独立的测试规范文档,使文档结构更清晰易维护:

**新增文档**:
- web-ui-testing-standards.md: Web UI包测试规范(Vitest + Testing Library + Playwright)
- web-server-testing-standards.md: Web Server包测试规范(Vitest + hono/testing)
- backend-module-testing-standards.md: 后端模块包测试规范(Vitest + TypeORM)

**更新文档**:
- testing-strategy.md: 重构为概述文档,保留整体架构和测试金字塔模型,引用各专用规范
- coding-standards.md: 移除测试相关内容,专注于编码标准、代码风格、安全实践

**改进点**:
- 各包类型有独立的测试规范,避免文档臃肿
- 开发人员可快速找到对应包的测试标准
- 详细的测试示例和最佳实践集中在专用文档中
- 保持概述文档作为整体测试架构的入口

🤖 Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 3 hete
szülő
commit
16b49e4220

+ 740 - 0
docs/architecture/backend-module-testing-standards.md

@@ -0,0 +1,740 @@
+# 后端模块包测试规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-12-26 | 从测试策略文档拆分,专注后端模块包测试 | James (Claude Code) |
+
+## 概述
+
+本文档定义了后端模块包的测试标准和最佳实践。
+- **目标**: `packages/*-module` - 业务模块包(user-module、auth-module、file-module等)
+- **测试类型**: 单元测试 + 集成测试
+
+## 测试框架栈
+
+- **Vitest**: 测试运行器
+- **hono/testing**: Hono路由测试
+- **TypeORM**: 数据库测试
+- **PostgreSQL**: 测试数据库
+- **shared-test-util**: 共享测试基础设施
+
+## 测试分层策略
+
+### 1. 单元测试(Unit Tests)
+- **范围**: 单个服务、工具函数、Schema验证
+- **目标**: 验证独立单元的正确性
+- **位置**: `packages/*-module/tests/unit/**/*.test.ts`
+- **框架**: Vitest
+- **覆盖率目标**: ≥ 80%
+
+### 2. 集成测试(Integration Tests)
+- **范围**: 路由、服务、数据库集成
+- **目标**: 验证模块内部组件协作
+- **位置**: `packages/*-module/tests/integration/**/*.test.ts`
+- **框架**: Vitest + hono/testing + TypeORM
+- **覆盖率目标**: ≥ 60%
+
+## 测试文件结构
+
+```
+packages/user-module/
+├── src/
+│   ├── entities/
+│   │   ├── user.entity.ts
+│   │   └── role.entity.ts
+│   ├── services/
+│   │   ├── user.service.ts
+│   │   └── role.service.ts
+│   ├── schemas/
+│   │   ├── user.schema.ts
+│   │   └── role.schema.ts
+│   └── routes/
+│       ├── user.routes.ts
+│       └── role.routes.ts
+└── tests/
+    ├── unit/
+    │   ├── services/
+    │   │   ├── user.service.test.ts
+    │   │   └── role.service.test.ts
+    │   └── schemas/
+    │       ├── user.schema.test.ts
+    │       └── role.schema.test.ts
+    ├── integration/
+    │   ├── routes/
+    │   │   ├── user.routes.integration.test.ts
+    │   │   └── role.routes.integration.test.ts
+    │   └── services/
+    │       └── user.service.integration.test.ts
+    └── fixtures/
+        ├── test-db.ts        # 测试数据库配置
+        └── factories.ts      # 测试数据工厂
+```
+
+## 单元测试最佳实践
+
+### 1. 测试Service层
+```typescript
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { UserService } from '../src/services/user.service';
+import { User } from '../src/entities/user.entity';
+import { Repository } from 'typeorm';
+
+describe('UserService', () => {
+  let userService: UserService;
+  let mockUserRepo: Partial<Repository<User>>;
+
+  beforeEach(() => {
+    // 模拟Repository
+    mockUserRepo = {
+      findOne: vi.fn(),
+      find: vi.fn(),
+      create: vi.fn(),
+      save: vi.fn(),
+      update: vi.fn(),
+      delete: vi.fn()
+    };
+
+    userService = new UserService(mockUserRepo as Repository<User>);
+  });
+
+  describe('findById()', () => {
+    it('应该返回用户数据', async () => {
+      // Arrange
+      const mockUser = { id: 1, username: 'testuser', email: 'test@example.com' };
+      mockUserRepo.findOne?.mockResolvedValue(mockUser);
+
+      // Act
+      const result = await userService.findById(1);
+
+      // Assert
+      expect(result).toEqual(mockUser);
+      expect(mockUserRepo.findOne).toHaveBeenCalledWith({
+        where: { id: 1 }
+      });
+    });
+
+    it('应该返回null当用户不存在', async () => {
+      mockUserRepo.findOne?.mockResolvedValue(null);
+
+      const result = await userService.findById(999);
+
+      expect(result).toBeNull();
+    });
+  });
+
+  describe('createUser()', () => {
+    it('应该创建新用户', async () => {
+      const userData = {
+        username: 'newuser',
+        email: 'new@example.com',
+        password: 'hashedpassword'
+      };
+
+      const createdUser = { id: 1, ...userData };
+      mockUserRepo.create?.mockReturnValue(createdUser);
+      mockUserRepo.save?.mockResolvedValue(createdUser);
+
+      const result = await userService.createUser(userData);
+
+      expect(result).toEqual(createdUser);
+      expect(mockUserRepo.create).toHaveBeenCalledWith(userData);
+      expect(mockUserRepo.save).toHaveBeenCalledWith(createdUser);
+    });
+
+    it('应该抛出错误当用户名已存在', async () => {
+      mockUserRepo.findOne?.mockResolvedValue({ id: 1, username: 'existing' });
+
+      await expect(
+        userService.createUser({ username: 'existing', email: 'test@example.com', password: 'hash' })
+      ).rejects.toThrow('用户名已存在');
+    });
+  });
+});
+```
+
+### 2. 测试Schema验证
+```typescript
+import { describe, it, expect } from 'vitest';
+import { userSchema } from '../src/schemas/user.schema';
+
+describe('userSchema', () => {
+  it('应该验证有效的用户数据', async () => {
+    const validData = {
+      username: 'testuser',
+      email: 'test@example.com',
+      password: 'password123'
+    };
+
+    const result = await userSchema.parseAsync(validData);
+
+    expect(result).toEqual(validData);
+  });
+
+  it('应该拒绝无效的邮箱格式', async () => {
+    const invalidData = {
+      username: 'testuser',
+      email: 'invalid-email',
+      password: 'password123'
+    };
+
+    await expect(userSchema.parseAsync(invalidData)).rejects.toThrow();
+  });
+
+  it('应该拒绝空密码', async () => {
+    const invalidData = {
+      username: 'testuser',
+      email: 'test@example.com',
+      password: ''
+    };
+
+    await expect(userSchema.parseAsync(invalidData)).rejects.toThrow();
+  });
+});
+```
+
+### 3. 测试工具函数
+```typescript
+import { describe, it, expect } from 'vitest';
+import { hashPassword, comparePassword } from '../src/utils/password.util';
+
+describe('密码工具函数', () => {
+  describe('hashPassword()', () => {
+    it('应该返回哈希后的密码', async () => {
+      const password = 'password123';
+      const hash = await hashPassword(password);
+
+      expect(hash).not.toBe(password);
+      expect(hash).toMatch(/^\$2[ayb]\$.{56}$/); // bcrypt格式
+    });
+
+    it('应该为相同密码生成不同的哈希值', async () => {
+      const password = 'password123';
+      const hash1 = await hashPassword(password);
+      const hash2 = await hashPassword(password);
+
+      expect(hash1).not.toBe(hash2);
+    });
+  });
+
+  describe('comparePassword()', () => {
+    it('应该返回true当密码匹配', async () => {
+      const password = 'password123';
+      const hash = await hashPassword(password);
+
+      const result = await comparePassword(password, hash);
+
+      expect(result).toBe(true);
+    });
+
+    it('应该返回false当密码不匹配', async () => {
+      const hash = await hashPassword('password123');
+
+      const result = await comparePassword('wrongpassword', hash);
+
+      expect(result).toBe(false);
+    });
+  });
+});
+```
+
+## 集成测试最佳实践
+
+### 1. 测试路由层
+```typescript
+import { describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { integrateRoutes } from 'hono/testing';
+import { Hono } from 'hono';
+import { getTestDataSource } from './fixtures/test-db';
+import { userRoutes } from '../src/routes/user.routes';
+import { seedTestUser } from './fixtures/factories';
+
+describe('用户路由集成测试', () => {
+  let dataSource: DataSource;
+  let app: Hono;
+
+  beforeEach(async () => {
+    dataSource = await getTestDataSource();
+    await dataSource.initialize();
+
+    app = new Hono();
+    app.route('/api/users', userRoutes);
+  });
+
+  afterEach(async () => {
+    await dataSource.destroy();
+  });
+
+  describe('GET /api/users', () => {
+    it('应该返回用户列表', async () => {
+      // Arrange
+      await seedTestUser(dataSource, { username: 'user1' });
+      await seedTestUser(dataSource, { username: 'user2' });
+
+      // Act
+      const res = await integrateRoutes(app).GET('/api/users');
+
+      // Assert
+      expect(res.status).toBe(200);
+      const json = await res.json();
+      expect(json.data).toHaveLength(2);
+      expect(json.data[0].username).toBe('user1');
+    });
+
+    it('应该支持分页查询', async () => {
+      // 创建15个用户
+      for (let i = 1; i <= 15; i++) {
+        await seedTestUser(dataSource, { username: `user${i}` });
+      }
+
+      // 查询第一页
+      const res1 = await integrateRoutes(app).GET('/api/users?page=1&pageSize=10');
+      expect(res1.status).toBe(200);
+      const json1 = await res1.json();
+      expect(json1.data).toHaveLength(10);
+      expect(json1.total).toBe(15);
+
+      // 查询第二页
+      const res2 = await integrateRoutes(app).GET('/api/users?page=2&pageSize=10');
+      expect(res2.status).toBe(200);
+      const json2 = await res2.json();
+      expect(json2.data).toHaveLength(5);
+    });
+  });
+
+  describe('GET /api/users/:id', () => {
+    it('应该返回用户详情', async () => {
+      const user = await seedTestUser(dataSource, { username: 'testuser' });
+
+      const res = await integrateRoutes(app).GET(`/api/users/${user.id}`);
+
+      expect(res.status).toBe(200);
+      const json = await res.json();
+      expect(json.username).toBe('testuser');
+    });
+
+    it('应该返回404当用户不存在', async () => {
+      const res = await integrateRoutes(app).GET('/api/users/99999');
+
+      expect(res.status).toBe(404);
+    });
+  });
+
+  describe('POST /api/users', () => {
+    it('应该创建新用户', async () => {
+      const userData = {
+        username: 'newuser',
+        email: 'new@example.com',
+        password: 'password123'
+      };
+
+      const res = await integrateRoutes(app).POST('/api/users', { json: userData });
+
+      expect(res.status).toBe(201);
+      const json = await res.json();
+      expect(json.username).toBe('newuser');
+
+      // 验证数据库中的数据
+      const userRepo = dataSource.getRepository(User);
+      const user = await userRepo.findOne({ where: { username: 'newuser' } });
+      expect(user).toBeDefined();
+    });
+
+    it('应该拒绝重复的用户名', async () => {
+      await seedTestUser(dataSource, { username: 'existing' });
+
+      const res = await integrateRoutes(app).POST('/api/users', {
+        json: {
+          username: 'existing',
+          email: 'different@example.com',
+          password: 'password123'
+        }
+      });
+
+      expect(res.status).toBe(400);
+      const json = await res.json();
+      expect(json.error).toContain('用户名已存在');
+    });
+  });
+});
+```
+
+### 2. 测试Service与数据库集成
+```typescript
+describe('UserService与数据库集成', () => {
+  let dataSource: DataSource;
+  let userService: UserService;
+
+  beforeEach(async () => {
+    dataSource = await getTestDataSource();
+    await dataSource.initialize();
+
+    const userRepo = dataSource.getRepository(User);
+    userService = new UserService(userRepo);
+  });
+
+  afterEach(async () => {
+    await dataSource.destroy();
+  });
+
+  it('应该从数据库查询用户', async () => {
+    // 创建测试数据
+    const userRepo = dataSource.getRepository(User);
+    const createdUser = await userRepo.save({
+      username: 'testuser',
+      email: 'test@example.com',
+      password: 'hashedpassword'
+    });
+
+    // 测试查询
+    const foundUser = await userService.findById(createdUser.id);
+
+    expect(foundUser).toBeDefined();
+    expect(foundUser?.username).toBe('testuser');
+  });
+
+  it('应该正确处理事务', async () => {
+    await dataSource.transaction(async (manager) => {
+      const user1 = await manager.save(User, {
+        username: 'user1',
+        email: 'user1@example.com',
+        password: 'hash'
+      });
+
+      const user2 = await manager.save(User, {
+        username: 'user2',
+        email: 'user2@example.com',
+        password: 'hash'
+      });
+
+      expect(user1.id).toBeDefined();
+      expect(user2.id).toBeDefined();
+    });
+  });
+});
+```
+
+### 3. 测试中间件集成
+```typescript
+import { authMiddleware } from '../src/middleware/auth.middleware';
+import { generateToken } from '@d8d/shared-utils/jwt.util';
+
+describe('认证中间件集成测试', () => {
+  it('应该允许有效token的请求', async () => {
+    const app = new Hono();
+    app.use('/api/protected/*', authMiddleware);
+    app.get('/api/protected/data', (c) => c.json({ message: 'Protected data' }));
+
+    const token = generateToken({ userId: 1, username: 'testuser' });
+    const res = await integrateRoutes(app).GET('/api/protected/data', {
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    });
+
+    expect(res.status).toBe(200);
+    expect(await res.json()).toEqual({ message: 'Protected data' });
+  });
+
+  it('应该拒绝无效token的请求', async () => {
+    const app = new Hono();
+    app.use('/api/protected/*', authMiddleware);
+    app.get('/api/protected/data', (c) => c.json({ message: 'Protected data' }));
+
+    const res = await integrateRoutes(app).GET('/api/protected/data', {
+      headers: {
+        Authorization: 'Bearer invalid-token'
+      }
+    });
+
+    expect(res.status).toBe(401);
+  });
+});
+```
+
+## 测试数据工厂模式
+
+### factories.ts
+```typescript
+import { User } from '../src/entities/user.entity';
+import { Repository } from 'typeorm';
+
+export function buildUser(overrides = {}): Partial<User> {
+  return {
+    id: Math.floor(Math.random() * 10000),
+    username: `testuser_${Date.now()}`,
+    email: `test_${Date.now()}@example.com`,
+    password: 'hashedpassword',
+    role: 'user',
+    active: true,
+    createdAt: new Date(),
+    updatedAt: new Date(),
+    ...overrides
+  };
+}
+
+export async function createTestUser(
+  userRepo: Repository<User>,
+  overrides = {}
+): Promise<User> {
+  const userData = buildUser(overrides);
+  const user = userRepo.create(userData);
+  return await userRepo.save(user);
+}
+
+// 批量创建测试数据
+export async function createTestUsers(
+  userRepo: Repository<User>,
+  count: number,
+  overrides = {}
+): Promise<User[]> {
+  const users = [];
+  for (let i = 0; i < count; i++) {
+    const user = await createTestUser(userRepo, {
+      username: `user${i}`,
+      email: `user${i}@example.com`,
+      ...overrides
+    });
+    users.push(user);
+  }
+  return users;
+}
+```
+
+## 数据库测试策略
+
+### 1. 使用事务回滚
+```typescript
+describe('使用事务回滚', () => {
+  let queryRunner: QueryRunner;
+  let dataSource: DataSource;
+
+  beforeEach(async () => {
+    dataSource = await getTestDataSource();
+    await dataSource.initialize();
+    queryRunner = dataSource.createQueryRunner();
+    await queryRunner.startTransaction();
+  });
+
+  afterEach(async () => {
+    await queryRunner.rollbackTransaction();
+    await queryRunner.release();
+    await dataSource.destroy();
+  });
+
+  it('每个测试后自动回滚', async () => {
+    const userRepo = queryRunner.manager.getRepository(User);
+    await userRepo.save({
+      username: 'testuser',
+      email: 'test@example.com',
+      password: 'hash'
+    });
+
+    // 数据存在于事务中
+    const users = await userRepo.find();
+    expect(users).toHaveLength(1);
+
+    // 测试结束后自动回滚,数据库为空
+  });
+});
+```
+
+### 2. 使用测试数据库
+```typescript
+// tests/fixtures/test-db.ts
+import { DataSource } from 'typeorm';
+import { User } from '../src/entities/user.entity';
+import { Role } from '../src/entities/role.entity';
+
+export async function getTestDataSource(): Promise<DataSource> {
+  return new DataSource({
+    type: 'postgres',
+    host: 'localhost',
+    port: 5432,
+    username: 'postgres',
+    password: 'test_password',
+    database: 'test_d8dai', // 独立的测试数据库
+    entities: [User, Role],
+    synchronize: true, // 自动同步表结构
+    dropSchema: true, // 每次测试前清空数据库
+    logging: false // 测试环境关闭SQL日志
+  });
+}
+```
+
+## 覆盖率标准
+
+| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
+|----------|----------|----------|--------------|
+| 单元测试 | 70% | 80% | 90% |
+| 集成测试 | 50% | 60% | 70% |
+
+**关键模块定义**:
+- 认证授权Service:90%单元测试覆盖率
+- 数据库操作Service:85%单元测试覆盖率
+- 核心业务逻辑:80%集成测试覆盖率
+
+## 运行测试
+
+### 本地开发
+```bash
+# 运行所有测试
+cd packages/user-module && pnpm test
+
+# 只运行单元测试
+pnpm test:unit
+
+# 只运行集成测试
+pnpm test:integration
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 运行特定测试文件
+pnpm test user.service.test.ts
+
+# 运行特定测试用例
+pnpm test --testNamePattern="应该创建用户"
+```
+
+### CI/CD
+```yaml
+user-module-tests:
+  runs-on: ubuntu-latest
+  services:
+    postgres:
+      image: postgres:17
+      env:
+        POSTGRES_PASSWORD: test_password
+        POSTGRES_DB: test_d8dai
+  steps:
+    - run: cd packages/user-module && pnpm install
+    - run: cd packages/user-module && pnpm test
+
+all-modules-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd packages/user-module && pnpm test
+    - run: cd packages/auth-module && pnpm test
+    - run: cd packages/file-module && pnpm test
+```
+
+## 测试命名约定
+
+### 文件命名
+- Service单元测试:`[service].service.test.ts`
+- Schema单元测试:`[schema].schema.test.ts`
+- 路由集成测试:`[route].routes.integration.test.ts`
+- Service集成测试:`[service].service.integration.test.ts`
+
+### 测试描述
+```typescript
+describe('UserService', () => {
+  describe('findById()', () => {
+    it('应该返回用户数据', async () => { });
+    it('应该返回null当用户不存在', async () => { });
+  });
+
+  describe('createUser()', () => {
+    it('应该创建新用户', async () => { });
+    it('应该拒绝重复的用户名', async () => { });
+  });
+});
+```
+
+## 常见错误避免
+
+### ❌ 不要在单元测试中使用真实数据库
+```typescript
+// 错误:单元测试连接真实数据库
+it('应该查询用户', async () => {
+  const dataSource = await getTestDataSource(); // 不要在单元测试中
+  // ...
+});
+
+// 正确:使用模拟的Repository
+it('应该查询用户', async () => {
+  const mockRepo = { findOne: vi.fn() };
+  const userService = new UserService(mockRepo);
+  // ...
+});
+```
+
+### ❌ 不要在集成测试中mock数据库
+```typescript
+// 错误:集成测试中mock数据库
+vi.mock('typeorm', () => ({
+  getRepository: vi.fn(() => mockRepo)
+}));
+
+// 正确:使用真实数据库
+const dataSource = await getTestDataSource();
+const userRepo = dataSource.getRepository(User);
+```
+
+### ❌ 不要共享测试数据
+```typescript
+// 错误:测试间共享数据
+let testUserId: number;
+
+it('应该创建用户', async () => {
+  const user = await createUser({ username: 'test' });
+  testUserId = user.id; // 污染下一个测试
+});
+
+it('应该更新用户', async () => {
+  await updateUser(testUserId, { username: 'updated' }); // 依赖上一个测试
+});
+
+// 正确:每个测试独立创建数据
+it('应该更新用户', async () => {
+  const user = await createTestUser(dataSource);
+  await updateUser(user.id, { username: 'updated' });
+});
+```
+
+## 调试技巧
+
+### 1. 查看SQL查询
+```typescript
+const dataSource = new DataSource({
+  // ...
+  logging: true, // 显示所有SQL
+  maxQueryExecutionTime: 100 // 记录慢查询
+});
+```
+
+### 2. 打印测试数据
+```typescript
+it('调试测试', async () => {
+  const users = await userService.findAll();
+  console.debug('用户列表:', users);
+  console.debug('用户数量:', users.length);
+});
+```
+
+### 3. 使用only运行特定测试
+```typescript
+it.only('只运行这个测试', async () => {
+  // ...
+});
+```
+
+## 参考实现
+
+- 用户模块包:`packages/user-module/tests/`
+- 认证模块包:`packages/auth-module/tests/`
+- 文件模块包:`packages/file-module/tests/`
+
+## 相关文档
+
+- [测试策略概述](./testing-strategy.md)
+- [Web UI包测试规范](./web-ui-testing-standards.md)
+- [Web Server包测试规范](./web-server-testing-standards.md)
+- [Mini UI包测试规范](./mini-ui-testing-standards.md)
+- [后端模块包开发规范](./backend-module-package-standards.md)
+
+---
+
+**文档状态**: 正式版
+**适用范围**: packages/*-module

+ 259 - 60
docs/architecture/coding-standards.md

@@ -1,64 +1,65 @@
-# 编码标准和测试策略
+# 编码标准
 
 ## 版本信息
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
+| 3.0 | 2025-12-26 | 拆分测试策略到独立文档,保留编码标准 | James (Claude Code) |
 | 2.5 | 2025-12-26 | 添加Mini UI包开发规范章节 | Bob (Scrum Master) |
 | 2.4 | 2025-09-20 | 与主架构文档版本一致 | Winston |
 
 ## 现有标准合规性
-- **代码风格**: TypeScript严格模式,一致的缩进和命名
-- **linting规则**: 已配置ESLint,支持TypeScript和React
-- **测试模式**: 完整的测试框架已配置(Vitest + Testing Library + Playwright)
-- **文档风格**: 代码注释良好,测试策略文档完整
-- **UI包开发**: 必须遵循[UI包开发规范](./ui-package-standards.md)(基于史诗008经验总结)
-- **Mini UI包开发**: 必须遵循[Mini UI包开发规范](./mini-ui-package-standards.md)(基于史诗011和017经验总结)
-
-## 增强特定标准
-- **测试框架**: 使用Vitest + Testing Library + hono/testing + Playwright
-- **测试位置**: `tests/` 文件夹与源码并列
-- **覆盖率目标**: 核心业务逻辑 > 80%
-- **测试类型**: 单元测试、集成测试、E2E测试
-
-## 关键集成规则
-- **现有API兼容性**: 确保测试不破坏现有API契约
-- **数据库集成**: 使用测试数据库,避免污染生产数据
-- **错误处理**: 测试各种错误场景和边界条件
-- **日志一致性**: 测试日志格式和错误信息
-
-## UI包开发提示
-
-### 必须遵循的规范
-开发UI包时,**必须**参考并遵循[UI包开发规范](./ui-package-standards.md),该规范基于史诗008(AllIn UI模块移植)的经验总结。
-
-### 关键检查点(基于史诗008经验)
-1. **API路径映射验证**:开发前必须验证故事中的API路径映射与实际后端路由定义的一致性
-2. **类型推断最佳实践**:必须使用RPC推断类型,而不是直接导入schema类型
-3. **测试选择器优化**:必须为关键交互元素添加`data-testid`属性
-4. **表单组件模式**:必须使用条件渲染两个独立的Form组件
-5. **API调用一致性**:必须根据实际路由名称修正API调用
-
-### 常见错误避免
+
+### 代码风格
+- **TypeScript严格模式**: 所有项目必须启用严格类型检查
+- **一致的缩进**: 使用2个空格缩进
+- **命名约定**:
+  - 文件名: kebab-case (如: `user.service.ts`)
+  - 类名: PascalCase (如: `UserService`)
+  - 函数/变量: camelCase (如: `getUserById`)
+  - 常量: UPPER_SNAKE_CASE (如: `API_BASE_URL`)
+  - 接口: PascalCase,无I前缀 (如: `User`)
+
+### Linting规则
+- **ESLint**: 已配置ESLint,支持TypeScript和React
+- **Prettier**: 统一代码格式化
+- **提交前检查**: 使用husky进行pre-commit钩子检查
+
+### 文档风格
+- **代码注释**: 关键逻辑必须添加注释说明
+- **JSDoc**: 公共API必须包含JSDoc注释
+- **README**: 每个包必须有独立的README说明用途和使用方法
+
+## 开发规范引用
+
+### UI包开发
+开发Web UI包时,**必须**参考并遵循[UI包开发规范](./ui-package-standards.md),该规范基于史诗008(AllIn UI模块移植)的经验总结。
+
+**关键检查点**:
+1. **API路径映射验证**: 开发前必须验证故事中的API路径映射与实际后端路由定义的一致性
+2. **类型推断最佳实践**: 必须使用RPC推断类型,而不是直接导入schema类型
+3. **测试选择器优化**: 必须为关键交互元素添加`data-testid`属性
+4. **表单组件模式**: 必须使用条件渲染两个独立的Form组件
+5. **API调用一致性**: 必须根据实际路由名称修正API调用
+
+**常见错误避免**:
 - ❌ 不要直接导入schema类型(可能导致Date/string类型不匹配)
 - ❌ 不要使用`getByText()`查找可能重复的文本元素
 - ❌ 不要在单个Form组件上动态切换props
 - ❌ 不要使用故事中描述但实际不存在的路由名称
 
-### 参考实现
+**参考实现**:
 - 广告管理UI包:`packages/advertisement-management-ui`
 - 平台管理UI包:`allin-packages/platform-management-ui`
 - 渠道管理UI包:`allin-packages/channel-management-ui`(史诗008.002)
 
-## Mini UI包开发提示
-
-### 必须遵循的规范
+### Mini UI包开发
 开发Mini UI包(Taro小程序UI包)时,**必须**参考并遵循[Mini UI包开发规范](./mini-ui-package-standards.md),该规范基于史诗011(用人方小程序)和史诗017(人才小程序)的实施经验总结。
 
-### 关键检查点(基于史诗011和017经验)
+**关键检查点**:
 
 #### 1. Taro小程序布局规范
-- **View组件默认横向布局**View容器内的子元素默认是横向布局(`flex-row`),必须显式添加 `flex flex-col` 类才能实现垂直布局
-- **Text组件默认内联显示**Text组件默认是内联显示(类似span),需要使用`flex flex-col`强制垂直排列
+- **View组件默认横向布局**: View容器内的子元素默认是横向布局(`flex-row`),必须显式添加 `flex flex-col` 类才能实现垂直布局
+- **Text组件默认内联显示**: Text组件默认是内联显示(类似span),需要使用`flex flex-col`强制垂直排列
 
 **正确示例**:
 ```typescript
@@ -78,9 +79,9 @@
 ```
 
 #### 2. 图标使用规范
-- **必须使用Heroicons图标类**不要使用emoji或文本符号
-- **图标类命名格式**`i-heroicons-{icon-name}-{size}-{style}`
-- **必须添加尺寸类**如 `w-5 h-5`、`text-lg` 等
+- **必须使用Heroicons图标类**: 不要使用emoji或文本符号
+- **图标类命名格式**: `i-heroicons-{icon-name}-{size}-{style}`
+- **必须添加尺寸类**: 如 `w-5 h-5`、`text-lg` 等
 
 **正确示例**:
 ```typescript
@@ -107,30 +108,228 @@
 - `qr-code-20-solid` - 二维码
 
 #### 3. Navbar导航栏集成规范
-- **TabBar页面(一级)**:使用Navbar无返回按钮(`leftIcon=""`、`leftText=""`)
-- **非TabBar页面(二级)**:使用Navbar带返回按钮(`leftIcon="i-heroicons-chevron-left-20-solid"`)
-- **Navbar组件来源**:`@d8d/mini-shared-ui-components/components/navbar`
-
-#### 4. 测试框架选择
-- **Mini项目使用Jest**:不是Vitest(与Web应用不同)
-- **测试配置文件**:`jest.config.cjs`
-- **测试工具包**:`@d8d/mini-testing-utils`
-- **测试规范**:必须遵循[Mini UI包测试规范](./mini-ui-testing-standards.md)(基于故事017.003实施经验)
-
-#### 5. API客户端模式
-- **每个UI包独立管理**:每个Mini UI包包含自己的API客户端和RPC类型
-- **使用相对路径导入**:UI包内部必须使用相对路径,不要使用别名
-- **RPC推断类型**:必须使用RPC推断类型,而不是直接导入schema类型
-
-### 常见错误避免
+- **TabBar页面(一级)**: 使用Navbar无返回按钮(`leftIcon=""`、`leftText=""`)
+- **非TabBar页面(二级)**: 使用Navbar带返回按钮(`leftIcon="i-heroicons-chevron-left-20-solid"`)
+- **Navbar组件来源**: `@d8d/mini-shared-ui-components/components/navbar`
+
+#### 4. API客户端模式
+- **每个UI包独立管理**: 每个Mini UI包包含自己的API客户端和RPC类型
+- **使用相对路径导入**: UI包内部必须使用相对路径,不要使用别名
+- **RPC推断类型**: 必须使用RPC推断类型,而不是直接导入schema类型
+
+**常见错误避免**:
 - ❌ 不要忘记添加 `flex flex-col` 实现垂直布局
 - ❌ 不要使用emoji代替Heroicons图标
 - ❌ 不要忘记为图标添加尺寸类(`w-5 h-5`、`text-lg`等)
 - ❌ 不要在Mini UI包内部导入中使用别名(`@/`、`~/`等)
 - ❌ 不要使用Vitest作为Mini项目的测试框架(应使用Jest)
 
-### 参考实现
+**参考实现**:
 - 用人方小程序UI包:`mini-ui-packages/yongren-dashboard-ui`
 - 人才小程序UI包:`mini-ui-packages/rencai-dashboard-ui`
 - 共享组件包:`mini-ui-packages/mini-shared-ui-components`
 
+### 后端模块包开发
+开发后端模块包时,**必须**参考并遵循[后端模块包开发规范](./backend-module-package-standards.md)。
+
+**核心要求**:
+1. 模块化架构: entities / services / schemas / routes
+2. 依赖注入: 使用构造函数注入
+3. 错误处理: 统一的错误处理机制
+4. 日志记录: 使用结构化日志
+
+## 类型安全
+
+### TypeScript配置
+```json
+{
+  "compilerOptions": {
+    "strict": true,
+    "noImplicitAny": true,
+    "strictNullChecks": true,
+    "strictFunctionTypes": true,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true
+  }
+}
+```
+
+### 类型定义
+```typescript
+// ✅ 推荐: 使用interface定义对象形状
+interface User {
+  id: number;
+  username: string;
+  email: string;
+  createdAt: Date;
+}
+
+// ✅ 推荐: 使用type定义联合类型
+type Status = 'pending' | 'active' | 'inactive';
+
+// ✅ 推荐: 使用泛型增强类型复用
+interface ApiResponse<T> {
+  status: number;
+  data: T;
+  message?: string;
+}
+```
+
+## 错误处理
+
+### 统一错误处理
+```typescript
+// 定义自定义错误类
+class ValidationError extends Error {
+  constructor(public errors: Record<string, string[]>) {
+    super('验证失败');
+    this.name = 'ValidationError';
+  }
+}
+
+// 使用自定义错误
+function validateUser(data: unknown): User {
+  const result = userSchema.safeParse(data);
+  if (!result.success) {
+    throw new ValidationError(result.error.flatten().fieldErrors);
+  }
+  return result.data;
+}
+```
+
+### 错误日志
+```typescript
+import { logger } from '@d8d/shared-utils/logger';
+
+try {
+  await userService.createUser(userData);
+} catch (error) {
+  logger.error('创建用户失败', {
+    error: error.message,
+    stack: error.stack,
+    userData: JSON.stringify(userData)
+  });
+  throw error;
+}
+```
+
+## 安全最佳实践
+
+### 输入验证
+```typescript
+// ✅ 使用Schema验证
+import { userSchema } from './schemas/user.schema';
+
+const validatedData = await userSchema.parseAsync(inputData);
+
+// ❌ 不要直接信任用户输入
+const user = { username: req.body.username }; // 不安全
+```
+
+### 敏感数据处理
+```typescript
+// ✅ 从响应中排除敏感字段
+function sanitizeUser(user: User): Partial<User> {
+  const { password, ...sanitized } = user;
+  return sanitized;
+}
+
+// ✅ 日志中不记录敏感信息
+logger.info('用户登录', { userId: user.id }); // 正确
+logger.info('用户登录', { user }); // 错误 - 会记录密码
+```
+
+### SQL注入防护
+```typescript
+// ✅ 使用TypeORM参数化查询
+const users = await userRepo.find({
+  where: { username: username }
+});
+
+// ❌ 不要拼接SQL字符串
+const query = `SELECT * FROM users WHERE username = '${username}'`; // 危险
+```
+
+## 性能优化
+
+### 数据库查询优化
+```typescript
+// ✅ 只查询需要的字段
+const users = await userRepo.find({
+  select: ['id', 'username', 'email']
+});
+
+// ✅ 使用索引字段查询
+const user = await userRepo.findOne({
+  where: { email: userEmail } // email字段应有索引
+});
+
+// ❌ 避免N+1查询
+const orders = await orderRepo.find({
+  relations: ['user', 'products'] // 使用join而不是循环查询
+});
+```
+
+### 缓存策略
+```typescript
+// ✅ 使用Redis缓存
+import { cacheGet, cacheSet } from '@d8d/shared-utils/redis.util';
+
+async function getUserById(id: number) {
+  const cached = await cacheGet(`user:${id}`);
+  if (cached) return JSON.parse(cached);
+
+  const user = await userRepo.findOne({ where: { id } });
+  await cacheSet(`user:${id}`, JSON.stringify(user), 3600);
+  return user;
+}
+```
+
+## 代码组织
+
+### 文件结构
+```
+packages/user-module/
+├── src/
+│   ├── entities/        # 数据实体
+│   ├── services/        # 业务逻辑
+│   ├── schemas/         # 验证Schema
+│   ├── routes/          # API路由
+│   ├── middleware/      # 中间件
+│   ├── utils/           # 工具函数
+│   └── index.ts         # 包入口
+├── tests/               # 测试文件
+├── README.md            # 包说明
+└── package.json
+```
+
+### 导出规范
+```typescript
+// index.ts - 统一导出
+export * from './entities';
+export * from './services';
+export { userRoutes } from './routes';
+export { userSchema } from './schemas';
+```
+
+## 相关文档
+
+### 测试规范
+- [测试策略概述](./testing-strategy.md)
+- [Web UI包测试规范](./web-ui-testing-standards.md)
+- [Web Server包测试规范](./web-server-testing-standards.md)
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [Mini UI包测试规范](./mini-ui-testing-standards.md)
+
+### 开发规范
+- [UI包开发规范](./ui-package-standards.md)
+- [Mini UI包开发规范](./mini-ui-package-standards.md)
+- [后端模块包开发规范](./backend-module-package-standards.md)
+- [API设计规范](./api-design-integration.md)
+
+---
+
+**文档状态**: 正式版
+**下次评审**: 2026-01-26

+ 157 - 441
docs/architecture/testing-strategy.md

@@ -3,181 +3,95 @@
 ## 版本信息
 | 版本 | 日期 | 描述 | 作者 |
 |------|------|------|------|
+| 3.0 | 2025-12-26 | 重构为概述文档,拆分详细规范到独立文档 | James (Claude Code) |
 | 2.10 | 2025-12-12 | 添加使用共享测试工具处理复杂组件的规范 | James (Claude Code) |
 | 2.9 | 2025-12-12 | 添加测试用例编写规范,基于订单管理集成测试经验 | James (Claude Code) |
 | 2.8 | 2025-11-11 | 更新包测试结构,添加模块化包测试策略 | Winston |
 | 2.7 | 2025-11-09 | 更新为monorepo测试架构,清理重复测试文件 | James |
-| 2.6 | 2025-10-15 | 完成遗留测试文件迁移到统一的tests目录结构 | Winston |
-| 2.5 | 2025-10-14 | 更新测试文件位置到统一的tests目录结构 | Claude |
-| 2.4 | 2025-09-20 | 更新测试策略与主架构文档版本一致 | Winston |
 
 ## 概述
 
-本文档定义了D8D Starter项目的完整测试策略,基于monorepo架构和现有的测试基础设施。测试策略遵循测试金字塔模型,确保代码质量、功能稳定性和系统可靠性
+本文档是D8D Starter项目的测试策略概述,定义了整体的测试架构和原则。详细的测试规范请参考各包的专用测试规范文档
 
-### 测试架构更新 (v2.8)
+### 测试架构概览
 
-项目已重构为模块化包架构,测试架构相应调整为:
-- **基础设施包**: shared-types、shared-utils、shared-crud、shared-test-util
-- **业务模块包**: user-module、auth-module、file-module、geo-areas
-- **应用层**: server (重构后),包含模块集成测试
-- **web**: Web应用,包含组件测试、集成测试和E2E测试
-- **CI/CD**: 独立的工作流分别处理各包的测试
+项目采用分层测试架构,遵循测试金字塔模型:
 
-### 包测试架构 (v2.8)
+```
+           /\
+          /  \        E2E测试 (Playwright)
+         /____\       关键业务流程 100%
+        /      \
+       /        \     集成测试 (Vitest)
+      /          \    模块间协作 ≥60%
+     /____________\
+    /              \   单元测试 (Vitest/Jest)
+   /________________\  独立单元 ≥80%
+```
 
-项目采用分层测试架构,每个包独立测试:
-- **基础设施包**: 纯单元测试,不依赖外部服务
-- **业务模块包**: 单元测试 + 集成测试,验证模块功能
-- **应用层**: 集成测试,验证模块间协作
-- **共享测试工具**: shared-test-util 提供统一的测试基础设施
+### 包分类测试策略
 
-## 测试金字塔策略
+| 包类型 | 测试类型 | 主要框架 | 详细规范 |
+|--------|----------|----------|----------|
+| **Web UI包** | 组件测试 + 集成测试 + E2E测试 | Vitest + Testing Library + Playwright | [Web UI包测试规范](./web-ui-testing-standards.md) |
+| **Web Server包** | 集成测试 | Vitest + hono/testing | [Web Server包测试规范](./web-server-testing-standards.md) |
+| **后端模块包** | 单元测试 + 集成测试 | Vitest + TypeORM | [后端模块包测试规范](./backend-module-testing-standards.md) |
+| **Mini UI包** | 组件测试 + 集成测试 | Jest + @testing-library/react | [Mini UI包测试规范](./mini-ui-testing-standards.md) |
+
+## 测试分层策略
 
 ### 单元测试 (Unit Tests)
 - **范围**: 单个函数、类或组件
-- **目标**: 验证独立单元的correctness
-- **位置**:
-  - **基础设施包**: `packages/shared-*/tests/unit/**/*.test.ts`
-  - **业务模块包**: `packages/*-module/tests/unit/**/*.test.ts`
-  - **server包**: `packages/server/tests/unit/**/*.test.ts`
-  - **web应用**: `web/tests/unit/**/*.test.{ts,tsx}`
-  - **Mini UI包**: `mini-ui-packages/*/tests/unit/**/*.test.{ts,tsx}` (使用Jest,参考[Mini UI包测试规范](./mini-ui-testing-standards.md))
-- **框架**: Vitest(Web/Server)、Jest(Mini UI包)
-- **覆盖率目标**: ≥ 80%
+- **目标**: 验证独立单元的正确性
 - **执行频率**: 每次代码变更
+- **执行速度**: 快(毫秒级)
+- **覆盖率目标**: ≥ 80%
+
+**适用包**:
+- 后端模块包的Service层、Schema验证
+- Web UI包的独立组件
+- 共享工具包的纯函数
 
 ### 集成测试 (Integration Tests)
 - **范围**: 多个组件/服务协作
 - **目标**: 验证模块间集成和交互
-- **位置**:
-  - **业务模块包**: `packages/*-module/tests/integration/**/*.test.ts`
-  - **server包**: `packages/server/tests/integration/**/*.test.ts` (模块集成测试)
-  - **web应用**: `web/tests/integration/**/*.test.{ts,tsx}`
-- **框架**: Vitest + Testing Library + hono/testing + shared-test-util
-- **覆盖率目标**: ≥ 60%
 - **执行频率**: 每次API变更
+- **执行速度**: 中等(秒级)
+- **覆盖率目标**: ≥ 60%
+
+**适用包**:
+- Web Server包的API端点集成
+- 后端模块包的路由与数据库集成
+- Web UI包的组件与API集成
 
 ### E2E测试 (End-to-End Tests)
 - **范围**: 完整用户流程
 - **目标**: 验证端到端业务流程
-- **位置**: `web/tests/e2e/**/*.test.{ts,tsx}`
-- **框架**: Playwright
-- **覆盖率目标**: 关键用户流程100%
 - **执行频率**: 每日或每次重大变更
+- **执行速度**: 慢(分钟级)
+- **覆盖率目标**: 关键用户流程100%
 
-## 测试环境配置
+**适用包**:
+- Web应用的完整用户场景
+- Mini小程序的关键业务流程
 
-### 开发环境
-```typescript
-// vitest.config.ts - 开发环境配置
-export default defineConfig({
-  test: {
-    projects: [
-      // Node.js 环境项目 - 后端测试
-      {
-        test: {
-          include: [
-            'tests/unit/server/**/*.test.{ts,js}',
-            'tests/integration/server/**/*.test.{ts,js}'
-          ],
-          // ... 其他配置
-        }
-      },
-      // Happy DOM 环境项目 - 前端组件测试
-      {
-        test: {
-          include: [
-            'tests/unit/client/**/*.test.{ts,js,tsx,jsx}',
-            'tests/integration/client/**/*.test.{ts,js,tsx,jsx}'
-          ],
-          // ... 其他配置
-        }
-      }
-    ]
-  }
-});
-```
+## 测试框架栈
 
-### CI/CD环境
-```yaml
-# GitHub Actions 测试配置 (模块化包架构)
-name: Test Pipeline
-
-jobs:
-  # 基础设施包测试
-  shared-packages-tests:
-    runs-on: ubuntu-latest
-    steps:
-      - run: cd packages/shared-types && pnpm test
-      - run: cd packages/shared-utils && pnpm test
-      - run: cd packages/shared-crud && pnpm test
-      - run: cd packages/shared-test-util && pnpm test
-
-  # 业务模块包测试
-  business-modules-tests:
-    runs-on: ubuntu-latest
-    services:
-      postgres:
-        image: postgres:17
-        env:
-          POSTGRES_PASSWORD: test_password
-          POSTGRES_DB: test_d8dai
-    steps:
-      - run: cd packages/user-module && pnpm test
-      - run: cd packages/auth-module && pnpm test
-      - run: cd packages/file-module && pnpm test
-      - run: cd packages/geo-areas && pnpm test
-
-  # 服务器集成测试
-  server-integration-tests:
-    runs-on: ubuntu-latest
-    services:
-      postgres:
-        image: postgres:17
-        env:
-          POSTGRES_PASSWORD: test_password
-          POSTGRES_DB: test_d8dai
-    steps:
-      - run: cd packages/server && pnpm test
-
-  # Web应用测试
-  web-integration-tests:
-    runs-on: ubuntu-latest
-    services:
-      postgres:
-        image: postgres:17
-        env:
-          POSTGRES_PASSWORD: test_password
-          POSTGRES_DB: test_d8dai
-    steps:
-      - run: cd web && pnpm test:integration
-
-  web-component-tests:
-    runs-on: ubuntu-latest
-    steps:
-      - run: cd web && pnpm test:components
-
-  web-e2e-tests:
-    runs-on: ubuntu-latest
-    steps:
-      - run: cd web && pnpm test:e2e:chromium
-```
+### Web应用测试
+- **Vitest**: 测试运行器
+- **Testing Library**: React组件测试
+- **Playwright**: E2E测试
+- **Happy DOM**: 轻量级DOM环境
 
-## 测试覆盖率标准
+### 后端测试
+- **Vitest**: 测试运行器
+- **hono/testing**: Hono路由测试
+- **TypeORM**: 数据库测试
+- **PostgreSQL**: 测试数据库
 
-### 各层覆盖率要求
-| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
-|----------|----------|----------|--------------|
-| 单元测试 | 70% | 80% | 90% |
-| 集成测试 | 50% | 60% | 70% |
-| E2E测试 | 关键流程100% | 主要流程80% | - |
-
-### 关键模块定义
-- **认证授权模块**: 必须达到90%单元测试覆盖率
-- **数据库操作模块**: 必须达到85%单元测试覆盖率
-- **核心业务逻辑**: 必须达到80%集成测试覆盖率
-- **用户管理功能**: 必须100% E2E测试覆盖
+### Mini小程序测试
+- **Jest**: 测试运行器(与Web应用不同)
+- **@testing-library/react**: 组件测试
 
 ## 测试数据管理
 
@@ -205,75 +119,63 @@ const inactiveUser = createTestUser({ active: false });
 - **E2E测试**: 使用接近生产环境的数据库
 
 ### 数据清理策略
-1. **事务回滚** (推荐)
-2. **数据库清理** (每个测试后)
-3. **测试数据隔离** (使用唯一标识符)
+1. **事务回滚** (推荐) - 测试后自动回滚
+2. **数据库清理** (每个测试后) - 清理测试数据
+3. **测试数据隔离** (使用唯一标识符) - 避免数据冲突
 
 ## 测试执行流程
 
 ### 本地开发测试
 
-#### 基础设施
+#### 后端模块
 ```bash
-# 运行所有基础设施包测试
-cd packages/shared-types && pnpm test
-cd packages/shared-utils && pnpm test
-cd packages/shared-crud && pnpm test
-cd packages/shared-test-util && pnpm test
-
-# 生成覆盖率报告
-cd packages/shared-utils && pnpm test:coverage
-```
-
-#### 业务模块包
-```bash
-# 运行所有业务模块包测试
+# 运行所有测试
 cd packages/user-module && pnpm test
-cd packages/auth-module && pnpm test
-cd packages/file-module && pnpm test
-cd packages/geo-areas && pnpm test
 
 # 运行单元测试
-cd packages/user-module && pnpm test:unit
+pnpm test:unit
 
 # 运行集成测试
-cd packages/auth-module && pnpm test:integration
+pnpm test:integration
 
 # 生成覆盖率报告
-cd packages/user-module && pnpm test:coverage
+pnpm test:coverage
 ```
 
-#### server包
+#### Web Server包
 ```bash
-# 运行所有测试
-cd packages/server && pnpm test
-
 # 运行集成测试
-cd packages/server && pnpm test:integration
+cd packages/server && pnpm test
 
 # 生成覆盖率报告
-cd packages/server && pnpm test:coverage
+pnpm test:coverage
 ```
 
-#### web应用
+#### Web应用
 ```bash
 # 运行所有测试
 cd web && pnpm test
 
-# 运行单元测试
-cd web && pnpm test:unit
+# 运行组件测试
+pnpm test:components
 
 # 运行集成测试
-cd web && pnpm test:integration
-
-# 运行组件测试
-cd web && pnpm test:components
+pnpm test:integration
 
 # 运行E2E测试
-cd web && pnpm test:e2e:chromium
+pnpm test:e2e:chromium
 
 # 生成覆盖率报告
-cd web && pnpm test:coverage
+pnpm test:coverage
+```
+
+#### Mini UI包
+```bash
+# 进入UI包目录
+cd mini-ui-packages/rencai-dashboard-ui
+
+# 运行测试(使用Jest)
+pnpm test
 ```
 
 ### CI/CD流水线测试
@@ -284,6 +186,21 @@ cd web && pnpm test:coverage
 5. **覆盖率检查** → 满足最低要求
 6. **测试报告** → 生成详细报告
 
+## 覆盖率标准
+
+### 各层覆盖率要求
+| 测试类型 | 最低要求 | 目标要求 | 关键模块要求 |
+|----------|----------|----------|--------------|
+| 单元测试 | 70% | 80% | 90% |
+| 集成测试 | 50% | 60% | 70% |
+| E2E测试 | 关键流程100% | 主要流程80% | - |
+
+### 关键模块定义
+- **认证授权模块**: 必须达到90%单元测试覆盖率
+- **数据库操作模块**: 必须达到85%单元测试覆盖率
+- **核心业务逻辑**: 必须达到80%集成测试覆盖率
+- **用户管理功能**: 必须100% E2E测试覆盖
+
 ## 质量门禁
 
 ### 测试通过标准
@@ -327,287 +244,86 @@ cd web && pnpm test:coverage
 - **autocannon**: API性能测试
 - **Playwright**: E2E性能监控
 
-## 测试文档标准
-
-### 测试代码规范
-```typescript
-// 良好的测试示例
-describe('UserService', () => {
-  describe('createUser()', () => {
-    it('应该创建新用户并返回用户对象', async () => {
-      // Arrange
-      const userData = { username: 'testuser', email: 'test@example.com' };
-
-      // Act
-      const result = await userService.createUser(userData);
-
-      // Assert
-      expect(result).toHaveProperty('id');
-      expect(result.username).toBe('testuser');
-      expect(result.email).toBe('test@example.com');
-    });
-
-    it('应该拒绝重复的用户名', async () => {
-      // Arrange
-      const existingUser = await createTestUser({ username: 'existing' });
-
-      // Act & Assert
-      await expect(
-        userService.createUser({ username: 'existing', email: 'new@example.com' })
-      ).rejects.toThrow('用户名已存在');
-    });
-  });
-});
-```
-
-### 测试命名约定
-- **文件名**: `[module].test.ts` 或 `[module].integration.test.ts`
-- **描述**: 使用「应该...」格式描述测试行为
-- **用例**: 明确描述测试场景和预期结果
-
-### 测试用例编写规范
-
-基于订单管理集成测试的经验总结,制定以下测试用例编写规范:
-
-#### 1. 使用真实组件而非模拟组件
-**原则**: 集成测试应尽可能使用真实的组件,而不是过度简化的模拟组件。
-
-**示例**:
-```typescript
-// ❌ 避免:使用过度简化的模拟组件
-vi.mock('@d8d/allin-disability-person-management-ui', () => ({
-  DisabledPersonSelector: vi.fn(({ open, onOpenChange, onSelect }) => {
-    return (
-      <div data-testid="disabled-person-selector-mock">
-        <button onClick={() => onSelect(mockPerson)}>选择测试人员</button>
-      </div>
-    );
-  }),
-}));
-
-// ✅ 推荐:使用真实的组件,模拟其依赖的API
-// 在RPC客户端模拟中添加组件所需的API
-vi.mock('@d8d/shared-ui-components/utils/hc', () => ({
-  rpcClient: vi.fn(() => ({
-    searchDisabledPersons: {
-      $get: vi.fn(() => Promise.resolve({
-        status: 200,
-        json: async () => ({
-          data: [{
-            id: 1,
-            name: '测试残疾人',
-            // 包含真实组件需要的所有字段
-            idCard: '110101199001011234',
-            disabilityId: 'D123456',
-            // ... 其他字段
-          }],
-          total: 1
-        })
-      }))
-    }
-  }))
-}));
-```
-
-#### 2. 理解组件的工作模式
-**原则**: 测试前必须理解真实组件的工作模式(单选/多选、交互方式等)。
-
-**示例**:
-```typescript
-// 订单创建中的残疾人选择器使用多选模式 (mode="multiple")
-// 因此测试需要:
-// 1. 勾选复选框(而不是点击表格行)
-// 2. 点击确认按钮
-
-// 查找并勾选复选框
-const checkbox = screen.getByRole('checkbox', { name: '选择' });
-await userEvent.click(checkbox);
-
-// 点击确认选择按钮
-const confirmButton = screen.getByTestId('confirm-batch-button');
-await userEvent.click(confirmButton);
-```
-
-#### 3. 提供完整的模拟数据
-**原则**: 模拟数据应包含真实组件需要的所有字段,避免因字段缺失导致测试失败。
-
-**示例**:
-```typescript
-// 残疾人选择器表格需要显示idCard字段
-const mockPerson = {
-  id: 1,
-  name: '测试残疾人',
-  gender: '男',
-  idCard: '110101199001011234', // 必须包含的字段
-  disabilityId: 'D123456',
-  disabilityType: '肢体残疾',
-  disabilityLevel: '三级',
-  phone: '13800138000',
-  province: '北京',
-  city: '北京市',
-  provinceId: 1,
-  cityId: 2,
-  isInBlackList: 0
-};
-```
-
-#### 4. 使用适当的测试选择器
-**原则**: 优先使用语义化的选择器(role、label),其次使用test-id,避免使用不稳定的选择器。
+## 监控和报告
 
-**示例**:
-```typescript
-// ✅ 推荐:使用role选择器
-const checkbox = screen.getByRole('checkbox', { name: '选择' });
+### 测试监控指标
+- **测试通过率**: > 95%
+- **测试执行时间**: < 10分钟(单元+集成)
+- **测试稳定性**: 无flaky tests
+- **覆盖率趋势**: 持续改进或保持
 
-// ✅ 推荐:使用test-id选择器
-const confirmButton = screen.getByTestId('confirm-batch-button');
+### 测试报告要求
+- **HTML报告**: 详细的覆盖率报告
+- **JUnit报告**: CI/CD集成
+- **自定义报告**: 业务指标测试报告
+- **历史趋势**: 测试质量趋势分析
 
-// ✅ 推荐:使用文本选择器(当文本稳定时)
-const dialogTitle = screen.getByText('选择残疾人');
+## 调试技巧
 
-// ❌ 避免:使用不稳定的CSS类选择器
-const button = screen.getByClassName('btn-primary');
-```
+### 查看测试详情
+```bash
+# 运行特定测试查看详细信息
+pnpm test --testNamePattern "测试名称"
 
-#### 5. 处理异步加载和状态变化
-**原则**: 集成测试需要正确处理组件的异步加载和状态变化。
+# 显示完整输出
+pnpm test --reporter=verbose
 
-**示例**:
-```typescript
-// 等待组件加载完成
-await waitFor(() => {
-  expect(screen.getByTestId('disabled-persons-table')).toBeInTheDocument();
-});
-
-// 等待组件关闭
-await waitFor(() => {
-  expect(screen.queryByTestId('disabled-persons-table')).not.toBeInTheDocument();
-});
+# 监听模式(开发时)
+pnpm test --watch
 ```
 
-#### 6. 验证完整的用户流程
-**原则**: 集成测试应验证完整的用户流程,而不仅仅是单个操作。
+### 调试E2E测试
+```bash
+# E2E测试失败时先查看页面结构
+cat test-results/**/error-context.md
 
-**示例**:
-```typescript
-// 完整的订单创建流程:
-// 1. 打开订单表单
-// 2. 填写基本信息
-// 3. 打开残疾人选择器
-// 4. 选择残疾人
-// 5. 提交表单
-// 6. 验证结果
-
-it('应该成功创建订单并绑定人员', async () => {
-  // 1. 打开订单表单
-  const createButton = screen.getByTestId('create-order-button');
-  await userEvent.click(createButton);
-
-  // 2. 填写基本信息
-  const orderNameInput = screen.getByPlaceholderText('请输入订单名称');
-  fireEvent.change(orderNameInput, { target: { value: '测试订单' } });
-
-  // 3. 打开残疾人选择器
-  const selectPersonsButton = screen.getByTestId('select-persons-button');
-  fireEvent.click(selectPersonsButton);
-
-  // 4. 选择残疾人
-  await waitFor(() => {
-    expect(screen.getByTestId('disabled-persons-table')).toBeInTheDocument();
-  });
-
-  const checkbox = screen.getByRole('checkbox', { name: '选择' });
-  await userEvent.click(checkbox);
-
-  const confirmButton = screen.getByTestId('confirm-batch-button');
-  await userEvent.click(confirmButton);
-
-  // 5. 提交表单
-  const submitButton = screen.getByTestId('order-create-submit-button');
-  await userEvent.click(submitButton);
-
-  // 6. 验证结果
-  await waitFor(() => {
-    expect(screen.getByText('订单创建成功')).toBeInTheDocument();
-  });
-});
+# 使用调试模式
+pnpm test:e2e:chromium --debug
 ```
 
-#### 7. 使用共享测试工具处理复杂组件
-**原则**: 对于复杂的UI组件(如Radix UI Select),应使用共享UI包中的测试工具函数,而不是直接操作DOM元素。
-
-**示例**:
+### 表单调试
 ```typescript
-// ❌ 避免:直接操作Radix UI Select组件的DOM元素
-const selectButton = screen.getByTestId('platform-selector-create');
-await userEvent.click(selectButton);
-const hiddenSelect = document.querySelector('select[data-radix-select-viewport]');
-await userEvent.selectOptions(hiddenSelect, '1');
-
-// ✅ 推荐:使用共享UI包的测试工具函数
-import { completeRadixSelectFlow } from '@d8d/shared-ui-components/tests/utils';
-
-// 处理Radix UI Select组件的完整选择流程
-await completeRadixSelectFlow('platform-selector-create', '1', { useFireEvent: true });
-await completeRadixSelectFlow('company-selector-create', '1', { useFireEvent: true });
-await completeRadixSelectFlow('channel-selector-create', '1', { useFireEvent: true });
+// 表单提交失败时,在form onsubmit的第二个参数中加console.debug
+form.handleSubmit(handleSubmit, (errors) => console.debug('表单验证错误:', errors))
 ```
 
-**注意**: 共享UI包的测试工具提供了以下功能:
-- `completeRadixSelectFlow`: 完整的Radix UI Select选择流程
-- `findHiddenSelectElement`: 查找隐藏的select元素
-- `selectRadixOption`: 选择Radix UI Select选项
-- `waitForRadixSelectEnabled`: 等待Select组件启用
-
-这些工具封装了复杂的DOM操作逻辑,提高了测试代码的可维护性和可读性。
-
-#### 8. 清理调试信息
-**原则**: 提交代码前应移除不必要的调试信息(console.log、console.debug)。
-
-**示例**:
-```typescript
-// ❌ 避免:提交包含调试信息的代码
-console.log('测试:调用onSelect,人员数据:', mockPerson);
-console.debug('所有test ID:', allElements.map(el => el.getAttribute('data-testid')));
-
-// ✅ 推荐:提交前移除调试信息
-// 只在开发时临时添加调试信息,完成后立即移除
-```
+## 相关文档
 
-## 监控和报告
+### 专用测试规范
+- **[Web UI包测试规范](./web-ui-testing-standards.md)** - Web UI组件和Web应用测试
+- **[Web Server包测试规范](./web-server-testing-standards.md)** - API服务器集成测试
+- **[后端模块包测试规范](./backend-module-testing-standards.md)** - 业务模块单元和集成测试
+- **[Mini UI包测试规范](./mini-ui-testing-standards.md)** - Taro小程序UI包测试
 
-### 测试监控指标
-- **测试通过率**: > 95%
-- **测试执行时间**: < 10分钟(单元+集成)
-- **测试稳定性**: 无flaky tests
-- **覆盖率趋势**: 持续改进或保持
+### 开发规范
+- **[编码标准](./coding-standards.md)** - 代码风格和最佳实践
+- **[UI包开发规范](./ui-package-standards.md)** - Web UI包开发
+- **[Mini UI包开发规范](./mini-ui-package-standards.md)** - Mini UI包开发
+- **[后端模块包开发规范](./backend-module-package-standards.md)** - 后端模块开发
 
-### 测试报告要求
-- **HTML报告**: 详细的覆盖率报告
-- **JUnit报告**: CI/CD集成
-- **自定义报告**: 业务指标测试报告
-- **历史趋势**: 测试质量趋势分析
+### 架构文档
+- **[技术栈](./tech-stack.md)** - 项目技术栈和版本
+- **[源码树](./source-tree.md)** - 项目文件结构
+- **[API设计规范](./api-design-integration.md)** - API设计标准
 
-## 附录
+## 工具版本
 
-### 相关文档
-- [集成测试最佳实践](../integration-testing-best-practices.md)
-- [编码标准](./coding-standards.md)
-- [API设计规范](./api-design-integration.md)
-- [Mini UI包测试规范](./mini-ui-testing-standards.md) - Taro小程序UI包测试标准和最佳实践
+| 工具 | 版本 | 用途 |
+|------|------|------|
+| Vitest | 3.2.4 | Web/后端测试运行器 |
+| Jest | 最新 | Mini UI包测试运行器 |
+| Testing Library | 16.3.0 | React组件测试 |
+| Playwright | 1.55.0 | E2E测试 |
+| Happy DOM | 最新 | 轻量级DOM环境 |
+| hono/testing | 内置 | Hono路由测试 |
+| TypeORM | 0.3.20 | 数据库测试 |
+| shared-test-util | 1.0.0 | 共享测试基础设施 |
 
-### 工具版本
-- **Vitest**: 3.2.4
-- **Testing Library**: 16.3.0
-- **Playwright**: 1.55.0
-- **hono/testing**: 内置(Hono 4.8.5)
-- **shared-test-util**: 1.0.0 (测试基础设施包)
-- **TypeORM**: 0.3.20 (数据库测试)
-- **Redis**: 7.0.0 (会话管理测试)
+## 更新日志
 
-### 更新日志
 | 日期 | 版本 | 描述 |
 |------|------|------|
+| 2025-12-26 | 3.0 | 重构为概述文档,拆分详细规范到独立文档 |
 | 2025-12-12 | 2.10 | 添加使用共享测试工具处理复杂组件的规范 |
 | 2025-12-12 | 2.9 | 添加测试用例编写规范,基于订单管理集成测试经验 |
 | 2025-11-11 | 2.8 | 更新包测试结构,添加模块化包测试策略 |
@@ -620,4 +336,4 @@ console.debug('所有test ID:', allElements.map(el => el.getAttribute('data-test
 ---
 
 **文档状态**: 正式版
-**下次评审**: 2026-01-12
+**下次评审**: 2026-01-26

+ 530 - 0
docs/architecture/web-server-testing-standards.md

@@ -0,0 +1,530 @@
+# Web Server 包测试规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-12-26 | 从测试策略文档拆分,专注Web Server包测试 | James (Claude Code) |
+
+## 概述
+
+本文档定义了Web Server包的测试标准和最佳实践。
+- **目标**: `packages/server` - API服务器包
+- **测试类型**: 集成测试(模块间协作测试)
+
+## 测试框架栈
+
+- **Vitest**: 测试运行器
+- **hono/testing**: Hono官方测试工具
+- **TypeORM**: 数据库测试
+- **PostgreSQL**: 测试数据库
+- **shared-test-util**: 共享测试基础设施
+
+## 测试策略
+
+### 集成测试(Integration Tests)
+- **范围**: 模块间集成、API端点集成
+- **目标**: 验证各业务模块在server环境中的正确集成
+- **位置**: `packages/server/tests/integration/**/*.test.ts`
+- **框架**: Vitest + hono/testing + TypeORM
+- **覆盖率目标**: ≥ 60%
+
+## 测试文件结构
+
+```
+packages/server/
+├── src/
+│   ├── api.ts              # API路由导出
+│   └── index.ts            # 服务器入口
+└── tests/
+    ├── integration/
+    │   ├── auth.integration.test.ts      # 认证集成测试
+    │   ├── users.integration.test.ts     # 用户管理集成测试
+    │   ├── files.integration.test.ts     # 文件管理集成测试
+    │   └── api-integration.test.ts       # API端点集成测试
+    └── fixtures/
+        ├── test-db.ts                    # 测试数据库配置
+        └── test-data.ts                  # 测试数据工厂
+```
+
+## 集成测试最佳实践
+
+### 1. 使用hono/testing测试API端点
+```typescript
+import { test } from 'vitest';
+import { integrateRoutes } from 'hono/testing';
+import app from '../src/api';
+
+describe('POST /api/auth/login', () => {
+  it('应该成功登录并返回token', async () => {
+    // Arrange
+    const testData = {
+      username: 'testuser',
+      password: 'password123'
+    };
+
+    // Act
+    const res = await integrateRoutes(app).POST('/api/auth/login', {
+      json: testData
+    });
+
+    // Assert
+    expect(res.status).toBe(200);
+    const json = await res.json();
+    expect(json).toHaveProperty('token');
+    expect(json.user.username).toBe('testuser');
+  });
+});
+```
+
+### 2. 测试数据库集成
+```typescript
+import { describe, it, expect, beforeEach, afterEach } from 'vitest';
+import { App } from 'hono';
+import { DataSource } from 'typeorm';
+import { getTestDataSource } from './fixtures/test-db';
+import { User } from '@d8d/user-module/entities';
+
+describe('用户管理集成测试', () => {
+  let dataSource: DataSource;
+
+  beforeEach(async () => {
+    // 设置测试数据库
+    dataSource = await getTestDataSource();
+    await dataSource.initialize();
+  });
+
+  afterEach(async () => {
+    // 清理测试数据库
+    await dataSource.destroy();
+  });
+
+  it('应该创建用户并返回用户数据', async () => {
+    const app = new App();
+    app.route('/api/users', userRoutes);
+
+    const res = await integrateRoutes(app).POST('/api/users', {
+      json: {
+        username: 'testuser',
+        email: 'test@example.com',
+        password: 'password123'
+      }
+    });
+
+    expect(res.status).toBe(201);
+
+    // 验证数据库中的数据
+    const userRepo = dataSource.getRepository(User);
+    const user = await userRepo.findOne({ where: { username: 'testuser' } });
+    expect(user).toBeDefined();
+    expect(user?.email).toBe('test@example.com');
+  });
+});
+```
+
+### 3. 测试认证中间件
+```typescript
+import { generateToken } from '@d8d/shared-utils/jwt.util';
+
+describe('认证保护的API端点', () => {
+  it('应该拒绝未认证的请求', async () => {
+    const res = await integrateRoutes(app).GET('/api/users/me');
+
+    expect(res.status).toBe(401);
+    expect(await res.json()).toEqual({
+      error: '未授权访问'
+    });
+  });
+
+  it('应该接受有效token的请求', async () => {
+    const token = generateToken({ userId: 1, username: 'testuser' });
+
+    const res = await integrateRoutes(app).GET('/api/users/me', {
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    });
+
+    expect(res.status).toBe(200);
+    const json = await res.json();
+    expect(json.username).toBe('testuser');
+  });
+});
+```
+
+### 4. 测试模块间集成
+```typescript
+describe('认证与用户模块集成', () => {
+  it('应该成功注册用户并自动登录', async () => {
+    const app = new App();
+    app.route('/api/auth', authRoutes);
+    app.route('/api/users', userRoutes);
+
+    // 1. 注册用户
+    const registerRes = await integrateRoutes(app).POST('/api/auth/register', {
+      json: {
+        username: 'newuser',
+        email: 'new@example.com',
+        password: 'password123'
+      }
+    });
+
+    expect(registerRes.status).toBe(201);
+
+    // 2. 登录
+    const loginRes = await integrateRoutes(app).POST('/api/auth/login', {
+      json: {
+        username: 'newuser',
+        password: 'password123'
+      }
+    });
+
+    expect(loginRes.status).toBe(200);
+    const { token } = await loginRes.json();
+    expect(token).toBeDefined();
+
+    // 3. 使用token访问受保护的端点
+    const meRes = await integrateRoutes(app).GET('/api/users/me', {
+      headers: {
+        Authorization: `Bearer ${token}`
+      }
+    });
+
+    expect(meRes.status).toBe(200);
+    const user = await meRes.json();
+    expect(user.username).toBe('newuser');
+  });
+});
+```
+
+### 5. 测试文件上传集成
+```typescript
+import { createReadStream } from 'fs';
+import { FormData } from 'hono/client';
+
+describe('文件上传集成测试', () => {
+  it('应该成功上传文件并返回文件URL', async () => {
+    const formData = new FormData();
+    formData.append('file', createReadStream('test/fixtures/test-file.png'));
+
+    const res = await integrateRoutes(app).POST('/api/files/upload', {
+      body: formData
+    });
+
+    expect(res.status).toBe(201);
+    const json = await res.json();
+    expect(json).toHaveProperty('url');
+    expect(json).toHaveProperty('id');
+  });
+});
+```
+
+### 6. 测试错误处理
+```typescript
+describe('API错误处理', () => {
+  it('应该返回404当资源不存在', async () => {
+    const res = await integrateRoutes(app).GET('/api/users/99999');
+
+    expect(res.status).toBe(404);
+    expect(await res.json()).toEqual({
+      error: '用户不存在'
+    });
+  });
+
+  it('应该返回400当请求数据无效', async () => {
+    const res = await integrateRoutes(app).POST('/api/users', {
+      json: {
+        username: '', // 无效的用户名
+        email: 'invalid-email' // 无效的邮箱
+      }
+    });
+
+    expect(res.status).toBe(400);
+    const json = await res.json();
+    expect(json).toHaveProperty('errors');
+  });
+});
+```
+
+### 7. 使用共享测试工具
+```typescript
+import { createIntegrationTestApp, setupTestDatabase, teardownTestDatabase } from '@d8d/shared-test-util';
+
+describe('使用共享测试工具', () => {
+  let dataSource: DataSource;
+  let app: Hono;
+
+  beforeAll(async () => {
+    dataSource = await setupTestDatabase();
+    app = await createIntegrationTestApp(dataSource);
+  });
+
+  afterAll(async () => {
+    await teardownTestDatabase(dataSource);
+  });
+
+  it('应该使用共享工具运行集成测试', async () => {
+    const res = await integrateRoutes(app).GET('/api/users');
+
+    expect(res.status).toBe(200);
+  });
+});
+```
+
+## 测试数据管理
+
+### 测试数据工厂
+```typescript
+// tests/fixtures/test-data.ts
+import { User } from '@d8d/user-module/entities';
+
+export function createTestUser(overrides = {}): Partial<User> {
+  return {
+    id: 1,
+    username: 'testuser',
+    email: 'test@example.com',
+    password: 'hashedpassword',
+    role: 'user',
+    active: true,
+    createdAt: new Date(),
+    ...overrides
+  };
+}
+
+export async function seedTestUser(dataSource: DataSource, userData = {}) {
+  const userRepo = dataSource.getRepository(User);
+  const user = userRepo.create(createTestUser(userData));
+  return await userRepo.save(user);
+}
+```
+
+### 数据库清理策略
+```typescript
+// 选项1: 事务回滚(推荐)
+describe('使用事务回滚', () => {
+  let queryRunner: QueryRunner;
+
+  beforeEach(async () => {
+    queryRunner = dataSource.createQueryRunner();
+    await queryRunner.startTransaction();
+  });
+
+  afterEach(async () => {
+    await queryRunner.rollbackTransaction();
+    await queryRunner.release();
+  });
+});
+
+// 选项2: 每个测试后清理
+describe('使用数据库清理', () => {
+  afterEach(async () => {
+    const entities = dataSource.entityMetadatas;
+    for (const entity of entities) {
+      const repository = dataSource.getRepository(entity.name);
+      await repository.clear();
+    }
+  });
+});
+```
+
+## 测试命名约定
+
+### 文件命名
+- 集成测试:`[module].integration.test.ts`
+- API测试:`[endpoint].integration.test.ts`
+
+### 测试描述
+```typescript
+describe('用户管理API', () => {
+  describe('GET /api/users', () => {
+    it('应该返回用户列表', async () => { });
+    it('应该支持分页', async () => { });
+    it('应该支持搜索过滤', async () => { });
+  });
+
+  describe('POST /api/users', () => {
+    it('应该创建新用户', async () => { });
+    it('应该验证重复用户名', async () => { });
+    it('应该验证邮箱格式', async () => { });
+  });
+});
+```
+
+## 环境配置
+
+### 测试环境变量
+```typescript
+// vitest.config.ts
+export default defineConfig({
+  test: {
+    setupFiles: ['./tests/setup.ts'],
+    env: {
+      NODE_ENV: 'test',
+      DATABASE_URL: 'postgresql://postgres:test_password@localhost:5432/test_d8dai',
+      JWT_SECRET: 'test_secret',
+      REDIS_URL: 'redis://localhost:6379/1'
+    }
+  }
+});
+```
+
+### 测试数据库设置
+```typescript
+// tests/fixtures/test-db.ts
+import { DataSource } from 'typeorm';
+import { User } from '@d8d/user-module/entities';
+import { File } from '@d8d/file-module/entities';
+
+export async function getTestDataSource(): Promise<DataSource> {
+  return new DataSource({
+    type: 'postgres',
+    host: 'localhost',
+    port: 5432,
+    username: 'postgres',
+    password: 'test_password',
+    database: 'test_d8dai',
+    entities: [User, File],
+    synchronize: true, // 测试环境自动同步表结构
+    dropSchema: true, // 每次测试前清空数据库
+    logging: false
+  });
+}
+```
+
+## 覆盖率标准
+
+| 测试类型 | 最低要求 | 目标要求 |
+|----------|----------|----------|
+| 集成测试 | 50% | 60% |
+
+**关键端点要求**:
+- 认证端点:100%覆盖
+- 用户管理端点:80%覆盖
+- 文件上传端点:70%覆盖
+
+## 运行测试
+
+### 本地开发
+```bash
+# 运行所有集成测试
+cd packages/server && pnpm test
+
+# 运行特定集成测试
+pnpm test users.integration.test.ts
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 运行特定测试用例
+pnpm test --testNamePattern="应该成功创建用户"
+```
+
+### CI/CD
+```yaml
+server-integration-tests:
+  runs-on: ubuntu-latest
+  services:
+    postgres:
+      image: postgres:17
+      env:
+        POSTGRES_PASSWORD: test_password
+        POSTGRES_DB: test_d8dai
+      options: >-
+        --health-cmd pg_isready
+        --health-interval 10s
+        --health-timeout 5s
+        --health-retries 5
+  steps:
+    - uses: actions/checkout@v3
+    - uses: pnpm/action-setup@v2
+    - run: cd packages/server && pnpm install
+    - run: cd packages/server && pnpm test
+```
+
+## 调试技巧
+
+### 1. 使用调试模式
+```bash
+# 运行特定测试并显示详细信息
+pnpm test --testNamePattern="用户登录" --reporter=verbose
+
+# 监听模式(开发时)
+pnpm test --watch
+```
+
+### 2. 查看响应详情
+```typescript
+const res = await integrateRoutes(app).POST('/api/users', { json: userData });
+
+// 打印完整响应
+console.log('Status:', res.status);
+console.log('Headers:', res.headers);
+console.log('Body:', await res.json());
+```
+
+### 3. 数据库调试
+```typescript
+// 启用SQL查询日志
+const dataSource = new DataSource({
+  // ...其他配置
+  logging: true, // 显示所有SQL查询
+  maxQueryExecutionTime: 1000 // 记录慢查询
+});
+```
+
+## 常见错误避免
+
+### ❌ 不要在集成测试中使用mock
+```typescript
+// 错误:模拟数据库查询
+vi.mock('@d8d/user-module/services/user.service', () => ({
+  UserService: {
+    findAll: vi.fn(() => Promise.resolve(mockUsers))
+  }
+}));
+
+// 正确:使用真实的数据库和服务
+```
+
+### ❌ 不要忽略异步清理
+```typescript
+// 错误:不清理数据库
+afterEach(() => {
+  // 数据库没有被清理
+});
+
+// 正确:确保数据库清理
+afterEach(async () => {
+  await dataSource.dropDatabase();
+});
+```
+
+### ❌ 不要硬编码测试数据
+```typescript
+// 错误:硬编码ID
+it('应该返回用户详情', async () => {
+  const res = await integrateRoutes(app).GET('/api/users/123');
+});
+
+// 正确:使用动态创建的数据
+it('应该返回用户详情', async () => {
+  const user = await seedTestUser(dataSource);
+  const res = await integrateRoutes(app).GET(`/api/users/${user.id}`);
+});
+```
+
+## 参考实现
+
+- Server包集成测试:`packages/server/tests/integration/`
+- 共享测试工具:`packages/shared-test-util/`
+
+## 相关文档
+
+- [测试策略概述](./testing-strategy.md)
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [Web UI包测试规范](./web-ui-testing-standards.md)
+- [后端模块包开发规范](./backend-module-package-standards.md)
+
+---
+
+**文档状态**: 正式版
+**适用范围**: packages/server

+ 439 - 0
docs/architecture/web-ui-testing-standards.md

@@ -0,0 +1,439 @@
+# Web UI 包测试规范
+
+## 版本信息
+| 版本 | 日期 | 描述 | 作者 |
+|------|------|------|------|
+| 1.0 | 2025-12-26 | 从测试策略文档拆分,专注Web UI包测试 | James (Claude Code) |
+
+## 概述
+
+本文档定义了Web UI包的测试标准和最佳实践,包括:
+- **packages/*-ui**: Web UI组件包(用户管理UI、文件管理UI等)
+- **web/tests/**: Web应用测试(组件测试、集成测试、E2E测试)
+
+## 测试框架栈
+
+### 单元测试和集成测试
+- **Vitest**: 测试运行器
+- **Testing Library**: React组件测试(`@testing-library/react`、`@testing-library/user-event`)
+- **Happy DOM**: 轻量级DOM环境(`@happy-dom/global-registrator`)
+
+### E2E测试
+- **Playwright**: 端到端测试框架
+
+## 测试分层策略
+
+### 1. 组件测试(Component Tests)
+- **范围**: 单个UI组件
+- **目标**: 验证组件渲染、交互和状态
+- **位置**: `packages/*-ui/tests/**/*.test.tsx` 或 `web/tests/unit/client/**/*.test.tsx`
+- **框架**: Vitest + Testing Library
+- **覆盖率目标**: ≥ 80%
+
+### 2. 集成测试(Integration Tests)
+- **范围**: 多个组件协作、与API集成
+- **目标**: 验证组件间交互和数据流
+- **位置**: `web/tests/integration/**/*.test.tsx`
+- **框架**: Vitest + Testing Library + MSW(API模拟)
+- **覆盖率目标**: ≥ 60%
+
+### 3. E2E测试(End-to-End Tests)
+- **范围**: 完整用户流程
+- **目标**: 验证端到端业务流程
+- **位置**: `web/tests/e2e/**/*.test.ts`
+- **框架**: Playwright
+- **覆盖率目标**: 关键用户流程100%
+
+## 测试文件结构
+
+### UI包结构
+```
+packages/user-management-ui/
+├── src/
+│   ├── components/
+│   │   ├── UserTable.tsx
+│   │   └── UserForm.tsx
+│   └── index.ts
+└── tests/
+    ├── unit/
+    │   ├── UserTable.test.tsx
+    │   └── UserForm.test.tsx
+    └── integration/
+        └── UserManagementFlow.test.tsx
+```
+
+### Web应用结构
+```
+web/tests/
+├── unit/
+│   └── client/
+│       ├── pages/
+│       │   └── Users.test.tsx
+│       └── components/
+│           └── DataTablePagination.test.tsx
+├── integration/
+│   └── client/
+│       └── user-management.test.tsx
+└── e2e/
+    ├── login.spec.ts
+    └── user-management.spec.ts
+```
+
+## 组件测试最佳实践
+
+### 1. 使用Testing Library原则
+```typescript
+// ✅ 推荐:从用户角度测试
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+test('应该允许用户创建新用户', async () => {
+  const user = userEvent.setup();
+  render(<UserForm />);
+
+  // 填写表单
+  await user.type(screen.getByLabelText(/用户名/), 'testuser');
+  await user.type(screen.getByLabelText(/邮箱/), 'test@example.com');
+
+  // 提交表单
+  await user.click(screen.getByRole('button', { name: /创建/ }));
+
+  // 验证结果
+  expect(screen.getByText(/创建成功/)).toBeInTheDocument();
+});
+
+// ❌ 避免:测试实现细节
+test('submitButton onClick 应该被调用', () => {
+  const mockOnClick = vi.fn();
+  render(<Button onClick={mockOnClick}>提交</Button>);
+
+  fireEvent.click(screen.getByText('提交'));
+  expect(mockOnClick).toHaveBeenCalled();
+});
+```
+
+### 2. 使用数据测试ID
+```typescript
+// 组件代码
+<Button data-testid="submit-button" type="submit">提交</Button>
+
+// 测试代码
+const submitButton = screen.getByTestId('submit-button');
+await user.click(submitButton);
+```
+
+### 3. 模拟API调用
+```typescript
+import { vi } from 'vitest';
+import { render, screen } from '@testing-library/react';
+import { UserForm } from './UserForm';
+
+// 模拟RPC客户端
+vi.mock('@d8d/shared-ui-components/utils/hc', () => ({
+  rpcClient: vi.fn(() => ({
+    users: {
+      create: {
+        $post: vi.fn(() => Promise.resolve({
+          status: 201,
+          json: async () => ({ id: 1, username: 'testuser' })
+        }))
+      }
+    }
+  }))
+}));
+```
+
+### 4. 测试异步状态
+```typescript
+test('应该显示加载状态', async () => {
+  render(<UserList />);
+
+  // 初始加载状态
+  expect(screen.getByText(/加载中/)).toBeInTheDocument();
+
+  // 等待数据加载完成
+  await waitFor(() => {
+    expect(screen.getByText(/用户列表/)).toBeInTheDocument();
+  });
+});
+```
+
+## 集成测试最佳实践
+
+### 1. 使用真实组件而非模拟
+```typescript
+// ✅ 推荐:使用真实组件,模拟API
+vi.mock('@d8d/shared-ui-components/utils/hc', () => ({
+  rpcClient: vi.fn(() => ({
+    users: {
+      $get: vi.fn(() => Promise.resolve({
+        status: 200,
+        json: async () => ({ data: mockUsers })
+      }))
+    }
+  }))
+}));
+
+// ❌ 避免:模拟整个组件
+vi.mock('@d8d/user-management-ui', () => ({
+  UserTable: () => <div data-testid="mock-user-table" />
+}));
+```
+
+### 2. 提供完整的模拟数据
+```typescript
+const mockUsers = [
+  {
+    id: 1,
+    username: 'testuser',
+    email: 'test@example.com',
+    role: 'user',
+    createdAt: '2025-01-01T00:00:00.000Z',
+    // 包含真实组件需要的所有字段
+  }
+];
+```
+
+### 3. 验证完整的用户流程
+```typescript
+test('应该成功创建用户并刷新列表', async () => {
+  const user = userEvent.setup();
+  render(<UserManagementPage />);
+
+  // 打开创建表单
+  await user.click(screen.getByTestId('create-user-button'));
+
+  // 填写表单
+  await user.type(screen.getByLabelText(/用户名/), 'newuser');
+  await user.type(screen.getByLabelText(/邮箱/), 'new@example.com'));
+
+  // 提交表单
+  await user.click(screen.getByRole('button', { name: /创建/ }));
+
+  // 验证成功消息
+  await waitFor(() => {
+    expect(screen.getByText(/创建成功/)).toBeInTheDocument();
+  });
+
+  // 验证列表刷新
+  await waitFor(() => {
+    expect(screen.getByText('newuser')).toBeInTheDocument();
+  });
+});
+```
+
+### 4. 使用共享测试工具处理复杂组件
+```typescript
+import { completeRadixSelectFlow } from '@d8d/shared-ui-components/tests/utils';
+
+// 处理Radix UI Select组件的完整选择流程
+await completeRadixSelectFlow('role-selector', 'admin', { useFireEvent: true });
+```
+
+## E2E测试最佳实践
+
+### 1. 使用Page Object模式
+```typescript
+// tests/e2e/pages/login.page.ts
+export class LoginPage {
+  constructor(private page: Page) {}
+
+  async goto() {
+    await this.page.goto('/login');
+  }
+
+  async login(username: string, password: string) {
+    await this.page.fill('input[name="username"]', username);
+    await this.page.fill('input[name="password"]', password);
+    await this.page.click('button[type="submit"]');
+  }
+
+  async expectWelcomeMessage() {
+    await expect(this.page.getByText(/欢迎/)).toBeVisible();
+  }
+}
+
+// 测试文件
+test('用户登录流程', async ({ page }) => {
+  const loginPage = new LoginPage(page);
+  await loginPage.goto();
+  await loginPage.login('testuser', 'password');
+  await loginPage.expectWelcomeMessage();
+});
+```
+
+### 2. 使用稳定的定位器
+```typescript
+// ✅ 推荐:使用语义化定位器
+await page.click('button:has-text("提交")');
+await page.fill('input[placeholder="请输入用户名"]', 'testuser');
+await page.click('[data-testid="submit-button"]');
+
+// ❌ 避免:使用不稳定的CSS选择器
+await page.click('.btn-primary');
+```
+
+### 3. 等待策略
+```typescript
+// 等待元素可见
+await page.waitForSelector('[data-testid="user-list"]');
+
+// 等待网络请求完成
+await page.waitForLoadState('networkidle');
+
+// 等待特定条件
+await page.waitForURL('/dashboard');
+```
+
+## 测试命名约定
+
+### 文件命名
+- 组件测试:`[ComponentName].test.tsx`
+- 集成测试:`[feature].integration.test.tsx`
+- E2E测试:`[feature].spec.ts`
+
+### 测试描述
+```typescript
+describe('UserForm', () => {
+  describe('表单验证', () => {
+    it('应该验证必填字段', async () => { });
+    it('应该验证邮箱格式', async () => { });
+  });
+
+  describe('表单提交', () => {
+    it('应该成功创建用户', async () => { });
+    it('应该处理网络错误', async () => { });
+  });
+});
+```
+
+## 常见错误避免
+
+### ❌ 不要测试实现细节
+```typescript
+// 错误:测试useState调用
+expect(useState).toHaveBeenCalledWith([]);
+
+// 正确:测试渲染结果
+expect(screen.getByText('用户列表')).toBeInTheDocument();
+```
+
+### ❌ 不要过度模拟
+```typescript
+// 错误:模拟整个组件库
+vi.mock('@d8d/shared-ui-components', () => ({
+  Button: () => <button data-testid="mock-button" />
+}));
+
+// 正确:使用真实组件,模拟其依赖
+```
+
+### ❌ 不要忽略异步操作
+```typescript
+// 错误:不等待异步操作
+fireEvent.click(submitButton);
+expect(successMessage).toBeInTheDocument();
+
+// 正确:等待异步操作完成
+await user.click(submitButton);
+await waitFor(() => {
+  expect(screen.getByText(/成功/)).toBeInTheDocument();
+});
+```
+
+## 覆盖率标准
+
+| 测试类型 | 最低要求 | 目标要求 |
+|----------|----------|----------|
+| 组件测试 | 70% | 80% |
+| 集成测试 | 50% | 60% |
+| E2E测试 | 关键流程100% | 主要流程80% |
+
+## 运行测试
+
+### 本地开发
+```bash
+# 运行所有测试
+pnpm test
+
+# 运行组件测试
+pnpm test:components
+
+# 运行集成测试
+pnpm test:integration
+
+# 运行E2E测试
+pnpm test:e2e:chromium
+
+# 生成覆盖率报告
+pnpm test:coverage
+
+# 运行特定测试
+pnpm test --testNamePattern="UserForm"
+```
+
+### CI/CD
+```yaml
+web-component-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd web && pnpm test:components
+
+web-integration-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd web && pnpm test:integration
+
+web-e2e-tests:
+  runs-on: ubuntu-latest
+  steps:
+    - run: cd web && pnpm test:e2e:chromium
+```
+
+## 调试技巧
+
+### 1. 使用调试模式
+```bash
+# 运行特定测试并显示详细信息
+pnpm test --testNamePattern="UserForm" --reporter=verbose
+
+# 在浏览器中打开调试器
+pnpm test:components --debug
+```
+
+### 2. 查看DOM结构
+```typescript
+// 在测试中打印DOM结构
+screen.debug();
+
+// 打印特定元素
+screen.debug(screen.getByTestId('user-table'));
+```
+
+### 3. E2E测试调试
+```typescript
+// 使用调试模式
+test('调试模式', async ({ page }) => {
+  await page.goto('/users');
+  await page.pause(); // 暂停执行,打开Playwright Inspector
+});
+```
+
+## 参考实现
+
+- 用户管理UI包:`packages/user-management-ui`
+- 文件管理UI包:`packages/file-management-ui`
+- Web应用测试:`web/tests/`
+
+## 相关文档
+
+- [测试策略概述](./testing-strategy.md)
+- [后端模块包测试规范](./backend-module-testing-standards.md)
+- [Web Server包测试规范](./web-server-testing-standards.md)
+- [Mini UI包测试规范](./mini-ui-testing-standards.md)
+- [UI包开发规范](./ui-package-standards.md)
+
+---
+
+**文档状态**: 正式版
+**适用范围**: Web UI包和Web应用