Files
TheriapolisV3/Theriapolis.Tests/Rules/StartingKitTests.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

123 lines
4.5 KiB
C#

using Theriapolis.Core.Data;
using Theriapolis.Core.Items;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.Rules.Stats;
using Xunit;
namespace Theriapolis.Tests.Rules;
/// <summary>
/// Verifies that every class's <c>starting_kit</c> in classes.json
/// references real items, that auto-equipped items land in their declared
/// slot, and that fresh characters arrive armed and armoured.
/// </summary>
public sealed class StartingKitTests
{
private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory));
[Fact]
public void EveryClass_HasNonEmptyStartingKit()
{
foreach (var c in _content.Classes.Values)
Assert.NotEmpty(c.StartingKit);
}
[Fact]
public void EveryStartingKitItem_ReferencesARealItem()
{
foreach (var c in _content.Classes.Values)
foreach (var entry in c.StartingKit)
Assert.True(
_content.Items.ContainsKey(entry.ItemId),
$"Class '{c.Id}' starting_kit references unknown item '{entry.ItemId}'");
}
[Fact]
public void EveryAutoEquipEntry_HasValidEquipSlot()
{
foreach (var c in _content.Classes.Values)
foreach (var entry in c.StartingKit)
{
if (!entry.AutoEquip) continue;
Assert.False(
string.IsNullOrEmpty(entry.EquipSlot),
$"Class '{c.Id}' auto-equip entry '{entry.ItemId}' has empty equip_slot");
Assert.NotNull(EquipSlotExtensions.FromJson(entry.EquipSlot));
}
}
[Theory]
[InlineData("fangsworn", "rend_sword", "chain_shirt", "buckler")]
[InlineData("bulwark", "hoof_club", "chain_mail", "standard_shield")]
[InlineData("covenant_keeper", "rend_sword", "chain_shirt", "standard_shield")]
[InlineData("claw_wright", "hoof_club", "studded_leather", "buckler")]
public void StartingKit_AppliedAndEquipped_FullKit(
string classId, string mainHand, string body, string offHand)
{
var c = BuildWithKit(classId);
AssertEquipped(c, EquipSlot.MainHand, mainHand);
AssertEquipped(c, EquipSlot.Body, body);
AssertEquipped(c, EquipSlot.OffHand, offHand);
}
[Theory]
[InlineData("feral", "paw_axe", "hide_vest")]
[InlineData("shadow_pelt", "thorn_blade", "studded_leather")]
[InlineData("scent_broker", "fang_knife", "leather_harness")]
[InlineData("muzzle_speaker", "fang_knife", "studded_leather")]
public void StartingKit_AppliedAndEquipped_NoShield(string classId, string mainHand, string body)
{
var c = BuildWithKit(classId);
AssertEquipped(c, EquipSlot.MainHand, mainHand);
AssertEquipped(c, EquipSlot.Body, body);
Assert.Null(c.Inventory.GetEquipped(EquipSlot.OffHand));
}
[Fact]
public void StartingKit_Skipped_WhenItemsTableNotPassed()
{
var c = MakeBuilder("fangsworn").Build(); // no items dict → no kit applied
Assert.Empty(c.Inventory.Items);
}
[Fact]
public void StartingKit_ProducesPositiveAcOverUnarmoredBaseline()
{
var c = BuildWithKit("fangsworn");
int armored = DerivedStats.ArmorClass(c);
Assert.True(armored >= 14, $"Fangsworn starting kit should produce AC ≥ 14 (chain shirt + buckler), got {armored}");
}
private Character BuildWithKit(string classId)
=> MakeBuilder(classId).Build(_content.Items);
private CharacterBuilder MakeBuilder(string classId)
{
var classDef = _content.Classes[classId];
var b = new CharacterBuilder
{
Clade = _content.Clades["canidae"],
Species = _content.Species["wolf"],
ClassDef = classDef,
Background = _content.Backgrounds["pack_raised"],
BaseAbilities = new AbilityScores(15, 14, 13, 12, 10, 8),
Name = "KitTest",
};
// Pick the right number of skills for this class.
int n = classDef.SkillsChoose;
foreach (var raw in classDef.SkillOptions)
{
if (b.ChosenClassSkills.Count >= n) break;
try { b.ChosenClassSkills.Add(SkillIdExtensions.FromJson(raw)); } catch { }
}
return b;
}
private static void AssertEquipped(Character c, EquipSlot slot, string expectedItemId)
{
var inst = c.Inventory.GetEquipped(slot);
Assert.NotNull(inst);
Assert.Equal(expectedItemId, inst!.Def.Id);
}
}