Many more tests and fixes
All checks were successful
Build / Build (push) Successful in 3m12s

This commit is contained in:
2026-08-27 17:11:12 +02:00
parent 32b3ef9c4a
commit d2a82b5fe3
204 changed files with 20255 additions and 242 deletions

View File

@@ -0,0 +1,152 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Static;
using PkmnLib.Static.Utils;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="Moonlight"/> 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."
/// </summary>
public class MoonlightTests
{
/// <summary>
/// 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.
/// </summary>
private static (Moonlight moonlight, IExecutingMove move, IPokemon user) CreateTestSetup(uint maxHp,
string? weatherName)
{
var moonlight = new Moonlight();
var move = Substitute.For<IExecutingMove>();
var battle = Substitute.For<IBattle>();
battle.WeatherName.Returns(weatherName == null ? (StringKey?)null : new StringKey(weatherName));
var battleData = Substitute.For<IPokemonBattleData>();
battleData.Battle.Returns(battle);
var user = Substitute.For<IPokemon>();
user.BattleData.Returns(battleData);
user.BoostedStats.Returns(new StatisticSet<uint>(maxHp, 0, 0, 0, 0, 0));
move.User.Returns(user);
return (moonlight, move, user);
}
/// <summary>
/// Helper to extract the heal amount from the user's received Heal calls.
/// </summary>
private static uint? GetHealAmount(IPokemon user)
{
var call = user.ReceivedCalls().FirstOrDefault(c => c.GetMethodInfo().Name == "Heal");
return call != null ? (uint)call.GetArguments()[0]! : null;
}
/// <summary>
/// Bulbapedia: "During no weather it restores ½ total HP ... rounded down."
/// </summary>
[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);
}
/// <summary>
/// Bulbapedia: "during harsh sunlight it restores ⅔ total HP ... rounded down."
/// </summary>
[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);
}
/// <summary>
/// Bulbapedia: "during other weather it restores ¼ total HP, rounded down." Rain, hail and sandstorm all
/// count as "other weather".
/// </summary>
[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);
}
/// <summary>
/// Bulbapedia (Generation VI onwards): "When used during strong winds it restores ½ total HP, rounded
/// down."
/// </summary>
[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);
}
/// <summary>
/// Bulbapedia: "Moonlight restores the user's current HP" — the heal is applied to the move's user.
/// </summary>
[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();
}
/// <summary>
/// Technical test: without battle data on the user (outside of battle) the secondary effect does nothing
/// and does not throw.
/// </summary>
[Test]
public async Task OnSecondaryEffect_UserHasNoBattleData_DoesNotHeal()
{
// Arrange
var moonlight = new Moonlight();
var move = Substitute.For<IExecutingMove>();
var user = Substitute.For<IPokemon>();
user.BattleData.Returns((IPokemonBattleData?)null);
move.User.Returns(user);
// Act
moonlight.OnSecondaryEffect(move, user, 0);
// Assert
await Assert.That(user.ReceivedCalls().Any(c => c.GetMethodInfo().Name == "Heal")).IsFalse();
}
}