neon-sprawl/docs/manual-qa/NEO-48.md

56 lines
1.9 KiB
Markdown

# Manual QA — NEO-48 (perk state GET + branch selection POST)
Reference: [implementation plan](../plans/NEO-48-implementation-plan.md), [NEO-47 plan](../plans/NEO-47-implementation-plan.md) (unlock engine).
## Preconditions
- Run `NeonSprawl.Server` (`dotnet run` from `server/NeonSprawl.Server`; default `http://localhost:5253`).
- Dev player `dev-local-1` exists in position state (default in-memory / seeded Postgres).
## GET empty state
```bash
curl -sS "http://localhost:5253/game/players/dev-local-1/perk-state" | jq .
```
- [ ] HTTP **200**; `schemaVersion` **1**; `branchPicks` **[]**; `unlockedPerkIds` **[]** (fresh server).
## POST tier-1 pick (happy path)
1. Grant salvage XP to reach level **2** (prototype curve: **100** total XP):
```bash
curl -sS -X POST "http://localhost:5253/game/players/dev-local-1/skill-progression" \
-H "Content-Type: application/json" \
-d '{"schemaVersion":1,"skillId":"salvage","amount":100,"sourceKind":"activity"}' | jq .
```
2. Select tier-1 branch:
```bash
curl -sS -X POST "http://localhost:5253/game/players/dev-local-1/perk-state" \
-H "Content-Type: application/json" \
-d '{"schemaVersion":1,"skillId":"salvage","tierIndex":1,"branchId":"scrap_efficiency"}' | jq .
```
- [ ] `selected: true`; `perkState.branchPicks` contains salvage tier 1 `scrap_efficiency`; `unlockedEvents` empty at tier 1.
- [ ] Repeat **GET** — same pick visible.
## POST deny
- [ ] Second pick same tier different branch → `selected: false`, `reasonCode` **`branch_already_chosen`**.
- [ ] On fresh server without XP grant → `level_too_low`.
## Bruno
- [ ] Run folder `bruno/neon-sprawl-server/perk-state/` (GET + POST happy + deny smoke).
- [ ] Run **`Post branch select deny branch already chosen`** (self-contained; pre-request grant + first pick).
## Regression
```bash
cd server && dotnet test NeonSprawl.sln
```
Expect all tests pass including `PerkStateApiTests`.