skara-brae-common/docs/openup/test/test-cases.md
Jan-Peter von Hunnius 4798f55660 docs(openup): move WI-2 OpenUP work set verbatim from skara-brae-server
All OpenUP artifacts are versioned in skara-brae-common under docs/openup/
per the 2026-10-03 decision reversal in design-wi-2-architecture-cleanup.md
(supersedes the earlier in-server-docs decision). Verbatim move, no content
changes: analysis-design/, requirements/, test/ (6 files).
2026-10-03 17:17:44 +02:00

60 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Skara Brae — Test Cases (existing coverage mapped to use cases)
Version 1.0 (WI-2, 2026-10-03) · OpenUP artifact: `test/test-cases.md`
Baseline: 45 unit tests, all green on `wi-2-architecture-cleanup` @ 899fde8 (`cargo test --workspace`).
## 1. Existing tests → use-case mapping
| Test (file::name) | Verifies | UC |
|---|---|---|
| character::`stat_bonus_at_20_is_zero` / `_below_20_is_negative` / `_above_20_is_positive` / `_handles_large_values` | OB/DB stat-bonus curve incl. BIGINT headroom | UC-08, UC-05 |
| character::`xp_for_level_1_is_zero` / `_2_is_1800` / `_scales_subexponentially` / `_does_not_overflow` | XP curve 18·100·(level−1)² | UC-12 |
| character::`level_up_fails_at_max_level` / `_succeeds_with_enough_xp_and_gold` / `_fails_without_enough_xp` / `_fails_without_enough_gold` | review-board rules | UC-12 |
| character::`level_up_cost_scales` | 500·level cost | UC-12 |
| character::`calc_max_hp_depends_on_class_and_con` / `calc_max_energy_and_nerve_vary_by_class` | class tables | UC-03 |
| character::`training_cost_scales_with_stat` | 5·1.3^(stat/10) cost curve | UC-05 |
| character::`apply_regen_restores_resources` / `_no_change_if_no_time_passed` | lazy regen (180 s / 300 s / 300 s) | shared sub-flow Regenerate Resources |
| character::`roll_stats_returns_all_five` / `race_modifiers_apply` | character creation rolls ± race mods | UC-03 |
| character::`create_character_is_deterministic_under_seeded_rng` | WI-1 injectable RNG at creation | UC-03, NFR-05 |
| combat::`open_ended_roll_stays_in_range` | d100 open-ended re-rolls (≥96 / ≤5) | UC-08 |
| combat::`offensive_bonus_includes_stat_and_base` / `defensive_bonus_includes_stat_and_base` | OB/DB composition | UC-08 |
| combat::`crit_damage_cap_equal_level_is_none` / `_higher_defender_reduces` / `_scaling` | level-gap crit caps (0.60/0.40/0.15/0.05) | UC-08 |
| combat::`resolve_attack_can_hit_or_miss` | round structure and HP clamping | UC-08 |
| combat::`resolve_combat_produces_winner` | fight termination, winner/loser | UC-08 |
| combat::`parse_attack_type_maps_known_types` | slash/crush/puncture + default crush | UC-08 |
| services::`pick_enemy_respects_weights` | weighted encounter draw | UC-08 |
| services::`train_stat_increases_stat` / `_fails_without_energy` / `_fails_without_gold` / `_fails_invalid_stat` / `train_constitution_recals_hp` | gym rules incl. max-HP recompute | UC-05 |
| services::`equip_item_moves_from_inventory_to_slot` / `_swaps_previous` / `_fails_if_not_in_inventory` | equip semantics | UC-06 |
| services::`unequip_item_moves_to_inventory` / `_fails_empty_slot` | unequip semantics | UC-06 |
| services::`equipped_damage_sums_weapon_slots` / `total_defense_sums_all_slots` / `equipped_attack_type_returns_weapon_type` / `_defaults_to_crush` | equipment → combat stats | UC-08 |
| (WI-1) seeded determinism pattern across the above | RNG injection | NFR-05 |
## 2. Coverage gaps the WI-2 refactor must not lose (and currently uncovered areas)
**Covered code being moved** — the run_adventure handler body (routes/mod.rs:409–567) is currently **not directly tested**; its ingredients (regen, pick_enemy, equipped stats, combat resolution) are covered per §1. WI-2 therefore **adds** direct service-level tests while moving the code:
| New test | Vector (design note §7) | UC |
|---|---|---|
| adventure::`run_adventure_win_applies_rewards_and_loot` | A1: seeded win → xp/gold/nerve/loot/inventory/exact outcome fields | UC-08 main |
| adventure::`run_adventure_defeat_sets_hp_to_zero` | A2: overwhelming enemy → no rewards | UC-08 alt |
| adventure::`run_adventure_rejects_incapacitated` | A3: hp = 0 → `CharacterIncapacitated`, nerve intact | UC-08 exception |
| adventure::`run_adventure_rejects_insufficient_nerve` | A4 → `NotEnoughNerve` | UC-08 exception |
| adventure::`run_adventure_applies_regen_before_guards` | A5: regen visible in combat HP | shared sub-flow |
| adventure::`run_adventure_charges_spell_upkeep_on_win_and_loss` | A6 both paths | UC-08 step 8 / UC-11 interplay |
| adventure::`run_adventure_filters_loot_to_catalog_items` | A7 drop for unknown item id skipped | UC-08 step 6 / NFR-07 |
| adventure::`run_adventure_is_deterministic_under_seeded_rng` (+ optional golden CombatLog fixture) | A8 two seeds-42 runs identical | NFR-05 |
| routes::`wire_identity_adventure_response` … `wire_identity_learn_response` (W1–W6) | byte-for-byte `serde_json::to_string` equality of each new DTO against golden strings recorded from the current `json!` expressions **before** the refactor; golden keys in lexicographic order **at every nesting level** (serde_json has no `preserve_order` — json! objects emit sorted keys; the adventure DTO's `combat_log` is the §4.3 Value bridge, defect #14) | UC-08/09/11, NFR-19 |
**New helper/constant pins** (design note §4.4–4.5): `as_str` parity for `ItemType`/`Quality` (wire-format guarantee NFR-19), `gym_display_order_covers_all_training_grounds` (pins GymResponse.stats order to routes/mod.rs:588–594, which differs from TRAINING_GROUNDS order), `race/class` table exhaustiveness, embedded-JSON loader parses (5), `caster_spell_level` spot values, `spell_learn_cost` guard spot checks (0, −1, 8 → 999_999).
**Known remaining gaps (documented, out of WI-2 scope — see design note Q3):**
- Heal cost logic (UC-09) and spell learn/activate rules (UC-11) live in handlers and have no unit tests; WI-2 only types their responses.
- Shop buy/sell stock transitions (UC-07), auth flows (UC-01/02), and level-up handler status mapping (UC-12 → all 422) have no HTTP-level integration tests. The exhaustive `domain_status` match (compile-time totality) is the WI-2 mitigation for the mapping itself.
- No test asserts JSON field **order**; the response structs declare fields in wire order and the golden CombatLog fixture pins the largest payload.
## 3. Regression protocol for WI-2 (coder must run per commit)
1. `cargo fmt --check`
2. `cargo clippy --all-targets -- -D warnings`
3. `cargo test --workspace` — 45 baseline tests green at every step; the only permitted test edits are the **9** mechanical assertion updates listed in design note §4.1 (3 `level_up`, 3 `train_stat`, 1 `equip_item`, 1 `unequip_item`, 1 `pick_enemy_respects_weights` — the last gains an Ok-path `.unwrap()` because `pick_enemy` becomes `Result`-returning) plus newly added tests.
4. Manual wire spot-check (recommended once, after commit 5): run the server, exercise `GET /api/characters/{id}/adventure`, `POST …/heal`, `GET …/spells`, `POST …/spells` (both modes) and diff responses **byte-for-byte** against pre-refactor output — field names, key **order** (lexicographic, since serde_json runs without `preserve_order`), null-ness, number formatting.