From 3f664261b72ac77d430013b5b5bd9637834be615 Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sat, 23 May 2026 19:44:07 -0400 Subject: [PATCH] NEO-54: preserve validation reason codes when player store missing Deny helper returns Denied + reasonCode even without a snapshot bucket, so invalid_quantity/invalid_item are stable before NEO-55 HTTP mapping. --- .../Items/PlayerInventoryOperationsTests.cs | 22 +++++++++++++++++++ .../Game/Items/PlayerInventoryOperations.cs | 6 ++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/server/NeonSprawl.Server.Tests/Game/Items/PlayerInventoryOperationsTests.cs b/server/NeonSprawl.Server.Tests/Game/Items/PlayerInventoryOperationsTests.cs index fbac0b9..4bed3ae 100644 --- a/server/NeonSprawl.Server.Tests/Game/Items/PlayerInventoryOperationsTests.cs +++ b/server/NeonSprawl.Server.Tests/Game/Items/PlayerInventoryOperationsTests.cs @@ -271,6 +271,28 @@ public sealed class PlayerInventoryOperationsTests Assert.Null(outcome.Snapshot); } + [Fact] + public void TryAddStack_ForUnknownPlayer_WithInvalidQuantity_ShouldDenyWithReasonCode() + { + // Arrange + using var factory = new InMemoryWebApplicationFactory(); + var registry = factory.Services.GetRequiredService(); + var store = factory.Services.GetRequiredService(); + + // Act + var outcome = PlayerInventoryOperations.TryAddStack( + "unknown-player-xyz", + "scrap_metal_bulk", + quantity: 0, + registry, + store); + + // Assert + Assert.Equal(PlayerInventoryMutationKind.Denied, outcome.Kind); + Assert.Equal(PlayerInventoryReasonCodes.InvalidQuantity, outcome.ReasonCode); + Assert.Null(outcome.Snapshot); + } + private static int CountItem(PlayerInventorySnapshot snapshot, string itemId) { var total = 0; diff --git a/server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs b/server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs index b92543b..d6b1b97 100644 --- a/server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs +++ b/server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs @@ -124,12 +124,12 @@ public static class PlayerInventoryOperations private static PlayerInventoryMutationOutcome Deny(string playerId, IPlayerInventoryStore store, string reasonCode) { - if (!store.TryGetSnapshot(playerId, out var snapshot)) + if (store.TryGetSnapshot(playerId, out var snapshot)) { - return new PlayerInventoryMutationOutcome(PlayerInventoryMutationKind.StoreMissing, null, null); + return new PlayerInventoryMutationOutcome(PlayerInventoryMutationKind.Denied, reasonCode, snapshot); } - return new PlayerInventoryMutationOutcome(PlayerInventoryMutationKind.Denied, reasonCode, snapshot); + return new PlayerInventoryMutationOutcome(PlayerInventoryMutationKind.Denied, reasonCode, null); } private static int MergeIntoExistingStacks(InventorySlotState[] slots, string itemId, int remaining, int stackMax)