using PkmnLib.Dynamic.Models;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
///
/// Tests for the move script.
/// Gen VII Bulbapedia behavior: "Moonlight restores the user's current HP based on the weather in the battle.
/// During no weather it restores ½ total HP, during harsh sunlight it restores ⅔ total HP, and during other
/// weather it restores ¼ total HP, rounded down." Generation VI onwards: "When used during strong winds it
/// restores ½ total HP, rounded down."
///
public class MoonlightTests
{
///
/// Creates a fully mocked test setup for Moonlight tests. Moonlight targets the user itself, so the
/// returned Pokémon is used as both the move's user and the target of the secondary effect.
///
private static (Moonlight moonlight, IExecutingMove move, IBattlePokemon user) CreateTestSetup(uint maxHp,
string? weatherName)
{
var moonlight = new Moonlight();
var move = Substitute.For();
var battle = Substitute.For();
battle.WeatherName.Returns(weatherName == null ? (StringKey?)null : new StringKey(weatherName));
var user = Substitute.For();
user.BoostedStats.Returns(new StatisticSet(maxHp, 0, 0, 0, 0, 0));
user.Battle.Returns(battle);
move.User.Returns(user);
return (moonlight, move, user);
}
///
/// Helper to extract the heal amount from the user's received Heal calls.
///
private static uint? GetHealAmount(IBattlePokemon user)
{
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Heal");
return call != null ? (uint)call.GetArguments()[0]! : null;
}
///
/// Bulbapedia: "During no weather it restores ½ total HP ... rounded down."
///
[Test, Arguments(100u, 50u), Arguments(99u, 49u), Arguments(255u, 127u)]
public async Task OnSecondaryEffect_NoWeather_HealsHalfMaxHp(uint maxHp, uint expectedHeal)
{
// Arrange
var (moonlight, move, user) = CreateTestSetup(maxHp, null);
// Act
moonlight.OnSecondaryEffect(move, user, 0);
// Assert
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(expectedHeal);
}
///
/// Bulbapedia: "during harsh sunlight it restores ⅔ total HP ... rounded down."
///
[Test, Arguments(100u, 66u), Arguments(99u, 66u), Arguments(300u, 200u)]
public async Task OnSecondaryEffect_HarshSunlight_HealsTwoThirdsMaxHp(uint maxHp, uint expectedHeal)
{
// Arrange
var (moonlight, move, user) = CreateTestSetup(maxHp, "harsh_sunlight");
// Act
moonlight.OnSecondaryEffect(move, user, 0);
// Assert
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(expectedHeal);
}
///
/// Bulbapedia: "during other weather it restores ¼ total HP, rounded down." Rain, hail and sandstorm all
/// count as "other weather".
///
[Test, Arguments("rain", 100u, 25u), Arguments("hail", 100u, 25u), Arguments("sandstorm", 100u, 25u),
Arguments("rain", 103u, 25u), Arguments("sandstorm", 7u, 1u)]
public async Task OnSecondaryEffect_OtherWeather_HealsQuarterMaxHp(string weather, uint maxHp, uint expectedHeal)
{
// Arrange
var (moonlight, move, user) = CreateTestSetup(maxHp, weather);
// Act
moonlight.OnSecondaryEffect(move, user, 0);
// Assert
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(expectedHeal);
}
///
/// Bulbapedia (Generation VI onwards): "When used during strong winds it restores ½ total HP, rounded
/// down."
///
[Test, Arguments(100u, 50u), Arguments(99u, 49u)]
public async Task OnSecondaryEffect_StrongWinds_HealsHalfMaxHp(uint maxHp, uint expectedHeal)
{
// Arrange
var (moonlight, move, user) = CreateTestSetup(maxHp, "strong_winds");
// Act
moonlight.OnSecondaryEffect(move, user, 0);
// Assert
await Assert.That(GetHealAmount(user)!.Value).IsEqualTo(expectedHeal);
}
///
/// Bulbapedia: "Moonlight restores the user's current HP" — the heal is applied to the move's user.
///
[Test]
public async Task OnSecondaryEffect_NoWeather_HealsTheUser()
{
// Arrange
var (moonlight, move, user) = CreateTestSetup(100, null);
// Act
moonlight.OnSecondaryEffect(move, user, 0);
// Assert
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Heal")).IsTrue();
}
}