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,134 @@
using PkmnLib.Dynamic.Models;
using PkmnLib.Dynamic.ScriptHandling;
using PkmnLib.Dynamic.ScriptHandling.Registry;
using PkmnLib.Plugin.Gen7.Scripts.Moves;
using PkmnLib.Plugin.Gen7.Scripts.Pokemon;
namespace PkmnLib.Plugin.Gen7.Tests.Scripts.Moves;
/// <summary>
/// Tests for the <see cref="LaserFocus"/> move script and its volatile effect script
/// <see cref="LaserFocusEffect"/>.
/// Gen VII Bulbapedia behavior: "Laser Focus causes the user's move to result in a critical hit until the
/// end of the next turn, unless that move's target has Battle Armor, Shell Armor, or is under the effect
/// of Lucky Chant."
/// </summary>
public class LaserFocusTests
{
/// <summary>
/// Creates a real <see cref="ScriptSet"/> hosting the given effect, so that
/// <see cref="Script.RemoveSelf"/> calls can be observed through <see cref="IScriptSet.Contains"/>.
/// </summary>
private static IScriptSet CreateHostedSet(Script effect)
{
var owner = Substitute.For<IPokemon>();
owner.GetScripts().Returns(_ => new ScriptIterator(Array.Empty<IEnumerable<ScriptContainer>>()));
IScriptSet set = new ScriptSet(owner);
set.Add(effect);
return set;
}
/// <summary>
/// Bulbapedia: "Laser Focus causes the user's move to result in a critical hit" — using the move places
/// the <see cref="LaserFocusEffect"/> on the target (Laser Focus targets the user itself).
/// </summary>
[Test]
public async Task OnSecondaryEffect_Used_AddsLaserFocusEffectToTarget()
{
// Arrange
var script = new LaserFocus();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
var targetVolatile = Substitute.For<IScriptSet>();
target.Volatile.Returns(targetVolatile);
// Act
script.OnSecondaryEffect(move, target, 0);
// Assert
targetVolatile.Received(1).Add(Arg.Any<LaserFocusEffect>());
await Assert.That(targetVolatile.ReceivedCalls().Count(c => c.GetMethodInfo().Name == "Add")).IsEqualTo(1);
}
/// <summary>
/// Bulbapedia: the user's move will "result in a critical hit" — the effect raises the critical stage
/// to at least 3, which the Gen 7 damage calculator treats as a guaranteed critical hit.
/// </summary>
[Test]
public async Task ChangeCriticalStage_EffectActive_GuaranteesCriticalHit()
{
// Arrange
var effect = new LaserFocusEffect();
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
byte stage = 0;
// Act
effect.ChangeCriticalStage(move, target, 0, ref stage);
// Assert
await Assert.That(stage).IsGreaterThanOrEqualTo((byte)3);
}
/// <summary>
/// Bulbapedia: the guarantee lasts "until the end of the next turn" — it is not consumed by a single
/// critical-stage check, so e.g. every hit of a multi-hit move is a critical hit.
/// </summary>
[Test]
public async Task ChangeCriticalStage_AfterFirstCheck_EffectStillActive()
{
// Arrange
var effect = new LaserFocusEffect();
var set = CreateHostedSet(effect);
var move = Substitute.For<IExecutingMove>();
var target = Substitute.For<IPokemon>();
byte stage = 0;
// Act
effect.ChangeCriticalStage(move, target, 0, ref stage);
// Assert
await Assert.That(set.Contains(ScriptUtils.ResolveName<LaserFocusEffect>())).IsTrue();
}
/// <summary>
/// Bulbapedia: the guarantee lasts "until the end of the next turn" — at the end of the turn Laser
/// Focus was used the effect is still active, so the move used on the following turn is a critical hit.
/// </summary>
[Test]
public async Task OnEndTurn_EndOfTurnUsed_EffectStillActive()
{
// Arrange
var effect = new LaserFocusEffect();
var set = CreateHostedSet(effect);
// Act - the end of the turn Laser Focus was used
if (effect is IScriptOnEndTurn onEndTurn)
onEndTurn.OnEndTurn(Substitute.For<IScriptSource>(), Substitute.For<IBattle>());
// Assert
await Assert.That(set.Contains(ScriptUtils.ResolveName<LaserFocusEffect>())).IsTrue();
}
/// <summary>
/// Bulbapedia: the guarantee only lasts "until the end of the next turn" — if the user does not move,
/// the effect expires at the end of the turn after it was used.
/// </summary>
[Test]
public async Task OnEndTurn_EndOfNextTurn_EffectExpires()
{
// Arrange
var effect = new LaserFocusEffect();
var set = CreateHostedSet(effect);
// Act - end of the turn Laser Focus was used, and end of the next turn
if (effect is IScriptOnEndTurn onEndTurn)
{
onEndTurn.OnEndTurn(Substitute.For<IScriptSource>(), Substitute.For<IBattle>());
onEndTurn.OnEndTurn(Substitute.For<IScriptSource>(), Substitute.For<IBattle>());
}
// Assert
await Assert.That(set.Contains(ScriptUtils.ResolveName<LaserFocusEffect>())).IsFalse();
}
}