using Nightborn.Core.Combat; using NUnit.Framework; namespace Nightborn.Core.Tests; public class BloodEnergyEconomyTests { [Test] public void AttackGain_ShouldIncreaseEnergy() { var sut = new BloodEnergyEconomy(); sut.Add(sut.CalculateAttackGain(1f)); Assert.That(sut.Current, Is.EqualTo(5f).Within(0.0001f)); } [Test] public void Spend_WhenEnoughEnergy_ShouldSucceed() { var sut = new BloodEnergyEconomy(); sut.Add(50f); var ok = sut.Spend(40f); Assert.That(ok, Is.True); Assert.That(sut.Current, Is.EqualTo(10f).Within(0.0001f)); } [Test] public void Spend_WhenInsufficientEnergy_ShouldFailWithoutSideEffects() { var sut = new BloodEnergyEconomy(); sut.Add(10f); var ok = sut.Spend(40f); Assert.That(ok, Is.False); Assert.That(sut.Current, Is.EqualTo(10f).Within(0.0001f)); } [Test] public void Add_ShouldClampToMaxAndFireFullEvent() { var sut = new BloodEnergyEconomy(); var fullFired = false; sut.OnEnergyFull += () => fullFired = true; sut.Add(95f); sut.Add(10f); Assert.That(sut.Current, Is.EqualTo(100f).Within(0.0001f)); Assert.That(fullFired, Is.True); } [Test] public void Reset_ShouldSetEnergyToZero() { var sut = new BloodEnergyEconomy(); sut.Add(60f); sut.Reset(); Assert.That(sut.Current, Is.EqualTo(0f).Within(0.0001f)); } [Test] public void EnergyEmpty_ShouldFireWhenReachesZero() { var sut = new BloodEnergyEconomy(); var emptyFired = false; sut.OnEnergyEmpty += () => emptyFired = true; sut.Add(24f); sut.Spend(24f); Assert.That(emptyFired, Is.True); } [Test] public void SwitchCost_ShouldRespectDiscountAndMinimumCost() { var sut = new BloodEnergyEconomy(); var discounted = sut.CalculateSwitchCost(0.4f); var minimum = sut.CalculateSwitchCost(1.0f); Assert.That(discounted, Is.EqualTo(24f).Within(0.0001f)); Assert.That(minimum, Is.EqualTo(1f).Within(0.0001f)); } [Test] public void UpgradeBonusReduction_ShouldClampCurrentToNewMax() { var cfg = new BloodEnergyConfig { BaseMaxEnergy = 100f }; var sut = new BloodEnergyEconomy(cfg); sut.Add(80f); cfg.BaseMaxEnergy = 50f; sut.SetUpgradeBonus(0f); Assert.That(sut.Max, Is.EqualTo(50f).Within(0.0001f)); Assert.That(sut.Current, Is.EqualTo(50f).Within(0.0001f)); } [Test] public void Add_WhenDead_ShouldBeIgnored() { var sut = new BloodEnergyEconomy(); sut.SetDead(true); sut.Add(10f); Assert.That(sut.Current, Is.EqualTo(0f).Within(0.0001f)); } [Test] public void ProcessFrame_ShouldApplyGainBeforeSpend() { var sut = new BloodEnergyEconomy(); sut.Add(35f); var spendOk = sut.ProcessFrame(gainAmount: 10f, spendAmount: 40f); Assert.That(spendOk, Is.True); Assert.That(sut.Current, Is.EqualTo(5f).Within(0.0001f)); } [Test] public void ProcessFrame_WhenGainStillInsufficient_ShouldFailSpend() { var sut = new BloodEnergyEconomy(); sut.Add(20f); var spendOk = sut.ProcessFrame(gainAmount: 5f, spendAmount: 40f); Assert.That(spendOk, Is.False); Assert.That(sut.Current, Is.EqualTo(25f).Within(0.0001f)); } [Test] public void WaveClearBonus_ShouldCapAtTwoTimesFixedBonus() { var cfg = new BloodEnergyConfig { FixedWaveBonus = 30f }; var sut = new BloodEnergyEconomy(cfg); Assert.That(sut.CalculateWaveClearBonus(1), Is.EqualTo(30f).Within(0.0001f)); Assert.That(sut.CalculateWaveClearBonus(2), Is.EqualTo(60f).Within(0.0001f)); Assert.That(sut.CalculateWaveClearBonus(10), Is.EqualTo(60f).Within(0.0001f)); } [Test] public void HurtGain_ShouldClampDamageRatioToOne() { var sut = new BloodEnergyEconomy(new BloodEnergyConfig { BaseHurtGain = 12f }); var gain = sut.CalculateHurtGain(damageTaken: 200f, maxHealth: 100f); Assert.That(gain, Is.EqualTo(12f).Within(0.0001f)); } [Test] public void Add_NonPositiveAmount_ShouldNotChangeEnergyOrFireEvent() { var sut = new BloodEnergyEconomy(); var changedCount = 0; sut.OnEnergyChanged += (_, _) => changedCount++; sut.Add(0f); sut.Add(-5f); Assert.That(sut.Current, Is.EqualTo(0f).Within(0.0001f)); Assert.That(changedCount, Is.EqualTo(0)); } [Test] public void Spend_NonPositiveAmount_ShouldFailAndNotChangeEnergy() { var sut = new BloodEnergyEconomy(); sut.Add(10f); var zero = sut.Spend(0f); var negative = sut.Spend(-1f); Assert.That(zero, Is.False); Assert.That(negative, Is.False); Assert.That(sut.Current, Is.EqualTo(10f).Within(0.0001f)); } }