neon-sprawl/server/NeonSprawl.Server.Tests/Game/Items/PlayerInventoryOperationsTe...

266 lines
9.2 KiB
C#

using Microsoft.Extensions.DependencyInjection;
using NeonSprawl.Server.Game.Items;
using Xunit;
namespace NeonSprawl.Server.Tests.Game.Items;
public sealed class PlayerInventoryOperationsTests
{
[Fact]
public void TryAddStack_ToEmptyBag_ShouldPlaceInFirstSlot()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
// Act
var outcome = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"scrap_metal_bulk",
quantity: 10,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Applied, outcome.Kind);
Assert.NotNull(outcome.Snapshot);
Assert.Equal("scrap_metal_bulk", outcome.Snapshot!.BagSlots[0].ItemId);
Assert.Equal(10, outcome.Snapshot.BagSlots[0].Quantity);
}
[Fact]
public void TryAddStack_ShouldMergeIntoPartialStackRespectingStackMax()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
_ = PlayerInventoryOperations.TryAddStack("dev-local-1", "field_stim_mk0", 15, registry, store);
// Act
var outcome = PlayerInventoryOperations.TryAddStack("dev-local-1", "field_stim_mk0", 5, registry, store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Applied, outcome.Kind);
Assert.Equal(20, CountItem(outcome.Snapshot!, "field_stim_mk0"));
Assert.Equal(1, OccupiedBagSlotCount(outcome.Snapshot!));
}
[Fact]
public void TryAddStack_WhenBagFull_ShouldDenyWithInventoryFull()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
for (var i = 0; i < PlayerInventorySnapshot.BagSlotCount; i++)
{
var add = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"survey_drone_kit",
quantity: 1,
registry,
store);
Assert.Equal(PlayerInventoryMutationKind.Applied, add.Kind);
}
store.TryGetSnapshot("dev-local-1", out var before);
// Act
var outcome = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"scrap_metal_bulk",
quantity: 1,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Denied, outcome.Kind);
Assert.Equal(PlayerInventoryReasonCodes.InventoryFull, outcome.ReasonCode);
store.TryGetSnapshot("dev-local-1", out var after);
Assert.Equal(TotalBagQuantity(before!), TotalBagQuantity(after!));
}
[Fact]
public void TryAddStack_ToEquipment_ShouldUseEquipmentContainer()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
// Act
var outcome = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"prototype_armor_shell",
quantity: 1,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Applied, outcome.Kind);
Assert.Equal("prototype_armor_shell", outcome.Snapshot!.EquipmentSlots[0].ItemId);
Assert.Equal(1, outcome.Snapshot.EquipmentSlots[0].Quantity);
Assert.All(outcome.Snapshot.BagSlots, static s => Assert.True(s.IsEmpty));
}
[Fact]
public void TryAddStack_WhenEquipmentOccupied_ShouldDenyWithInventoryFull()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
_ = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"prototype_armor_shell",
quantity: 1,
registry,
store);
// Act
var outcome = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"prototype_armor_shell",
quantity: 1,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Denied, outcome.Kind);
Assert.Equal(PlayerInventoryReasonCodes.InventoryFull, outcome.ReasonCode);
}
[Fact]
public void TryAddStack_ForUnknownItem_ShouldDenyWithInvalidItem()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
// Act
var outcome = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"nonexistent_item_id",
quantity: 1,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Denied, outcome.Kind);
Assert.Equal(PlayerInventoryReasonCodes.InvalidItem, outcome.ReasonCode);
}
[Fact]
public void TryAddStack_ForNonPositiveQuantity_ShouldDenyWithInvalidQuantity()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
// Act
var outcome = PlayerInventoryOperations.TryAddStack(
"dev-local-1",
"scrap_metal_bulk",
quantity: 0,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Denied, outcome.Kind);
Assert.Equal(PlayerInventoryReasonCodes.InvalidQuantity, outcome.ReasonCode);
}
[Fact]
public void TryRemoveStack_ShouldDrainLowestSlotIndexFirst()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
_ = PlayerInventoryOperations.TryAddStack("dev-local-1", "field_stim_mk0", 20, registry, store);
_ = PlayerInventoryOperations.TryAddStack("dev-local-1", "field_stim_mk0", 5, registry, store);
// Act
var outcome = PlayerInventoryOperations.TryRemoveStack(
"dev-local-1",
"field_stim_mk0",
quantity: 20,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Applied, outcome.Kind);
Assert.True(outcome.Snapshot!.BagSlots[0].IsEmpty);
Assert.Equal("field_stim_mk0", outcome.Snapshot.BagSlots[1].ItemId);
Assert.Equal(5, outcome.Snapshot.BagSlots[1].Quantity);
}
[Fact]
public void TryRemoveStack_WhenInsufficientQuantity_ShouldDenyWithoutMutation()
{
// Arrange
using var factory = new InMemoryWebApplicationFactory();
var registry = factory.Services.GetRequiredService<IItemDefinitionRegistry>();
var store = factory.Services.GetRequiredService<IPlayerInventoryStore>();
_ = PlayerInventoryOperations.TryAddStack("dev-local-1", "scrap_metal_bulk", 5, registry, store);
store.TryGetSnapshot("dev-local-1", out var before);
// Act
var outcome = PlayerInventoryOperations.TryRemoveStack(
"dev-local-1",
"scrap_metal_bulk",
quantity: 6,
registry,
store);
// Assert
Assert.Equal(PlayerInventoryMutationKind.Denied, outcome.Kind);
Assert.Equal(PlayerInventoryReasonCodes.InsufficientQuantity, outcome.ReasonCode);
store.TryGetSnapshot("dev-local-1", out var after);
Assert.Equal(TotalBagQuantity(before!), TotalBagQuantity(after!));
}
private static int CountItem(PlayerInventorySnapshot snapshot, string itemId)
{
var total = 0;
foreach (var slot in snapshot.BagSlots)
{
if (!slot.IsEmpty && slot.ItemId == itemId)
{
total += slot.Quantity;
}
}
return total;
}
private static int OccupiedBagSlotCount(PlayerInventorySnapshot snapshot)
{
var count = 0;
foreach (var slot in snapshot.BagSlots)
{
if (!slot.IsEmpty)
{
count++;
}
}
return count;
}
private static int TotalBagQuantity(PlayerInventorySnapshot snapshot)
{
var total = 0;
foreach (var slot in snapshot.BagSlots)
{
total += slot.Quantity;
}
return total;
}
}