fix(runtime): close Campaign LA7b review findings

This commit is contained in:
Erik 2026-08-14 18:55:48 +02:00
parent 0e82cbf700
commit 1b9e7e41f9
11 changed files with 700 additions and 28 deletions

View file

@ -8,6 +8,18 @@ namespace AcDream.Runtime.Tests.Session;
public sealed class LiveSessionControllerTests
{
private sealed class ManualTimeProvider : TimeProvider
{
private long _timestamp;
public override long TimestampFrequency => TimeSpan.TicksPerSecond;
public override long GetTimestamp() => _timestamp;
public void Advance(TimeSpan elapsed) =>
_timestamp += elapsed.Ticks;
}
private sealed class TestTransport : IWorldSessionTransport
{
public void Send(ReadOnlySpan<byte> datagram) { }
@ -466,7 +478,8 @@ public sealed class LiveSessionControllerTests
var calls = new List<string>();
var operations = new TestOperations(calls);
var host = new TestHost(calls);
var controller = new LiveSessionController(operations);
var time = new ManualTimeProvider();
var controller = new LiveSessionController(operations, time);
Assert.Equal(
LiveSessionStartStatus.AwaitingCharacterSelection,
controller.Start(LiveOptions(awaitSelection: true), host).Status);
@ -499,8 +512,21 @@ public sealed class LiveSessionControllerTests
// ACE may send no response for an unknown guid. The synchronous
// command has already returned and does not gate later commands.
Assert.True(controller.Highlight(generation, 0x50000002u).Accepted);
Assert.Equal(
RuntimeCommandStatus.Rejected,
controller.Restore(generation).Status);
controller.Tick();
Assert.Equal(1, operations.TickCount);
time.Advance(RuntimeCharacterSelectionState.RestoreCorrelationTimeout);
controller.Tick();
Assert.Equal(
RuntimeCharacterSelectionOperation.None,
controller.CharacterSelection.Snapshot.Operation);
Assert.True(controller.CharacterSelection.Snapshot.Buttons.CanRestore);
Assert.True(controller.Restore(generation).Accepted);
Assert.Equal(
[0x50000001u, 0x50000002u],
operations.RestoreRequests);
Assert.False(controller.IsInWorld);
}

View file

@ -23,6 +23,18 @@ public sealed class RuntimeCharacterSelectionStateTests
onDelta(delta);
}
private sealed class ManualTimeProvider : TimeProvider
{
private long _timestamp;
public override long TimestampFrequency => TimeSpan.TicksPerSecond;
public override long GetTimestamp() => _timestamp;
public void Advance(TimeSpan elapsed) =>
_timestamp += elapsed.Ticks;
}
[Fact]
public void ApplyRoster_PortsRetailOrderFallbackAndDisabledButtonMatrix()
{
@ -154,9 +166,10 @@ public sealed class RuntimeCharacterSelectionStateTests
}
[Fact]
public void RestoreResponse_FromSupersededRequestCannotCompleteTheCurrentRequest()
public void DelayedFlagOnlyRestoreResponse_CannotCompleteNewerRequest()
{
using var state = new RuntimeCharacterSelectionState();
var time = new ManualTimeProvider();
using var state = new RuntimeCharacterSelectionState(time);
state.Begin(new RuntimeGenerationToken(12));
state.ApplyRoster(Roster(
new(0x50000001u, "First", 1u),
@ -164,18 +177,55 @@ public sealed class RuntimeCharacterSelectionStateTests
Assert.True(state.TryBeginRestore(out _));
Assert.True(state.TryHighlight(0x50000002u));
Assert.False(state.TryBeginRestore(out _));
time.Advance(RuntimeCharacterSelectionState.RestoreCorrelationTimeout);
Assert.True(state.SweepRestoreCorrelation());
Assert.True(state.TryBeginRestore(out _));
long revision = state.Snapshot.Revision;
state.ApplyRestore(new CharacterRestore.Parsed(
1u,
0x50000001u,
"First",
0u));
VerificationFlag: 2u,
Guid: null,
Name: null,
SecondsGreyedOut: null));
Assert.Equal(
RuntimeCharacterSelectionOperation.RestoreRequested,
state.Snapshot.Operation);
Assert.Equal(0x50000002u, state.Snapshot.LastRestoreRequestedCharacterId);
Assert.Equal(revision, state.Snapshot.Revision);
Assert.True(state.View.TryGet(0x50000001u, out var first));
Assert.True(first.IsPendingDelete);
Assert.True(state.View.TryGet(0x50000002u, out var second));
Assert.True(second.IsPendingDelete);
Assert.False(state.Snapshot.Buttons.CanRestore);
}
[Fact]
public void NoReplyRestoreTimeout_ReleasesOnlyRestoreGate()
{
var time = new ManualTimeProvider();
using var state = new RuntimeCharacterSelectionState(time);
state.Begin(new RuntimeGenerationToken(13));
state.ApplyRoster(Roster(
new(0x50000001u, "Pending", 1u),
new(0x50000002u, "Ready", 0u)));
Assert.True(state.TryHighlight(0x50000001u));
Assert.True(state.TryBeginRestore(out _));
Assert.False(state.Snapshot.Buttons.CanRestore);
Assert.True(state.TryHighlight(0x50000002u));
Assert.True(state.BeginEnter(out var ready));
Assert.Equal(0x50000002u, ready.CharacterId);
state.ReturnToSelection();
Assert.True(state.TryHighlight(0x50000001u));
Assert.True(state.TryBeginRestore(out _));
time.Advance(RuntimeCharacterSelectionState.RestoreCorrelationTimeout);
Assert.True(state.SweepRestoreCorrelation());
Assert.Equal(
RuntimeCharacterSelectionOperation.None,
state.Snapshot.Operation);
Assert.True(state.Snapshot.Buttons.CanRestore);
}
[Fact]