using PkmnLib.Dynamic.Libraries;
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.Models.Choices;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static.Libraries;
using PkmnLib.Static.Moves;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
///
/// Tests for the script, which implements Metronome.
/// Behavior is verified against the Bulbapedia page for Metronome (Generation VII).
///
public class MetronomeTests
{
///
/// Creates a fully mocked test setup for Metronome tests. The user's move library and battle random are
/// mocked so that tests control which move "randomly" rolls.
///
private static (Metronome metronome, IMoveChoice choice, IBattlePokemon user, IReadOnlyMoveLibrary moveLibrary,
IBattleRandom random) CreateTestSetup()
{
var metronome = new Metronome();
var user = Substitute.For();
var choice = Substitute.For();
choice.User.Returns(user);
var battle = Substitute.For();
var random = Substitute.For();
battle.Random.Returns(random);
user.Battle.Returns(battle);
var moveLibrary = Substitute.For();
var staticLibrary = Substitute.For();
staticLibrary.Moves.Returns(moveLibrary);
var dynamicLibrary = Substitute.For();
dynamicLibrary.StaticLibrary.Returns(staticLibrary);
user.Library.Returns(dynamicLibrary);
return (metronome, choice, user, moveLibrary, random);
}
///
/// Creates a mocked with the given name.
///
private static IMoveData CreateMoveData(string name)
{
var moveData = Substitute.For();
moveData.Name.Returns(new StringKey(name));
return moveData;
}
///
/// Bulbapedia: "The Metronome user executes a randomly selected move."
/// The move rolled from the move library replaces the used move name.
///
[Test]
public async Task ChangeMove_SelectableMoveRolled_MoveNameChangesToRolledMove()
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
}
///
/// Bulbapedia: "Metronome cannot select a Max Move, a Z-Move, or any of the moves" in its list of
/// unselectable moves. When an unselectable move is rolled, the script rerolls until it finds a
/// selectable one.
///
[Test]
public async Task ChangeMove_UnselectableMoveRolledFirst_RerollsUntilSelectableMove()
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var protect = CreateMoveData("protect");
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(protect, tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert
await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
}
///
/// Bulbapedia: "Metronome cannot select a Max Move, a Z-Move, or any of the moves" in its list of
/// unselectable moves. A sample of moves that are unselectable in Generation VII (including Metronome
/// itself) is rolled first; the script must skip them and use the next (selectable) roll instead.
///
[Test, Arguments("metronome"), Arguments("protect"), Arguments("detect"), Arguments("endure"), Arguments("counter"),
Arguments("mirror_coat"), Arguments("mirror_move"), Arguments("mimic"), Arguments("sketch"),
Arguments("sleep_talk"), Arguments("struggle"), Arguments("transform"), Arguments("destiny_bond"),
Arguments("focus_punch"), Arguments("helping_hand"), Arguments("assist"), Arguments("copycat"),
Arguments("chatter"), Arguments("after_you"), Arguments("quash"), Arguments("wide_guard"),
Arguments("quick_guard"), Arguments("instruct"), Arguments("spectral_thief"), Arguments("crafty_shield"),
Arguments("snore")]
public async Task ChangeMove_Gen7UnselectableMoveRolled_IsNeverSelected(string unselectableMove)
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var rolled = CreateMoveData(unselectableMove);
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(rolled, tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert - the unselectable move was skipped in favor of the next roll.
await Assert.That(moveName).IsEqualTo(new StringKey("tackle"));
}
///
/// Bulbapedia: "The Metronome user executes a randomly selected move." Moves such as Dig, Fly, and Roar do
/// not appear in the Generation VII column of Bulbapedia's list of moves Metronome cannot select, so
/// Metronome must be able to call them (they are only excluded for other copying moves such as Assist).
///
[Test, Arguments("dig"), Arguments("fly"), Arguments("roar")]
public async Task ChangeMove_MoveSelectableByMetronomeButBannedForAssistRolled_IsSelected(string selectableMove)
{
// Arrange
var (metronome, choice, _, moveLibrary, random) = CreateTestSetup();
var rolled = CreateMoveData(selectableMove);
var tackle = CreateMoveData("tackle");
moveLibrary.GetRandom(random).Returns(rolled, tackle);
var moveName = new StringKey("metronome");
// Act
metronome.ChangeMove(choice, ref moveName);
// Assert - the first roll is selectable by Metronome in Generation VII, so it must be used.
await Assert.That(moveName).IsEqualTo(new StringKey(selectableMove));
}
}