tzurot-testing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Testing Procedures

测试流程

Invoke with /tzurot-testing for test-related procedures.
Testing patterns are in
.claude/rules/02-code-standards.md
- they apply automatically.
调用/tzurot-testing 执行各类测试相关流程。
测试模式定义在
.claude/rules/02-code-standards.md
文件中
- 会自动生效。

Running Tests

运行测试

bash
undefined
bash
undefined

Run all tests

Run all tests

pnpm test
pnpm test

Run specific service

Run specific service

pnpm --filter @tzurot/ai-worker test
pnpm --filter @tzurot/ai-worker test

Run specific file

Run specific file

pnpm test -- MyService.test.ts
pnpm test -- MyService.test.ts

Run with coverage

Run with coverage

pnpm test:coverage
pnpm test:coverage

Run only changed packages

Run only changed packages

pnpm focus:test
undefined
pnpm focus:test
undefined

Coverage Audit Procedure

覆盖率审计流程

bash
undefined
bash
undefined

Run unified audit (CI does this automatically)

Run unified audit (CI does this automatically)

pnpm ops test:audit
pnpm ops test:audit

Filter by category

Filter by category

pnpm ops test:audit --category=services pnpm ops test:audit --category=contracts
pnpm ops test:audit --category=services pnpm ops test:audit --category=contracts

Update baseline (after closing gaps)

Update baseline (after closing gaps)

pnpm ops test:audit --update
pnpm ops test:audit --update

Strict mode (fails on ANY gap)

Strict mode (fails on ANY gap)

pnpm ops test:audit --strict

**Unified Baseline**: `test-coverage-baseline.json` (project root)
pnpm ops test:audit --strict

**统一基准文件**:`test-coverage-baseline.json`(项目根目录)

Test File Types

测试文件类型

TypePatternLocationInfrastructure
Unit
*.test.ts
Next to sourceFully mocked
Integration
*.int.test.ts
Next to sourcePGLite
Schema
*.schema.test.ts
common-types/types/
Zod only
类型命名模式存放位置基础设施
单元测试
*.test.ts
源代码旁完全模拟
集成测试
*.int.test.ts
源代码旁PGLite
Schema测试
*.schema.test.ts
common-types/types/
仅使用Zod

Debugging Test Failures

调试测试失败问题

1. Run Specific Test

1. 运行指定测试

bash
pnpm test -- MyService.test.ts --reporter=verbose
bash
pnpm test -- MyService.test.ts --reporter=verbose

2. Check for Fake Timer Issues

2. 检查虚假计时器问题

typescript
// ❌ WRONG - Promise rejection warning
const promise = asyncFunction();
await vi.runAllTimersAsync(); // Rejection happens here!
await expect(promise).rejects.toThrow(); // Too late

// ✅ CORRECT - Attach handler BEFORE advancing
const promise = asyncFunction();
const assertion = expect(promise).rejects.toThrow('Error');
await vi.runAllTimersAsync();
await assertion;
typescript
// ❌ WRONG - Promise rejection warning
const promise = asyncFunction();
await vi.runAllTimersAsync(); // Rejection happens here!
await expect(promise).rejects.toThrow(); // Too late

// ✅ CORRECT - Attach handler BEFORE advancing
const promise = asyncFunction();
const assertion = expect(promise).rejects.toThrow('Error');
await vi.runAllTimersAsync();
await assertion;

3. Reset Mock State

3. 重置模拟状态

typescript
beforeEach(() => {
  vi.clearAllMocks(); // Clear call history, keep impl
});
afterEach(() => {
  vi.restoreAllMocks(); // Restore originals (spies only)
});
typescript
beforeEach(() => {
  vi.clearAllMocks(); // Clear call history, keep impl
});
afterEach(() => {
  vi.restoreAllMocks(); // Restore originals (spies only)
});

Creating Mock Factories

创建模拟工厂

typescript
// Use async factory for vi.mock hoisting
vi.mock('./MyService.js', async () => {
  const { mockMyService } = await import('../test/mocks/MyService.mock.js');
  return mockMyService;
});

// Import accessors after vi.mock
import { getMyServiceMock } from '../test/mocks/index.js';

it('should call service', () => {
  expect(getMyServiceMock().someMethod).toHaveBeenCalled();
});
typescript
// Use async factory for vi.mock hoisting
vi.mock('./MyService.js', async () => {
  const { mockMyService } = await import('../test/mocks/MyService.mock.js');
  return mockMyService;
});

// Import accessors after vi.mock
import { getMyServiceMock } from '../test/mocks/index.js';

it('should call service', () => {
  expect(getMyServiceMock().someMethod).toHaveBeenCalled();
});

Integration Tests with PGLite

基于PGLite的集成测试

typescript
describe('UserService', () => {
  let pglite: PGlite;
  let prisma: PrismaClient;

  beforeAll(async () => {
    pglite = new PGlite({ extensions: { vector } });
    await pglite.exec(loadPGliteSchema());
    prisma = new PrismaClient({ adapter: new PrismaPGlite(pglite) });
  });

  it('should create user', async () => {
    const service = new UserService(prisma);
    const userId = await service.getOrCreateUser('123', 'testuser');
    expect(userId).toBeDefined();
  });
});
⚠️ ALWAYS use
loadPGliteSchema()
- NEVER create tables manually!
typescript
describe('UserService', () => {
  let pglite: PGlite;
  let prisma: PrismaClient;

  beforeAll(async () => {
    pglite = new PGlite({ extensions: { vector } });
    await pglite.exec(loadPGliteSchema());
    prisma = new PrismaClient({ adapter: new PrismaPGlite(pglite) });
  });

  it('should create user', async () => {
    const service = new UserService(prisma);
    const userId = await service.getOrCreateUser('123', 'testuser');
    expect(userId).toBeDefined();
  });
});
⚠️ 务必使用
loadPGliteSchema()
- 禁止手动创建表!

Integration Test Triggers

集成测试触发条件

Integration tests (
*.int.test.ts
) run separately from unit tests and are not included in
pnpm test
or pre-push hooks.
Always run
pnpm test:int
after:
ChangeWhy
Add/remove slash command options
CommandHandler.int.test.ts
snapshots capture full command structure
Add/remove subcommandsSame snapshot tests
Restructure command directories
getCommandFiles()
discovery changes affect command loading
Change component prefix routingIntegration tests verify button/select menu routing
Update snapshots with:
pnpm vitest run --config vitest.int.config.ts <file> --update
集成测试(
*.int.test.ts
)与单元测试分开运行,不会包含在
pnpm test
或预推送钩子中。
在以下变更后务必运行
pnpm test:int
变更类型原因
添加/移除斜杠命令选项
CommandHandler.int.test.ts
的快照会捕获完整的命令结构
添加/移除子命令同样涉及快照测试
重构命令目录结构
getCommandFiles()
的发现逻辑变更会影响命令加载
修改组件前缀路由集成测试会验证按钮/选择菜单的路由
更新快照命令
pnpm vitest run --config vitest.int.config.ts <file> --update

Definition of Done

完成标准

  • New service files have
    .int.test.ts
  • New API schemas have
    .schema.test.ts
  • Coverage doesn't drop (Codecov enforces 80%)
  • Run
    pnpm ops test:audit
    to verify no new gaps
  • 新服务文件需包含
    .int.test.ts
    测试文件
  • 新API Schema需包含
    .schema.test.ts
    测试文件
  • 覆盖率不得下降(Codecov强制要求80%覆盖率)
  • 运行
    pnpm ops test:audit
    验证无新的覆盖率缺口

References

参考资料

  • Full testing guide:
    docs/reference/guides/TESTING.md
  • Mock factories:
    services/*/src/test/mocks/
  • PGLite setup:
    docs/reference/testing/PGLITE_SETUP.md
  • Coverage audit:
    docs/reference/testing/COVERAGE_AUDIT_SYSTEM.md
  • Rules:
    .claude/rules/02-code-standards.md
  • 完整测试指南:
    docs/reference/guides/TESTING.md
  • 模拟工厂示例:
    services/*/src/test/mocks/
  • PGLite配置:
    docs/reference/testing/PGLITE_SETUP.md
  • 覆盖率审计系统:
    docs/reference/testing/COVERAGE_AUDIT_SYSTEM.md
  • 编码规范:
    .claude/rules/02-code-standards.md