More unit tests, fixes
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
using PkmnLib.Dynamic.Models;
|
||||
using PkmnLib.Plugin.Gen7.Scripts.Moves;
|
||||
using PkmnLib.Static;
|
||||
|
||||
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the <see cref="BellyDrum"/> move script.
|
||||
/// Gen VII Bulbapedia behavior: "Belly Drum deducts half of the user's maximum HP (rounded down) from its
|
||||
/// current HP and, in return, it maximizes the user's Attack stat by raising it to +6 stages".
|
||||
/// </summary>
|
||||
public class BellyDrumTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a fully mocked test setup for Belly Drum. The target of the secondary effect is the user itself.
|
||||
/// </summary>
|
||||
private static (BellyDrum script, IExecutingMove move, IPokemon user, IHitData hitData) CreateTestSetup(uint maxHp,
|
||||
uint currentHp, sbyte attackBoost = 0)
|
||||
{
|
||||
var script = new BellyDrum();
|
||||
var user = Substitute.For<IPokemon>();
|
||||
user.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 10, 10, 10, 10, 10));
|
||||
user.CurrentHealth.Returns(currentHp);
|
||||
user.StatBoost.Returns(new StatBoostStatisticSet(0, attackBoost, 0, 0, 0, 0));
|
||||
|
||||
var hitData = Substitute.For<IHitData>();
|
||||
var move = Substitute.For<IExecutingMove>();
|
||||
move.GetHitData(user, 0).Returns(hitData);
|
||||
move.User.Returns(user);
|
||||
|
||||
return (script, move, user, hitData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to extract the received Damage call from the user, if any.
|
||||
/// </summary>
|
||||
private static (uint damage, DamageSource source, bool forceDamage)? GetDamageCall(IPokemon user)
|
||||
{
|
||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Damage");
|
||||
if (call == null)
|
||||
return null;
|
||||
var args = call.GetArguments();
|
||||
return ((uint)args[0]!, (DamageSource)args[1]!, (bool)args[3]!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper to extract the received ChangeStatBoost call from the user, if any.
|
||||
/// </summary>
|
||||
private static (Statistic stat, sbyte change, bool selfInflicted)? GetStatBoostCall(IPokemon user)
|
||||
{
|
||||
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
|
||||
if (call == null)
|
||||
return null;
|
||||
var args = call.GetArguments();
|
||||
return ((Statistic)args[0]!, (sbyte)args[1]!, (bool)args[2]!);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Belly Drum deducts half of the user's maximum HP (rounded down) from its current HP".
|
||||
/// Verifies the deduction amount, including the rounding down for odd maximum HP values. The deduction is
|
||||
/// a cost rather than regular damage, so it is forced <see cref="DamageSource.Misc"/> damage.
|
||||
/// </summary>
|
||||
[Test, Arguments(100u, 50u), Arguments(101u, 50u), Arguments(99u, 49u), Arguments(255u, 127u)]
|
||||
public async Task OnSecondaryEffect_SufficientHp_DeductsHalfMaxHpRoundedDown(uint maxHp, uint expectedDeduction)
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, _) = CreateTestSetup(maxHp, maxHp);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
await Assert.That(GetDamageCall(user)).IsEqualTo((expectedDeduction, DamageSource.Misc, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "it maximizes the user's Attack stat by raising it to +6 stages".
|
||||
/// The user receives a self-inflicted Attack boost large enough to reach the +6 cap.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_SufficientHp_MaximizesAttack()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, _) = CreateTestSetup(100, 100);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
var boostCall = GetStatBoostCall(user);
|
||||
await Assert.That(boostCall).IsNotNull();
|
||||
await Assert.That(boostCall!.Value.stat).IsEqualTo(Statistic.Attack);
|
||||
await Assert.That(boostCall.Value.change).IsGreaterThanOrEqualTo((sbyte)6);
|
||||
await Assert.That(boostCall.Value.selfInflicted).IsTrue();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "it maximizes the user's Attack stat by raising it to +6 stages, even if the user's
|
||||
/// temporary Attack bonus stages were below 0 prior to using Belly Drum."
|
||||
/// From -6 stages only a raise of at least 12 stages reaches +6.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_AttackStagesBelowZero_StillRaisedToPlusSix()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, _) = CreateTestSetup(100, 100, -6);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
var boostCall = GetStatBoostCall(user);
|
||||
await Assert.That(boostCall).IsNotNull();
|
||||
await Assert.That(boostCall!.Value.change).IsGreaterThanOrEqualTo((sbyte)12);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Belly Drum fails if the user's current HP is less than half its maximum".
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnSecondaryEffect_CurrentHpBelowHalf_Fails()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, hitData) = CreateTestSetup(100, 49);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Belly Drum fails if the user's current HP is less than half its maximum".
|
||||
/// When the move fails, no HP is deducted and the Attack stat is not changed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_CurrentHpBelowHalf_NoHpDeductedAndNoAttackChange()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, _) = CreateTestSetup(100, 49);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
await Assert.That(GetDamageCall(user)).IsNull();
|
||||
await Assert.That(GetStatBoostCall(user)).IsNull();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Belly Drum fails if the user's current HP is less than half its maximum".
|
||||
/// At exactly half its maximum HP the user cannot pay the cost without fainting (deducting half of the
|
||||
/// maximum HP would reduce it to 0 HP), so the move fails as well.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnSecondaryEffect_CurrentHpExactlyHalf_Fails()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, hitData) = CreateTestSetup(100, 50);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Belly Drum fails if the user's current HP is less than half its maximum, or if the user's
|
||||
/// Attack is already at +6 (even if the user has Contrary)."
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void OnSecondaryEffect_AttackAlreadyAtPlusSix_Fails()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, hitData) = CreateTestSetup(100, 100, 6);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
hitData.Received(1).Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bulbapedia: "Belly Drum fails if [...] the user's Attack is already at +6".
|
||||
/// A failed Belly Drum does not deduct HP: the HP cost only applies when the move works (the only
|
||||
/// exception Bulbapedia lists is a Contrary user already at -6 stages).
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task OnSecondaryEffect_AttackAlreadyAtPlusSix_NoHpDeducted()
|
||||
{
|
||||
// Arrange
|
||||
var (script, move, user, _) = CreateTestSetup(100, 100, 6);
|
||||
|
||||
// Act
|
||||
script.OnSecondaryEffect(move, user, 0);
|
||||
|
||||
// Assert
|
||||
await Assert.That(GetDamageCall(user)).IsNull();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user