Files
PkmnLib.NET/Plugins/PkmnLib.Plugin.Gen7.Tests/Scripts/Moves/CraftyShieldTests.cs
Deukhoofd 6a82c20cf2
All checks were successful
Build / Build (push) Successful in 1m57s
More tests, more fixes
2026-07-05 18:26:55 +02:00

119 lines
4.1 KiB
C#

using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Plugin.Gen7.Scripts.Side;
using PkmnLib.Static.Moves;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="CraftyShield"/> move script and the <see cref="CraftyShieldEffect"/> it
/// applies. Gen VII Bulbapedia behavior: "Crafty Shield protects all Pokémon on the user's side of the
/// field from status moves."
/// </summary>
public class CraftyShieldTests
{
private static (CraftyShield script, IExecutingMove move, IScriptSet sideVolatile) CreateTestSetup()
{
var script = new CraftyShield();
var side = Substitute.For<IBattleSide>();
// Use a real script set so the side script added by Crafty Shield can be inspected afterwards.
var sideVolatile = new ScriptSet(side);
side.VolatileScripts.Returns(sideVolatile);
side.GetScripts().Returns(_ => new ScriptIterator(new List<IEnumerable<ScriptContainer>>()));
var battle = Substitute.For<IBattle>();
battle.Sides.Returns(new[] { side });
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
battleData.SideIndex.Returns((byte)0);
var user = Substitute.For<IPokemon>();
user.BattleData.Returns(battleData);
var move = Substitute.For<IExecutingMove>();
move.User.Returns(user);
return (script, move, sideVolatile);
}
private static IExecutingMove CreateExecutingMoveOfCategory(MoveCategory category)
{
var useMove = Substitute.For<IMoveData>();
useMove.Category.Returns(category);
var move = Substitute.For<IExecutingMove>();
move.UseMove.Returns(useMove);
return move;
}
/// <summary>
/// Bulbapedia: "Crafty Shield protects all Pokémon on the user's side of the field" — using the move
/// attaches the <see cref="CraftyShieldEffect"/> to the user's side.
/// </summary>
[Test]
public async Task OnSecondaryEffect_Used_AddsCraftyShieldEffectToUsersSide()
{
// Arrange
var (script, move, sideVolatile) = CreateTestSetup();
// Act
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
// Assert
await Assert.That(sideVolatile.Contains(ScriptUtils.ResolveName<CraftyShieldEffect>())).IsTrue();
}
/// <summary>
/// Bulbapedia: the shield protects "from status moves" — an incoming status move is stopped.
/// </summary>
[Test]
public async Task CraftyShieldEffect_StatusMove_IsStopped()
{
// Arrange
var effect = new CraftyShieldEffect();
var move = CreateExecutingMoveOfCategory(MoveCategory.Status);
var stop = false;
// Act
effect.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsTrue();
}
/// <summary>
/// Bulbapedia: Crafty Shield only blocks status moves — damaging moves go through it unhindered.
/// </summary>
[Test, Arguments(MoveCategory.Physical), Arguments(MoveCategory.Special)]
public async Task CraftyShieldEffect_DamagingMove_IsNotStopped(MoveCategory category)
{
// Arrange
var effect = new CraftyShieldEffect();
var move = CreateExecutingMoveOfCategory(category);
var stop = false;
// Act
effect.StopBeforeMove(move, ref stop);
// Assert
await Assert.That(stop).IsFalse();
}
/// <summary>
/// Technical test: outside of battle (no battle data) no shield can be raised and nothing happens.
/// </summary>
[Test]
public async Task OnSecondaryEffect_NoBattleData_DoesNothing()
{
// Arrange
var (script, move, sideVolatile) = CreateTestSetup();
move.User.BattleData.Returns((IPokemonBattleData?)null);
// Act
script.OnSecondaryEffect(move, Substitute.For<IPokemon>(), 0);
// Assert - no effect is added
await Assert.That(sideVolatile.Contains(ScriptUtils.ResolveName<CraftyShieldEffect>())).IsFalse();
}
}