diff --git a/server/channelserver/client_connection_simulation_test.go b/server/channelserver/client_connection_simulation_test.go index 49fa00ffa..1bb69de2a 100644 --- a/server/channelserver/client_connection_simulation_test.go +++ b/server/channelserver/client_connection_simulation_test.go @@ -515,8 +515,10 @@ func TestClientConnection_ReconnectAfterCrash(t *testing.T) { logoutPlayer(session2) } -// TestClientConnection_PacketDuringLogout tests race condition -// What happens if save packet arrives during logout? +// TestClientConnection_PacketDuringLogout tests that a save followed by +// logout produces valid, non-corrupted data. In production the dispatch +// loop processes packets sequentially per session, so these two operations +// can never truly overlap — we test them in the same order here. func TestClientConnection_PacketDuringLogout(t *testing.T) { db := SetupTestDB(t) defer TeardownTestDB(t, db) @@ -527,7 +529,7 @@ func TestClientConnection_PacketDuringLogout(t *testing.T) { userID := CreateTestUser(t, db, "race_user") charID := CreateTestCharacter(t, db, userID, "RaceChar") - t.Log("Testing race condition: packet during logout") + t.Log("Testing save-then-logout sequence") session := createTestSessionForServerWithChar(server, charID, "RaceChar") // Note: Not calling Start() - testing handlers directly @@ -547,26 +549,12 @@ func TestClientConnection_PacketDuringLogout(t *testing.T) { RawDataPayload: compressed, } - var wg sync.WaitGroup - wg.Add(2) + // Process save then logout sequentially, matching production dispatch order + handleMsgMhfSavedata(session, savePkt) + t.Log("Save packet processed") - // Goroutine 1: Send save packet - go func() { - defer wg.Done() - handleMsgMhfSavedata(session, savePkt) - t.Log("Save packet processed") - }() - - // Goroutine 2: Trigger logout (almost) simultaneously - go func() { - defer wg.Done() - time.Sleep(10 * time.Millisecond) // Small delay - logoutPlayer(session) - t.Log("Logout processed") - }() - - wg.Wait() - time.Sleep(100 * time.Millisecond) + logoutPlayer(session) + t.Log("Logout processed") // Verify final state var savedCompressed []byte @@ -576,7 +564,7 @@ func TestClientConnection_PacketDuringLogout(t *testing.T) { } if len(savedCompressed) == 0 { - t.Fatal("Race condition caused data loss - no savedata in DB") + t.Fatal("No savedata in DB after save+logout sequence") } decompressed, err := nullcomp.Decompress(savedCompressed) @@ -587,12 +575,5 @@ func TestClientConnection_PacketDuringLogout(t *testing.T) { t.Fatalf("Decompressed data too short (%d bytes), expected at least 15000", len(decompressed)) } - // Both outcomes are valid: either the save handler wrote last (0xCC preserved) - // or the logout handler wrote last (0xCC overwritten with the logout's fresh - // DB read). The important thing is no crash, no data loss, and valid data. - if decompressed[14000] == 0xCC { - t.Log("Race outcome: save handler wrote last - marker byte preserved") - } else { - t.Log("Race outcome: logout handler wrote last - marker byte overwritten (valid)") - } + t.Log("Save-then-logout sequence completed with valid data") }