Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/AcupressureTests.cs
Deukhoofd f9878e76ba
All checks were successful
Build / Build (push) Successful in 1m7s
Adds more tests, fixes
2026-07-04 13:03:54 +02:00

160 lines
6.7 KiB
C#

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="Acupressure"/> move script.
/// Gen VII Bulbapedia behavior: "Acupressure chooses one of the target's stats at random and raises it by
/// two stages. It can raise either the target's Attack, Defense, Special Attack, Special Defense, Speed,
/// accuracy, or evasion stat but will not attempt to raise a stat that is already maximized, meaning that
/// the move will fail if all stats are maximized."
/// </summary>
public class AcupressureTests
{
private static (Acupressure script, IExecutingMove move, IPokemon target, IBattleRandom random, IHitData hitData)
CreateTestSetup()
{
var script = new Acupressure();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
var hitData = Substitute.For<IHitData>();
move.GetHitData(target, 0).Returns(hitData);
var random = Substitute.For<IBattleRandom>();
var battle = Substitute.For<IBattle>();
battle.Random.Returns(random);
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
var user = Substitute.For<IPokemon>();
user.BattleData.Returns(battleData);
move.User.Returns(user);
target.StatBoost.Returns(new StatBoostStatisticSet());
return (script, move, target, random, hitData);
}
/// <summary>
/// Helper to extract the stat argument of a ChangeStatBoost call received by the target.
/// </summary>
private static object?[]? GetStatBoostCallArgs(IPokemon target)
{
var call = target.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost");
return call?.GetArguments();
}
/// <summary>
/// Bulbapedia: "Acupressure chooses one of the target's stats at random and raises it by two stages."
/// When the random roll selects Attack, the target's Attack is raised by exactly two stages.
/// </summary>
[Test]
public async Task OnSecondaryEffect_RandomSelectsAttack_RaisesAttackByTwoStages()
{
// Arrange
var (script, move, target, random, _) = CreateTestSetup();
// Attack is the first entry in the pool of raisable stats (HP is excluded).
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(0);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
var args = GetStatBoostCallArgs(target);
await Assert.That(args).IsNotNull();
await Assert.That((Statistic)args![0]!).IsEqualTo(Statistic.Attack);
await Assert.That((sbyte)args[1]!).IsEqualTo((sbyte)2);
}
/// <summary>
/// Bulbapedia: "It can raise either the target's Attack, Defense, Special Attack, Special Defense, Speed,
/// accuracy, or evasion stat" — HP is not one of the raisable stats. When the random roll returns the lowest
/// value of its range, the selected stat must be Attack, not HP.
/// </summary>
[Test]
public async Task OnSecondaryEffect_RandomMinimumRoll_SelectsAttackNotHp()
{
// Arrange
var (script, move, target, random, _) = CreateTestSetup();
// Return the inclusive lower bound of whatever range is requested.
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(ci => ci.ArgAt<int>(0));
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
var args = GetStatBoostCallArgs(target);
await Assert.That(args).IsNotNull();
await Assert.That((Statistic)args![0]!).IsEqualTo(Statistic.Attack);
}
/// <summary>
/// Bulbapedia: "It can raise either the target's Attack, Defense, Special Attack, Special Defense, Speed,
/// accuracy, or evasion stat". Accuracy and evasion must be selectable: when the random roll returns the
/// highest value of its range, the selected stat should be one of the stats beyond Speed in the
/// <see cref="Statistic"/> enum (Evasion or Accuracy).
/// </summary>
[Test]
public async Task OnSecondaryEffect_RandomMaximumRoll_CanSelectAccuracyOrEvasion()
{
// Arrange
var (script, move, target, random, _) = CreateTestSetup();
// Return the highest value of whatever range is requested (upper bound is exclusive).
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(ci => ci.ArgAt<int>(1) - 1);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert - the highest selectable stat should be Evasion or Accuracy
var args = GetStatBoostCallArgs(target);
await Assert.That(args).IsNotNull();
var stat = (Statistic)args![0]!;
await Assert.That(stat is Statistic.Evasion or Statistic.Accuracy).IsTrue();
}
/// <summary>
/// Bulbapedia: "the move will fail if all stats are maximized." All raisable stats (Attack through accuracy;
/// HP boosts cannot be raised by any move and stay at 0) are at +6, so the move must fail without attempting
/// a stat change.
/// </summary>
[Test]
public async Task OnSecondaryEffect_AllRaisableStatsMaximized_Fails()
{
// Arrange
var (script, move, target, _, hitData) = CreateTestSetup();
var statBoost = new StatBoostStatisticSet(0, 6, 6, 6, 6, 6) { Evasion = 6, Accuracy = 6 };
target.StatBoost.Returns(statBoost);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
hitData.Received(1).Fail();
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost")).IsFalse();
}
/// <summary>
/// Bulbapedia: "will not attempt to raise a stat that is already maximized". With the target's Attack at +6
/// and other stats available, Attack must never be the raised stat, regardless of what the random roll
/// returns. The rolls cover every index of the six-stat selection pool that remains.
/// </summary>
[Test, Arguments(0), Arguments(1), Arguments(2), Arguments(3), Arguments(4), Arguments(5)]
public async Task OnSecondaryEffect_StatAlreadyMaximized_NeverRaisesThatStat(int roll)
{
// Arrange
var (script, move, target, random, _) = CreateTestSetup();
var statBoost = new StatBoostStatisticSet(0, 6, 0, 0, 0, 0);
target.StatBoost.Returns(statBoost);
random.GetInt(Arg.Any<int>(), Arg.Any<int>()).Returns(roll);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert - a stat is raised, but never the maximized Attack
var args = GetStatBoostCallArgs(target);
await Assert.That(args).IsNotNull();
await Assert.That((Statistic)args![0]!).IsNotEqualTo(Statistic.Attack);
}
}