Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/BugBiteTests.cs
2026-07-05 13:01:12 +02:00

243 lines
9.1 KiB
C#

using PkmnLib.Dynamic.Events;
using PkmnLib.Dynamic.Libraries;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="BugBite"/> move script.
/// Behavior is verified against the Bulbapedia page for Bug Bite.
/// </summary>
public class BugBiteTests
{
private const string BerryEffectName = "test_berry_effect";
/// <summary>
/// Minimal <see cref="ItemScript"/> that records whether its effect was applied through
/// <see cref="ItemScript.OnUse"/>.
/// </summary>
private class RecordingItemScript : ItemScript
{
public RecordingItemScript(IItem item) : base(item)
{
}
public bool WasUsed { get; private set; }
/// <inheritdoc />
public override bool IsItemUsable => true;
/// <inheritdoc />
public override void OnUse(EventHook eventHook) => WasUsed = true;
}
/// <summary>
/// Creates a fully mocked test setup for BugBite tests. The battle's <see cref="ScriptResolver"/> is a
/// real resolver with a single registered item script constructor for <see cref="BerryEffectName"/>, so
/// eating a Berry runs a <see cref="RecordingItemScript"/> that the test can inspect.
/// </summary>
private static (BugBite bugBite, IExecutingMove move, IPokemon target, IHitData hitData, List<RecordingItemScript>
createdItemScripts) CreateTestSetup(IItem? targetHeldItem, bool canSteal = true)
{
var bugBite = new BugBite();
var createdItemScripts = new List<RecordingItemScript>();
var resolver = new ScriptResolver(new Dictionary<(ScriptCategory, StringKey), Func<Script>>(),
new Dictionary<StringKey, Func<IItem, ItemScript>>
{
{
new StringKey(BerryEffectName), item =>
{
var script = new RecordingItemScript(item);
createdItemScripts.Add(script);
return script;
}
},
});
var battle = Substitute.For<IBattle>();
battle.EventHook.Returns(new EventHook());
var dynamicLibrary = Substitute.For<IDynamicLibrary>();
dynamicLibrary.ScriptResolver.Returns(resolver);
battle.Library.Returns(dynamicLibrary);
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
var user = Substitute.For<IPokemon>();
user.BattleData.Returns(battleData);
var move = Substitute.For<IExecutingMove>();
move.User.Returns(user);
move.Battle.Returns(battle);
var target = Substitute.For<IPokemon>();
target.HeldItem.Returns(targetHeldItem);
if (targetHeldItem != null && canSteal)
{
target.TryStealHeldItem(out Arg.Any<IItem?>()).Returns(x =>
{
x[0] = targetHeldItem;
return true;
});
}
else
{
target.TryStealHeldItem(out Arg.Any<IItem?>()).Returns(false);
}
var hitData = Substitute.For<IHitData>();
move.GetHitData(target, 0).Returns(hitData);
return (bugBite, move, target, hitData, createdItemScripts);
}
/// <summary>
/// Creates a mocked Berry item whose battle effect is <see cref="BerryEffectName"/>.
/// </summary>
private static IItem CreateBerry()
{
var berry = Substitute.For<IItem>();
berry.Category.Returns(ItemCategory.Berry);
berry.BattleEffect.Returns(new SecondaryEffectImpl(-1, new StringKey(BerryEffectName),
new Dictionary<StringKey, object?>()));
return berry;
}
/// <summary>
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
/// Eating the Berry removes it from the target via <see cref="IPokemon.ForceSetHeldItem"/>.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsBerry_BerryIsRemovedFromTarget()
{
// Arrange
var (bugBite, move, target, _, _) = CreateTestSetup(CreateBerry());
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(target.ReceivedCalls().Any(c =>
c.GetMethodInfo().Name == "ForceSetHeldItem" && c.GetArguments()[0] == null)).IsTrue();
}
/// <summary>
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
/// The Berry's item script is executed for the user, applying its effect.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsBerry_UserGainsBerryEffect()
{
// Arrange
var (bugBite, move, target, _, createdItemScripts) = CreateTestSetup(CreateBerry());
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert - exactly one berry script was resolved and its effect was applied
await Assert.That(createdItemScripts.Count).IsEqualTo(1);
await Assert.That(createdItemScripts[0].WasUsed).IsTrue();
}
/// <summary>
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
/// When the Berry is successfully eaten, the secondary effect must not be marked as failed.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsBerry_HitDoesNotFail()
{
// Arrange
var (bugBite, move, target, hitData, _) = CreateTestSetup(CreateBerry());
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse();
}
/// <summary>
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
/// A held item that is not a Berry is not eaten: the secondary effect fails and the target keeps
/// its item.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsNonBerryItem_EffectFailsAndItemIsKept()
{
// Arrange
var nonBerry = Substitute.For<IItem>();
nonBerry.Category.Returns(ItemCategory.MiscItem);
var (bugBite, move, target, hitData, _) = CreateTestSetup(nonBerry);
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsTrue();
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ForceSetHeldItem")).IsFalse();
}
/// <summary>
/// Bulbapedia: "If the target is holding a Berry, the user will eat the Berry and gain its effect."
/// With no held item at all there is nothing to eat, so the secondary effect fails.
/// </summary>
[Test]
public async Task OnSecondaryEffect_TargetHoldsNoItem_EffectFails()
{
// Arrange
var (bugBite, move, target, hitData, _) = CreateTestSetup(null);
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsTrue();
}
/// <summary>
/// Bulbapedia: "Bug Bite will not consume the Berry of a target that has the Ability Sticky Hold."
/// When the Berry cannot be stolen (<see cref="IPokemon.TryStealHeldItem"/> returns false, as with
/// Sticky Hold), the target keeps its Berry and the effect fails.
/// </summary>
[Test]
public async Task OnSecondaryEffect_BerryCannotBeStolen_TargetKeepsBerry()
{
// Arrange
var (bugBite, move, target, hitData, createdItemScripts) = CreateTestSetup(CreateBerry(), false);
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert - the berry is not removed, its effect is not gained, and the effect fails
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ForceSetHeldItem")).IsFalse();
await Assert.That(createdItemScripts.Count).IsEqualTo(0);
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsTrue();
}
/// <summary>
/// If the user has no <see cref="IPokemon.BattleData"/>, the script does nothing: no Berry is eaten
/// and the hit is not failed.
/// </summary>
[Test]
public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNothing()
{
// Arrange
var (bugBite, move, target, hitData, _) = CreateTestSetup(CreateBerry());
var user = Substitute.For<IPokemon>();
user.BattleData.Returns((IPokemonBattleData?)null);
move.User.Returns(user);
// Act
bugBite.OnSecondaryEffect(move, target, 0);
// Assert
await Assert.That(target.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "ForceSetHeldItem")).IsFalse();
await Assert.That(hitData.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Fail")).IsFalse();
}
}