fix(channelserver): remove false race in PacketDuringLogout test

The test ran handleMsgMhfSavedata and logoutPlayer concurrently on the
same session, triggering data races on s.playtime and Save(). In
production the dispatch loop processes packets sequentially per session,
so this overlap is impossible. Run the operations sequentially to match
real behavior while still validating no data loss.
This commit is contained in:
Houmgaor
2026-03-01 18:56:52 +01:00
parent 6143902f39
commit 07a587213d

View File

@@ -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")
}