NEO-53: address code review suggestions

Register footnote NEO-53, Bruno exact id-order test, test indent fix,
review doc strikethroughs.
pull/88/head
VinPropane 2026-05-23 19:12:20 -04:00
parent 68c4980e42
commit 52590fa749
4 changed files with 46 additions and 32 deletions

View File

@ -23,10 +23,24 @@ tests {
test("items are ascending by id (ordinal)", function () {
const body = res.getBody();
const ids = body.items.map((x) => x.id);
// Match server StringComparer.Ordinal (prototype ids are ASCII snake_case).
const sorted = [...ids].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
expect(ids).to.eql(sorted);
});
test("frozen prototype six matches registry id order", function () {
const body = res.getBody();
const ids = body.items.map((x) => x.id);
expect(ids).to.eql([
"contract_handoff_token",
"field_stim_mk0",
"prototype_armor_shell",
"refined_plate_stock",
"scrap_metal_bulk",
"survey_drone_kit",
]);
});
test("frozen prototype six is present", function () {
const body = res.getBody();
const ids = new Set(body.items.map((x) => x.id));

View File

@ -48,7 +48,7 @@ Fleshed-out scope, contracts, and integration notes live in **per-module documen
| E3.M4 | SinkAndDurabilityLifecycle | E3.M3, E8.M3 | DurabilityState, ItemSinkEvent, RepairCostRule | Pre-production | Planned |
| E3.M5 | EconomyBalancePolicy | E3.M4, E9.M2 | EconomyPolicy, PriceBandRule, FaucetSinkRatio | Pre-production | Planned |
**E3.M3 note:** Epic 3 **Slice 1** backlog in Linear ([Epic 3 — Crafting, Gathering, and Itemization Economy](https://linear.app/neon-sprawl/project/epic-3-crafting-gathering-and-itemization-economy-65785ed05bc2)): [NEO-50](https://linear.app/neon-sprawl/issue/NEO-50) → [NEO-56](https://linear.app/neon-sprawl/issue/NEO-56); label **`E3.M3`**. See [E3M3-prototype-backlog.md](../../plans/E3M3-prototype-backlog.md), [E3_M3_ItemizationAndInventorySchema.md](E3_M3_ItemizationAndInventorySchema.md). **NEO-50** (content + CI), **NEO-51** (server fail-fast load), and **NEO-52** (`IItemDefinitionRegistry` + DI) moved the register row to **In Progress**; later slices update the alignment table as they land.
**E3.M3 note:** Epic 3 **Slice 1** backlog in Linear ([Epic 3 — Crafting, Gathering, and Itemization Economy](https://linear.app/neon-sprawl/project/epic-3-crafting-gathering-and-itemization-economy-65785ed05bc2)): [NEO-50](https://linear.app/neon-sprawl/issue/NEO-50) → [NEO-56](https://linear.app/neon-sprawl/issue/NEO-56); label **`E3.M3`**. See [E3M3-prototype-backlog.md](../../plans/E3M3-prototype-backlog.md), [E3_M3_ItemizationAndInventorySchema.md](E3_M3_ItemizationAndInventorySchema.md). **NEO-50** (content + CI), **NEO-51** (server fail-fast load), **NEO-52** (`IItemDefinitionRegistry` + DI), and **NEO-53** (`GET /game/world/item-definitions`) moved the register row to **In Progress**; later slices update the alignment table as they land.
### Epic 4 — World Topology

View File

@ -19,7 +19,7 @@ NEO-53 adds **`GET /game/world/item-definitions`**: a versioned read-only JSON p
| [`docs/plans/NEO-53-implementation-plan.md`](../plans/NEO-53-implementation-plan.md) | **Matches** — route, five required row fields, registry-only injection, frozen six order, Bruno/manual QA/README, acceptance checklist complete. |
| [`docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md`](../decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) | **Matches** — NEO-53 HTTP bullet under **Related implementation slices**; frozen roster table aligns with test/Bruno assertions. |
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) | **Matches** — E3.M3 row notes NEO-53 landed with plan, manual QA, README, Bruno links. |
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Partially matches** — E3.M3 **In Progress**; footnote still lists NEO-50NEO-52 only (see Suggestion 1). |
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) | **Matches** — E3.M3 **In Progress**; footnote includes NEO-53. |
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **Matches** — read-only world catalog; server remains source of truth; no client-trusted mutation. |
| [`docs/manual-qa/NEO-53.md`](../manual-qa/NEO-53.md) | **Matches** — curl, schema v1, six ids, spot-checks aligned with automated tests. |
| [`server/README.md`](../../server/README.md) | **Matches** — Item definitions section with GET path and curl example. |
@ -32,15 +32,15 @@ None.
## Suggestions
1. **Dependency register footnote (optional)** — Extend the **E3.M3 note** in [`module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) to mention **NEO-53** alongside NEO-50NEO-52 so the register matches the alignment table (same pattern as NEO-52 review).
1. ~~**Dependency register footnote (optional)** — Extend the **E3.M3 note** in [`module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) to mention **NEO-53** alongside NEO-50NEO-52 so the register matches the alignment table (same pattern as NEO-52 review).~~ **Done.**
2. **Bruno — exact id order (optional)** — C# integration test asserts the full frozen six sequence; Bruno only checks ascending sort plus set membership. Adding an explicit `ids` array assertion (or mirroring the C# frozen list) would catch registry ordering regressions in manual runs without running `dotnet test`.
2. ~~**Bruno — exact id order (optional)** — C# integration test asserts the full frozen six sequence; Bruno only checks ascending sort plus set membership. Adding an explicit `ids` array assertion (or mirroring the C# frozen list) would catch registry ordering regressions in manual runs without running `dotnet test`.~~ **Done.**`frozen prototype six matches registry id order` test in [`Get item definitions.bru`](../../bruno/neon-sprawl-server/item-definitions/Get%20item%20definitions.bru).
## Nits
- Nit: **`ItemDefinitionsWorldApiTests`** — `[Fact]` and method body use **2-space** indent while the class uses **4-space**; [`SkillDefinitionsWorldApiTests.cs`](../../server/NeonSprawl.Server.Tests/Game/Skills/SkillDefinitionsWorldApiTests.cs) is consistently 4-space. Format-only tidy.
- ~~Nit: **`ItemDefinitionsWorldApiTests`** — `[Fact]` and method body use **2-space** indent while the class uses **4-space**; [`SkillDefinitionsWorldApiTests.cs`](../../server/NeonSprawl.Server.Tests/Game/Skills/SkillDefinitionsWorldApiTests.cs) is consistently 4-space. Format-only tidy.~~ **Done.**
- Nit: Bruno **`items are ascending by id`** block omits the **StringComparer.Ordinal** comment present in [`Get skill definitions.bru`](../../bruno/neon-sprawl-server/skill-definitions/Get%20skill%20definitions.bru); harmless for ASCII snake_case ids but copy the comment for parity if touching the file.
- ~~Nit: Bruno **`items are ascending by id`** block omits the **StringComparer.Ordinal** comment present in [`Get skill definitions.bru`](../../bruno/neon-sprawl-server/skill-definitions/Get%20skill%20definitions.bru); harmless for ASCII snake_case ids but copy the comment for parity if touching the file.~~ **Done.**
- Nit: **`ItemDefinitionsWorldApi`** allocates a `List<ItemDefinitionJson>` per request — same pattern as skills; fine at six rows.

View File

@ -18,32 +18,32 @@ public class ItemDefinitionsWorldApiTests
"survey_drone_kit",
];
[Fact]
public async Task GetItemDefinitions_ShouldReturnSchemaV1_WithFrozenSixInIdOrder()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/world/item-definitions");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadFromJsonAsync<ItemDefinitionsListResponse>();
Assert.NotNull(body);
Assert.Equal(ItemDefinitionsListResponse.CurrentSchemaVersion, body!.SchemaVersion);
Assert.NotNull(body.Items);
var ids = body.Items.Select(static i => i.Id).ToList();
Assert.Equal(FrozenSixInIdOrder, ids);
[Fact]
public async Task GetItemDefinitions_ShouldReturnSchemaV1_WithFrozenSixInIdOrder()
{
// Arrange
await using var factory = new InMemoryWebApplicationFactory();
var client = factory.CreateClient();
// Act
var response = await client.GetAsync("/game/world/item-definitions");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var body = await response.Content.ReadFromJsonAsync<ItemDefinitionsListResponse>();
Assert.NotNull(body);
Assert.Equal(ItemDefinitionsListResponse.CurrentSchemaVersion, body!.SchemaVersion);
Assert.NotNull(body.Items);
var ids = body.Items.Select(static i => i.Id).ToList();
Assert.Equal(FrozenSixInIdOrder, ids);
var scrap = body.Items.Single(i => i.Id == "scrap_metal_bulk");
Assert.Equal("Scrap Metal (Bulk)", scrap.DisplayName);
Assert.Equal("material", scrap.PrototypeRole);
Assert.Equal(999, scrap.StackMax);
Assert.Equal("bag", scrap.InventorySlotKind);
var scrap = body.Items.Single(i => i.Id == "scrap_metal_bulk");
Assert.Equal("Scrap Metal (Bulk)", scrap.DisplayName);
Assert.Equal("material", scrap.PrototypeRole);
Assert.Equal(999, scrap.StackMax);
Assert.Equal("bag", scrap.InventorySlotKind);
var armor = body.Items.Single(i => i.Id == "prototype_armor_shell");
Assert.Equal("equip_stub", armor.PrototypeRole);
Assert.Equal(1, armor.StackMax);
Assert.Equal("equipment", armor.InventorySlotKind);
}
var armor = body.Items.Single(i => i.Id == "prototype_armor_shell");
Assert.Equal("equip_stub", armor.PrototypeRole);
Assert.Equal(1, armor.StackMax);
Assert.Equal("equipment", armor.InventorySlotKind);
}
}