mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
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:
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user