using PkmnLib.Dynamic.Models; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Static; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script. /// Gen VII Bulbapedia behavior ("Generation VII onwards"): "It now raises the user's Attack stat by three /// stages if it causes the targeted Pokémon to faint." /// public class FellStingerTests { /// /// Creates a fully mocked test setup for Fell Stinger tests. /// private static (FellStinger script, IExecutingMove move, IBattlePokemon user, IBattlePokemon target) CreateTestSetup(bool targetFainted) { var script = new FellStinger(); var move = Substitute.For(); var user = Substitute.For(); move.User.Returns(user); var target = Substitute.For(); target.IsFainted.Returns(targetFainted); return (script, move, user, target); } /// /// Helper to find the first ChangeStatBoost call received by a Pokémon substitute. /// private static object[]? GetStatBoostArguments(IBattlePokemon pokemon) { var call = pokemon.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "ChangeStatBoost"); return call?.GetArguments()!; } /// /// Bulbapedia: "It now raises the user's Attack stat by three stages if it causes the targeted Pokémon /// to faint." /// When the target fainted from the hit, the user's Attack stat is boosted. /// [Test] public async Task OnAfterHits_TargetFainted_RaisesUserAttack() { // Arrange var (script, move, user, target) = CreateTestSetup(true); // Act script.OnAfterHits(move, target); // Assert var arguments = GetStatBoostArguments(user); await Assert.That(arguments).IsNotNull(); await Assert.That((Statistic)arguments![0]!).IsEqualTo(Statistic.Attack); } /// /// Bulbapedia: "It now raises the user's Attack stat by three stages if it causes the targeted Pokémon /// to faint." /// In Generation VII the boost is three stages (up from two stages in Generation VI). /// [Test] public async Task OnAfterHits_TargetFainted_AttackRaisedByThreeStages() { // Arrange var (script, move, user, target) = CreateTestSetup(true); // Act script.OnAfterHits(move, target); // Assert var arguments = GetStatBoostArguments(user); await Assert.That(arguments).IsNotNull(); await Assert.That((sbyte)arguments![1]!).IsEqualTo((sbyte)3); } /// /// Bulbapedia: "It now raises the user's Attack stat by three stages if it causes the targeted Pokémon /// to faint." /// The boost is applied by the user to itself, so it must be self-inflicted and not forced. /// [Test] public async Task OnAfterHits_TargetFainted_BoostIsSelfInflictedAndNotForced() { // Arrange var (script, move, user, target) = CreateTestSetup(true); // Act script.OnAfterHits(move, target); // Assert var arguments = GetStatBoostArguments(user); await Assert.That(arguments).IsNotNull(); await Assert.That((bool)arguments![2]!).IsTrue(); await Assert.That((bool)arguments[3]!).IsFalse(); } /// /// Bulbapedia: the Attack boost only happens "if it causes the targeted Pokémon to faint". /// If the target survives the hit, no stat boost is applied. /// [Test] public async Task OnAfterHits_TargetNotFainted_NoStatBoost() { // Arrange var (script, move, user, target) = CreateTestSetup(false); // Act script.OnAfterHits(move, target); // Assert - ChangeStatBoost should never be called. (Checked via ReceivedCalls, as arg matchers cannot // be used with EventBatchId parameters: its parameterless constructor generates a random Guid.) await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ChangeStatBoost")).IsFalse(); } }