Files
Claude-Code-Game-Studios/.claude/rules/test-standards.md
T
DonchitosandClaude Opus 5 d05699707f Release 1.1.0 — one config file, and a process weight you choose
`project.yaml` at the repo root is now the single source of truth for a
project's configuration, replacing `production/stage.txt`,
`production/review-mode.txt` and `.claude/docs/technical-preferences.md` (all
three still read as a fallback; nothing is force-deleted).

**`modes.rigor` — one question instead of six.** `minimal`, `standard` or
`full` fronts six knobs: `modes.workflow`, `docs.density`, `qa.level`,
`modes.story_granularity`, `modes.review_mode` and `team.size`. It defaults to
`minimal`, which is a change from 1.0's behaviour: the heavier tier was
measured costing several times more to reach working code without producing a
better result, and returning nothing when a fresh developer inherited the
project. Pin `modes.rigor: standard` to keep 1.0's process level.

Also in this release:

- `/settings` to view or change any setting, and `project.local.yaml` for
  per-developer preferences that should not be committed.
- `system_overrides` to hold one system to a higher standard than the rest of
  the project, and `testing.strict` to set per test type whether missing
  evidence blocks a story or warns.
- A story that changes anything the player sees is not closed until the game
  has been launched, observed, and a screenshot retained in
  `production/qa/evidence/`. A parse check is not a run. This holds at every
  rigor level, including `minimal`, where automated tests are waived but the
  look is not.
- The code root is engine-specific and enforced as such: Godot `src/`, Unity
  `Assets/`, Unreal `Source/<Module>/`. Two of the three will not build a
  project laid out any other way.
- Migration tooling for existing projects: `.claude/scripts/migrate-v1-config.sh`
  converts a 1.0 layout, non-destructively by default, with a report to review
  before `--finalize` deletes anything.
- Ask-before-write holds regardless of personal automation settings.

Upgrading from 1.0 or earlier: see UPGRADING.md. Full list of changes:
CHANGELOG.md.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016xvzG3i7pWQg4LjyDwWMXd
2026-09-23 12:25:11 +10:00

2.0 KiB

paths
paths
tests/**

Test Standards

  • Test naming: test_[system]_[scenario]_[expected_result] pattern

  • Every test must have a clear arrange/act/assert structure

  • Unit tests must not depend on external state (filesystem, network, database)

  • Integration tests must clean up after themselves

  • Performance tests must specify acceptable thresholds and fail if exceeded

  • Test data must be defined in the test or in dedicated fixtures, never shared mutable state

  • Mock external dependencies — tests should be fast and deterministic

  • Every bug fix must have a regression test that would have caught the original bug — and you must watch it fail before you trust it. Run the new test against the unfixed code, confirm it fails, then apply the fix and confirm it passes. A regression test that has only ever been seen passing is not known to test anything.

    This is .claude/rules/skill-authoring.md's "a gate you have not watched fail is not a gate", applied to tests. It is written out here because stating the goal is not enough. A regression test for an iteration-order defect can use a fixture where no cell takes part in two transfers per tick — it then passes against the very bug it was written for, and looks authoritative doing it. The fixture, not the assertion, is what makes it useless, and only running it against the unfixed code exposes that.

Examples

Correct (proper naming + Arrange/Act/Assert):

func test_health_system_take_damage_reduces_health() -> void:
    # Arrange
    var health := HealthComponent.new()
    health.max_health = 100
    health.current_health = 100

    # Act
    health.take_damage(25)

    # Assert
    assert_eq(health.current_health, 75)

Incorrect:

func test1() -> void:  # VIOLATION: no descriptive name
    var h := HealthComponent.new()
    h.take_damage(25)  # VIOLATION: no arrange step, no clear assert
    assert_true(h.current_health < 100)  # VIOLATION: imprecise assertion