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

37 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
description: 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).
alwaysApply: 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, a **blank line after each label**, **Act** limited to the behavior under test, and **response-body reads for verification in Assert** (not mixed into Act with the HTTP call). See [C# style — Unit and integration tests](csharp-style.md#unit-and-integration-tests-arrange-act-assert). 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](csharp-style.md#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, with a **blank line after each label** before that phases statements. 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)](gdscript-style.md#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](architecture-authority.md).