Files
TheriapolisV3/Theriapolis.Tests/Items/InventoryTests.cs
T
Christopher Wiebe b451f83174 Initial commit: Theriapolis baseline at port/godot branch point
Captures the pre-Godot-port state of the codebase. This is the rollback
anchor for the Godot port (M0 of theriapolis-rpg-implementation-plan-godot-port.md).
All Phase 0 through Phase 6.5 work is included; Phase 7 is in flight.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-30 20:40:51 -07:00

131 lines
4.8 KiB
C#

using Theriapolis.Core.Data;
using Theriapolis.Core.Items;
using Theriapolis.Core.Rules.Stats;
using Xunit;
namespace Theriapolis.Tests.Items;
public sealed class InventoryTests
{
private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory));
[Fact]
public void Add_AppendsItemAndIncreasesWeight()
{
var inv = new Inventory();
var sword = inv.Add(_content.Items["rend_sword"]);
Assert.Single(inv.Items);
Assert.Equal(_content.Items["rend_sword"].WeightLb, inv.TotalWeightLb);
Assert.Equal(1, sword.Qty);
}
[Fact]
public void TryEquip_PutsItemInRequestedSlot()
{
var inv = new Inventory();
var sword = inv.Add(_content.Items["rend_sword"]);
bool ok = inv.TryEquip(sword, EquipSlot.MainHand, out var err);
Assert.True(ok, err);
Assert.Equal(EquipSlot.MainHand, sword.EquippedAt);
Assert.Same(sword, inv.GetEquipped(EquipSlot.MainHand));
}
[Fact]
public void TryEquip_RefusesIfItemNotInInventory()
{
var inv = new Inventory();
var sword = new ItemInstance(_content.Items["rend_sword"]); // not added to inv
bool ok = inv.TryEquip(sword, EquipSlot.MainHand, out var err);
Assert.False(ok);
Assert.Contains("not in this inventory", err);
}
[Fact]
public void TryEquip_TwoHandedWeaponBlocksWhenOffHandOccupied()
{
var inv = new Inventory();
var shield = inv.Add(_content.Items["buckler"]);
var lance = inv.Add(_content.Items["gore_lance"]); // two_handed
Assert.True(inv.TryEquip(shield, EquipSlot.OffHand, out _));
bool ok = inv.TryEquip(lance, EquipSlot.MainHand, out var err);
Assert.False(ok);
Assert.Contains("two-handed", err.ToLowerInvariant());
}
[Fact]
public void TryEquip_OffHandBlockedWhenMainHandHoldsTwoHanded()
{
var inv = new Inventory();
var lance = inv.Add(_content.Items["gore_lance"]);
var shield = inv.Add(_content.Items["buckler"]);
Assert.True(inv.TryEquip(lance, EquipSlot.MainHand, out _));
bool ok = inv.TryEquip(shield, EquipSlot.OffHand, out var err);
Assert.False(ok);
Assert.Contains("two-handed", err.ToLowerInvariant());
}
[Fact]
public void TryEquip_NaturalWeaponEnhancerRequiresMatchingSlot()
{
var inv = new Inventory();
var fangCaps = inv.Add(_content.Items["fang_caps_steel"]);
// Wrong slot:
bool ok = inv.TryEquip(fangCaps, EquipSlot.NaturalWeaponClaw, out var err);
Assert.False(ok);
Assert.Contains("fits", err.ToLowerInvariant());
// Correct slot:
Assert.True(inv.TryEquip(fangCaps, EquipSlot.NaturalWeaponFang, out _));
}
[Fact]
public void TryUnequip_ClearsSlot()
{
var inv = new Inventory();
var sword = inv.Add(_content.Items["rend_sword"]);
inv.TryEquip(sword, EquipSlot.MainHand, out _);
Assert.True(inv.TryUnequip(EquipSlot.MainHand, out _));
Assert.Null(sword.EquippedAt);
Assert.Null(inv.GetEquipped(EquipSlot.MainHand));
}
[Fact]
public void Remove_AlsoUnregistersFromEquippedSlot()
{
var inv = new Inventory();
var sword = inv.Add(_content.Items["rend_sword"]);
inv.TryEquip(sword, EquipSlot.MainHand, out _);
Assert.True(inv.Remove(sword));
Assert.Empty(inv.Items);
Assert.Null(inv.GetEquipped(EquipSlot.MainHand));
}
// ── SizeMatch ────────────────────────────────────────────────────────
[Fact]
public void SizeMatch_DirectFitReturnsMatch()
{
var rendSword = _content.Items["rend_sword"]; // medium + large
Assert.Equal(SizeMatch.MatchResult.Match, SizeMatch.Check(rendSword, SizeCategory.Medium));
}
[Fact]
public void SizeMatch_AdaptivePropertyOverridesMissingSize()
{
var pack = _content.Items["adaptive_pack"];
// adaptive_pack lists s/m/l explicitly, so match — but we test the Adaptive
// path by querying with a Tiny wearer that's not in the list.
var result = SizeMatch.Check(pack, SizeCategory.Tiny);
// adaptive_pack lists "small" so Tiny falls through to "tiny" not in list,
// but its "adaptive" property kicks in for the Adaptive branch.
Assert.NotEqual(SizeMatch.MatchResult.WrongSize, result);
}
[Fact]
public void SizeMatch_NonAdaptiveWrongSizeIsFlagged()
{
var rendSword = _content.Items["rend_sword"]; // medium + large only
// Small wearer: not in sizes, no adaptive property → WrongSize.
Assert.Equal(SizeMatch.MatchResult.WrongSize, SizeMatch.Check(rendSword, SizeCategory.Small));
}
}