Files
SVSimServer/SVSim.BattleEngine.Tests/BattleAmbientTests.cs
gamer147 3b5f2e18b3 refactor(engine-ambient): IsForecast/IsRandomDraw read ambient first, static fallback
Step 2 of multi-instancing migration. Both flags now resolve through
BattleAmbient.Current when a scope is active, otherwise hit a static fallback
that preserves today's behavior unchanged for unwrapped callers.

Suite green: SVSim.BattleEngine.Tests pass; SVSim.UnitTests baseline holds.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-07 21:11:49 -04:00

133 lines
4.1 KiB
C#

#nullable enable
using SVSim.BattleEngine.Ambient;
using NUnit.Framework;
using System.Threading.Tasks;
namespace SVSim.BattleEngine.Tests;
[TestFixture, Parallelizable(ParallelScope.Self)]
public class BattleAmbientTests
{
[Test]
public void Current_IsNull_WhenNoScope()
{
Assert.That(BattleAmbient.Current, Is.Null);
}
[Test]
public void Require_Throws_WhenNoScope()
{
Assert.Throws<System.InvalidOperationException>(() => BattleAmbient.Require());
}
[Test]
public void Enter_SetsCurrent_RestoresOnDispose()
{
var ctx = new BattleAmbientContext { ViewerId = 42 };
Assert.That(BattleAmbient.Current, Is.Null);
using (var _ = BattleAmbient.Enter(ctx))
{
Assert.That(BattleAmbient.Current, Is.SameAs(ctx));
Assert.That(BattleAmbient.Require().ViewerId, Is.EqualTo(42));
}
Assert.That(BattleAmbient.Current, Is.Null);
}
[Test]
public void Enter_Nested_RestoresPriorOnDispose()
{
var outer = new BattleAmbientContext { ViewerId = 1 };
var inner = new BattleAmbientContext { ViewerId = 2 };
using (var _o = BattleAmbient.Enter(outer))
{
Assert.That(BattleAmbient.Current!.ViewerId, Is.EqualTo(1));
using (var _i = BattleAmbient.Enter(inner))
{
Assert.That(BattleAmbient.Current!.ViewerId, Is.EqualTo(2));
}
Assert.That(BattleAmbient.Current!.ViewerId, Is.EqualTo(1));
}
}
[Test]
public async Task Enter_FlowsAcrossAwait()
{
var ctx = new BattleAmbientContext { ViewerId = 99 };
using (var _ = BattleAmbient.Enter(ctx))
{
await Task.Yield();
Assert.That(BattleAmbient.Current, Is.SameAs(ctx));
}
}
[Test]
public async Task Enter_IsolatedBetweenConcurrentTasks()
{
var ctxA = new BattleAmbientContext { ViewerId = 100 };
var ctxB = new BattleAmbientContext { ViewerId = 200 };
var taskA = Task.Run(async () => {
using var _ = BattleAmbient.Enter(ctxA);
await Task.Delay(20);
return BattleAmbient.Current!.ViewerId;
});
var taskB = Task.Run(async () => {
using var _ = BattleAmbient.Enter(ctxB);
await Task.Delay(20);
return BattleAmbient.Current!.ViewerId;
});
var results = await Task.WhenAll(taskA, taskB);
Assert.That(results[0], Is.EqualTo(100));
Assert.That(results[1], Is.EqualTo(200));
}
[Test]
public void IsForecast_ReadsAmbient_WhenScopeActive()
{
var ctx = new BattleAmbientContext { IsForecast = false };
using var _ = BattleAmbient.Enter(ctx);
Assert.That(BattleManagerBase.IsForecast, Is.False);
ctx.IsForecast = true;
Assert.That(BattleManagerBase.IsForecast, Is.True);
}
[Test]
public void IsForecast_WriteInsideScope_WritesAmbient_NotFallback()
{
var ctx = new BattleAmbientContext { IsForecast = false };
using (var _ = BattleAmbient.Enter(ctx))
{
BattleManagerBase.IsForecast = true;
Assert.That(ctx.IsForecast, Is.True);
}
}
[Test]
public void IsForecast_OutsideScope_FallsBackToStatic()
{
Assert.That(BattleAmbient.Current, Is.Null);
BattleManagerBase.IsForecast = true;
Assert.That(BattleManagerBase.IsForecast, Is.True);
BattleManagerBase.IsForecast = false;
Assert.That(BattleManagerBase.IsForecast, Is.False);
}
[Test]
public void IsRandomDraw_RoundtripsAmbient_And_Fallback()
{
Assert.That(BattleAmbient.Current, Is.Null);
BattleManagerBase.IsRandomDraw = true;
Assert.That(BattleManagerBase.IsRandomDraw, Is.True);
var ctx = new BattleAmbientContext { IsRandomDraw = false };
using (var _ = BattleAmbient.Enter(ctx))
{
Assert.That(BattleManagerBase.IsRandomDraw, Is.False);
}
Assert.That(BattleManagerBase.IsRandomDraw, Is.True);
BattleManagerBase.IsRandomDraw = false;
}
}