neon-sprawl/.cursor/rules/testing-expectations.md

5.6 KiB

description alwaysApply
Unit vs integration tests; integration when data persistence/mutation exists; C# xUnit; Bruno .bru requests when server HTTP endpoints change; Godot client unit tests with script changes (NEO-12 harness + gdscript.yml CI). true

Testing expectations (Neon Sprawl)

C# server

  • Non-trivial logic (validation, simulation rules, serialization boundaries, idempotency, security-sensitive paths) should have automated tests once a test project exists in the repo.
  • Default framework: xUnit for new test projects unless the repo already standardizes on something else—stay consistent with existing *.Tests projects.
  • Layout (mandatory for new/changed tests): every [Fact] / [Theory] method in *Tests.cs must use full AAA: // Arrange, // Act, // Assert on their own lines, Act limited to the behavior under test, and response-body reads for verification in Assert (not mixed into Act with the HTTP call). A blank line after each phase comment is not required. See C# style — Unit and integration tests. Treat anything less as incomplete for merge.
  • At-write-time default: start new tests with snippet xut / xutc from .vscode/csharp.code-snippets or copy server/NeonSprawl.Server.Tests/TestTemplates/AAA_TEST_METHOD_TEMPLATE.cs.txt so blank-line AAA is present from the first keystroke.
  • Names: MethodName_ShouldExpectedOutcome_WhenScenario; see C# style — Test method naming convention.
  • No test project yet: It is acceptable to ship a small change without a suite only while the server is mostly scaffolding. When you add the first testable module, add a NeonSprawl.Server.Tests (or equivalent) project and start covering that area; do not leave new core logic permanently untested.
  • Bug fixes: Prefer a regression test when the fix is non-obvious or has broken before.
  • HTTP endpoints: Any change set that adds or changes a route or wire contract on NeonSprawl.Server (new MapGet / MapPost, path change, request/response JSON shape, status codes) must add or update the Bruno collection under bruno/neon-sprawl-server/ so the API stays manually exercisable from the repo: at least one .bru request per new/changed route (happy path plus a representative 4xx/404 case when the server defines one). Group requests in subfolders by area (same pattern as targeting/, position/, interaction/, hotbar-loadout/, ability-cast/). Prefer Bruno tests blocks on those requests for simple smoke checks (status + key JSON fields) when the behavior is stable enough not to churn every edit. If Bruno is unsuitable for a given endpoint (e.g. streaming-only), say so briefly in the PR or plan instead of silently omitting coverage.

Integration testing (data manipulation)

  • Once the server reads or writes durable state (e.g. PostgreSQL via EF Core, Dapper, or similar; migrations; transactional inventory/progression/economy paths), expect integration tests that exercise the real persistence boundary, not only mocked repositories.
  • Cover at least: happy path, constraints / failures (unique keys, FKs, expected rollbacks), and idempotent or retry-safe behavior where the design requires it.
  • How to run them: Prefer a project-standard approach (dedicated test database, Testcontainers, or CI service container) documented in server/ README or docs/—do not leave integration tests machine-only without notes.
  • Spike exception: A story may defer integration tests only when persistence is explicitly out of scope; call that out in the plan and add tests when data manipulation lands.

Godot client (GDScript)

  • Harness (NEO-12): GdUnit4 lives under client/addons/gdUnit4/; suites under client/test/. client/README.md documents local and headless runs; .github/workflows/gdscript.yml runs gdlint, gdformat, and headless GdUnit when client/scripts/, client/test/, client/addons/, or client/project.godot change.
  • GdUnit tests (client/test/**/*.gd): every new or changed test function must use the same AAA discipline as C#: # Arrange, # Act, # Assert each on its own line, in order. A blank line after each label is not required. Act holds only the invocation(s) under test; assertions (and any parsing done purely to assert) live under Assert. See gdscript-style — GdUnit test layout (AAA).
  • Production script changes: Any PR that adds or changes GDScript under client/scripts/ (and any other application .gd outside client/addons/ and client/test/) must add or update tests under client/test/ in the same change set. If a change is not reasonably testable, say so in the PR (or story plan) with a short reason—do not skip tests silently.
  • Scenes / assets / manual checks: Scene tweaks, visuals, and flows that unit tests do not cover still deserve manual verification notes in the PR when behavior could regress.

Cross-cutting

  • If CI is added later, treat its required steps (e.g. dotnet test, schema validation) as gates: fix failures or update workflows/docs intentionally—do not silence checks without a recorded reason.
  • When a story is explicitly client-only or spike (no server), match that scope in tests too (manual client checks only), and call out temporariness per architecture authority.