using PkmnLib.Dynamic.Models; using PkmnLib.Dynamic.Models.Choices; using PkmnLib.Dynamic.ScriptHandling; using PkmnLib.Dynamic.ScriptHandling.Registry; using PkmnLib.Plugin.Gen7.Scripts.Moves; using PkmnLib.Plugin.Gen7.Scripts.MoveVolatile; using PkmnLib.Static; namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves; /// /// Tests for the move script and its move volatile. /// Gen VII Bulbapedia behavior: "If the target has not yet used a move, Electrify makes the target's move /// Electric-type for that turn, even if it is a status move." /// public class ElectrifyTests { /// /// Creates a fully mocked test setup where the battle's choice queue is a real /// . When is false, the queue /// only contains another Pokémon's choice, simulating a target that already moved this turn. /// private static (Electrify script, IExecutingMove move, IPokemon target, IScriptSet choiceVolatile, IHitData hitData) CreateTestSetup(bool targetStillHasChoice = true, bool hasQueue = true) { var script = new Electrify(); var move = Substitute.For(); var target = Substitute.For(); var hitData = Substitute.For(); move.GetHitData(target, 0).Returns(hitData); var targetChoice = Substitute.For(); targetChoice.GetScripts().Returns(_ => new ScriptIterator(Array.Empty>())); IScriptSet choiceVolatile = new ScriptSet(targetChoice); targetChoice.Volatile.Returns(choiceVolatile); targetChoice.User.Returns(target); var queue = new BattleChoiceQueue(targetStillHasChoice ? [targetChoice] : [Substitute.For()]); var battle = Substitute.For(); battle.ChoiceQueue.Returns(hasQueue ? queue : null); var battleData = Substitute.For(); battleData.Battle.Returns(battle); target.BattleData.Returns(battleData); return (script, move, target, choiceVolatile, hitData); } /// /// Bulbapedia: "If the target has not yet used a move, Electrify makes the target's move Electric-type /// for that turn" — the is attached to the target's pending move choice. /// [Test] public async Task OnSecondaryEffect_TargetHasNotMovedYet_AddsElectrifyEffectToTargetChoice() { // Arrange var (script, move, target, choiceVolatile, hitData) = CreateTestSetup(); // Act script.OnSecondaryEffect(move, target, 0); // Assert await Assert.That(choiceVolatile.Contains(ScriptUtils.ResolveName())).IsTrue(); hitData.DidNotReceive().Fail(); } /// /// Bulbapedia: the effect only applies "if the target has not yet used a move" — when the target has /// no remaining choice in the queue this turn, the move fails. /// [Test] public void OnSecondaryEffect_TargetAlreadyMoved_FailsHit() { // Arrange var (script, move, target, _, hitData) = CreateTestSetup(false); // Act script.OnSecondaryEffect(move, target, 0); // Assert hitData.Received(1).Fail(); } /// /// Technical test: when the battle has no active choice queue, the script returns without throwing and /// without failing the hit. /// [Test] public void OnSecondaryEffect_NoChoiceQueue_DoesNothing() { // Arrange var (script, move, target, _, hitData) = CreateTestSetup(hasQueue: false); // Act script.OnSecondaryEffect(move, target, 0); // Assert hitData.DidNotReceive().Fail(); } /// /// Bulbapedia: "Electrify makes the target's move Electric-type for that turn, even if it is a status /// move" — the changes the executed move's type to Electric. /// [Test] public async Task ChangeMoveType_ChangesMoveTypeToElectric() { // Arrange var effect = new ElectrifyEffect(); var library = LibraryHelpers.LoadLibrary(); var battle = Substitute.For(); battle.Library.Returns(library); var battleData = Substitute.For(); battleData.Battle.Returns(battle); var target = Substitute.For(); target.BattleData.Returns(battleData); await Assert.That(library.StaticLibrary.Types.TryGetTypeIdentifier("normal", out var normal)).IsTrue(); await Assert.That(library.StaticLibrary.Types.TryGetTypeIdentifier("electric", out var electric)).IsTrue(); TypeIdentifier? moveType = normal; // Act effect.ChangeMoveType(Substitute.For(), target, 0, ref moveType); // Assert await Assert.That(moveType).IsNotNull(); await Assert.That(moveType!.Value).IsEqualTo(electric); } /// /// Technical test: without battle data on the target the move type cannot be resolved and remains /// unchanged. /// [Test] public async Task ChangeMoveType_TargetHasNoBattleData_TypeUnchanged() { // Arrange var effect = new ElectrifyEffect(); var target = Substitute.For(); target.BattleData.Returns((IPokemonBattleData?)null); TypeIdentifier? moveType = null; // Act effect.ChangeMoveType(Substitute.For(), target, 0, ref moveType); // Assert await Assert.That(moveType).IsNull(); } }