template/.planning/codebase/TESTING.md

6.8 KiB

Testing Patterns

Analysis Date: 2026-09-07

Test Framework

Runner:

  • Vitest ^4.0.18 — declared in package.json devDependencies

Config: None present. There is no vitest.config.ts, vitest.config.js, vitest.setup.*, or any test-related config file anywhere in the repo. Vitest would run with its default configuration (files matching **/*.{test,spec}.?(c|m)[jt]s?(x)).

Assertion Library:

  • Vitest's bundled assertions (expect), plus Jest-compatible describe/it/test globals. No @testing-library/*, jsdom, or happy-dom is installed.

Run Commands (package.json):

pnpm test              # Run all tests (runs `vitest`)

There is no dedicated watch mode or coverage script. To run watch/coverage manually:

pnpm vitest --watch    # Watch mode
pnpm vitest --coverage # Coverage (requires @vitest/coverage-* provider, not installed)

Test File Organization

Location: No test files currently exist in the repository. A repo-wide search for *.test.ts, *.spec.ts, *.test.tsx, *.spec.tsx returns zero matches.

Planned location (per .agents/review.md): test-case design documents live in docs/test/ (with subfolders where appropriate), following the example template docs/test/template.md. Note: the docs/test/ directory and docs/test/template.md do not exist yet — the workflow is defined but no tests have been authored.

Naming: No established on-disk convention yet. Follow the Vitest default: co-located *.test.ts (or *.spec.ts) next to the module under test, or a __tests__/ directory.

Test Structure

No test source exists to extract a concrete pattern from. The authoritative testing workflow is defined in .agents/review.md and is a manual, human-in-the-loop process:

  1. The user requests tests for a feature.
  2. The agent analyzes the feature and proposes test cases in a markdown document (placed in docs/test/, following docs/test/template.md).
  3. The user reviews the proposal over several rounds until the plan is finalized.
  4. The agent writes the test cases from the document. The agent must not run the test command; the user runs it.
  5. The user reports results; simple issues are fixed by the user, complex ones may be handed back to the agent.

Test-case design principle (from .agents/review.md): test cases must cover valid inputs AND invalid inputs / exception paths. For invalid paths, the expectation is usually that the system either throws correctly or produces a sensible logger output (rather than silently returning a wrong value).

Document structure for a test-case proposal:

# 测试目的

测试 XXX 系统的基本功能及异常处理。

# 测试用例

## 测试用例 1

- 设计目的:为什么需要这一测试用例(其来源/推导),而非它做什么。
- 针对接口:最重要的若干接口,最好五个以内。

### 测试内容

描述测试内容,并写出预期结果。

Mocking

Framework: None configured. Vitest provides vi.mock(), vi.fn(), vi.spyOn() which are available without extra deps, but no project-specific mocking pattern exists yet.

Relevant for future tests — the logger.catch mechanism (packages/common/src/logger.ts:189): the engine routes all errors/warnings through the logger singleton rather than throwing. Tests can therefore assert error behavior via logger.catch(fn) which returns { ret, info } (captured messages) instead of expecting exceptions. The logger also exposes disable()/enable() to silence output during tests.

What to Mock (prospective): browser globals (document, window, main, Mota) since much engine code references them at module load (e.g. packages/common/src/logger.ts:24-40 references main.replayChecking and document). Data-layer packages (@user/data-base, @user/data-system, @user/data-common) are designed to run in Node for replay verification, so they are the most unit-testable without a DOM.

What NOT to Mock (prospective): the data-layer interfaces themselves (IDataCommon, IDataBase, IDataSystem) — they are designed to be instantiated in Node and driven through their interfaces.

Fixtures and Factories

Test Data: No fixtures or factory helpers exist yet. Note the engine's createXxx factory convention (dev.md "模块初始化"): if a module needs initialization, expose a createXxx function wired up through index.ts. Test setup would follow this pattern rather than relying on module side effects (which are forbidden by dev.md "无副作用").

Location: docs/test/ (for design docs); no fixture directory established.

Coverage

Requirements: None enforced. No coverage script, no coverage provider installed, no CI coverage gate.

View Coverage: not available without installing a @vitest/coverage-* provider and running pnpm vitest --coverage.

Test Types

Unit Tests:

  • Not yet written. The layered data-side packages (@user/data-common, @user/data-base, @user/data-system) are explicitly designed to run in Node ("数据端可在 node 环境中单独运行" — dev.md "双端分离"), making them the natural first targets for unit tests.

Integration Tests:

  • Not present. The IDataCommon / IDataBase / IDataSystem layer interfaces (packages-user/data-common/src/types.ts, packages-user/data-system/src/types.ts) form a seam where integration tests could assemble a full data-side stack in Node.

E2E Tests:

  • Not used. No Playwright/Cypress. The closest is the replay-verification system (packages-user/data-common/src/replay/) which validates that gameplay is deterministic, but it is a runtime feature, not a test harness.

CI

  • .github/workflows/page.yml only builds and deploys static content to GitHub Pages; it runs pnpm build, not tests.
  • .github/workflows/codeql.yml runs CodeQL static analysis; not unit tests.
  • There is currently no CI step that runs the test suite (and no committed test suite to run).

Common Patterns

Async Testing (prospective): the engine is heavily async/await-based (see ObjectMover.moveProgress in packages-user/data-common/src/common/mover.ts:626). Use await inside it blocks and Promise.withResolvers()/expect(...).resolves patterns as appropriate.

Error Testing (prospective): prefer logger.catch(() => { ... }) and assert on info (the captured { level, message, code }[]) rather than expecting thrown exceptions — the engine is designed to never throw in normal operation.

Determinism (the engine's own "testing" philosophy): the replay system in packages-user/data-common/src/replay/ exists to guarantee that a gameplay run is reproducible. When writing tests for game logic, favor deterministic data-driven inputs so results can be asserted exactly.


Testing analysis: 2026-09-07