test(core/slice-1): full portal+logout event-sequence ordering + no-duplicate-fire coverage

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 20:00:39 +02:00
parent f1b59f3a64
commit c3d6eccf51

View file

@ -341,4 +341,115 @@ public sealed class TeleportAnimSequencerTests
Assert.Equal(TeleportAnimState.TunnelContinue, seq.State); Assert.Equal(TeleportAnimState.TunnelContinue, seq.State);
Assert.True(snap2.ShowPleaseWait); Assert.True(snap2.ShowPleaseWait);
} }
// --- Task 1.5: Full portal event sequence and logout event sequence ---
// Helper: run the sequencer to completion collecting ordered events.
private static List<TeleportAnimEvent> RunToOff(
TeleportEntryKind kind, bool worldReadyAfterFirstTunnel = true, float step = 0.05f)
{
var seq = new TeleportAnimSequencer();
var allEvts = new List<TeleportAnimEvent>();
seq.Begin(kind);
int safetyNet = 5000;
while (seq.IsActive && --safetyNet > 0)
{
// Simulate world becoming ready on first Tick inside Tunnel
bool ready = worldReadyAfterFirstTunnel && seq.State == TeleportAnimState.Tunnel;
var (_, evts) = seq.Tick(step, worldReady: ready);
allEvts.AddRange(evts);
}
return allEvts;
}
[Fact]
public void Portal_FullSequence_EventsInOrder()
{
var evts = RunToOff(TeleportEntryKind.Portal);
// Required events in order: PlayEnterSound, Place, PlayExitSound, FireLoginComplete.
// EnterTunnel is optional but must come before Place if present.
int enterIdx = evts.IndexOf(TeleportAnimEvent.PlayEnterSound);
int placeIdx = evts.IndexOf(TeleportAnimEvent.Place);
int exitIdx = evts.IndexOf(TeleportAnimEvent.PlayExitSound);
int loginIdx = evts.IndexOf(TeleportAnimEvent.FireLoginComplete);
Assert.True(enterIdx >= 0, "PlayEnterSound must fire");
Assert.True(placeIdx >= 0, "Place must fire");
Assert.True(exitIdx >= 0, "PlayExitSound must fire");
Assert.True(loginIdx >= 0, "FireLoginComplete must fire");
Assert.True(enterIdx < placeIdx, "PlayEnterSound must precede Place");
Assert.True(placeIdx < exitIdx, "Place must precede PlayExitSound");
Assert.True(exitIdx < loginIdx, "PlayExitSound must precede FireLoginComplete");
}
[Fact]
public void Logout_FullSequence_EventsInOrder_WithEnterTunnelAfterFades()
{
var evts = RunToOff(TeleportEntryKind.Logout);
int enterIdx = evts.IndexOf(TeleportAnimEvent.PlayEnterSound);
int tunnelIdx = evts.IndexOf(TeleportAnimEvent.EnterTunnel);
int placeIdx = evts.IndexOf(TeleportAnimEvent.Place);
int exitIdx = evts.IndexOf(TeleportAnimEvent.PlayExitSound);
int loginIdx = evts.IndexOf(TeleportAnimEvent.FireLoginComplete);
Assert.True(enterIdx >= 0, "PlayEnterSound must fire");
Assert.True(tunnelIdx >= 0, "EnterTunnel must fire (Logout path goes through WorldFadeOut->TunnelFadeIn->Tunnel)");
Assert.True(placeIdx >= 0, "Place must fire");
Assert.True(exitIdx >= 0, "PlayExitSound must fire");
Assert.True(loginIdx >= 0, "FireLoginComplete must fire");
Assert.True(enterIdx < tunnelIdx, "PlayEnterSound must precede EnterTunnel");
Assert.True(tunnelIdx < placeIdx, "EnterTunnel must precede Place");
Assert.True(placeIdx < exitIdx, "Place must precede PlayExitSound");
Assert.True(exitIdx < loginIdx, "PlayExitSound must precede FireLoginComplete");
}
[Fact]
public void Portal_NoEventsFiredTwice()
{
var evts = RunToOff(TeleportEntryKind.Portal);
Assert.Equal(1, evts.Count(e => e == TeleportAnimEvent.PlayEnterSound));
Assert.Equal(1, evts.Count(e => e == TeleportAnimEvent.Place));
Assert.Equal(1, evts.Count(e => e == TeleportAnimEvent.PlayExitSound));
Assert.Equal(1, evts.Count(e => e == TeleportAnimEvent.FireLoginComplete));
}
[Fact]
public void Death_BehavesIdenticallyToPortal_FullSequence()
{
var portalEvts = RunToOff(TeleportEntryKind.Portal);
var deathEvts = RunToOff(TeleportEntryKind.Death);
// Same event sequence (both enter at Tunnel)
Assert.Equal(portalEvts, deathEvts);
}
[Fact]
public void Login_BehavesIdenticallyToPortal_FullSequence()
{
var portalEvts = RunToOff(TeleportEntryKind.Portal);
var loginEvts = RunToOff(TeleportEntryKind.Login);
Assert.Equal(portalEvts, loginEvts);
}
[Fact]
public void AfterOff_IsActiveIsFalse_AndStateIsOff()
{
RunToOff(TeleportEntryKind.Portal);
var seq = new TeleportAnimSequencer();
seq.Begin(TeleportEntryKind.Portal);
// Drain to Off
for (int i = 0; i < 5000 && seq.IsActive; i++)
{
bool ready = seq.State == TeleportAnimState.Tunnel;
seq.Tick(0.05f, worldReady: ready);
}
Assert.False(seq.IsActive);
Assert.Equal(TeleportAnimState.Off, seq.State);
}
} }