using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
///
/// Tests for the move script.
/// Behavior is verified against the Bulbapedia page for Knock Off (Generation VII).
///
public class KnockOffTests
{
///
/// Creates a fully mocked test setup for Knock Off tests. When is true,
/// succeeds and yields a mocked item.
///
private static (KnockOff script, IExecutingMove move, IPokemon target, IPokemon user, IHitData hitData)
CreateTestSetup(bool targetHasItem)
{
var script = new KnockOff();
var move = Substitute.For();
var target = Substitute.For();
var hitData = Substitute.For();
move.GetHitData(target, 0).Returns(hitData);
target.TryStealHeldItem(out Arg.Any()).Returns(callInfo =>
{
callInfo[0] = targetHasItem ? Substitute.For() : null;
return targetHasItem;
});
var user = Substitute.For();
move.User.Returns(user);
return (script, move, target, user, hitData);
}
///
/// Bulbapedia: "Knock Off inflicts damage and knocks off the target's held item if it has one."
/// (Generation V onwards: "Knock Off now removes the held item rather than rendering it unusable".)
///
[Test]
public async Task OnSecondaryEffect_TargetHoldsItem_ItemIsRemoved()
{
// Arrange
var (script, move, target, _, hitData) = CreateTestSetup(true);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
target.Received(1).TryStealHeldItem(out Arg.Any());
hitData.DidNotReceive().Fail();
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "TryStealHeldItem")).IsTrue();
}
///
/// Bulbapedia: "Knock Off inflicts damage and knocks off the target's held item if it has one."
/// Against a target without a held item the move simply deals its damage without any further effect; it
/// does not fail.
///
[Test]
public async Task OnSecondaryEffect_TargetHoldsNoItem_HitIsNotMarkedAsFailed()
{
// Arrange
var (script, move, target, _, hitData) = CreateTestSetup(false);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
hitData.DidNotReceive().Fail();
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse();
}
///
/// Bulbapedia (Generation V onwards): "If the user faints due to the target's Ability (Rough Skin or
/// Iron Barbs) or held Rocky Helmet, it cannot remove the target's held item."
/// Those effects damage the user before the secondary effect runs, so a user that has fainted by then must
/// not remove the item.
///
[Test]
public async Task OnSecondaryEffect_UserIsFainted_ItemIsNotRemoved()
{
// Arrange
var (script, move, target, user, _) = CreateTestSetup(true);
user.IsFainted.Returns(true);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "TryStealHeldItem")).IsFalse();
}
///
/// Bulbapedia (Generation VI onwards): "If Knock Off is used on a Pokémon that is holding an item that can
/// be knocked off, its base power will be boosted by 50%."
/// With Knock Off's base power of 65 the boosted power is 97 (65 × 1.5, fractions dropped).
///
[Test]
public async Task ChangeBasePower_TargetHoldsRemovableItem_BasePowerBoostedByFiftyPercent()
{
// Arrange
var (script, move, target, _, _) = CreateTestSetup(true);
target.HeldItem.Returns(Substitute.For());
ushort basePower = 65;
// Act
((IScriptChangeBasePower)script).ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)97);
}
///
/// Bulbapedia (Generation VI onwards): "If Knock Off is used on a Pokémon that is holding an item that can
/// be knocked off, its base power will be boosted by 50%."
/// A target without a held item does not grant the boost, so the base power stays unchanged.
///
[Test]
public async Task ChangeBasePower_TargetHoldsNoItem_BasePowerUnchanged()
{
// Arrange
var (script, move, target, _, _) = CreateTestSetup(false);
target.HeldItem.Returns((IItem?)null);
ushort basePower = 65;
// Act
((IScriptChangeBasePower)script).ChangeBasePower(move, target, 0, ref basePower);
// Assert
await Assert.That(basePower).IsEqualTo((ushort)65);
}
}