acdream/tests/AcDream.Runtime.Tests/Gameplay/RuntimeActionStateTests.cs
Erik b298f99f91 refactor(runtime): own canonical action state
Move selection, combat, and interaction target mode under one Runtime owner; make plugins, retained UI, session routing, and typed runtime views borrow its exact children; and add failure-safe reset, instance isolation, source ownership, and normalized checkpoint coverage without changing retail ordering.

Co-authored-by: Codex <codex@openai.com>
2026-07-26 10:44:09 +02:00

120 lines
4.3 KiB
C#

using AcDream.Core.Combat;
using AcDream.Core.Selection;
using AcDream.Runtime.Gameplay;
namespace AcDream.Runtime.Tests.Gameplay;
public sealed class RuntimeActionStateTests
{
[Fact]
public void ViewProjectsTheExactCanonicalChildrenAndRevisions()
{
using var actions = new RuntimeActionState();
actions.Selection.Select(
0x50000001u,
SelectionChangeSource.World);
actions.Combat.SetCombatMode(CombatMode.Missile);
actions.Combat.OnUpdateHealth(0x50000001u, 0.625f);
actions.Interaction.EnterUseItemOnTarget(0x70000001u);
RuntimeActionSnapshot snapshot = actions.View.Snapshot;
Assert.Equal(1, snapshot.SelectionRevision);
Assert.Equal(0x50000001u, snapshot.SelectedObjectId);
Assert.Equal(2, snapshot.CombatRevision);
Assert.Equal(CombatMode.Missile, snapshot.CombatMode);
Assert.Equal(1, snapshot.TrackedTargetHealthCount);
Assert.Equal(1, snapshot.InteractionRevision);
Assert.Equal(
InteractionModeKind.UseItemOnTarget,
snapshot.InteractionMode);
Assert.Equal(0x70000001u, snapshot.InteractionSourceObjectId);
Assert.True(
actions.View.TryGetHealth(0x50000001u, out float health));
Assert.Equal(0.625f, health);
}
[Fact]
public void ResetSessionConvergesEveryChildAfterObserverFailures()
{
var actions = new RuntimeActionState();
actions.Selection.Select(
0x50000001u,
SelectionChangeSource.World);
actions.Combat.SetCombatMode(CombatMode.Melee);
actions.Combat.OnUpdateHealth(0x50000001u, 0.25f);
actions.Interaction.EnterExamine();
actions.Selection.Changed += static _ =>
throw new InvalidOperationException("selection observer");
actions.Combat.CombatModeChanged += static _ =>
throw new InvalidOperationException("combat observer");
actions.Interaction.Changed += static _ =>
throw new InvalidOperationException("interaction observer");
Assert.Throws<AggregateException>(actions.ResetSession);
RuntimeActionOwnershipSnapshot snapshot =
actions.CaptureOwnership();
Assert.Equal(0u, snapshot.SelectedObjectId);
Assert.Equal(0u, snapshot.PreviousObjectId);
Assert.Equal(0u, snapshot.PreviousValidObjectId);
Assert.Equal(CombatMode.NonCombat, snapshot.CombatMode);
Assert.Equal(0, snapshot.TrackedTargetHealthCount);
Assert.Equal(InteractionMode.None, snapshot.InteractionMode);
Assert.Throws<AggregateException>(actions.Dispose);
}
[Fact]
public void DisposeIsTerminalAndConvergedAfterObserverFailures()
{
var actions = new RuntimeActionState();
actions.Selection.Select(
0x50000001u,
SelectionChangeSource.World);
actions.Combat.SetCombatMode(CombatMode.Magic);
actions.Combat.OnUpdateHealth(0x50000001u, 0.5f);
actions.Interaction.EnterUse();
actions.Selection.Changed += static _ =>
throw new InvalidOperationException("selection observer");
actions.Combat.CombatModeChanged += static _ =>
throw new InvalidOperationException("combat observer");
actions.Interaction.Changed += static _ =>
throw new InvalidOperationException("interaction observer");
Assert.Throws<AggregateException>(actions.Dispose);
RuntimeActionOwnershipSnapshot snapshot =
actions.CaptureOwnership();
Assert.True(snapshot.IsConverged);
actions.Dispose();
}
[Fact]
public void InstancesAreFullyIsolated()
{
using var first = new RuntimeActionState();
using var second = new RuntimeActionState();
first.Selection.Select(
0x50000001u,
SelectionChangeSource.World);
first.Combat.SetCombatMode(CombatMode.Missile);
first.Interaction.EnterUse();
Assert.Equal(
new RuntimeActionSnapshot(
0,
0u,
0u,
0u,
0,
CombatMode.NonCombat,
0,
0,
InteractionModeKind.None,
0u),
second.View.Snapshot);
}
}