NEO-77: Address code review — Bruno folder, E5M1 gate test, docs.
Add ability-catalog folder.bru, extra-id E5M1 gate AAA test, dependency register NEO-77 note, and strike through resolved review suggestions.pull/111/head
parent
42ea2598c3
commit
fd2ffee089
|
|
@ -0,0 +1,3 @@
|
|||
meta {
|
||||
name: ability-catalog
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ Fleshed-out scope, contracts, and integration notes live in **per-module documen
|
|||
| E5.M3 | EncounterAndRewardTables | E5.M1, E3.M3, E7.M2 | EncounterDef, RewardTable, EncounterCompleteEvent | Prototype | Planned |
|
||||
| E5.M4 | GroupCombatScaling | E5.M3, E8.M1 | ScalingProfile, PartySizeModifier, CombatDifficultyBand | Pre-production | Planned |
|
||||
|
||||
**E5.M1 note:** Epic 5 **Slice 1** backlog in Linear ([Epic 5 — PvE Combat and Encounter Content](https://linear.app/neon-sprawl/project/epic-5-pve-combat-and-encounter-content-cf3c94c3-5346-40e0-8aaf-281e846f2846)): [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); label **`E5.M1`**. Gig XP on defeat: **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). See [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1_CombatRulesEngine.md](E5_M1_CombatRulesEngine.md). Builds on E1.M4 cast funnel (NEO-28/31/32); extends **`POST …/ability-cast`** with **`CombatResolution`**. **NEO-76 landed:** frozen four-ability catalog + CI ([NEO-76 plan](../../plans/NEO-76-implementation-plan.md)).
|
||||
**E5.M1 note:** Epic 5 **Slice 1** backlog in Linear ([Epic 5 — PvE Combat and Encounter Content](https://linear.app/neon-sprawl/project/epic-5-pve-combat-and-encounter-content-cf3c94c3-5346-40e0-8aaf-281e846f2846)): [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); label **`E5.M1`**. Gig XP on defeat: **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). See [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1_CombatRulesEngine.md](E5_M1_CombatRulesEngine.md). Builds on E1.M4 cast funnel (NEO-28/31/32); extends **`POST …/ability-cast`** with **`CombatResolution`**. **NEO-76 landed:** frozen four-ability catalog + CI ([NEO-76 plan](../../plans/NEO-76-implementation-plan.md)). **NEO-77 landed:** fail-fast server load of `content/abilities/*_abilities.json` ([NEO-77 plan](../../plans/NEO-77-implementation-plan.md)).
|
||||
|
||||
### Epic 6 — PvP Security
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@
|
|||
|
||||
| Test file | What it covers |
|
||||
|-----------|----------------|
|
||||
| `server/NeonSprawl.Server.Tests/Game/Combat/AbilityDefinitionCatalogLoaderTests.cs` | **Unit:** valid four-ability prototype catalog loads; `abilities` not array; schema violation (negative `baseDamage`, zero `cooldownSeconds`, empty `displayName`); duplicate `id` across files; wrong/missing E5M1 ids; `schemaVersion` ≠ 1; missing dir/schema; no `*_abilities.json` files; invalid JSON. **Host:** `InMemoryWebApplicationFactory` + `/health` + DI `AbilityDefinitionCatalog` count 4; `WebApplicationFactory` with empty abilities dir fails startup with actionable message. |
|
||||
| `server/NeonSprawl.Server.Tests/Game/Combat/AbilityDefinitionCatalogLoaderTests.cs` | **Unit:** valid four-ability prototype catalog loads; `abilities` not array; schema violation (negative `baseDamage`, zero `cooldownSeconds`, empty `displayName`); duplicate `id` across files; E5M1 gate failure (missing id + extra/wrong id); `schemaVersion` ≠ 1; missing dir/schema; no `*_abilities.json` files; invalid JSON. **Host:** `InMemoryWebApplicationFactory` + `/health` + DI `AbilityDefinitionCatalog` count 4; `WebApplicationFactory` with empty abilities dir fails startup with actionable message. |
|
||||
|
||||
**Manual QA:** Skipped — no player-visible HTTP; acceptance is fully covered by AAA loader/host tests above.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ NEO-77 delivers **E5M1-02**: fail-fast startup load of `content/abilities/*_abil
|
|||
| [`docs/plans/E5M1-prototype-backlog.md`](../plans/E5M1-prototype-backlog.md) · **E5M1-02** | **Matches** — AC checkboxes + landed note. |
|
||||
| [`docs/decomposition/modules/E5_M1_CombatRulesEngine.md`](../decomposition/modules/E5_M1_CombatRulesEngine.md) | **Matches** — Status line and freeze rules reference NEO-77 server load + NEO-79 registry deferral. |
|
||||
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) | **Matches** — E5.M1 row updated with NEO-77 landed note; next backlog item NEO-79. |
|
||||
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Partially matches** — E5.M1 table **Status** **In Progress** is correct; **E5.M1 note** still cites NEO-76 only (optional bump to mention NEO-77 server load, same pattern as post–NEO-66 E3.M2 note lag). |
|
||||
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Matches** — E5.M1 table **Status** **In Progress** is correct; **E5.M1 note** cites NEO-76 + NEO-77 server load. |
|
||||
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **N/A** — server startup catalog only; no client mutation or combat resolution. |
|
||||
| [`scripts/validate_content.py`](../../scripts/validate_content.py) | **Matches** — C# loader mirrors `_validate_ability_catalogs` + `_prototype_e5m1_ability_gate`; sync comment on `PrototypeE5M1AbilityCatalogRules.ExpectedAbilityIds`. |
|
||||
| [`server/README.md`](../../server/README.md) | **Matches** — Ability catalog section (config keys, discovery, fail-fast, E5M1 parity). |
|
||||
|
|
@ -35,9 +35,9 @@ None.
|
|||
|
||||
## Suggestions
|
||||
|
||||
1. **Add `folder.bru` under `bruno/neon-sprawl-server/ability-catalog/`** — Other catalog smoke folders (`item-catalog`, `recipe-catalog`, `resource-node-catalog`, `mastery-catalog`) include a `folder.bru` meta file; ability-catalog currently has only the health request. Add for Bruno collection parity.
|
||||
1. ~~**Add `folder.bru` under `bruno/neon-sprawl-server/ability-catalog/`** — Other catalog smoke folders (`item-catalog`, `recipe-catalog`, `resource-node-catalog`, `mastery-catalog`) include a `folder.bru` meta file; ability-catalog currently has only the health request. Add for Bruno collection parity.~~ **Done.** Added `bruno/neon-sprawl-server/ability-catalog/folder.bru`.
|
||||
|
||||
2. **E5M1 gate test — extra/wrong id** — `Load_ShouldThrow_WhenE5M1GateFails` removes one ability (missing id). Plan test table also lists wrong roster; consider an AAA case that swaps in an unknown id (e.g. `prototype_extra`) to cover the “got […]” branch, not only “missing”.
|
||||
2. ~~**E5M1 gate test — extra/wrong id** — `Load_ShouldThrow_WhenE5M1GateFails` removes one ability (missing id). Plan test table also lists wrong roster; consider an AAA case that swaps in an unknown id (e.g. `prototype_extra`) to cover the “got […]” branch, not only “missing”.~~ **Done.** Renamed missing-id test to `Load_ShouldThrow_WhenE5M1GateFailsWithMissingId`; added `Load_ShouldThrow_WhenE5M1GateFailsWithExtraId`. Plan test table updated.
|
||||
|
||||
## Nits
|
||||
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@ public class AbilityDefinitionCatalogLoaderTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_ShouldThrow_WhenE5M1GateFails()
|
||||
public void Load_ShouldThrow_WhenE5M1GateFailsWithMissingId()
|
||||
{
|
||||
// Arrange
|
||||
var (_, abilitiesDir, schemaPath) = CreateTempContentLayout();
|
||||
|
|
@ -178,6 +178,23 @@ public class AbilityDefinitionCatalogLoaderTests
|
|||
Assert.Contains("prototype E5M1 expects exactly ability ids", ioe.Message, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_ShouldThrow_WhenE5M1GateFailsWithExtraId()
|
||||
{
|
||||
// Arrange
|
||||
var (_, abilitiesDir, schemaPath) = CreateTempContentLayout();
|
||||
var root = JsonNode.Parse(ValidPrototypeCatalogJson) as JsonObject
|
||||
?? throw new InvalidOperationException("expected object root");
|
||||
GetAbilityRow(root, "prototype_burst")["id"] = "prototype_extra";
|
||||
WriteCatalog(abilitiesDir, root.ToJsonString());
|
||||
// Act
|
||||
var ex = Record.Exception(() => LoadCatalog(abilitiesDir, schemaPath));
|
||||
// Assert
|
||||
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
||||
Assert.Contains("prototype E5M1 expects exactly ability ids", ioe.Message, StringComparison.Ordinal);
|
||||
Assert.Contains("prototype_extra", ioe.Message, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_ShouldThrow_WhenBaseDamageIsNegative()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue