diff --git a/server/channelserver/handlers_achievement_test.go b/server/channelserver/handlers_achievement_test.go index 195ece2f6..70ef65656 100644 --- a/server/channelserver/handlers_achievement_test.go +++ b/server/channelserver/handlers_achievement_test.go @@ -597,3 +597,64 @@ func TestHandleMsgMhfAddAchievement_BoundaryID32(t *testing.T) { t.Errorf("IncrementScore called with ID %d, want 32", mock.incrementedID) } } + +func TestHandleMsgMhfSetCaAchievementHist_Response(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfSetCaAchievementHist{ + AckHandle: 44444, + } + + handleMsgMhfSetCaAchievementHist(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +// Tests consolidated from handlers_coverage3_test.go + +func TestEmptyHandlers_AchievementGo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgMhfDisplayedAchievement", func() { + handleMsgMhfDisplayedAchievement(session, &mhfpacket.MsgMhfDisplayedAchievement{}) + }}, + {"handleMsgMhfGetCaAchievementHist", func() { handleMsgMhfGetCaAchievementHist(session, nil) }}, + {"handleMsgMhfSetCaAchievement", func() { handleMsgMhfSetCaAchievement(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} + +func TestEmptyHandlers_MiscFiles_Achievement(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfPaymentAchievement panicked: %v", r) + } + }() + handleMsgMhfPaymentAchievement(session, nil) +} diff --git a/server/channelserver/handlers_cafe_coverage_test.go b/server/channelserver/handlers_cafe_coverage_test.go deleted file mode 100644 index 08db261c9..000000000 --- a/server/channelserver/handlers_cafe_coverage_test.go +++ /dev/null @@ -1,213 +0,0 @@ -package channelserver - -import ( - "erupe-ce/common/mhfcourse" - "erupe-ce/network/mhfpacket" - "errors" - "testing" - "time" -) - -// --- Cafe Duration Bonus Info tests --- - -func TestHandleMsgMhfGetCafeDurationBonusInfo_Error(t *testing.T) { - srv := createMockServer() - srv.cafeRepo = &mockCafeRepo{bonusesErr: errors.New("db error")} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetCafeDurationBonusInfo{AckHandle: 1} - handleMsgMhfGetCafeDurationBonusInfo(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfGetCafeDurationBonusInfo_WithBonuses(t *testing.T) { - srv := createMockServer() - srv.cafeRepo = &mockCafeRepo{ - bonuses: []CafeBonus{ - {ID: 1, TimeReq: 100, ItemType: 5, ItemID: 10, Quantity: 2, Claimed: false}, - {ID: 2, TimeReq: 200, ItemType: 6, ItemID: 20, Quantity: 1, Claimed: true}, - }, - } - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetCafeDurationBonusInfo{AckHandle: 1} - handleMsgMhfGetCafeDurationBonusInfo(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -// --- Receive Cafe Duration Bonus tests --- - -func TestHandleMsgMhfReceiveCafeDurationBonus_Error(t *testing.T) { - srv := createMockServer() - srv.cafeRepo = &mockCafeRepo{claimableErr: errors.New("db error")} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfReceiveCafeDurationBonus{AckHandle: 1} - handleMsgMhfReceiveCafeDurationBonus(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfReceiveCafeDurationBonus_WithClaimable(t *testing.T) { - srv := createMockServer() - srv.cafeRepo = &mockCafeRepo{ - claimable: []CafeBonus{ - {ID: 1, ItemType: 5, ItemID: 10, Quantity: 2}, - }, - } - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - // Course 30 is required for claimable items - s.courses = []mhfcourse.Course{{ID: 30}} - - pkt := &mhfpacket.MsgMhfReceiveCafeDurationBonus{AckHandle: 1} - handleMsgMhfReceiveCafeDurationBonus(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -// --- Post Cafe Duration Bonus Received tests --- - -func TestHandleMsgMhfPostCafeDurationBonusReceived_Empty(t *testing.T) { - srv := createMockServer() - srv.cafeRepo = &mockCafeRepo{} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostCafeDurationBonusReceived{AckHandle: 1, CafeBonusID: []uint32{}} - handleMsgMhfPostCafeDurationBonusReceived(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfPostCafeDurationBonusReceived_WithBonusIDs(t *testing.T) { - srv := createMockServer() - srv.cafeRepo = &mockCafeRepo{ - bonusItemType: 17, // netcafe point type - bonusItemQty: 100, - } - charRepo := newMockCharacterRepo() - charRepo.ints["netcafe_points"] = 50 - srv.charRepo = charRepo - s := createMockSession(100, srv) - srv.erupeConfig.GameplayOptions.MaximumNP = 99999 - - pkt := &mhfpacket.MsgMhfPostCafeDurationBonusReceived{AckHandle: 1, CafeBonusID: []uint32{1, 2}} - handleMsgMhfPostCafeDurationBonusReceived(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -// --- Daily Cafe Point tests --- - -func TestHandleMsgMhfCheckDailyCafepoint_Eligible(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - // Set daily_time far in the past so midday.After(dailyTime) is true - charRepo.times["daily_time"] = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) - charRepo.ints["netcafe_points"] = 10 - srv.charRepo = charRepo - srv.erupeConfig.GameplayOptions.MaximumNP = 99999 - srv.erupeConfig.GameplayOptions.BonusQuestAllowance = 10 - srv.erupeConfig.GameplayOptions.DailyQuestAllowance = 5 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfCheckDailyCafepoint{AckHandle: 1} - handleMsgMhfCheckDailyCafepoint(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfCheckDailyCafepoint_NotEligible(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - // Set daily_time far in the future so midday.After(dailyTime) is false - charRepo.times["daily_time"] = time.Date(2099, 12, 31, 23, 59, 59, 0, time.UTC) - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfCheckDailyCafepoint{AckHandle: 1} - handleMsgMhfCheckDailyCafepoint(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -// --- Cafe Duration tests --- - -func TestHandleMsgMhfGetCafeDuration(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - // cafe_reset in the future so we don't trigger reset logic - charRepo.times["cafe_reset"] = time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC) - charRepo.ints["cafe_time"] = 3600 - srv.charRepo = charRepo - srv.cafeRepo = &mockCafeRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} - handleMsgMhfGetCafeDuration(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} diff --git a/server/channelserver/handlers_cafe_extra_coverage_test.go b/server/channelserver/handlers_cafe_extra_coverage_test.go deleted file mode 100644 index 62caad8d3..000000000 --- a/server/channelserver/handlers_cafe_extra_coverage_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package channelserver - -import ( - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" - "testing" - "time" -) - -func TestHandleMsgMhfGetCafeDuration_ResetPath(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - // cafe_reset in the past to trigger reset logic - charRepo.times["cafe_reset"] = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) - charRepo.ints["cafe_time"] = 1800 - srv.charRepo = charRepo - srv.cafeRepo = &mockCafeRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} - handleMsgMhfGetCafeDuration(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetCafeDuration_NoResetTime(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - // No cafe_reset set → ReadTime returns error → sets new reset time - charRepo.ints["cafe_time"] = 100 - srv.charRepo = charRepo - srv.cafeRepo = &mockCafeRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} - handleMsgMhfGetCafeDuration(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetCafeDuration_ZZClient(t *testing.T) { - srv := createMockServer() - srv.erupeConfig.RealClientMode = cfg.ZZ - charRepo := newMockCharacterRepo() - charRepo.times["cafe_reset"] = time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC) - charRepo.ints["cafe_time"] = 3600 - srv.charRepo = charRepo - srv.cafeRepo = &mockCafeRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} - handleMsgMhfGetCafeDuration(s, pkt) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_cafe_test.go b/server/channelserver/handlers_cafe_test.go index b00413708..0e99cb765 100644 --- a/server/channelserver/handlers_cafe_test.go +++ b/server/channelserver/handlers_cafe_test.go @@ -1,10 +1,12 @@ package channelserver import ( + cfg "erupe-ce/config" + "erupe-ce/common/mhfcourse" + "erupe-ce/network/mhfpacket" + "errors" "testing" "time" - - "erupe-ce/network/mhfpacket" ) func TestHandleMsgMhfGetBoostTime(t *testing.T) { @@ -334,3 +336,251 @@ func TestHandleMsgMhfGetBoostRight_NoRecord(t *testing.T) { t.Error("No response packet queued") } } + +// --- Cafe Duration Bonus Info tests --- + +func TestHandleMsgMhfGetCafeDurationBonusInfo_Error(t *testing.T) { + srv := createMockServer() + srv.cafeRepo = &mockCafeRepo{bonusesErr: errors.New("db error")} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetCafeDurationBonusInfo{AckHandle: 1} + handleMsgMhfGetCafeDurationBonusInfo(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetCafeDurationBonusInfo_WithBonuses(t *testing.T) { + srv := createMockServer() + srv.cafeRepo = &mockCafeRepo{ + bonuses: []CafeBonus{ + {ID: 1, TimeReq: 100, ItemType: 5, ItemID: 10, Quantity: 2, Claimed: false}, + {ID: 2, TimeReq: 200, ItemType: 6, ItemID: 20, Quantity: 1, Claimed: true}, + }, + } + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetCafeDurationBonusInfo{AckHandle: 1} + handleMsgMhfGetCafeDurationBonusInfo(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +// --- Receive Cafe Duration Bonus tests --- + +func TestHandleMsgMhfReceiveCafeDurationBonus_Error(t *testing.T) { + srv := createMockServer() + srv.cafeRepo = &mockCafeRepo{claimableErr: errors.New("db error")} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfReceiveCafeDurationBonus{AckHandle: 1} + handleMsgMhfReceiveCafeDurationBonus(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfReceiveCafeDurationBonus_WithClaimable(t *testing.T) { + srv := createMockServer() + srv.cafeRepo = &mockCafeRepo{ + claimable: []CafeBonus{ + {ID: 1, ItemType: 5, ItemID: 10, Quantity: 2}, + }, + } + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + // Course 30 is required for claimable items + s.courses = []mhfcourse.Course{{ID: 30}} + + pkt := &mhfpacket.MsgMhfReceiveCafeDurationBonus{AckHandle: 1} + handleMsgMhfReceiveCafeDurationBonus(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +// --- Post Cafe Duration Bonus Received tests --- + +func TestHandleMsgMhfPostCafeDurationBonusReceived_Empty(t *testing.T) { + srv := createMockServer() + srv.cafeRepo = &mockCafeRepo{} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostCafeDurationBonusReceived{AckHandle: 1, CafeBonusID: []uint32{}} + handleMsgMhfPostCafeDurationBonusReceived(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfPostCafeDurationBonusReceived_WithBonusIDs(t *testing.T) { + srv := createMockServer() + srv.cafeRepo = &mockCafeRepo{ + bonusItemType: 17, // netcafe point type + bonusItemQty: 100, + } + charRepo := newMockCharacterRepo() + charRepo.ints["netcafe_points"] = 50 + srv.charRepo = charRepo + s := createMockSession(100, srv) + srv.erupeConfig.GameplayOptions.MaximumNP = 99999 + + pkt := &mhfpacket.MsgMhfPostCafeDurationBonusReceived{AckHandle: 1, CafeBonusID: []uint32{1, 2}} + handleMsgMhfPostCafeDurationBonusReceived(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +// --- Daily Cafe Point tests --- + +func TestHandleMsgMhfCheckDailyCafepoint_Eligible(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + // Set daily_time far in the past so midday.After(dailyTime) is true + charRepo.times["daily_time"] = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) + charRepo.ints["netcafe_points"] = 10 + srv.charRepo = charRepo + srv.erupeConfig.GameplayOptions.MaximumNP = 99999 + srv.erupeConfig.GameplayOptions.BonusQuestAllowance = 10 + srv.erupeConfig.GameplayOptions.DailyQuestAllowance = 5 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfCheckDailyCafepoint{AckHandle: 1} + handleMsgMhfCheckDailyCafepoint(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfCheckDailyCafepoint_NotEligible(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + // Set daily_time far in the future so midday.After(dailyTime) is false + charRepo.times["daily_time"] = time.Date(2099, 12, 31, 23, 59, 59, 0, time.UTC) + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfCheckDailyCafepoint{AckHandle: 1} + handleMsgMhfCheckDailyCafepoint(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +// --- Cafe Duration tests --- + +func TestHandleMsgMhfGetCafeDuration(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + // cafe_reset in the future so we don't trigger reset logic + charRepo.times["cafe_reset"] = time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC) + charRepo.ints["cafe_time"] = 3600 + srv.charRepo = charRepo + srv.cafeRepo = &mockCafeRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} + handleMsgMhfGetCafeDuration(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetCafeDuration_ResetPath(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + // cafe_reset in the past to trigger reset logic + charRepo.times["cafe_reset"] = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC) + charRepo.ints["cafe_time"] = 1800 + srv.charRepo = charRepo + srv.cafeRepo = &mockCafeRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} + handleMsgMhfGetCafeDuration(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetCafeDuration_NoResetTime(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + // No cafe_reset set -> ReadTime returns error -> sets new reset time + charRepo.ints["cafe_time"] = 100 + srv.charRepo = charRepo + srv.cafeRepo = &mockCafeRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} + handleMsgMhfGetCafeDuration(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetCafeDuration_ZZClient(t *testing.T) { + srv := createMockServer() + srv.erupeConfig.RealClientMode = cfg.ZZ + charRepo := newMockCharacterRepo() + charRepo.times["cafe_reset"] = time.Date(2099, 12, 31, 0, 0, 0, 0, time.UTC) + charRepo.ints["cafe_time"] = 3600 + srv.charRepo = charRepo + srv.cafeRepo = &mockCafeRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetCafeDuration{AckHandle: 1} + handleMsgMhfGetCafeDuration(s, pkt) + <-s.sendPackets +} diff --git a/server/channelserver/handlers_campaign_test.go b/server/channelserver/handlers_campaign_test.go index 152e054b8..f964109ac 100644 --- a/server/channelserver/handlers_campaign_test.go +++ b/server/channelserver/handlers_campaign_test.go @@ -68,3 +68,45 @@ func TestHandleMsgMhfApplyCampaign(t *testing.T) { t.Error("No response packet queued") } } + +// Tests consolidated from handlers_core_test.go + +func TestHandleMsgMhfEnumerateItem(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateItem{ + AckHandle: 12345, + } + + handleMsgMhfEnumerateItem(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfAcquireItem(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfAcquireItem{ + AckHandle: 12345, + } + + handleMsgMhfAcquireItem(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} diff --git a/server/channelserver/handlers_caravan_test.go b/server/channelserver/handlers_caravan_test.go index 67c59a70f..fdd354276 100644 --- a/server/channelserver/handlers_caravan_test.go +++ b/server/channelserver/handlers_caravan_test.go @@ -120,6 +120,54 @@ func TestHandleMsgMhfCaravanRanking(t *testing.T) { } } +// Tests consolidated from handlers_coverage3_test.go + +func TestNonTrivialHandlers_CaravanGo(t *testing.T) { + server := createMockServer() + + tests := []struct { + name string + fn func(s *Session) + }{ + {"handleMsgMhfGetRyoudama", func(s *Session) { + handleMsgMhfGetRyoudama(s, &mhfpacket.MsgMhfGetRyoudama{AckHandle: 1}) + }}, + {"handleMsgMhfGetTinyBin", func(s *Session) { + handleMsgMhfGetTinyBin(s, &mhfpacket.MsgMhfGetTinyBin{AckHandle: 1}) + }}, + {"handleMsgMhfPostTinyBin", func(s *Session) { + handleMsgMhfPostTinyBin(s, &mhfpacket.MsgMhfPostTinyBin{AckHandle: 1}) + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + session := createMockSession(1, server) + tt.fn(session) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("%s: response should have data", tt.name) + } + default: + t.Errorf("%s: no response queued", tt.name) + } + }) + } +} + +func TestEmptyHandlers_MiscFiles_Caravan(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfPostRyoudama panicked: %v", r) + } + }() + handleMsgMhfPostRyoudama(session, nil) +} + func TestHandleMsgMhfCaravanMyRank(t *testing.T) { server := createMockServer() session := createMockSession(1, server) diff --git a/server/channelserver/handlers_cast_binary_test.go b/server/channelserver/handlers_cast_binary_test.go index 38c35c9ba..8c642357a 100644 --- a/server/channelserver/handlers_cast_binary_test.go +++ b/server/channelserver/handlers_cast_binary_test.go @@ -711,3 +711,15 @@ func BenchmarkSlicesIndexFunc(b *testing.B) { _ = slices.IndexFunc(courses, predicate) } } + +func TestEmptyCastedBinaryHandler(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysCastedBinary panicked: %v", r) + } + }() + handleMsgSysCastedBinary(session, nil) +} diff --git a/server/channelserver/handlers_clients_test.go b/server/channelserver/handlers_clients_test.go index 236695788..8b6284155 100644 --- a/server/channelserver/handlers_clients_test.go +++ b/server/channelserver/handlers_clients_test.go @@ -541,6 +541,32 @@ func TestOprMember_EdgeCases_Integration(t *testing.T) { } } +// Tests consolidated from handlers_coverage3_test.go + +func TestEmptyHandlers_ClientsGo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgMhfShutClient", func() { handleMsgMhfShutClient(session, nil) }}, + {"handleMsgSysHideClient", func() { handleMsgSysHideClient(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} + // BenchmarkEnumerateClients benchmarks client enumeration func BenchmarkEnumerateClients(b *testing.B) { logger, _ := zap.NewDevelopment() diff --git a/server/channelserver/handlers_core_test.go b/server/channelserver/handlers_core_test.go deleted file mode 100644 index 94d241a57..000000000 --- a/server/channelserver/handlers_core_test.go +++ /dev/null @@ -1,698 +0,0 @@ -package channelserver - -import ( - "testing" - - "erupe-ce/common/byteframe" - "erupe-ce/network/mhfpacket" -) - -// Test empty handlers don't panic - -func TestHandleMsgHead(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgHead panicked: %v", r) - } - }() - - handleMsgHead(session, nil) -} - -func TestHandleMsgSysExtendThreshold(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysExtendThreshold panicked: %v", r) - } - }() - - handleMsgSysExtendThreshold(session, nil) -} - -func TestHandleMsgSysEnd(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysEnd panicked: %v", r) - } - }() - - handleMsgSysEnd(session, nil) -} - -func TestHandleMsgSysNop(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysNop panicked: %v", r) - } - }() - - handleMsgSysNop(session, nil) -} - -func TestHandleMsgSysAck(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysAck panicked: %v", r) - } - }() - - handleMsgSysAck(session, nil) -} - -func TestHandleMsgCaExchangeItem(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgCaExchangeItem panicked: %v", r) - } - }() - - handleMsgCaExchangeItem(session, nil) -} - -func TestHandleMsgMhfServerCommand(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgMhfServerCommand panicked: %v", r) - } - }() - - handleMsgMhfServerCommand(session, nil) -} - -func TestHandleMsgMhfSetLoginwindow(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgMhfSetLoginwindow panicked: %v", r) - } - }() - - handleMsgMhfSetLoginwindow(session, nil) -} - -func TestHandleMsgSysTransBinary(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysTransBinary panicked: %v", r) - } - }() - - handleMsgSysTransBinary(session, nil) -} - -func TestHandleMsgSysCollectBinary(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysCollectBinary panicked: %v", r) - } - }() - - handleMsgSysCollectBinary(session, nil) -} - -func TestHandleMsgSysGetState(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysGetState panicked: %v", r) - } - }() - - handleMsgSysGetState(session, nil) -} - -func TestHandleMsgSysSerialize(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysSerialize panicked: %v", r) - } - }() - - handleMsgSysSerialize(session, nil) -} - -func TestHandleMsgSysEnumlobby(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysEnumlobby panicked: %v", r) - } - }() - - handleMsgSysEnumlobby(session, nil) -} - -func TestHandleMsgSysEnumuser(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysEnumuser panicked: %v", r) - } - }() - - handleMsgSysEnumuser(session, nil) -} - -func TestHandleMsgSysInfokyserver(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysInfokyserver panicked: %v", r) - } - }() - - handleMsgSysInfokyserver(session, nil) -} - -func TestHandleMsgMhfGetCaUniqueID(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgMhfGetCaUniqueID panicked: %v", r) - } - }() - - handleMsgMhfGetCaUniqueID(session, nil) -} - -func TestHandleMsgMhfEnumerateItem(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumerateItem{ - AckHandle: 12345, - } - - handleMsgMhfEnumerateItem(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfAcquireItem(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfAcquireItem{ - AckHandle: 12345, - } - - handleMsgMhfAcquireItem(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfGetExtraInfo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgMhfGetExtraInfo panicked: %v", r) - } - }() - - handleMsgMhfGetExtraInfo(session, nil) -} - -// Test handlers that return simple responses - -func TestHandleMsgMhfTransferItem(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfTransferItem{ - AckHandle: 12345, - } - - handleMsgMhfTransferItem(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfEnumeratePrice(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumeratePrice{ - AckHandle: 12345, - } - - handleMsgMhfEnumeratePrice(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateOrder(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumerateOrder{ - AckHandle: 12345, - } - - handleMsgMhfEnumerateOrder(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test terminal log handler - -func TestHandleMsgSysTerminalLog(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTerminalLog{ - AckHandle: 12345, - LogID: 100, - Entries: []mhfpacket.TerminalLogEntry{}, - } - - handleMsgSysTerminalLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysTerminalLog_WithEntries(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTerminalLog{ - AckHandle: 12345, - LogID: 100, - Entries: []mhfpacket.TerminalLogEntry{ - {Type1: 1, Type2: 2}, - {Type1: 3, Type2: 4}, - }, - } - - handleMsgSysTerminalLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test ping handler -func TestHandleMsgSysPing(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysPing{ - AckHandle: 12345, - } - - handleMsgSysPing(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test time handler -func TestHandleMsgSysTime(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTime{ - GetRemoteTime: true, - } - - handleMsgSysTime(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test issue logkey handler -func TestHandleMsgSysIssueLogkey(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysIssueLogkey{ - AckHandle: 12345, - } - - handleMsgSysIssueLogkey(session, pkt) - - // Verify logkey was set - if len(session.logKey) != 16 { - t.Errorf("logKey length = %d, want 16", len(session.logKey)) - } - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test record log handler -func TestHandleMsgSysRecordLog(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - // Setup stage - stage := NewStage("test_stage") - session.stage = stage - stage.reservedClientSlots[session.charID] = true - - pkt := &mhfpacket.MsgSysRecordLog{ - AckHandle: 12345, - Data: make([]byte, 256), // Must be large enough for ByteFrame reads (32 offset + 176 uint8s) - } - - handleMsgSysRecordLog(session, pkt) - - // Verify charID removed from reserved slots - if _, exists := stage.reservedClientSlots[session.charID]; exists { - t.Error("charID should be removed from reserved slots") - } - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test unlock global sema handler -func TestHandleMsgSysUnlockGlobalSema(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysUnlockGlobalSema{ - AckHandle: 12345, - } - - handleMsgSysUnlockGlobalSema(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test more empty handlers -func TestHandleMsgSysSetStatus(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysSetStatus panicked: %v", r) - } - }() - - handleMsgSysSetStatus(session, nil) -} - -func TestHandleMsgSysEcho(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysEcho panicked: %v", r) - } - }() - - handleMsgSysEcho(session, nil) -} - -func TestHandleMsgSysUpdateRight(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysUpdateRight panicked: %v", r) - } - }() - - handleMsgSysUpdateRight(session, nil) -} - -func TestHandleMsgSysAuthQuery(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysAuthQuery panicked: %v", r) - } - }() - - handleMsgSysAuthQuery(session, nil) -} - -func TestHandleMsgSysAuthTerminal(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysAuthTerminal panicked: %v", r) - } - }() - - handleMsgSysAuthTerminal(session, nil) -} - -// Test lock global sema handler -func TestHandleMsgSysLockGlobalSema_NoMatch(t *testing.T) { - server := createMockServer() - server.GlobalID = "test-server" - server.Registry = NewLocalChannelRegistry([]*Server{}) - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysLockGlobalSema{ - AckHandle: 12345, - UserIDString: "user123", - ServerChannelIDString: "channel1", - } - - handleMsgSysLockGlobalSema(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysLockGlobalSema_WithChannel(t *testing.T) { - server := createMockServer() - server.GlobalID = "test-server" - - // Create a mock channel with stages - channel := &Server{ - GlobalID: "other-server", - } - channel.stages.Store("stage_user123", NewStage("stage_user123")) - server.Registry = NewLocalChannelRegistry([]*Server{channel}) - - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysLockGlobalSema{ - AckHandle: 12345, - UserIDString: "user123", - ServerChannelIDString: "channel1", - } - - handleMsgSysLockGlobalSema(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysLockGlobalSema_SameServer(t *testing.T) { - server := createMockServer() - server.GlobalID = "test-server" - - // Create a mock channel with same GlobalID - channel := &Server{ - GlobalID: "test-server", - } - channel.stages.Store("stage_user456", NewStage("stage_user456")) - server.Registry = NewLocalChannelRegistry([]*Server{channel}) - - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysLockGlobalSema{ - AckHandle: 12345, - UserIDString: "user456", - ServerChannelIDString: "channel2", - } - - handleMsgSysLockGlobalSema(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfAnnounce(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfAnnounce{ - AckHandle: 12345, - IPAddress: 0x7F000001, // 127.0.0.1 - Port: 54001, - StageID: []byte("test_stage"), - Data: byteframe.NewByteFrameFromBytes([]byte{0x00}), - } - - handleMsgMhfAnnounce(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysRightsReload(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysRightsReload{ - AckHandle: 12345, - } - - // This will panic due to nil db, which is expected in test - defer func() { - if r := recover(); r != nil { - t.Log("Expected panic due to nil database in test") - } - }() - - handleMsgSysRightsReload(session, pkt) -} diff --git a/server/channelserver/handlers_coverage2_test.go b/server/channelserver/handlers_coverage2_test.go deleted file mode 100644 index d60fab4d3..000000000 --- a/server/channelserver/handlers_coverage2_test.go +++ /dev/null @@ -1,922 +0,0 @@ -package channelserver - -import ( - "testing" - - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" -) - -// Tests for guild handlers that do not require database access. - -func TestHandleMsgMhfEntryRookieGuild(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEntryRookieGuild{ - AckHandle: 12345, - Unk: 42, - } - - handleMsgMhfEntryRookieGuild(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfGenerateUdGuildMap(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfGenerateUdGuildMap{ - AckHandle: 12345, - } - - handleMsgMhfGenerateUdGuildMap(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfCheckMonthlyItem(t *testing.T) { - server := createMockServer() - server.stampRepo = &mockStampRepoForItems{monthlyClaimedErr: errNotFound} - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfCheckMonthlyItem{ - AckHandle: 12345, - Type: 0, - } - - handleMsgMhfCheckMonthlyItem(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfAcquireMonthlyItem(t *testing.T) { - server := createMockServer() - server.stampRepo = &mockStampRepoForItems{} - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfAcquireMonthlyItem{ - AckHandle: 12345, - } - - handleMsgMhfAcquireMonthlyItem(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateInvGuild(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumerateInvGuild{ - AckHandle: 12345, - } - - handleMsgMhfEnumerateInvGuild(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfOperationInvGuild(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfOperationInvGuild{ - AckHandle: 12345, - Operation: 1, - } - - handleMsgMhfOperationInvGuild(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Tests for mercenary handlers that do not require database access. - -func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs1(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfMercenaryHuntdata{ - AckHandle: 12345, - RequestType: 1, - } - - handleMsgMhfMercenaryHuntdata(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs0(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfMercenaryHuntdata{ - AckHandle: 12345, - RequestType: 0, - } - - handleMsgMhfMercenaryHuntdata(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs2(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfMercenaryHuntdata{ - AckHandle: 12345, - RequestType: 2, - } - - handleMsgMhfMercenaryHuntdata(session, pkt) - - // RequestType=2 takes the else branch (same as 0) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Tests for festa/ranking handlers. - -func TestHandleMsgMhfEnumerateRanking_DefaultBranch(t *testing.T) { - server := createMockServer() - server.erupeConfig = &cfg.Config{ - DebugOptions: cfg.DebugOptions{ - TournamentOverride: 0, - }, - } - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumerateRanking{ - AckHandle: 99999, - } - - handleMsgMhfEnumerateRanking(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateRanking_NegativeState(t *testing.T) { - server := createMockServer() - server.erupeConfig = &cfg.Config{ - DebugOptions: cfg.DebugOptions{ - TournamentOverride: -1, - }, - } - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumerateRanking{ - AckHandle: 99999, - } - - handleMsgMhfEnumerateRanking(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Tests for rengoku handlers. - -func TestHandleMsgMhfGetRengokuRankingRank_ResponseData(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{ - AckHandle: 55555, - } - - handleMsgMhfGetRengokuRankingRank(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Tests for empty handlers that are not covered in other test files. - -func TestEmptyHandlers_Coverage2(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - handler func(s *Session, p mhfpacket.MHFPacket) - }{ - {"handleMsgSysCastedBinary", handleMsgSysCastedBinary}, - {"handleMsgMhfResetTitle", handleMsgMhfResetTitle}, - {"handleMsgMhfUpdateForceGuildRank", handleMsgMhfUpdateForceGuildRank}, - {"handleMsgMhfUpdateGuild", handleMsgMhfUpdateGuild}, - {"handleMsgMhfUpdateGuildcard", handleMsgMhfUpdateGuildcard}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.handler(session, nil) - }) - } -} - -// Tests for handlers.go - handlers that produce responses without DB access. - -func TestHandleMsgSysTerminalLog_MultipleEntries(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTerminalLog{ - AckHandle: 12345, - LogID: 200, - Entries: []mhfpacket.TerminalLogEntry{ - {Type1: 10, Type2: 20}, - {Type1: 11, Type2: 21}, - {Type1: 12, Type2: 22}, - }, - } - - handleMsgSysTerminalLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysTerminalLog_ZeroLogID(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTerminalLog{ - AckHandle: 12345, - LogID: 0, - Entries: []mhfpacket.TerminalLogEntry{}, - } - - handleMsgSysTerminalLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysPing_DifferentAckHandle(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysPing{ - AckHandle: 0xFFFFFFFF, - } - - handleMsgSysPing(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysTime_GetRemoteTimeFalse(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTime{ - GetRemoteTime: false, - } - - handleMsgSysTime(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysIssueLogkey_LogKeyGenerated(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysIssueLogkey{ - AckHandle: 77777, - } - - handleMsgSysIssueLogkey(session, pkt) - - // Verify that the logKey was set on the session - session.Lock() - keyLen := len(session.logKey) - session.Unlock() - - if keyLen != 16 { - t.Errorf("logKey length = %d, want 16", keyLen) - } - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysIssueLogkey_Uniqueness(t *testing.T) { - server := createMockServer() - - // Generate two logkeys and verify they differ - session1 := createMockSession(1, server) - session2 := createMockSession(2, server) - - pkt1 := &mhfpacket.MsgSysIssueLogkey{AckHandle: 1} - pkt2 := &mhfpacket.MsgSysIssueLogkey{AckHandle: 2} - - handleMsgSysIssueLogkey(session1, pkt1) - handleMsgSysIssueLogkey(session2, pkt2) - - // Drain send packets - <-session1.sendPackets - <-session2.sendPackets - - session1.Lock() - key1 := make([]byte, len(session1.logKey)) - copy(key1, session1.logKey) - session1.Unlock() - - session2.Lock() - key2 := make([]byte, len(session2.logKey)) - copy(key2, session2.logKey) - session2.Unlock() - - if len(key1) != 16 || len(key2) != 16 { - t.Fatalf("logKeys should be 16 bytes each, got %d and %d", len(key1), len(key2)) - } - - same := true - for i := range key1 { - if key1[i] != key2[i] { - same = false - break - } - } - if same { - t.Error("Two generated logkeys should differ (extremely unlikely to be the same)") - } -} - -// Tests for event handlers. - -func TestHandleMsgMhfReleaseEvent_ErrorCode(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfReleaseEvent{ - AckHandle: 88888, - } - - handleMsgMhfReleaseEvent(session, pkt) - - // This handler manually sends a response with error code 0x41 - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateEvent_Stub(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfEnumerateEvent{ - AckHandle: 77777, - } - - handleMsgMhfEnumerateEvent(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Tests for achievement handler. - -func TestHandleMsgMhfSetCaAchievementHist_Response(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfSetCaAchievementHist{ - AckHandle: 44444, - } - - handleMsgMhfSetCaAchievementHist(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test concurrent handler invocations to catch potential data races. - -func TestHandlersConcurrentInvocations(t *testing.T) { - server := createMockServer() - - done := make(chan struct{}) - const numGoroutines = 10 - - for i := 0; i < numGoroutines; i++ { - go func(id uint32) { - defer func() { - if r := recover(); r != nil { - t.Errorf("goroutine %d panicked: %v", id, r) - } - done <- struct{}{} - }() - - session := createMockSession(id, server) - - // Run several handlers concurrently - handleMsgSysPing(session, &mhfpacket.MsgSysPing{AckHandle: id}) - <-session.sendPackets - - handleMsgSysTime(session, &mhfpacket.MsgSysTime{GetRemoteTime: true}) - <-session.sendPackets - - handleMsgSysIssueLogkey(session, &mhfpacket.MsgSysIssueLogkey{AckHandle: id}) - <-session.sendPackets - - handleMsgMhfMercenaryHuntdata(session, &mhfpacket.MsgMhfMercenaryHuntdata{AckHandle: id, RequestType: 1}) - <-session.sendPackets - - handleMsgMhfEnumerateMercenaryLog(session, &mhfpacket.MsgMhfEnumerateMercenaryLog{AckHandle: id}) - <-session.sendPackets - }(uint32(i + 100)) - } - - for i := 0; i < numGoroutines; i++ { - <-done - } -} - -// Test record log handler with stage setup. - -func TestHandleMsgSysRecordLog_RemovesReservation(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - stage := NewStage("test_stage_record") - session.stage = stage - stage.reservedClientSlots[session.charID] = true - - pkt := &mhfpacket.MsgSysRecordLog{ - AckHandle: 55555, - Data: make([]byte, 256), - } - - handleMsgSysRecordLog(session, pkt) - - if _, exists := stage.reservedClientSlots[session.charID]; exists { - t.Error("charID should be removed from reserved slots after record log") - } - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgSysRecordLog_NoExistingReservation(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - stage := NewStage("test_stage_no_reservation") - session.stage = stage - // No reservation exists for this charID - - pkt := &mhfpacket.MsgSysRecordLog{ - AckHandle: 55556, - Data: make([]byte, 256), - } - - // Should not panic even if charID is not in reservedClientSlots - handleMsgSysRecordLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test unlock global sema handler. - -func TestHandleMsgSysUnlockGlobalSema_Response(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysUnlockGlobalSema{ - AckHandle: 66666, - } - - handleMsgSysUnlockGlobalSema(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test handlers from handlers_event.go with edge cases. - -func TestHandleMsgMhfSetRestrictionEvent_Response(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfSetRestrictionEvent{ - AckHandle: 11111, - } - - handleMsgMhfSetRestrictionEvent(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -func TestHandleMsgMhfGetRestrictionEvent_Empty(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgMhfGetRestrictionEvent panicked: %v", r) - } - }() - - handleMsgMhfGetRestrictionEvent(session, nil) -} - -// Test handlers from handlers_mercenary.go - legend dispatch (no DB). - -func TestHandleMsgMhfLoadLegendDispatch_Response(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgMhfLoadLegendDispatch{ - AckHandle: 22222, - } - - handleMsgMhfLoadLegendDispatch(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// Test multiple handler invocations on the same session to verify session state is not corrupted. - -func TestMultipleHandlersOnSameSession(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - // Call multiple handlers in sequence - handleMsgSysPing(session, &mhfpacket.MsgSysPing{AckHandle: 1}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from Ping handler") - } - - handleMsgSysTime(session, &mhfpacket.MsgSysTime{GetRemoteTime: true}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from Time handler") - } - - handleMsgMhfRegisterEvent(session, &mhfpacket.MsgMhfRegisterEvent{AckHandle: 2, WorldID: 5, LandID: 10}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from RegisterEvent handler") - } - - handleMsgMhfReleaseEvent(session, &mhfpacket.MsgMhfReleaseEvent{AckHandle: 3}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from ReleaseEvent handler") - } - - handleMsgMhfEnumerateEvent(session, &mhfpacket.MsgMhfEnumerateEvent{AckHandle: 4}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from EnumerateEvent handler") - } - - handleMsgMhfSetCaAchievementHist(session, &mhfpacket.MsgMhfSetCaAchievementHist{AckHandle: 5}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from SetCaAchievementHist handler") - } - - handleMsgMhfGetRengokuRankingRank(session, &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: 6}) - select { - case <-session.sendPackets: - default: - t.Fatal("Expected packet from GetRengokuRankingRank handler") - } -} - -// Test festa timestamp generation. - -func TestGenerateFestaTimestamps_Debug(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - start uint32 - }{ - {"Debug_Start1", 1}, - {"Debug_Start2", 2}, - {"Debug_Start3", 3}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - timestamps := generateFestaTimestamps(session, tt.start, true) - if len(timestamps) != 5 { - t.Errorf("Expected 5 timestamps, got %d", len(timestamps)) - } - for i, ts := range timestamps { - if ts == 0 { - t.Errorf("Timestamp %d should not be zero", i) - } - } - }) - } -} - -func TestGenerateFestaTimestamps_NonDebug_FutureStart(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - // Use a far-future start time so it does not trigger cleanup - futureStart := uint32(TimeAdjusted().Unix() + 5000000) - timestamps := generateFestaTimestamps(session, futureStart, false) - - if len(timestamps) != 5 { - t.Errorf("Expected 5 timestamps, got %d", len(timestamps)) - } - if timestamps[0] != futureStart { - t.Errorf("First timestamp = %d, want %d", timestamps[0], futureStart) - } - // Verify intervals - if timestamps[1] != timestamps[0]+604800 { - t.Errorf("Second timestamp should be start+604800, got %d", timestamps[1]) - } - if timestamps[2] != timestamps[1]+604800 { - t.Errorf("Third timestamp should be second+604800, got %d", timestamps[2]) - } - if timestamps[3] != timestamps[2]+9000 { - t.Errorf("Fourth timestamp should be third+9000, got %d", timestamps[3]) - } - if timestamps[4] != timestamps[3]+1240200 { - t.Errorf("Fifth timestamp should be fourth+1240200, got %d", timestamps[4]) - } -} - -// Test trial struct from handlers_festa.go. - -func TestFestaTrialStruct(t *testing.T) { - trial := FestaTrial{ - ID: 100, - Objective: 2, - GoalID: 500, - TimesReq: 10, - Locale: 1, - Reward: 50, - } - if trial.ID != 100 { - t.Errorf("ID = %d, want 100", trial.ID) - } - if trial.Objective != 2 { - t.Errorf("Objective = %d, want 2", trial.Objective) - } - if trial.GoalID != 500 { - t.Errorf("GoalID = %d, want 500", trial.GoalID) - } - if trial.TimesReq != 10 { - t.Errorf("TimesReq = %d, want 10", trial.TimesReq) - } -} - -// Test prize struct from handlers_festa.go. - -func TestPrizeStruct(t *testing.T) { - prize := Prize{ - ID: 1, - Tier: 2, - SoulsReq: 100, - ItemID: 0x1234, - NumItem: 5, - Claimed: 1, - } - if prize.ID != 1 { - t.Errorf("ID = %d, want 1", prize.ID) - } - if prize.Tier != 2 { - t.Errorf("Tier = %d, want 2", prize.Tier) - } - if prize.SoulsReq != 100 { - t.Errorf("SoulsReq = %d, want 100", prize.SoulsReq) - } - if prize.Claimed != 1 { - t.Errorf("Claimed = %d, want 1", prize.Claimed) - } -} - -// Test Airou struct from handlers_mercenary.go. - -func TestAirouStruct(t *testing.T) { - cat := Airou{ - ID: 42, - Name: []byte("TestCat"), - Task: 4, - Personality: 2, - Class: 1, - Experience: 1500, - WeaponType: 6, - WeaponID: 100, - } - - if cat.ID != 42 { - t.Errorf("ID = %d, want 42", cat.ID) - } - if cat.Task != 4 { - t.Errorf("Task = %d, want 4", cat.Task) - } - if cat.Experience != 1500 { - t.Errorf("Experience = %d, want 1500", cat.Experience) - } - if cat.WeaponType != 6 { - t.Errorf("WeaponType = %d, want 6", cat.WeaponType) - } - if cat.WeaponID != 100 { - t.Errorf("WeaponID = %d, want 100", cat.WeaponID) - } -} - -// Test RengokuScore struct default values. - -func TestRengokuScoreStruct_Fields(t *testing.T) { - score := RengokuScore{ - Name: "Hunter", - Score: 99999, - } - - if score.Name != "Hunter" { - t.Errorf("Name = %s, want Hunter", score.Name) - } - if score.Score != 99999 { - t.Errorf("Score = %d, want 99999", score.Score) - } -} diff --git a/server/channelserver/handlers_coverage3_test.go b/server/channelserver/handlers_coverage3_test.go deleted file mode 100644 index 7ef3d818b..000000000 --- a/server/channelserver/handlers_coverage3_test.go +++ /dev/null @@ -1,1135 +0,0 @@ -package channelserver - -import ( - "sync" - "testing" - - "erupe-ce/network/mhfpacket" -) - -// ============================================================================= -// Category 1: Empty handlers from handlers.go -// These have empty function bodies and can be called with nil packet safely. -// ============================================================================= - -func TestEmptyHandlers_HandlersGo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - fn func() - }{ - {"handleMsgSysEcho", func() { handleMsgSysEcho(session, nil) }}, - {"handleMsgSysUpdateRight", func() { handleMsgSysUpdateRight(session, nil) }}, - {"handleMsgSysAuthQuery", func() { handleMsgSysAuthQuery(session, nil) }}, - {"handleMsgSysAuthTerminal", func() { handleMsgSysAuthTerminal(session, nil) }}, - {"handleMsgCaExchangeItem", func() { handleMsgCaExchangeItem(session, nil) }}, - {"handleMsgMhfServerCommand", func() { handleMsgMhfServerCommand(session, nil) }}, - {"handleMsgMhfSetLoginwindow", func() { handleMsgMhfSetLoginwindow(session, nil) }}, - {"handleMsgSysTransBinary", func() { handleMsgSysTransBinary(session, nil) }}, - {"handleMsgSysCollectBinary", func() { handleMsgSysCollectBinary(session, nil) }}, - {"handleMsgSysGetState", func() { handleMsgSysGetState(session, nil) }}, - {"handleMsgSysSerialize", func() { handleMsgSysSerialize(session, nil) }}, - {"handleMsgSysEnumlobby", func() { handleMsgSysEnumlobby(session, nil) }}, - {"handleMsgSysEnumuser", func() { handleMsgSysEnumuser(session, nil) }}, - {"handleMsgSysInfokyserver", func() { handleMsgSysInfokyserver(session, nil) }}, - {"handleMsgMhfGetCaUniqueID", func() { handleMsgMhfGetCaUniqueID(session, nil) }}, - {"handleMsgMhfGetExtraInfo", func() { handleMsgMhfGetExtraInfo(session, nil) }}, - {"handleMsgSysSetStatus", func() { handleMsgSysSetStatus(session, nil) }}, - {"handleMsgMhfStampcardPrize", func() { handleMsgMhfStampcardPrize(session, nil) }}, - {"handleMsgMhfKickExportForce", func() { handleMsgMhfKickExportForce(session, nil) }}, - {"handleMsgMhfRegistSpabiTime", func() { handleMsgMhfRegistSpabiTime(session, nil) }}, - {"handleMsgMhfDebugPostValue", func() { handleMsgMhfDebugPostValue(session, nil) }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.fn() - }) - } -} - -// ============================================================================= -// Category 2: Empty handlers from handlers_object.go -// All empty function bodies, safe to call with nil packet. -// ============================================================================= - -func TestEmptyHandlers_ObjectGo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - fn func() - }{ - {"handleMsgSysDeleteObject", func() { handleMsgSysDeleteObject(session, nil) }}, - {"handleMsgSysRotateObject", func() { handleMsgSysRotateObject(session, nil) }}, - {"handleMsgSysDuplicateObject", func() { handleMsgSysDuplicateObject(session, nil) }}, - {"handleMsgSysGetObjectBinary", func() { handleMsgSysGetObjectBinary(session, nil) }}, - {"handleMsgSysGetObjectOwner", func() { handleMsgSysGetObjectOwner(session, nil) }}, - {"handleMsgSysUpdateObjectBinary", func() { handleMsgSysUpdateObjectBinary(session, nil) }}, - {"handleMsgSysCleanupObject", func() { handleMsgSysCleanupObject(session, nil) }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.fn() - }) - } -} - -// ============================================================================= -// Category 3: Empty handlers from handlers_clients.go -// All empty function bodies, safe to call with nil packet. -// ============================================================================= - -func TestEmptyHandlers_ClientsGo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - fn func() - }{ - {"handleMsgMhfShutClient", func() { handleMsgMhfShutClient(session, nil) }}, - {"handleMsgSysHideClient", func() { handleMsgSysHideClient(session, nil) }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.fn() - }) - } -} - -// ============================================================================= -// Category 4: Empty handler from handlers_stage.go -// ============================================================================= - -func TestEmptyHandlers_StageGo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - fn func() - }{ - {"handleMsgSysStageDestruct", func() { handleMsgSysStageDestruct(session, nil) }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.fn() - }) - } -} - -// ============================================================================= -// Category 5: Empty handlers from handlers_achievement.go -// ============================================================================= - -func TestEmptyHandlers_AchievementGo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - fn func() - }{ - {"handleMsgMhfDisplayedAchievement", func() { - handleMsgMhfDisplayedAchievement(session, &mhfpacket.MsgMhfDisplayedAchievement{}) - }}, - {"handleMsgMhfGetCaAchievementHist", func() { handleMsgMhfGetCaAchievementHist(session, nil) }}, - {"handleMsgMhfSetCaAchievement", func() { handleMsgMhfSetCaAchievement(session, nil) }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.fn() - }) - } -} - -// ============================================================================= -// Category 6: Empty handlers from handlers_caravan.go -// ============================================================================= - -// TestEmptyHandlers_CaravanGo removed: caravan handlers on main do type assertions -// and require proper packet structs, not nil. - -// ============================================================================= -// Category 7: Simple ack handlers from handlers_tactics.go (no DB needed) -// ============================================================================= - -func TestSimpleAckHandlers_TacticsGo(t *testing.T) { - server := createMockServer() - - tests := []struct { - name string - fn func(s *Session) - }{ - {"handleMsgMhfAddUdTacticsPoint", func(s *Session) { - handleMsgMhfAddUdTacticsPoint(s, &mhfpacket.MsgMhfAddUdTacticsPoint{AckHandle: 1}) - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - session := createMockSession(1, server) - tt.fn(session) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("%s: response should have data", tt.name) - } - default: - t.Errorf("%s: no response queued", tt.name) - } - }) - } -} - -// TestSimpleAckHandlers_TowerGo removed: tower handlers on main access s.server.db -// and cannot be tested without a database connection. - -// ============================================================================= -// Category 9: Simple ack handlers from handlers_reward.go (no DB needed) -// ============================================================================= - -func TestSimpleAckHandlers_RewardGo(t *testing.T) { - server := createMockServer() - - tests := []struct { - name string - fn func(s *Session) - }{ - {"handleMsgMhfGetRewardSong", func(s *Session) { - handleMsgMhfGetRewardSong(s, &mhfpacket.MsgMhfGetRewardSong{AckHandle: 1}) - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - session := createMockSession(1, server) - tt.fn(session) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("%s: response should have data", tt.name) - } - default: - t.Errorf("%s: no response queued", tt.name) - } - }) - } -} - -// ============================================================================= -// Category 10: Simple ack handler from handlers_semaphore.go (no DB needed) -// handleMsgSysCreateSemaphore produces a response via doAckSimpleSucceed. -// ============================================================================= - -func TestSimpleAckHandlers_SemaphoreGo(t *testing.T) { - server := createMockServer() - - t.Run("handleMsgSysCreateSemaphore", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysCreateSemaphore(session, &mhfpacket.MsgSysCreateSemaphore{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("handleMsgSysCreateSemaphore: response should have data") - } - default: - t.Error("handleMsgSysCreateSemaphore: no response queued") - } - }) -} - -// ============================================================================= -// Category 11: handleMsgSysCreateAcquireSemaphore from handlers_semaphore.go -// This handler accesses s.server.semaphore map. It creates or acquires a -// semaphore, so it needs the semaphore map initialized on the server. -// ============================================================================= - -func TestHandleMsgSysCreateAcquireSemaphore(t *testing.T) { - server := createMockServer() - server.semaphore = make(map[string]*Semaphore) - - t.Run("creates_new_semaphore", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysCreateAcquireSemaphore(session, &mhfpacket.MsgSysCreateAcquireSemaphore{ - AckHandle: 1, - SemaphoreID: "test_sema_1", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - // Verify semaphore was created - if _, exists := server.semaphore["test_sema_1"]; !exists { - t.Error("semaphore should have been created in server map") - } - }) - - t.Run("acquires_existing_semaphore", func(t *testing.T) { - session := createMockSession(2, server) - // Acquire the same semaphore again - handleMsgSysCreateAcquireSemaphore(session, &mhfpacket.MsgSysCreateAcquireSemaphore{ - AckHandle: 2, - SemaphoreID: "test_sema_1", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("creates_ravi_semaphore", func(t *testing.T) { - session := createMockSession(3, server) - handleMsgSysCreateAcquireSemaphore(session, &mhfpacket.MsgSysCreateAcquireSemaphore{ - AckHandle: 3, - SemaphoreID: "hs_l0u3B51", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - if _, exists := server.semaphore["hs_l0u3B51"]; !exists { - t.Error("ravi semaphore should have been created") - } - }) -} - -// ============================================================================= -// Category 12: Additional simple ack handlers from various files (no DB) -// ============================================================================= - -// TestSimpleAckHandlers_MiscFiles removed: handleMsgMhfGetRengokuBinary panics -// on missing file (explicit panic in handler), cannot test without rengoku_data.bin. - -// ============================================================================= -// Category 13: Other empty handlers from various files -// ============================================================================= - -func TestEmptyHandlers_MiscFiles(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - tests := []struct { - name string - fn func() - }{ - // From handlers_reward.go - {"handleMsgMhfUseRewardSong", func() { handleMsgMhfUseRewardSong(session, nil) }}, - {"handleMsgMhfAddRewardSongCount", func() { handleMsgMhfAddRewardSongCount(session, nil) }}, - {"handleMsgMhfAcceptReadReward", func() { handleMsgMhfAcceptReadReward(session, nil) }}, - // From handlers_caravan.go - {"handleMsgMhfPostRyoudama", func() { handleMsgMhfPostRyoudama(session, nil) }}, - // From handlers_tactics.go - {"handleMsgMhfSetUdTacticsFollower", func() { handleMsgMhfSetUdTacticsFollower(session, nil) }}, - {"handleMsgMhfGetUdTacticsLog", func() { handleMsgMhfGetUdTacticsLog(session, nil) }}, - // From handlers_achievement.go - {"handleMsgMhfPaymentAchievement", func() { handleMsgMhfPaymentAchievement(session, nil) }}, - // From handlers.go (additional empty ones) - {"handleMsgMhfGetCogInfo", func() { handleMsgMhfGetCogInfo(session, nil) }}, - {"handleMsgMhfUseUdShopCoin", func() { handleMsgMhfUseUdShopCoin(session, nil) }}, - {"handleMsgMhfGetDailyMissionMaster", func() { handleMsgMhfGetDailyMissionMaster(session, nil) }}, - {"handleMsgMhfGetDailyMissionPersonal", func() { handleMsgMhfGetDailyMissionPersonal(session, nil) }}, - {"handleMsgMhfSetDailyMissionPersonal", func() { handleMsgMhfSetDailyMissionPersonal(session, nil) }}, - // From handlers_object.go (additional empty ones) - {"handleMsgSysAddObject", func() { handleMsgSysAddObject(session, nil) }}, - {"handleMsgSysDelObject", func() { handleMsgSysDelObject(session, nil) }}, - {"handleMsgSysDispObject", func() { handleMsgSysDispObject(session, nil) }}, - {"handleMsgSysHideObject", func() { handleMsgSysHideObject(session, nil) }}, - // From handlers.go (non-trivial but no pkt dereference) - {"handleMsgHead", func() { handleMsgHead(session, nil) }}, - {"handleMsgSysExtendThreshold", func() { handleMsgSysExtendThreshold(session, nil) }}, - {"handleMsgSysEnd", func() { handleMsgSysEnd(session, nil) }}, - {"handleMsgSysNop", func() { handleMsgSysNop(session, nil) }}, - {"handleMsgSysAck", func() { handleMsgSysAck(session, nil) }}, - // From handlers_semaphore.go - {"handleMsgSysReleaseSemaphore", func() { handleMsgSysReleaseSemaphore(session, nil) }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - defer func() { - if r := recover(); r != nil { - t.Errorf("%s panicked: %v", tt.name, r) - } - }() - tt.fn() - }) - } -} - -// ============================================================================= -// Category 14: Handlers that produce responses without DB access -// These are non-trivial handlers with static/canned responses. -// ============================================================================= - -func TestNonTrivialHandlers_NoDB(t *testing.T) { - server := createMockServer() - - t.Run("handleMsgMhfGetEarthStatus", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetEarthStatus(session, &mhfpacket.MsgMhfGetEarthStatus{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfGetEarthValue_Type1", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetEarthValue(session, &mhfpacket.MsgMhfGetEarthValue{AckHandle: 1, ReqType: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfGetEarthValue_Type2", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetEarthValue(session, &mhfpacket.MsgMhfGetEarthValue{AckHandle: 1, ReqType: 2}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfGetEarthValue_Type3", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetEarthValue(session, &mhfpacket.MsgMhfGetEarthValue{AckHandle: 1, ReqType: 3}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfGetSeibattle", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetSeibattle(session, &mhfpacket.MsgMhfGetSeibattle{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - // handleMsgMhfGetTrendWeapon removed: requires database access - - // handleMsgMhfUpdateUseTrendWeaponLog removed: requires database access - - t.Run("handleMsgMhfUpdateBeatLevel", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfUpdateBeatLevel(session, &mhfpacket.MsgMhfUpdateBeatLevel{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfReadBeatLevel", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfReadBeatLevel(session, &mhfpacket.MsgMhfReadBeatLevel{ - AckHandle: 1, - ValidIDCount: 2, - IDs: [16]uint32{100, 200}, - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfTransferItem", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfTransferItem(session, &mhfpacket.MsgMhfTransferItem{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfEnumerateOrder", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfEnumerateOrder(session, &mhfpacket.MsgMhfEnumerateOrder{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfGetUdShopCoin", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetUdShopCoin(session, &mhfpacket.MsgMhfGetUdShopCoin{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfGetLobbyCrowd", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetLobbyCrowd(session, &mhfpacket.MsgMhfGetLobbyCrowd{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgMhfEnumeratePrice", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfEnumeratePrice(session, &mhfpacket.MsgMhfEnumeratePrice{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 15: Handlers from handlers_tactics.go that produce responses (no DB) -// ============================================================================= - -func TestNonTrivialHandlers_TacticsGo(t *testing.T) { - server := createMockServer() - - tests := []struct { - name string - fn func(s *Session) - }{ - {"handleMsgMhfGetUdTacticsPoint", func(s *Session) { - handleMsgMhfGetUdTacticsPoint(s, &mhfpacket.MsgMhfGetUdTacticsPoint{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdTacticsRewardList", func(s *Session) { - handleMsgMhfGetUdTacticsRewardList(s, &mhfpacket.MsgMhfGetUdTacticsRewardList{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdTacticsFollower", func(s *Session) { - handleMsgMhfGetUdTacticsFollower(s, &mhfpacket.MsgMhfGetUdTacticsFollower{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdTacticsBonusQuest", func(s *Session) { - handleMsgMhfGetUdTacticsBonusQuest(s, &mhfpacket.MsgMhfGetUdTacticsBonusQuest{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdTacticsFirstQuestBonus", func(s *Session) { - handleMsgMhfGetUdTacticsFirstQuestBonus(s, &mhfpacket.MsgMhfGetUdTacticsFirstQuestBonus{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdTacticsRemainingPoint", func(s *Session) { - handleMsgMhfGetUdTacticsRemainingPoint(s, &mhfpacket.MsgMhfGetUdTacticsRemainingPoint{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdTacticsRanking", func(s *Session) { - handleMsgMhfGetUdTacticsRanking(s, &mhfpacket.MsgMhfGetUdTacticsRanking{AckHandle: 1}) - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - session := createMockSession(1, server) - tt.fn(session) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("%s: response should have data", tt.name) - } - default: - t.Errorf("%s: no response queued", tt.name) - } - }) - } -} - -// ============================================================================= -// Category 16: Handlers from handlers_tower.go that produce responses (no DB) -// ============================================================================= - -func TestNonTrivialHandlers_TowerGo(t *testing.T) { - server := createMockServer() - - tests := []struct { - name string - fn func(s *Session) - }{ - {"handleMsgMhfGetTenrouirai_Type1", func(s *Session) { - handleMsgMhfGetTenrouirai(s, &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, Unk0: 1}) - }}, - {"handleMsgMhfGetTenrouirai_Unknown", func(s *Session) { - handleMsgMhfGetTenrouirai(s, &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, Unk0: 0, DataType: 0}) - }}, - // handleMsgMhfGetTenrouirai_Type4, handleMsgMhfPostTenrouirai, handleMsgMhfGetGemInfo removed: require DB - {"handleMsgMhfGetWeeklySeibatuRankingReward", func(s *Session) { - handleMsgMhfGetWeeklySeibatuRankingReward(s, &mhfpacket.MsgMhfGetWeeklySeibatuRankingReward{AckHandle: 1}) - }}, - {"handleMsgMhfPresentBox", func(s *Session) { - handleMsgMhfPresentBox(s, &mhfpacket.MsgMhfPresentBox{AckHandle: 1}) - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - session := createMockSession(1, server) - tt.fn(session) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("%s: response should have data", tt.name) - } - default: - t.Errorf("%s: no response queued", tt.name) - } - }) - } -} - -// ============================================================================= -// Category 17: Handlers from handlers_reward.go that produce responses (no DB) -// ============================================================================= - -func TestNonTrivialHandlers_RewardGo(t *testing.T) { - server := createMockServer() - - tests := []struct { - name string - fn func(s *Session) - }{ - {"handleMsgMhfGetAdditionalBeatReward", func(s *Session) { - handleMsgMhfGetAdditionalBeatReward(s, &mhfpacket.MsgMhfGetAdditionalBeatReward{AckHandle: 1}) - }}, - {"handleMsgMhfGetUdRankingRewardList", func(s *Session) { - handleMsgMhfGetUdRankingRewardList(s, &mhfpacket.MsgMhfGetUdRankingRewardList{AckHandle: 1}) - }}, - {"handleMsgMhfAcquireMonthlyReward", func(s *Session) { - handleMsgMhfAcquireMonthlyReward(s, &mhfpacket.MsgMhfAcquireMonthlyReward{AckHandle: 1}) - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - session := createMockSession(1, server) - tt.fn(session) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("%s: response should have data", tt.name) - } - default: - t.Errorf("%s: no response queued", tt.name) - } - }) - } -} - -// ============================================================================= -// Category 18: Handlers from handlers_caravan.go that produce responses (no DB) -// ============================================================================= - -func TestNonTrivialHandlers_CaravanGo(t *testing.T) { - server := createMockServer() - - tests := []struct { - name string - fn func(s *Session) - }{ - {"handleMsgMhfGetRyoudama", func(s *Session) { - handleMsgMhfGetRyoudama(s, &mhfpacket.MsgMhfGetRyoudama{AckHandle: 1}) - }}, - {"handleMsgMhfGetTinyBin", func(s *Session) { - handleMsgMhfGetTinyBin(s, &mhfpacket.MsgMhfGetTinyBin{AckHandle: 1}) - }}, - {"handleMsgMhfPostTinyBin", func(s *Session) { - handleMsgMhfPostTinyBin(s, &mhfpacket.MsgMhfPostTinyBin{AckHandle: 1}) - }}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - session := createMockSession(1, server) - tt.fn(session) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("%s: response should have data", tt.name) - } - default: - t.Errorf("%s: no response queued", tt.name) - } - }) - } -} - -// ============================================================================= -// Category 19: Handlers from handlers_rengoku.go (no DB needed) -// ============================================================================= - -func TestNonTrivialHandlers_RengokuGo(t *testing.T) { - server := createMockServer() - - t.Run("handleMsgMhfGetRengokuRankingRank", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgMhfGetRengokuRankingRank(session, &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 20: Handlers from handlers.go that produce responses (no DB) -// ============================================================================= - -// TestNonTrivialHandlers_InfoScenarioCounter removed: requires database access. - -// ============================================================================= -// Category 21: handleMsgSysPing and handleMsgSysTime (no DB) -// ============================================================================= - -func TestSimpleHandlers_PingAndTime(t *testing.T) { - server := createMockServer() - - t.Run("handleMsgSysPing", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysPing(session, &mhfpacket.MsgSysPing{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("handleMsgSysTime", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysTime(session, &mhfpacket.MsgSysTime{}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 22: handleMsgSysIssueLogkey (no DB, uses crypto/rand) -// ============================================================================= - -func TestHandleMsgSysIssueLogkey_Coverage3(t *testing.T) { - server := createMockServer() - - t.Run("generates_logkey", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysIssueLogkey(session, &mhfpacket.MsgSysIssueLogkey{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - if session.logKey == nil { - t.Error("logKey should be set after IssueLogkey") - } - if len(session.logKey) != 16 { - t.Errorf("logKey length = %d, want 16", len(session.logKey)) - } - }) -} - -// ============================================================================= -// Category 23: handleMsgSysUnlockGlobalSema (no DB) -// ============================================================================= - -func TestHandleMsgSysUnlockGlobalSema_Coverage3(t *testing.T) { - server := createMockServer() - - t.Run("produces_response", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysUnlockGlobalSema(session, &mhfpacket.MsgSysUnlockGlobalSema{AckHandle: 1}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 24: handleMsgSysLockGlobalSema (no DB, but needs Channels) -// ============================================================================= - -func TestHandleMsgSysLockGlobalSema(t *testing.T) { - server := createMockServer() - server.Registry = NewLocalChannelRegistry(make([]*Server, 0)) - - t.Run("no_channels_returns_response", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysLockGlobalSema(session, &mhfpacket.MsgSysLockGlobalSema{ - AckHandle: 1, - UserIDString: "testuser", - ServerChannelIDString: "ch1", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 25: handleMsgSysCheckSemaphore (no DB) -// ============================================================================= - -func TestHandleMsgSysCheckSemaphore(t *testing.T) { - server := createMockServer() - server.semaphore = make(map[string]*Semaphore) - - t.Run("semaphore_not_exists", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysCheckSemaphore(session, &mhfpacket.MsgSysCheckSemaphore{ - AckHandle: 1, - SemaphoreID: "nonexistent", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("semaphore_exists", func(t *testing.T) { - session := createMockSession(1, server) - server.semaphore["existing_sema"] = NewSemaphore(session, "existing_sema", 1) - handleMsgSysCheckSemaphore(session, &mhfpacket.MsgSysCheckSemaphore{ - AckHandle: 1, - SemaphoreID: "existing_sema", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 26: handleMsgSysAcquireSemaphore (no DB) -// ============================================================================= - -func TestHandleMsgSysAcquireSemaphore(t *testing.T) { - server := createMockServer() - server.semaphore = make(map[string]*Semaphore) - - t.Run("semaphore_exists", func(t *testing.T) { - session := createMockSession(1, server) - server.semaphore["acquire_sema"] = NewSemaphore(session, "acquire_sema", 1) - handleMsgSysAcquireSemaphore(session, &mhfpacket.MsgSysAcquireSemaphore{ - AckHandle: 1, - SemaphoreID: "acquire_sema", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("semaphore_not_exists", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysAcquireSemaphore(session, &mhfpacket.MsgSysAcquireSemaphore{ - AckHandle: 1, - SemaphoreID: "nonexistent_sema", - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 27: handleMsgSysCreateStage (no DB) -// ============================================================================= - -func TestHandleMsgSysCreateStage_Coverage3(t *testing.T) { - server := createMockServer() - - t.Run("creates_new_stage", func(t *testing.T) { - session := createMockSession(1, server) - handleMsgSysCreateStage(session, &mhfpacket.MsgSysCreateStage{ - AckHandle: 1, - StageID: "test_create_stage", - PlayerCount: 4, - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - if _, exists := server.stages.Get("test_create_stage"); !exists { - t.Error("stage should have been created") - } - }) - - t.Run("duplicate_stage_fails", func(t *testing.T) { - session := createMockSession(1, server) - // Stage already exists from the previous test - handleMsgSysCreateStage(session, &mhfpacket.MsgSysCreateStage{ - AckHandle: 2, - StageID: "test_create_stage", - PlayerCount: 4, - }) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data even on failure") - } - default: - t.Error("no response queued") - } - }) -} - -// ============================================================================= -// Category 28: Concurrency test for empty handlers -// Verify that calling empty handlers concurrently does not panic. -// ============================================================================= - -func TestEmptyHandlers_Concurrent(t *testing.T) { - server := createMockServer() - - handlers := []func(*Session, mhfpacket.MHFPacket){ - handleMsgSysEcho, - handleMsgSysUpdateRight, - handleMsgSysAuthQuery, - handleMsgSysAuthTerminal, - handleMsgCaExchangeItem, - handleMsgMhfServerCommand, - handleMsgMhfSetLoginwindow, - handleMsgSysTransBinary, - handleMsgSysCollectBinary, - handleMsgSysGetState, - handleMsgSysSerialize, - handleMsgSysEnumlobby, - handleMsgSysEnumuser, - handleMsgSysInfokyserver, - handleMsgMhfGetCaUniqueID, - handleMsgMhfGetExtraInfo, - handleMsgSysSetStatus, - handleMsgSysDeleteObject, - handleMsgSysRotateObject, - handleMsgSysDuplicateObject, - handleMsgSysGetObjectBinary, - handleMsgSysGetObjectOwner, - handleMsgSysUpdateObjectBinary, - handleMsgSysCleanupObject, - handleMsgMhfShutClient, - handleMsgSysHideClient, - handleMsgSysStageDestruct, - } - - var wg sync.WaitGroup - for _, h := range handlers { - for i := 0; i < 10; i++ { - wg.Add(1) - go func(handler func(*Session, mhfpacket.MHFPacket)) { - defer wg.Done() - session := createMockSession(1, server) - handler(session, nil) - }(h) - } - } - wg.Wait() -} - -// ============================================================================= -// Category 29: stubEnumerateNoResults and stubGetNoResults helper coverage -// These are called by many handlers; test them directly too. -// ============================================================================= - -func TestStubHelpers(t *testing.T) { - server := createMockServer() - - t.Run("stubEnumerateNoResults", func(t *testing.T) { - session := createMockSession(1, server) - stubEnumerateNoResults(session, 1) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("doAckBufSucceed", func(t *testing.T) { - session := createMockSession(1, server) - doAckBufSucceed(session, 1, []byte{0x01, 0x02, 0x03}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("doAckBufFail", func(t *testing.T) { - session := createMockSession(1, server) - doAckBufFail(session, 1, []byte{0x01, 0x02, 0x03}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("doAckSimpleSucceed", func(t *testing.T) { - session := createMockSession(1, server) - doAckSimpleSucceed(session, 1, []byte{0x00, 0x00, 0x00, 0x00}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) - - t.Run("doAckSimpleFail", func(t *testing.T) { - session := createMockSession(1, server) - doAckSimpleFail(session, 1, []byte{0x00, 0x00, 0x00, 0x00}) - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } - }) -} diff --git a/server/channelserver/handlers_coverage4_test.go b/server/channelserver/handlers_coverage4_test.go deleted file mode 100644 index 388002268..000000000 --- a/server/channelserver/handlers_coverage4_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package channelserver - -import ( - "testing" - - "erupe-ce/network/mhfpacket" -) - -// ============================================================================= -// handleMsgMhfGetPaperData: 565-line pure data serialization function. -// Tests all switch cases: 0, 5, 6, >1000 (known & unknown), default <1000. -// ============================================================================= - -func TestHandleMsgMhfGetPaperData_Case0(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ - AckHandle: 1, - DataType: 0, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("case 0: response should have data") - } - default: - t.Error("case 0: no response queued") - } -} - -func TestHandleMsgMhfGetPaperData_Case5(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ - AckHandle: 1, - DataType: 5, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("case 5: response should have data") - } - default: - t.Error("case 5: no response queued") - } -} - -func TestHandleMsgMhfGetPaperData_Case6(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ - AckHandle: 1, - DataType: 6, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("case 6: response should have data") - } - default: - t.Error("case 6: no response queued") - } -} - -func TestHandleMsgMhfGetPaperData_GreaterThan1000_KnownKey(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - // 6001 is a known key in paperGiftData - handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ - AckHandle: 1, - DataType: 6001, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error(">1000 known: response should have data") - } - default: - t.Error(">1000 known: no response queued") - } -} - -func TestHandleMsgMhfGetPaperData_GreaterThan1000_UnknownKey(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - // 9999 is not a known key in paperGiftData - handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ - AckHandle: 1, - DataType: 9999, - }) - - select { - case p := <-session.sendPackets: - // Even unknown keys should produce a response (empty earth succeed) - _ = p - default: - t.Error(">1000 unknown: no response queued") - } -} - -func TestHandleMsgMhfGetPaperData_DefaultUnknownLessThan1000(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - // Unknown type < 1000, hits default case then falls to else branch - handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ - AckHandle: 1, - DataType: 99, - }) - - select { - case p := <-session.sendPackets: - _ = p - default: - t.Error("default <1000: no response queued") - } -} - -// ============================================================================= -// handleMsgMhfGetGachaPlayHistory and handleMsgMhfPlayFreeGacha -// ============================================================================= - -func TestHandleMsgMhfGetGachaPlayHistory(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetGachaPlayHistory(session, &mhfpacket.MsgMhfGetGachaPlayHistory{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -func TestHandleMsgMhfPlayFreeGacha(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfPlayFreeGacha(session, &mhfpacket.MsgMhfPlayFreeGacha{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -// Seibattle handlers: GetBreakSeibatuLevelReward, GetFixedSeibatuRankingTable, -// ReadLastWeekBeatRanking, ReadBeatLevelAllRanking, ReadBeatLevelMyRanking -// are already tested in handlers_misc_test.go and handlers_tower_test.go. - -// ============================================================================= -// grpToGR: pure function, no dependencies -// ============================================================================= - -func TestGrpToGR(t *testing.T) { - tests := []struct { - name string - input int - expected uint16 - }{ - {"zero", 0, 1}, - {"low_value", 500, 2}, - {"first_bracket", 1000, 2}, - {"mid_bracket", 208750, 51}, - {"second_bracket", 300000, 62}, - {"high_value", 593400, 100}, - {"third_bracket", 700000, 113}, - {"very_high", 993400, 150}, - {"above_993400", 1000000, 150}, - {"fourth_bracket", 1400900, 200}, - {"max_bracket", 11345900, 900}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got := grpToGR(tt.input) - if got != tt.expected { - t.Errorf("grpToGR(%d) = %d, want %d", tt.input, got, tt.expected) - } - }) - } -} - -// ============================================================================= -// dumpSaveData: test disabled path -// ============================================================================= - -func TestDumpSaveData_Disabled(t *testing.T) { - server := createMockServer() - server.erupeConfig.SaveDumps.Enabled = false - session := createMockSession(1, server) - - // Should return immediately without error - dumpSaveData(session, []byte{0x01, 0x02, 0x03}, "test") -} - -// ============================================================================= -// TimeGameAbsolute -// ============================================================================= - -func TestTimeGameAbsolute(t *testing.T) { - result := TimeGameAbsolute() - - // TimeGameAbsolute returns (adjustedUnix - 2160) % 5760 - // Result should be in range [0, 5760) - if result >= 5760 { - t.Errorf("TimeGameAbsolute() = %d, should be < 5760", result) - } -} - -// ============================================================================= -// handleMsgSysAuthData: empty handler -// ============================================================================= - -func TestHandleMsgSysAuthData(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - defer func() { - if r := recover(); r != nil { - t.Errorf("handleMsgSysAuthData panicked: %v", r) - } - }() - handleMsgSysAuthData(session, nil) -} diff --git a/server/channelserver/handlers_coverage5_test.go b/server/channelserver/handlers_coverage5_test.go deleted file mode 100644 index eeb1508f0..000000000 --- a/server/channelserver/handlers_coverage5_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package channelserver - -import ( - "testing" - - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" -) - -// ============================================================================= -// equipSkinHistSize: pure function, tests all 3 config branches -// ============================================================================= - -func TestEquipSkinHistSize_Default(t *testing.T) { - got := equipSkinHistSize(cfg.ZZ) - if got != 3200 { - t.Errorf("equipSkinHistSize(ZZ) = %d, want 3200", got) - } -} - -func TestEquipSkinHistSize_Z2(t *testing.T) { - got := equipSkinHistSize(cfg.Z2) - if got != 2560 { - t.Errorf("equipSkinHistSize(Z2) = %d, want 2560", got) - } -} - -func TestEquipSkinHistSize_Z1(t *testing.T) { - got := equipSkinHistSize(cfg.Z1) - if got != 1280 { - t.Errorf("equipSkinHistSize(Z1) = %d, want 1280", got) - } -} - -func TestEquipSkinHistSize_OlderMode(t *testing.T) { - got := equipSkinHistSize(cfg.G1) - if got != 1280 { - t.Errorf("equipSkinHistSize(G1) = %d, want 1280", got) - } -} - -// ============================================================================= -// DB-free guild handlers: simple ack stubs -// ============================================================================= - -func TestHandleMsgMhfAddGuildMissionCount(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfAddGuildMissionCount(session, &mhfpacket.MsgMhfAddGuildMissionCount{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -func TestHandleMsgMhfSetGuildMissionTarget(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfSetGuildMissionTarget(session, &mhfpacket.MsgMhfSetGuildMissionTarget{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -func TestHandleMsgMhfCancelGuildMissionTarget(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfCancelGuildMissionTarget(session, &mhfpacket.MsgMhfCancelGuildMissionTarget{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -func TestHandleMsgMhfGetGuildMissionRecord(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetGuildMissionRecord(session, &mhfpacket.MsgMhfGetGuildMissionRecord{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -func TestHandleMsgMhfAcquireGuildTresureSouvenir(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfAcquireGuildTresureSouvenir(session, &mhfpacket.MsgMhfAcquireGuildTresureSouvenir{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -func TestHandleMsgMhfGetUdGuildMapInfo(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetUdGuildMapInfo(session, &mhfpacket.MsgMhfGetUdGuildMapInfo{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -// ============================================================================= -// DB-free guild mission list handler (large static data) -// ============================================================================= - -func TestHandleMsgMhfGetGuildMissionList(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - handleMsgMhfGetGuildMissionList(session, &mhfpacket.MsgMhfGetGuildMissionList{ - AckHandle: 1, - }) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("response should have data") - } - default: - t.Error("no response queued") - } -} - -// handleMsgMhfEnumerateUnionItem requires DB (calls userGetItems) - -// handleMsgMhfRegistSpabiTime, handleMsgMhfKickExportForce, handleMsgMhfUseUdShopCoin -// are tested in handlers_misc_test.go - -// handleMsgMhfGetUdShopCoin and handleMsgMhfGetLobbyCrowd are tested in handlers_misc_test.go - -// handleMsgMhfEnumerateGuacot requires DB (calls getGoocooData) - -// handleMsgMhfPostRyoudama is tested in handlers_caravan_test.go -// handleMsgMhfResetTitle is tested in handlers_coverage2_test.go diff --git a/server/channelserver/handlers_coverage_test.go b/server/channelserver/handlers_coverage_test.go deleted file mode 100644 index c99676ee4..000000000 --- a/server/channelserver/handlers_coverage_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package channelserver - -import ( - "testing" - - "erupe-ce/network/mhfpacket" -) - -// Tests for handlers that do NOT require database access, exercising additional -// code paths not covered by existing test files (handlers_core_test.go, -// handlers_rengoku_test.go, etc.). - -// TestHandleMsgSysPing_DifferentAckHandles verifies ping works with various ack handles. -func TestHandleMsgSysPing_DifferentAckHandles(t *testing.T) { - server := createMockServer() - - ackHandles := []uint32{0, 1, 99999, 0xFFFFFFFF} - for _, ack := range ackHandles { - session := createMockSession(1, server) - pkt := &mhfpacket.MsgSysPing{AckHandle: ack} - - handleMsgSysPing(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("AckHandle=%d: Response packet should have data", ack) - } - default: - t.Errorf("AckHandle=%d: No response packet queued", ack) - } - } -} - -// TestHandleMsgSysTerminalLog_NoEntries verifies the handler works with nil entries. -func TestHandleMsgSysTerminalLog_NoEntries(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTerminalLog{ - AckHandle: 99999, - LogID: 0, - Entries: nil, - } - - handleMsgSysTerminalLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// TestHandleMsgSysTerminalLog_ManyEntries verifies the handler with many log entries. -func TestHandleMsgSysTerminalLog_ManyEntries(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - entries := make([]mhfpacket.TerminalLogEntry, 20) - for i := range entries { - entries[i] = mhfpacket.TerminalLogEntry{ - Index: uint32(i), - Type1: uint8(i % 256), - Type2: uint8((i + 1) % 256), - } - } - - pkt := &mhfpacket.MsgSysTerminalLog{ - AckHandle: 55555, - LogID: 42, - Entries: entries, - } - - handleMsgSysTerminalLog(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - default: - t.Error("No response packet queued") - } -} - -// TestHandleMsgSysTime_MultipleCalls verifies calling time handler repeatedly. -func TestHandleMsgSysTime_MultipleCalls(t *testing.T) { - server := createMockServer() - session := createMockSession(1, server) - - pkt := &mhfpacket.MsgSysTime{ - GetRemoteTime: false, - Timestamp: 0, - } - - for i := 0; i < 5; i++ { - handleMsgSysTime(session, pkt) - } - - // Should have 5 queued responses - count := 0 - for { - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("Response packet should have data") - } - count++ - default: - goto done - } - } -done: - if count != 5 { - t.Errorf("Expected 5 queued responses, got %d", count) - } -} - -// TestHandleMsgMhfGetRengokuRankingRank_DifferentAck verifies rengoku ranking -// works with different ack handles. -func TestHandleMsgMhfGetRengokuRankingRank_DifferentAck(t *testing.T) { - server := createMockServer() - - ackHandles := []uint32{0, 1, 54321, 0xDEADBEEF} - for _, ack := range ackHandles { - session := createMockSession(1, server) - pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: ack} - - handleMsgMhfGetRengokuRankingRank(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Errorf("AckHandle=%d: Response packet should have data", ack) - } - default: - t.Errorf("AckHandle=%d: No response packet queued", ack) - } - } -} diff --git a/server/channelserver/handlers_data_paper_test.go b/server/channelserver/handlers_data_paper_test.go index 054f94eac..a978beaac 100644 --- a/server/channelserver/handlers_data_paper_test.go +++ b/server/channelserver/handlers_data_paper_test.go @@ -395,3 +395,157 @@ func itoa(n uint32) string { } return string(buf[i:]) } + +// ============================================================================= +// Tests consolidated from handlers_coverage4_test.go +// ============================================================================= + +func TestHandleMsgMhfGetPaperData_Case0(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ + AckHandle: 1, + DataType: 0, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("case 0: response should have data") + } + default: + t.Error("case 0: no response queued") + } +} + +func TestHandleMsgMhfGetPaperData_Case5(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ + AckHandle: 1, + DataType: 5, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("case 5: response should have data") + } + default: + t.Error("case 5: no response queued") + } +} + +func TestHandleMsgMhfGetPaperData_Case6(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ + AckHandle: 1, + DataType: 6, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("case 6: response should have data") + } + default: + t.Error("case 6: no response queued") + } +} + +func TestHandleMsgMhfGetPaperData_GreaterThan1000_KnownKey(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + // 6001 is a known key in paperGiftData + handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ + AckHandle: 1, + DataType: 6001, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error(">1000 known: response should have data") + } + default: + t.Error(">1000 known: no response queued") + } +} + +func TestHandleMsgMhfGetPaperData_GreaterThan1000_UnknownKey(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + // 9999 is not a known key in paperGiftData + handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ + AckHandle: 1, + DataType: 9999, + }) + + select { + case p := <-session.sendPackets: + // Even unknown keys should produce a response (empty earth succeed) + _ = p + default: + t.Error(">1000 unknown: no response queued") + } +} + +func TestHandleMsgMhfGetPaperData_DefaultUnknownLessThan1000(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + // Unknown type < 1000, hits default case then falls to else branch + handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{ + AckHandle: 1, + DataType: 99, + }) + + select { + case p := <-session.sendPackets: + _ = p + default: + t.Error("default <1000: no response queued") + } +} + +func TestHandleMsgMhfGetGachaPlayHistory(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetGachaPlayHistory(session, &mhfpacket.MsgMhfGetGachaPlayHistory{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + +func TestHandleMsgMhfPlayFreeGacha(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfPlayFreeGacha(session, &mhfpacket.MsgMhfPlayFreeGacha{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} diff --git a/server/channelserver/handlers_data_test.go b/server/channelserver/handlers_data_test.go index 8d06b6e3a..ee2dc23b0 100644 --- a/server/channelserver/handlers_data_test.go +++ b/server/channelserver/handlers_data_test.go @@ -651,3 +651,57 @@ func TestConcurrentSaveData_Integration(t *testing.T) { } } } + +// ============================================================================= +// Tests consolidated from handlers_coverage4_test.go +// ============================================================================= + +func TestGrpToGR(t *testing.T) { + tests := []struct { + name string + input int + expected uint16 + }{ + {"zero", 0, 1}, + {"low_value", 500, 2}, + {"first_bracket", 1000, 2}, + {"mid_bracket", 208750, 51}, + {"second_bracket", 300000, 62}, + {"high_value", 593400, 100}, + {"third_bracket", 700000, 113}, + {"very_high", 993400, 150}, + {"above_993400", 1000000, 150}, + {"fourth_bracket", 1400900, 200}, + {"max_bracket", 11345900, 900}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := grpToGR(tt.input) + if got != tt.expected { + t.Errorf("grpToGR(%d) = %d, want %d", tt.input, got, tt.expected) + } + }) + } +} + +func TestDumpSaveData_Disabled(t *testing.T) { + server := createMockServer() + server.erupeConfig.SaveDumps.Enabled = false + session := createMockSession(1, server) + + // Should return immediately without error + dumpSaveData(session, []byte{0x01, 0x02, 0x03}, "test") +} + +func TestHandleMsgSysAuthData(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysAuthData panicked: %v", r) + } + }() + handleMsgSysAuthData(session, nil) +} diff --git a/server/channelserver/handlers_diva_schedule_coverage_test.go b/server/channelserver/handlers_diva_schedule_coverage_test.go deleted file mode 100644 index 19d0d3514..000000000 --- a/server/channelserver/handlers_diva_schedule_coverage_test.go +++ /dev/null @@ -1,91 +0,0 @@ -package channelserver - -import ( - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" - "testing" - "time" -) - -func TestHandleMsgMhfGetUdSchedule_DivaOverrideZero_ZZ(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{} - srv.erupeConfig.DebugOptions.DivaOverride = 0 - srv.erupeConfig.RealClientMode = cfg.ZZ - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetUdSchedule_DivaOverrideZero_OlderClient(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{} - srv.erupeConfig.DebugOptions.DivaOverride = 0 - srv.erupeConfig.RealClientMode = cfg.G10 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetUdSchedule_DivaOverride1(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{} - srv.erupeConfig.DebugOptions.DivaOverride = 1 - srv.erupeConfig.RealClientMode = cfg.ZZ - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetUdSchedule_DivaOverride2(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{} - srv.erupeConfig.DebugOptions.DivaOverride = 2 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetUdSchedule_DivaOverride3(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{} - srv.erupeConfig.DebugOptions.DivaOverride = 3 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetUdSchedule_WithExistingEvent(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{ - events: []DivaEvent{{ID: 1, StartTime: uint32(time.Now().Unix())}}, - } - srv.erupeConfig.DebugOptions.DivaOverride = -1 - srv.erupeConfig.RealClientMode = cfg.ZZ - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetUdSchedule_NoEvents(t *testing.T) { - srv := createMockServer() - srv.divaRepo = &mockDivaRepo{} - srv.erupeConfig.DebugOptions.DivaOverride = -1 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} - handleMsgMhfGetUdSchedule(s, pkt) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_diva_test.go b/server/channelserver/handlers_diva_test.go index 414078e80..9d53d44da 100644 --- a/server/channelserver/handlers_diva_test.go +++ b/server/channelserver/handlers_diva_test.go @@ -4,6 +4,8 @@ import ( "testing" "erupe-ce/network/mhfpacket" + "time" + cfg "erupe-ce/config" ) func TestHandleMsgMhfGetUdInfo(t *testing.T) { @@ -341,3 +343,86 @@ func TestGenerateDivaTimestamps_NonDebug_WithValidStart(t *testing.T) { t.Error("Third timestamp should be second + 3900") } } + +func TestHandleMsgMhfGetUdSchedule_DivaOverrideZero_ZZ(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{} + srv.erupeConfig.DebugOptions.DivaOverride = 0 + srv.erupeConfig.RealClientMode = cfg.ZZ + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetUdSchedule_DivaOverrideZero_OlderClient(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{} + srv.erupeConfig.DebugOptions.DivaOverride = 0 + srv.erupeConfig.RealClientMode = cfg.G10 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetUdSchedule_DivaOverride1(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{} + srv.erupeConfig.DebugOptions.DivaOverride = 1 + srv.erupeConfig.RealClientMode = cfg.ZZ + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetUdSchedule_DivaOverride2(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{} + srv.erupeConfig.DebugOptions.DivaOverride = 2 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetUdSchedule_DivaOverride3(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{} + srv.erupeConfig.DebugOptions.DivaOverride = 3 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetUdSchedule_WithExistingEvent(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{ + events: []DivaEvent{{ID: 1, StartTime: uint32(time.Now().Unix())}}, + } + srv.erupeConfig.DebugOptions.DivaOverride = -1 + srv.erupeConfig.RealClientMode = cfg.ZZ + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetUdSchedule_NoEvents(t *testing.T) { + srv := createMockServer() + srv.divaRepo = &mockDivaRepo{} + srv.erupeConfig.DebugOptions.DivaOverride = -1 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetUdSchedule{AckHandle: 1} + handleMsgMhfGetUdSchedule(s, pkt) + <-s.sendPackets +} diff --git a/server/channelserver/handlers_event_coverage_test.go b/server/channelserver/handlers_event_coverage_test.go deleted file mode 100644 index bbcdd79d2..000000000 --- a/server/channelserver/handlers_event_coverage_test.go +++ /dev/null @@ -1,226 +0,0 @@ -package channelserver - -import ( - "erupe-ce/network/mhfpacket" - "testing" - "time" -) - -func TestHandleMsgMhfGetWeeklySchedule(t *testing.T) { - srv := createMockServer() - srv.eventRepo = &mockEventRepo{} - srv.erupeConfig.GameplayOptions.MinFeatureWeapons = 1 - srv.erupeConfig.GameplayOptions.MaxFeatureWeapons = 3 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetWeeklySchedule{AckHandle: 1} - handleMsgMhfGetWeeklySchedule(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfGetKeepLoginBoostStatus_Disabled(t *testing.T) { - srv := createMockServer() - srv.eventRepo = &mockEventRepo{} - srv.erupeConfig.GameplayOptions.DisableLoginBoost = true - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetKeepLoginBoostStatus{AckHandle: 1} - handleMsgMhfGetKeepLoginBoostStatus(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfGetKeepLoginBoostStatus_EmptyBoosts(t *testing.T) { - srv := createMockServer() - srv.eventRepo = &mockEventRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetKeepLoginBoostStatus{AckHandle: 1} - handleMsgMhfGetKeepLoginBoostStatus(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfGetKeepLoginBoostStatus_WithBoosts(t *testing.T) { - srv := createMockServer() - srv.eventRepo = &mockEventRepo{ - loginBoosts: []loginBoost{ - {WeekReq: 1, Expiration: time.Now().Add(-7 * 24 * time.Hour)}, - {WeekReq: 2, Expiration: time.Now().Add(-14 * 24 * time.Hour)}, - {WeekReq: 3, Expiration: time.Now().Add(-21 * 24 * time.Hour)}, - {WeekReq: 4, Expiration: time.Now().Add(-28 * 24 * time.Hour)}, - {WeekReq: 5, Expiration: time.Now().Add(-35 * 24 * time.Hour)}, - }, - } - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetKeepLoginBoostStatus{AckHandle: 1} - handleMsgMhfGetKeepLoginBoostStatus(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfUseKeepLoginBoost(t *testing.T) { - tests := []struct { - name string - boostWeekUsed uint8 - }{ - {"week1", 1}, - {"week2", 2}, - {"week3", 3}, - {"week4", 4}, - {"week5", 5}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - srv := createMockServer() - srv.eventRepo = &mockEventRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUseKeepLoginBoost{AckHandle: 1, BoostWeekUsed: tt.boostWeekUsed} - handleMsgMhfUseKeepLoginBoost(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } - }) - } -} - -func TestHandleMsgMhfLoadScenarioData(t *testing.T) { - srv := createMockServer() - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfLoadScenarioData{AckHandle: 1} - handleMsgMhfLoadScenarioData(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfSaveScenarioData(t *testing.T) { - srv := createMockServer() - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfSaveScenarioData{AckHandle: 1, RawDataPayload: []byte{0x01, 0x02, 0x03}} - handleMsgMhfSaveScenarioData(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfListMember(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.strings["blocked"] = "" - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfListMember{AckHandle: 1} - handleMsgMhfListMember(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfOprMember_AddBlacklist(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.strings["blocked"] = "" - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOprMember{AckHandle: 1, Blacklist: true, Operation: false, CharIDs: []uint32{42}} - handleMsgMhfOprMember(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfOprMember_AddFriend(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.strings["friends"] = "" - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOprMember{AckHandle: 1, Blacklist: false, Operation: false, CharIDs: []uint32{42}} - handleMsgMhfOprMember(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfOprMember_RemoveBlacklist(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.strings["blocked"] = "42" - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOprMember{AckHandle: 1, Blacklist: true, Operation: true, CharIDs: []uint32{42}} - handleMsgMhfOprMember(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} diff --git a/server/channelserver/handlers_event_test.go b/server/channelserver/handlers_event_test.go index d7ce7dd94..2db3e91c2 100644 --- a/server/channelserver/handlers_event_test.go +++ b/server/channelserver/handlers_event_test.go @@ -6,6 +6,7 @@ import ( cfg "erupe-ce/config" "erupe-ce/network/mhfpacket" + "time" ) func TestHandleMsgMhfRegisterEvent(t *testing.T) { @@ -221,6 +222,80 @@ func TestGenerateFeatureWeapons_StartTimeZero(t *testing.T) { } } +func TestHandleMsgMhfReleaseEvent_ErrorCode(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfReleaseEvent{ + AckHandle: 88888, + } + + handleMsgMhfReleaseEvent(session, pkt) + + // This handler manually sends a response with error code 0x41 + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateEvent_Stub(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateEvent{ + AckHandle: 77777, + } + + handleMsgMhfEnumerateEvent(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfSetRestrictionEvent_Response(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfSetRestrictionEvent{ + AckHandle: 11111, + } + + handleMsgMhfSetRestrictionEvent(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfGetRestrictionEvent_Empty(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfGetRestrictionEvent panicked: %v", r) + } + }() + + handleMsgMhfGetRestrictionEvent(session, nil) +} + // TestHandleMsgMhfRegisterEvent_DifferentValues tests with various Unk2/Unk4 values. func TestHandleMsgMhfRegisterEvent_DifferentValues(t *testing.T) { server := createMockServer() @@ -257,3 +332,222 @@ func TestHandleMsgMhfRegisterEvent_DifferentValues(t *testing.T) { }) } } + +func TestHandleMsgMhfGetWeeklySchedule(t *testing.T) { + srv := createMockServer() + srv.eventRepo = &mockEventRepo{} + srv.erupeConfig.GameplayOptions.MinFeatureWeapons = 1 + srv.erupeConfig.GameplayOptions.MaxFeatureWeapons = 3 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetWeeklySchedule{AckHandle: 1} + handleMsgMhfGetWeeklySchedule(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetKeepLoginBoostStatus_Disabled(t *testing.T) { + srv := createMockServer() + srv.eventRepo = &mockEventRepo{} + srv.erupeConfig.GameplayOptions.DisableLoginBoost = true + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetKeepLoginBoostStatus{AckHandle: 1} + handleMsgMhfGetKeepLoginBoostStatus(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetKeepLoginBoostStatus_EmptyBoosts(t *testing.T) { + srv := createMockServer() + srv.eventRepo = &mockEventRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetKeepLoginBoostStatus{AckHandle: 1} + handleMsgMhfGetKeepLoginBoostStatus(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetKeepLoginBoostStatus_WithBoosts(t *testing.T) { + srv := createMockServer() + srv.eventRepo = &mockEventRepo{ + loginBoosts: []loginBoost{ + {WeekReq: 1, Expiration: time.Now().Add(-7 * 24 * time.Hour)}, + {WeekReq: 2, Expiration: time.Now().Add(-14 * 24 * time.Hour)}, + {WeekReq: 3, Expiration: time.Now().Add(-21 * 24 * time.Hour)}, + {WeekReq: 4, Expiration: time.Now().Add(-28 * 24 * time.Hour)}, + {WeekReq: 5, Expiration: time.Now().Add(-35 * 24 * time.Hour)}, + }, + } + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetKeepLoginBoostStatus{AckHandle: 1} + handleMsgMhfGetKeepLoginBoostStatus(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfUseKeepLoginBoost(t *testing.T) { + tests := []struct { + name string + boostWeekUsed uint8 + }{ + {"week1", 1}, + {"week2", 2}, + {"week3", 3}, + {"week4", 4}, + {"week5", 5}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srv := createMockServer() + srv.eventRepo = &mockEventRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUseKeepLoginBoost{AckHandle: 1, BoostWeekUsed: tt.boostWeekUsed} + handleMsgMhfUseKeepLoginBoost(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } + }) + } +} + +func TestHandleMsgMhfLoadScenarioData(t *testing.T) { + srv := createMockServer() + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfLoadScenarioData{AckHandle: 1} + handleMsgMhfLoadScenarioData(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfSaveScenarioData(t *testing.T) { + srv := createMockServer() + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfSaveScenarioData{AckHandle: 1, RawDataPayload: []byte{0x01, 0x02, 0x03}} + handleMsgMhfSaveScenarioData(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfListMember(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.strings["blocked"] = "" + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfListMember{AckHandle: 1} + handleMsgMhfListMember(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfOprMember_AddBlacklist(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.strings["blocked"] = "" + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOprMember{AckHandle: 1, Blacklist: true, Operation: false, CharIDs: []uint32{42}} + handleMsgMhfOprMember(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfOprMember_AddFriend(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.strings["friends"] = "" + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOprMember{AckHandle: 1, Blacklist: false, Operation: false, CharIDs: []uint32{42}} + handleMsgMhfOprMember(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfOprMember_RemoveBlacklist(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.strings["blocked"] = "42" + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOprMember{AckHandle: 1, Blacklist: true, Operation: true, CharIDs: []uint32{42}} + handleMsgMhfOprMember(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} diff --git a/server/channelserver/handlers_festa_coverage_test.go b/server/channelserver/handlers_festa_coverage_test.go deleted file mode 100644 index 3c7d19412..000000000 --- a/server/channelserver/handlers_festa_coverage_test.go +++ /dev/null @@ -1,301 +0,0 @@ -package channelserver - -import ( - "erupe-ce/network/mhfpacket" - "testing" -) - -func TestHandleMsgMhfSaveMezfesData(t *testing.T) { - srv := createMockServer() - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfSaveMezfesData{AckHandle: 1, RawDataPayload: []byte{0x01, 0x02}} - handleMsgMhfSaveMezfesData(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfLoadMezfesData(t *testing.T) { - srv := createMockServer() - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfLoadMezfesData{AckHandle: 1} - handleMsgMhfLoadMezfesData(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfVoteFesta(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfVoteFesta{AckHandle: 1, TrialID: 42} - handleMsgMhfVoteFesta(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfEntryFesta_NoGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{getErr: errNotFound} - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEntryFesta{AckHandle: 1} - handleMsgMhfEntryFesta(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfEntryFesta_WithGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1}} - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEntryFesta{AckHandle: 1} - handleMsgMhfEntryFesta(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfChargeFesta(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1}} - ensureFestaService(srv) - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfChargeFesta{AckHandle: 1, GuildID: 1, Souls: []uint16{10, 20}} - handleMsgMhfChargeFesta(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfAcquireFesta(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfAcquireFesta{AckHandle: 1} - handleMsgMhfAcquireFesta(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfAcquireFestaPersonalPrize(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfAcquireFestaPersonalPrize{AckHandle: 1, PrizeID: 5} - handleMsgMhfAcquireFestaPersonalPrize(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfAcquireFestaIntermediatePrize(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfAcquireFestaIntermediatePrize{AckHandle: 1, PrizeID: 3} - handleMsgMhfAcquireFestaIntermediatePrize(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateFestaPersonalPrize(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{ - prizes: []Prize{ - {ID: 1, Tier: 1, SoulsReq: 100, ItemID: 5, NumItem: 1, Claimed: 0}, - }, - } - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateFestaPersonalPrize{AckHandle: 1} - handleMsgMhfEnumerateFestaPersonalPrize(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateFestaIntermediatePrize(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateFestaIntermediatePrize{AckHandle: 1} - handleMsgMhfEnumerateFestaIntermediatePrize(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfStateFestaU_NoGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{getErr: errNotFound} - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfStateFestaU{AckHandle: 1} - handleMsgMhfStateFestaU(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfStateFestaU_WithGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1}} - srv.festaRepo = &mockFestaRepo{charSouls: 50} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfStateFestaU{AckHandle: 1} - handleMsgMhfStateFestaU(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfStateFestaG_NoGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{getErr: errNotFound} - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfStateFestaG{AckHandle: 1} - handleMsgMhfStateFestaG(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfStateFestaG_WithGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1, Souls: 500}} - srv.festaRepo = &mockFestaRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfStateFestaG{AckHandle: 1} - handleMsgMhfStateFestaG(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateFestaMember_NoGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{getErr: errNotFound} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateFestaMember{AckHandle: 1} - handleMsgMhfEnumerateFestaMember(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfEnumerateFestaMember_WithMembers(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{ - guild: &Guild{ID: 1}, - members: []*GuildMember{ - {CharID: 1, Souls: 100}, - {CharID: 2, Souls: 50}, - {CharID: 3, Souls: 0}, - }, - } - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateFestaMember{AckHandle: 1} - handleMsgMhfEnumerateFestaMember(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} diff --git a/server/channelserver/handlers_festa_info_coverage_test.go b/server/channelserver/handlers_festa_info_coverage_test.go deleted file mode 100644 index 020027131..000000000 --- a/server/channelserver/handlers_festa_info_coverage_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package channelserver - -import ( - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" - "testing" - "time" -) - -func TestHandleMsgMhfInfoFesta_OverrideZero(t *testing.T) { - srv := createMockServer() - srv.festaRepo = &mockFestaRepo{} - srv.erupeConfig.DebugOptions.FestaOverride = 0 - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfInfoFesta{AckHandle: 1} - handleMsgMhfInfoFesta(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfInfoFesta_WithActiveEvent(t *testing.T) { - srv := createMockServer() - srv.erupeConfig.DebugOptions.FestaOverride = 1 - srv.erupeConfig.RealClientMode = cfg.ZZ - srv.erupeConfig.GameplayOptions.MaximumFP = 50000 - srv.festaRepo = &mockFestaRepo{ - events: []FestaEvent{{ID: 1, StartTime: uint32(time.Now().Add(-24 * time.Hour).Unix())}}, - trials: []FestaTrial{ - {ID: 1, Objective: 1, GoalID: 100, TimesReq: 5, Locale: 0, Reward: 10, Monopoly: "blue"}, - }, - } - ensureFestaService(srv) - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfInfoFesta{AckHandle: 1} - handleMsgMhfInfoFesta(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfInfoFesta_FutureTimestamp(t *testing.T) { - srv := createMockServer() - srv.erupeConfig.DebugOptions.FestaOverride = -1 - srv.festaRepo = &mockFestaRepo{ - events: []FestaEvent{{ID: 1, StartTime: uint32(time.Now().Add(72 * time.Hour).Unix())}}, - } - ensureFestaService(srv) - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfInfoFesta{AckHandle: 1} - handleMsgMhfInfoFesta(s, pkt) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_festa_test.go b/server/channelserver/handlers_festa_test.go index 12cde0901..bb2e9020a 100644 --- a/server/channelserver/handlers_festa_test.go +++ b/server/channelserver/handlers_festa_test.go @@ -5,6 +5,7 @@ import ( cfg "erupe-ce/config" "erupe-ce/network/mhfpacket" + "time" ) func TestHandleMsgMhfEnumerateRanking_Default(t *testing.T) { @@ -106,3 +107,495 @@ func TestHandleMsgMhfEnumerateRanking_State3(t *testing.T) { t.Error("No response packet queued") } } + +func TestHandleMsgMhfEnumerateRanking_DefaultBranch(t *testing.T) { + server := createMockServer() + server.erupeConfig = &cfg.Config{ + DebugOptions: cfg.DebugOptions{ + TournamentOverride: 0, + }, + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateRanking{ + AckHandle: 99999, + } + + handleMsgMhfEnumerateRanking(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateRanking_NegativeState(t *testing.T) { + server := createMockServer() + server.erupeConfig = &cfg.Config{ + DebugOptions: cfg.DebugOptions{ + TournamentOverride: -1, + }, + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateRanking{ + AckHandle: 99999, + } + + handleMsgMhfEnumerateRanking(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestGenerateFestaTimestamps_Debug(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + start uint32 + }{ + {"Debug_Start1", 1}, + {"Debug_Start2", 2}, + {"Debug_Start3", 3}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + timestamps := generateFestaTimestamps(session, tt.start, true) + if len(timestamps) != 5 { + t.Errorf("Expected 5 timestamps, got %d", len(timestamps)) + } + for i, ts := range timestamps { + if ts == 0 { + t.Errorf("Timestamp %d should not be zero", i) + } + } + }) + } +} + +func TestGenerateFestaTimestamps_NonDebug_FutureStart(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + // Use a far-future start time so it does not trigger cleanup + futureStart := uint32(TimeAdjusted().Unix() + 5000000) + timestamps := generateFestaTimestamps(session, futureStart, false) + + if len(timestamps) != 5 { + t.Errorf("Expected 5 timestamps, got %d", len(timestamps)) + } + if timestamps[0] != futureStart { + t.Errorf("First timestamp = %d, want %d", timestamps[0], futureStart) + } + // Verify intervals + if timestamps[1] != timestamps[0]+604800 { + t.Errorf("Second timestamp should be start+604800, got %d", timestamps[1]) + } + if timestamps[2] != timestamps[1]+604800 { + t.Errorf("Third timestamp should be second+604800, got %d", timestamps[2]) + } + if timestamps[3] != timestamps[2]+9000 { + t.Errorf("Fourth timestamp should be third+9000, got %d", timestamps[3]) + } + if timestamps[4] != timestamps[3]+1240200 { + t.Errorf("Fifth timestamp should be fourth+1240200, got %d", timestamps[4]) + } +} + +func TestFestaTrialStruct(t *testing.T) { + trial := FestaTrial{ + ID: 100, + Objective: 2, + GoalID: 500, + TimesReq: 10, + Locale: 1, + Reward: 50, + } + if trial.ID != 100 { + t.Errorf("ID = %d, want 100", trial.ID) + } + if trial.Objective != 2 { + t.Errorf("Objective = %d, want 2", trial.Objective) + } + if trial.GoalID != 500 { + t.Errorf("GoalID = %d, want 500", trial.GoalID) + } + if trial.TimesReq != 10 { + t.Errorf("TimesReq = %d, want 10", trial.TimesReq) + } +} + +func TestPrizeStruct(t *testing.T) { + prize := Prize{ + ID: 1, + Tier: 2, + SoulsReq: 100, + ItemID: 0x1234, + NumItem: 5, + Claimed: 1, + } + if prize.ID != 1 { + t.Errorf("ID = %d, want 1", prize.ID) + } + if prize.Tier != 2 { + t.Errorf("Tier = %d, want 2", prize.Tier) + } + if prize.SoulsReq != 100 { + t.Errorf("SoulsReq = %d, want 100", prize.SoulsReq) + } + if prize.Claimed != 1 { + t.Errorf("Claimed = %d, want 1", prize.Claimed) + } +} + +func TestHandleMsgMhfSaveMezfesData(t *testing.T) { + srv := createMockServer() + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfSaveMezfesData{AckHandle: 1, RawDataPayload: []byte{0x01, 0x02}} + handleMsgMhfSaveMezfesData(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfLoadMezfesData(t *testing.T) { + srv := createMockServer() + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfLoadMezfesData{AckHandle: 1} + handleMsgMhfLoadMezfesData(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfVoteFesta(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfVoteFesta{AckHandle: 1, TrialID: 42} + handleMsgMhfVoteFesta(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfEntryFesta_NoGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{getErr: errNotFound} + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEntryFesta{AckHandle: 1} + handleMsgMhfEntryFesta(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfEntryFesta_WithGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1}} + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEntryFesta{AckHandle: 1} + handleMsgMhfEntryFesta(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfChargeFesta(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1}} + ensureFestaService(srv) + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfChargeFesta{AckHandle: 1, GuildID: 1, Souls: []uint16{10, 20}} + handleMsgMhfChargeFesta(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfAcquireFesta(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfAcquireFesta{AckHandle: 1} + handleMsgMhfAcquireFesta(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfAcquireFestaPersonalPrize(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfAcquireFestaPersonalPrize{AckHandle: 1, PrizeID: 5} + handleMsgMhfAcquireFestaPersonalPrize(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfAcquireFestaIntermediatePrize(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfAcquireFestaIntermediatePrize{AckHandle: 1, PrizeID: 3} + handleMsgMhfAcquireFestaIntermediatePrize(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateFestaPersonalPrize(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{ + prizes: []Prize{ + {ID: 1, Tier: 1, SoulsReq: 100, ItemID: 5, NumItem: 1, Claimed: 0}, + }, + } + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateFestaPersonalPrize{AckHandle: 1} + handleMsgMhfEnumerateFestaPersonalPrize(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateFestaIntermediatePrize(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateFestaIntermediatePrize{AckHandle: 1} + handleMsgMhfEnumerateFestaIntermediatePrize(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfStateFestaU_NoGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{getErr: errNotFound} + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfStateFestaU{AckHandle: 1} + handleMsgMhfStateFestaU(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfStateFestaU_WithGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1}} + srv.festaRepo = &mockFestaRepo{charSouls: 50} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfStateFestaU{AckHandle: 1} + handleMsgMhfStateFestaU(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfStateFestaG_NoGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{getErr: errNotFound} + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfStateFestaG{AckHandle: 1} + handleMsgMhfStateFestaG(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfStateFestaG_WithGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{guild: &Guild{ID: 1, Souls: 500}} + srv.festaRepo = &mockFestaRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfStateFestaG{AckHandle: 1} + handleMsgMhfStateFestaG(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateFestaMember_NoGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{getErr: errNotFound} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateFestaMember{AckHandle: 1} + handleMsgMhfEnumerateFestaMember(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateFestaMember_WithMembers(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{ + guild: &Guild{ID: 1}, + members: []*GuildMember{ + {CharID: 1, Souls: 100}, + {CharID: 2, Souls: 50}, + {CharID: 3, Souls: 0}, + }, + } + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateFestaMember{AckHandle: 1} + handleMsgMhfEnumerateFestaMember(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfInfoFesta_OverrideZero(t *testing.T) { + srv := createMockServer() + srv.festaRepo = &mockFestaRepo{} + srv.erupeConfig.DebugOptions.FestaOverride = 0 + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfInfoFesta{AckHandle: 1} + handleMsgMhfInfoFesta(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfInfoFesta_WithActiveEvent(t *testing.T) { + srv := createMockServer() + srv.erupeConfig.DebugOptions.FestaOverride = 1 + srv.erupeConfig.RealClientMode = cfg.ZZ + srv.erupeConfig.GameplayOptions.MaximumFP = 50000 + srv.festaRepo = &mockFestaRepo{ + events: []FestaEvent{{ID: 1, StartTime: uint32(time.Now().Add(-24 * time.Hour).Unix())}}, + trials: []FestaTrial{ + {ID: 1, Objective: 1, GoalID: 100, TimesReq: 5, Locale: 0, Reward: 10, Monopoly: "blue"}, + }, + } + ensureFestaService(srv) + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfInfoFesta{AckHandle: 1} + handleMsgMhfInfoFesta(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfInfoFesta_FutureTimestamp(t *testing.T) { + srv := createMockServer() + srv.erupeConfig.DebugOptions.FestaOverride = -1 + srv.festaRepo = &mockFestaRepo{ + events: []FestaEvent{{ID: 1, StartTime: uint32(time.Now().Add(72 * time.Hour).Unix())}}, + } + ensureFestaService(srv) + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfInfoFesta{AckHandle: 1} + handleMsgMhfInfoFesta(s, pkt) + <-s.sendPackets +} diff --git a/server/channelserver/handlers_guild_board_coverage_test.go b/server/channelserver/handlers_guild_board_coverage_test.go deleted file mode 100644 index 326f722ab..000000000 --- a/server/channelserver/handlers_guild_board_coverage_test.go +++ /dev/null @@ -1,173 +0,0 @@ -package channelserver - -import ( - "erupe-ce/network/mhfpacket" - "testing" -) - -func TestHandleMsgMhfUpdateGuildMessageBoard_CreatePost(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 0, - PostType: 0, - StampID: 1, - Title: "Test", - Body: "Hello", - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_CreatePostType1(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 0, - PostType: 1, - Title: "Notice", - Body: "Board", - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_DeletePost(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 1, - PostID: 42, - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_UpdatePost(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 2, - PostID: 1, - Title: "Updated", - Body: "New body", - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_UpdateStamp(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 3, - PostID: 1, - StampID: 5, - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_LikePost(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 4, - PostID: 1, - LikeState: true, - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_CheckNewPosts(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ - AckHandle: 1, - MessageOp: 5, - } - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_NoGuild(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{getErr: errNotFound} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{AckHandle: 1, MessageOp: 0} - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateGuildMessageBoard_Applicant(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild, hasAppResult: true} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{AckHandle: 1, MessageOp: 0} - handleMsgMhfUpdateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildMessageBoard(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{AckHandle: 1, BoardType: 0} - handleMsgMhfEnumerateGuildMessageBoard(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildMessageBoard_Type1(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{AckHandle: 1, BoardType: 1} - handleMsgMhfEnumerateGuildMessageBoard(s, pkt) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_guild_board_test.go b/server/channelserver/handlers_guild_board_test.go index aebc30159..fd9783a41 100644 --- a/server/channelserver/handlers_guild_board_test.go +++ b/server/channelserver/handlers_guild_board_test.go @@ -239,3 +239,170 @@ func TestEnumerateGuildMessageBoard_DBError(t *testing.T) { t.Error("No response packet queued") } } + +func TestHandleMsgMhfUpdateGuildMessageBoard_CreatePost(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 0, + PostType: 0, + StampID: 1, + Title: "Test", + Body: "Hello", + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_CreatePostType1(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 0, + PostType: 1, + Title: "Notice", + Body: "Board", + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_DeletePost(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 1, + PostID: 42, + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_UpdatePost(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 2, + PostID: 1, + Title: "Updated", + Body: "New body", + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_UpdateStamp(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 3, + PostID: 1, + StampID: 5, + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_LikePost(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 4, + PostID: 1, + LikeState: true, + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_CheckNewPosts(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{ + AckHandle: 1, + MessageOp: 5, + } + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_NoGuild(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{getErr: errNotFound} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{AckHandle: 1, MessageOp: 0} + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateGuildMessageBoard_Applicant(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild, hasAppResult: true} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{AckHandle: 1, MessageOp: 0} + handleMsgMhfUpdateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildMessageBoard(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{AckHandle: 1, BoardType: 0} + handleMsgMhfEnumerateGuildMessageBoard(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildMessageBoard_Type1(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{AckHandle: 1, BoardType: 1} + handleMsgMhfEnumerateGuildMessageBoard(s, pkt) + <-s.sendPackets +} diff --git a/server/channelserver/handlers_guild_coverage_test.go b/server/channelserver/handlers_guild_coverage_test.go deleted file mode 100644 index 729a1b75b..000000000 --- a/server/channelserver/handlers_guild_coverage_test.go +++ /dev/null @@ -1,246 +0,0 @@ -package channelserver - -import ( - "testing" - - "erupe-ce/network/mhfpacket" -) - -func TestHandleMsgMhfCreateGuild_Success(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfCreateGuild{AckHandle: 1, Name: "TestGuild"} - handleMsgMhfCreateGuild(session, pkt) - - select { - case p := <-session.sendPackets: - if len(p.data) == 0 { - t.Error("expected non-empty response") - } - default: - t.Error("expected a response packet") - } -} - -func TestHandleMsgMhfCreateGuild_Error(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{saveErr: errNotFound} - // Mock Create to return error - the mockGuildRepo.Create returns (0, nil) - // We need getErr to make it fail. Actually Create is a no-op stub returning nil. - // Let's use a custom approach - we need the Create method to error. - // The mock's Create always returns nil, so let's test the success path worked above - // and test ArrangeGuildMember error paths instead. - session := createMockSession(100, server) - pkt := &mhfpacket.MsgMhfCreateGuild{AckHandle: 1, Name: "TestGuild"} - handleMsgMhfCreateGuild(session, pkt) - <-session.sendPackets // consume the response -} - -func TestHandleMsgMhfArrangeGuildMember_Success(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1, GuildLeader: GuildLeader{LeaderCharID: 100}} - server.guildRepo = &mockGuildRepo{guild: guild} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfArrangeGuildMember{ - AckHandle: 1, - GuildID: 1, - CharIDs: []uint32{100, 200, 300}, - } - handleMsgMhfArrangeGuildMember(session, pkt) - - select { - case <-session.sendPackets: - default: - t.Error("expected response") - } -} - -func TestHandleMsgMhfArrangeGuildMember_GetByIDError(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{getErr: errNotFound} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfArrangeGuildMember{AckHandle: 1, GuildID: 999} - handleMsgMhfArrangeGuildMember(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfArrangeGuildMember_NotLeader(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1, GuildLeader: GuildLeader{LeaderCharID: 200, LeaderName: "Other"}} - server.guildRepo = &mockGuildRepo{guild: guild} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfArrangeGuildMember{AckHandle: 1, GuildID: 1} - handleMsgMhfArrangeGuildMember(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildMember_GuildIDPositive(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1, MemberCount: 2} - members := []*GuildMember{ - {CharID: 100, Name: "Player1", HR: 50, OrderIndex: 0, WeaponType: 3}, - {CharID: 200, Name: "Player2", HR: 100, OrderIndex: 1, WeaponType: 1}, - } - server.guildRepo = &mockGuildRepo{guild: guild, members: members} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 1} - handleMsgMhfEnumerateGuildMember(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildMember_GuildIDZero(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1, MemberCount: 1} - members := []*GuildMember{ - {CharID: 100, Name: "Player1", HR: 50, OrderIndex: 0}, - } - server.guildRepo = &mockGuildRepo{guild: guild, members: members} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 0} - handleMsgMhfEnumerateGuildMember(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildMember_NilGuild(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 0} - handleMsgMhfEnumerateGuildMember(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildMember_Applicant(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1} - server.guildRepo = &mockGuildRepo{guild: guild, hasAppResult: true} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 1} - handleMsgMhfEnumerateGuildMember(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfGetGuildManageRight(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1, MemberCount: 2} - members := []*GuildMember{ - {CharID: 100, Recruiter: true}, - {CharID: 200, Recruiter: false}, - } - server.guildRepo = &mockGuildRepo{guild: guild, members: members} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfGetGuildManageRight{AckHandle: 1} - handleMsgMhfGetGuildManageRight(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfGetGuildTargetMemberNum_NilGuild(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfGetGuildTargetMemberNum{AckHandle: 1, GuildID: 0} - handleMsgMhfGetGuildTargetMemberNum(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfGetGuildTargetMemberNum_WithGuild(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1, MemberCount: 5} - server.guildRepo = &mockGuildRepo{guild: guild} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfGetGuildTargetMemberNum{AckHandle: 1, GuildID: 1} - handleMsgMhfGetGuildTargetMemberNum(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfEnumerateGuildItem(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfEnumerateGuildItem{AckHandle: 1, GuildID: 1} - handleMsgMhfEnumerateGuildItem(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfUpdateGuildItem(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfUpdateGuildItem{AckHandle: 1, GuildID: 1} - handleMsgMhfUpdateGuildItem(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfUpdateGuildIcon_LeaderSuccess(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1} - membership := &GuildMember{CharID: 100, IsLeader: true} - server.guildRepo = &mockGuildRepo{guild: guild, membership: membership} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfUpdateGuildIcon{ - AckHandle: 1, - GuildID: 1, - IconParts: []mhfpacket.GuildIconMsgPart{ - {Index: 0, ID: 1, Page: 0, Size: 10, Rotation: 0, Red: 255, Green: 0, Blue: 0, PosX: 50, PosY: 50}, - }, - } - handleMsgMhfUpdateGuildIcon(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfUpdateGuildIcon_NotLeader(t *testing.T) { - server := createMockServer() - guild := &Guild{ID: 1} - membership := &GuildMember{CharID: 100, IsLeader: false, OrderIndex: 5} - server.guildRepo = &mockGuildRepo{guild: guild, membership: membership} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfUpdateGuildIcon{AckHandle: 1, GuildID: 1} - handleMsgMhfUpdateGuildIcon(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfUpdateGuildIcon_GetByIDError(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{getErr: errNotFound} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfUpdateGuildIcon{AckHandle: 1, GuildID: 999} - handleMsgMhfUpdateGuildIcon(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfReadGuildcard(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfReadGuildcard{AckHandle: 1} - handleMsgMhfReadGuildcard(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSetGuildManageRight(t *testing.T) { - server := createMockServer() - server.guildRepo = &mockGuildRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfSetGuildManageRight{AckHandle: 1, CharID: 200, Allowed: true} - handleMsgMhfSetGuildManageRight(session, pkt) - <-session.sendPackets -} diff --git a/server/channelserver/handlers_guild_mission_test.go b/server/channelserver/handlers_guild_mission_test.go new file mode 100644 index 000000000..bf3b3b086 --- /dev/null +++ b/server/channelserver/handlers_guild_mission_test.go @@ -0,0 +1,99 @@ +package channelserver + +import ( + "testing" + + "erupe-ce/network/mhfpacket" +) + +// --- Guild mission handler tests --- + +func TestHandleMsgMhfAddGuildMissionCount(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfAddGuildMissionCount(session, &mhfpacket.MsgMhfAddGuildMissionCount{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + +func TestHandleMsgMhfSetGuildMissionTarget(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfSetGuildMissionTarget(session, &mhfpacket.MsgMhfSetGuildMissionTarget{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + +func TestHandleMsgMhfCancelGuildMissionTarget(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfCancelGuildMissionTarget(session, &mhfpacket.MsgMhfCancelGuildMissionTarget{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + +func TestHandleMsgMhfGetGuildMissionRecord(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetGuildMissionRecord(session, &mhfpacket.MsgMhfGetGuildMissionRecord{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + +func TestHandleMsgMhfGetGuildMissionList(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetGuildMissionList(session, &mhfpacket.MsgMhfGetGuildMissionList{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} diff --git a/server/channelserver/handlers_guild_ops_coverage_test.go b/server/channelserver/handlers_guild_ops_coverage_test.go deleted file mode 100644 index 78b49b49b..000000000 --- a/server/channelserver/handlers_guild_ops_coverage_test.go +++ /dev/null @@ -1,109 +0,0 @@ -package channelserver - -import ( - "erupe-ce/common/byteframe" - "erupe-ce/common/stringsupport" - "erupe-ce/network/mhfpacket" - "testing" -) - -func TestHandleRenamePugi_Pugi1(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - s := createMockSession(100, srv) - - bf := byteframe.NewByteFrame() - nameBytes := stringsupport.UTF8ToSJIS("TestPugi") - bf.WriteBytes(nameBytes) - bf.WriteUint8(0) // null terminator - bf.Seek(0, 0) - - handleRenamePugi(s, bf, guild, 1) - if guild.PugiName1 != "TestPugi" { - t.Errorf("PugiName1 = %q, want TestPugi", guild.PugiName1) - } -} - -func TestHandleRenamePugi_Pugi2(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - s := createMockSession(100, srv) - - bf := byteframe.NewByteFrame() - nameBytes := stringsupport.UTF8ToSJIS("Pugi2") - bf.WriteBytes(nameBytes) - bf.WriteUint8(0) - bf.Seek(0, 0) - - handleRenamePugi(s, bf, guild, 2) - if guild.PugiName2 != "Pugi2" { - t.Errorf("PugiName2 = %q, want Pugi2", guild.PugiName2) - } -} - -func TestHandleRenamePugi_Pugi3Default(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - s := createMockSession(100, srv) - - bf := byteframe.NewByteFrame() - nameBytes := stringsupport.UTF8ToSJIS("Pugi3") - bf.WriteBytes(nameBytes) - bf.WriteUint8(0) - bf.Seek(0, 0) - - handleRenamePugi(s, bf, guild, 3) - if guild.PugiName3 != "Pugi3" { - t.Errorf("PugiName3 = %q, want Pugi3", guild.PugiName3) - } -} - -func TestHandleChangePugi_AllNums(t *testing.T) { - srv := createMockServer() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - s := createMockSession(100, srv) - - handleChangePugi(s, 5, guild, 1) - if guild.PugiOutfit1 != 5 { - t.Errorf("PugiOutfit1 = %d, want 5", guild.PugiOutfit1) - } - - handleChangePugi(s, 10, guild, 2) - if guild.PugiOutfit2 != 10 { - t.Errorf("PugiOutfit2 = %d, want 10", guild.PugiOutfit2) - } - - handleChangePugi(s, 15, guild, 3) - if guild.PugiOutfit3 != 15 { - t.Errorf("PugiOutfit3 = %d, want 15", guild.PugiOutfit3) - } -} - -func TestHandleAvoidLeadershipUpdate_Success(t *testing.T) { - srv := createMockServer() - membership := &GuildMember{CharID: 100, AvoidLeadership: false} - srv.guildRepo = &mockGuildRepo{membership: membership} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateGuild{AckHandle: 1} - handleAvoidLeadershipUpdate(s, pkt, true) - <-s.sendPackets - - if !membership.AvoidLeadership { - t.Error("AvoidLeadership should be true") - } -} - -func TestHandleAvoidLeadershipUpdate_GetMembershipError(t *testing.T) { - srv := createMockServer() - srv.guildRepo = &mockGuildRepo{getMemberErr: errNotFound} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateGuild{AckHandle: 1} - handleAvoidLeadershipUpdate(s, pkt, true) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_guild_ops_test.go b/server/channelserver/handlers_guild_ops_test.go index 8bf315840..dccc4e244 100644 --- a/server/channelserver/handlers_guild_ops_test.go +++ b/server/channelserver/handlers_guild_ops_test.go @@ -5,6 +5,7 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" + "erupe-ce/common/stringsupport" ) // --- handleMsgMhfOperateGuild tests --- @@ -618,3 +619,104 @@ func newMottoBF(sub, main uint8) *byteframe.ByteFrame { _, _ = bf.Seek(0, 0) return bf } + +func TestHandleRenamePugi_Pugi1(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + s := createMockSession(100, srv) + + bf := byteframe.NewByteFrame() + nameBytes := stringsupport.UTF8ToSJIS("TestPugi") + bf.WriteBytes(nameBytes) + bf.WriteUint8(0) // null terminator + bf.Seek(0, 0) + + handleRenamePugi(s, bf, guild, 1) + if guild.PugiName1 != "TestPugi" { + t.Errorf("PugiName1 = %q, want TestPugi", guild.PugiName1) + } +} + +func TestHandleRenamePugi_Pugi2(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + s := createMockSession(100, srv) + + bf := byteframe.NewByteFrame() + nameBytes := stringsupport.UTF8ToSJIS("Pugi2") + bf.WriteBytes(nameBytes) + bf.WriteUint8(0) + bf.Seek(0, 0) + + handleRenamePugi(s, bf, guild, 2) + if guild.PugiName2 != "Pugi2" { + t.Errorf("PugiName2 = %q, want Pugi2", guild.PugiName2) + } +} + +func TestHandleRenamePugi_Pugi3Default(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + s := createMockSession(100, srv) + + bf := byteframe.NewByteFrame() + nameBytes := stringsupport.UTF8ToSJIS("Pugi3") + bf.WriteBytes(nameBytes) + bf.WriteUint8(0) + bf.Seek(0, 0) + + handleRenamePugi(s, bf, guild, 3) + if guild.PugiName3 != "Pugi3" { + t.Errorf("PugiName3 = %q, want Pugi3", guild.PugiName3) + } +} + +func TestHandleChangePugi_AllNums(t *testing.T) { + srv := createMockServer() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + s := createMockSession(100, srv) + + handleChangePugi(s, 5, guild, 1) + if guild.PugiOutfit1 != 5 { + t.Errorf("PugiOutfit1 = %d, want 5", guild.PugiOutfit1) + } + + handleChangePugi(s, 10, guild, 2) + if guild.PugiOutfit2 != 10 { + t.Errorf("PugiOutfit2 = %d, want 10", guild.PugiOutfit2) + } + + handleChangePugi(s, 15, guild, 3) + if guild.PugiOutfit3 != 15 { + t.Errorf("PugiOutfit3 = %d, want 15", guild.PugiOutfit3) + } +} + +func TestHandleAvoidLeadershipUpdate_Success(t *testing.T) { + srv := createMockServer() + membership := &GuildMember{CharID: 100, AvoidLeadership: false} + srv.guildRepo = &mockGuildRepo{membership: membership} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateGuild{AckHandle: 1} + handleAvoidLeadershipUpdate(s, pkt, true) + <-s.sendPackets + + if !membership.AvoidLeadership { + t.Error("AvoidLeadership should be true") + } +} + +func TestHandleAvoidLeadershipUpdate_GetMembershipError(t *testing.T) { + srv := createMockServer() + srv.guildRepo = &mockGuildRepo{getMemberErr: errNotFound} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateGuild{AckHandle: 1} + handleAvoidLeadershipUpdate(s, pkt, true) + <-s.sendPackets +} diff --git a/server/channelserver/handlers_guild_test.go b/server/channelserver/handlers_guild_test.go index 5e5556458..073a4ddc0 100644 --- a/server/channelserver/handlers_guild_test.go +++ b/server/channelserver/handlers_guild_test.go @@ -824,6 +824,26 @@ func TestGuildAllianceRelationship(t *testing.T) { } } +// --- handleMsgMhfGetUdGuildMapInfo tests --- + +func TestHandleMsgMhfGetUdGuildMapInfo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfGetUdGuildMapInfo(session, &mhfpacket.MsgMhfGetUdGuildMapInfo{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + // --- handleMsgMhfCheckMonthlyItem tests --- func TestCheckMonthlyItem_NotClaimed(t *testing.T) { @@ -902,6 +922,156 @@ func TestCheckMonthlyItem_UnknownType(t *testing.T) { } } +func TestHandleMsgMhfEntryRookieGuild(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEntryRookieGuild{ + AckHandle: 12345, + Unk: 42, + } + + handleMsgMhfEntryRookieGuild(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfGenerateUdGuildMap(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGenerateUdGuildMap{ + AckHandle: 12345, + } + + handleMsgMhfGenerateUdGuildMap(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateInvGuild(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateInvGuild{ + AckHandle: 12345, + } + + handleMsgMhfEnumerateInvGuild(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfOperationInvGuild(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfOperationInvGuild{ + AckHandle: 12345, + Operation: 1, + } + + handleMsgMhfOperationInvGuild(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfCheckMonthlyItem_Coverage2(t *testing.T) { + server := createMockServer() + server.stampRepo = &mockStampRepoForItems{monthlyClaimedErr: errNotFound} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfCheckMonthlyItem{ + AckHandle: 12345, + Type: 0, + } + + handleMsgMhfCheckMonthlyItem(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfAcquireMonthlyItem_Coverage2(t *testing.T) { + server := createMockServer() + server.stampRepo = &mockStampRepoForItems{} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfAcquireMonthlyItem{ + AckHandle: 12345, + } + + handleMsgMhfAcquireMonthlyItem(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestEmptyGuildHandlers(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + handler func(s *Session, p mhfpacket.MHFPacket) + }{ + {"handleMsgMhfUpdateForceGuildRank", handleMsgMhfUpdateForceGuildRank}, + {"handleMsgMhfUpdateGuild", handleMsgMhfUpdateGuild}, + {"handleMsgMhfUpdateGuildcard", handleMsgMhfUpdateGuildcard}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.handler(session, nil) + }) + } +} + func TestAcquireMonthlyItem_MarksAsClaimed(t *testing.T) { server := createMockServer() stampMock := &mockStampRepoForItems{} @@ -924,3 +1094,242 @@ func TestAcquireMonthlyItem_MarksAsClaimed(t *testing.T) { t.Error("No response packet queued") } } + +func TestHandleMsgMhfCreateGuild_Success(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfCreateGuild{AckHandle: 1, Name: "TestGuild"} + handleMsgMhfCreateGuild(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("expected non-empty response") + } + default: + t.Error("expected a response packet") + } +} + +func TestHandleMsgMhfCreateGuild_Error(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{saveErr: errNotFound} + // Mock Create to return error - the mockGuildRepo.Create returns (0, nil) + // We need getErr to make it fail. Actually Create is a no-op stub returning nil. + // Let's use a custom approach - we need the Create method to error. + // The mock's Create always returns nil, so let's test the success path worked above + // and test ArrangeGuildMember error paths instead. + session := createMockSession(100, server) + pkt := &mhfpacket.MsgMhfCreateGuild{AckHandle: 1, Name: "TestGuild"} + handleMsgMhfCreateGuild(session, pkt) + <-session.sendPackets // consume the response +} + +func TestHandleMsgMhfArrangeGuildMember_Success(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1, GuildLeader: GuildLeader{LeaderCharID: 100}} + server.guildRepo = &mockGuildRepo{guild: guild} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfArrangeGuildMember{ + AckHandle: 1, + GuildID: 1, + CharIDs: []uint32{100, 200, 300}, + } + handleMsgMhfArrangeGuildMember(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("expected response") + } +} + +func TestHandleMsgMhfArrangeGuildMember_GetByIDError(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{getErr: errNotFound} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfArrangeGuildMember{AckHandle: 1, GuildID: 999} + handleMsgMhfArrangeGuildMember(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfArrangeGuildMember_NotLeader(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1, GuildLeader: GuildLeader{LeaderCharID: 200, LeaderName: "Other"}} + server.guildRepo = &mockGuildRepo{guild: guild} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfArrangeGuildMember{AckHandle: 1, GuildID: 1} + handleMsgMhfArrangeGuildMember(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildMember_GuildIDPositive(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1, MemberCount: 2} + members := []*GuildMember{ + {CharID: 100, Name: "Player1", HR: 50, OrderIndex: 0, WeaponType: 3}, + {CharID: 200, Name: "Player2", HR: 100, OrderIndex: 1, WeaponType: 1}, + } + server.guildRepo = &mockGuildRepo{guild: guild, members: members} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 1} + handleMsgMhfEnumerateGuildMember(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildMember_GuildIDZero(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1, MemberCount: 1} + members := []*GuildMember{ + {CharID: 100, Name: "Player1", HR: 50, OrderIndex: 0}, + } + server.guildRepo = &mockGuildRepo{guild: guild, members: members} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 0} + handleMsgMhfEnumerateGuildMember(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildMember_NilGuild(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 0} + handleMsgMhfEnumerateGuildMember(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildMember_Applicant(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1} + server.guildRepo = &mockGuildRepo{guild: guild, hasAppResult: true} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 1} + handleMsgMhfEnumerateGuildMember(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfGetGuildManageRight(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1, MemberCount: 2} + members := []*GuildMember{ + {CharID: 100, Recruiter: true}, + {CharID: 200, Recruiter: false}, + } + server.guildRepo = &mockGuildRepo{guild: guild, members: members} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfGetGuildManageRight{AckHandle: 1} + handleMsgMhfGetGuildManageRight(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfGetGuildTargetMemberNum_NilGuild(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfGetGuildTargetMemberNum{AckHandle: 1, GuildID: 0} + handleMsgMhfGetGuildTargetMemberNum(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfGetGuildTargetMemberNum_WithGuild(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1, MemberCount: 5} + server.guildRepo = &mockGuildRepo{guild: guild} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfGetGuildTargetMemberNum{AckHandle: 1, GuildID: 1} + handleMsgMhfGetGuildTargetMemberNum(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfEnumerateGuildItem(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfEnumerateGuildItem{AckHandle: 1, GuildID: 1} + handleMsgMhfEnumerateGuildItem(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfUpdateGuildItem(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfUpdateGuildItem{AckHandle: 1, GuildID: 1} + handleMsgMhfUpdateGuildItem(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfUpdateGuildIcon_LeaderSuccess(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1} + membership := &GuildMember{CharID: 100, IsLeader: true} + server.guildRepo = &mockGuildRepo{guild: guild, membership: membership} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfUpdateGuildIcon{ + AckHandle: 1, + GuildID: 1, + IconParts: []mhfpacket.GuildIconMsgPart{ + {Index: 0, ID: 1, Page: 0, Size: 10, Rotation: 0, Red: 255, Green: 0, Blue: 0, PosX: 50, PosY: 50}, + }, + } + handleMsgMhfUpdateGuildIcon(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfUpdateGuildIcon_NotLeader(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 1} + membership := &GuildMember{CharID: 100, IsLeader: false, OrderIndex: 5} + server.guildRepo = &mockGuildRepo{guild: guild, membership: membership} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfUpdateGuildIcon{AckHandle: 1, GuildID: 1} + handleMsgMhfUpdateGuildIcon(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfUpdateGuildIcon_GetByIDError(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{getErr: errNotFound} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfUpdateGuildIcon{AckHandle: 1, GuildID: 999} + handleMsgMhfUpdateGuildIcon(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfReadGuildcard(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfReadGuildcard{AckHandle: 1} + handleMsgMhfReadGuildcard(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSetGuildManageRight(t *testing.T) { + server := createMockServer() + server.guildRepo = &mockGuildRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfSetGuildManageRight{AckHandle: 1, CharID: 200, Allowed: true} + handleMsgMhfSetGuildManageRight(session, pkt) + <-session.sendPackets +} diff --git a/server/channelserver/handlers_guild_tresure_test.go b/server/channelserver/handlers_guild_tresure_test.go index ee4ac47bc..153b9d778 100644 --- a/server/channelserver/handlers_guild_tresure_test.go +++ b/server/channelserver/handlers_guild_tresure_test.go @@ -186,6 +186,26 @@ func TestOperateGuildTresureReport_Claim(t *testing.T) { } } +// --- handleMsgMhfAcquireGuildTresureSouvenir tests --- + +func TestHandleMsgMhfAcquireGuildTresureSouvenir(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + handleMsgMhfAcquireGuildTresureSouvenir(session, &mhfpacket.MsgMhfAcquireGuildTresureSouvenir{ + AckHandle: 1, + }) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } +} + // --- handleMsgMhfGetGuildTresureSouvenir tests --- func TestGetGuildTresureSouvenir_Empty(t *testing.T) { diff --git a/server/channelserver/handlers_helpers_test.go b/server/channelserver/handlers_helpers_test.go index 821deb973..db1f4bac1 100644 --- a/server/channelserver/handlers_helpers_test.go +++ b/server/channelserver/handlers_helpers_test.go @@ -190,6 +190,77 @@ func TestUpdateRights(t *testing.T) { } } +// Tests consolidated from handlers_coverage3_test.go + +func TestStubHelpers(t *testing.T) { + server := createMockServer() + + t.Run("stubEnumerateNoResults", func(t *testing.T) { + session := createMockSession(1, server) + stubEnumerateNoResults(session, 1) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("doAckBufSucceed", func(t *testing.T) { + session := createMockSession(1, server) + doAckBufSucceed(session, 1, []byte{0x01, 0x02, 0x03}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("doAckBufFail", func(t *testing.T) { + session := createMockSession(1, server) + doAckBufFail(session, 1, []byte{0x01, 0x02, 0x03}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("doAckSimpleSucceed", func(t *testing.T) { + session := createMockSession(1, server) + doAckSimpleSucceed(session, 1, []byte{0x00, 0x00, 0x00, 0x00}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("doAckSimpleFail", func(t *testing.T) { + session := createMockSession(1, server) + doAckSimpleFail(session, 1, []byte{0x00, 0x00, 0x00, 0x00}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + func TestUpdateRights_Error(t *testing.T) { server := createMockServer() userRepo := &mockUserRepoGacha{rightsErr: errors.New("db error")} diff --git a/server/channelserver/handlers_house_coverage_test.go b/server/channelserver/handlers_house_coverage_test.go deleted file mode 100644 index 462b87e5e..000000000 --- a/server/channelserver/handlers_house_coverage_test.go +++ /dev/null @@ -1,265 +0,0 @@ -package channelserver - -import ( - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" - "testing" -) - -func TestHandleMsgMhfEnumerateHouse_Method3_SearchByName(t *testing.T) { - srv := createMockServer() - srv.erupeConfig.RealClientMode = cfg.ZZ - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 3, Name: "TestHouse"} - handleMsgMhfEnumerateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateHouse_Method4_ByCharID(t *testing.T) { - srv := createMockServer() - srv.erupeConfig.RealClientMode = cfg.ZZ - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 4, CharID: 200} - handleMsgMhfEnumerateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateHouse_Method5_RecentVisitors(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 5} - handleMsgMhfEnumerateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateHouse_Method1_Friends(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - charRepo := newMockCharacterRepo() - charRepo.strings["friends"] = "" - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 1} - handleMsgMhfEnumerateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateHouse_Method2_GuildMembers(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - guild := &Guild{ID: 1} - srv.guildRepo = &mockGuildRepo{guild: guild} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 2} - handleMsgMhfEnumerateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateHouse_Method2_NoGuild(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - srv.guildRepo = &mockGuildRepo{getErr: errNotFound} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 2} - handleMsgMhfEnumerateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfSaveDecoMyset_ShortPayload(t *testing.T) { - srv := createMockServer() - srv.charRepo = newMockCharacterRepo() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfSaveDecoMyset{AckHandle: 1, RawDataPayload: []byte{0x00, 0x01}} - handleMsgMhfSaveDecoMyset(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfSaveDecoMyset_WithData(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - // Pre-populate with version byte + 0 sets - charRepo.columns["decomyset"] = []byte{0x01, 0x00} - srv.charRepo = charRepo - srv.erupeConfig.RealClientMode = cfg.ZZ - - s := createMockSession(100, srv) - - // Build payload: version byte + 1 set with index 0 + 76 bytes of data - payload := make([]byte, 3+2+76) - payload[0] = 0x01 // version - payload[1] = 0x01 // count - payload[2] = 0x00 // padding - - pkt := &mhfpacket.MsgMhfSaveDecoMyset{AckHandle: 1, RawDataPayload: payload} - handleMsgMhfSaveDecoMyset(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfInfoTournament_Type2(t *testing.T) { - srv := createMockServer() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfInfoTournament{AckHandle: 1, QueryType: 2} - handleMsgMhfInfoTournament(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateInterior_Normal(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateInterior{AckHandle: 1, InteriorData: make([]byte, 20)} - handleMsgMhfUpdateInterior(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateInterior_TooLarge(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateInterior{AckHandle: 1, InteriorData: make([]byte, 100)} - handleMsgMhfUpdateInterior(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateMyhouseInfo_Normal(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{AckHandle: 1, Data: make([]byte, 9)} - handleMsgMhfUpdateMyhouseInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateMyhouseInfo_TooLarge(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{AckHandle: 1, Data: make([]byte, 600)} - handleMsgMhfUpdateMyhouseInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetMyhouseInfo(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetMyhouseInfo{AckHandle: 1} - handleMsgMhfGetMyhouseInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateTitle(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateTitle{AckHandle: 1} - handleMsgMhfEnumerateTitle(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfAcquireTitle(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfAcquireTitle{AckHandle: 1, TitleIDs: []uint16{1, 2, 3}} - handleMsgMhfAcquireTitle(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfUpdateHouse(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateHouse{AckHandle: 1, State: 2, Password: "1234"} - handleMsgMhfUpdateHouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfOperateWarehouse_Op0(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 0} - handleMsgMhfOperateWarehouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfOperateWarehouse_Op1(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 1} - handleMsgMhfOperateWarehouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfOperateWarehouse_Op2_Rename(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 2, BoxType: 0, BoxIndex: 1, Name: "MyBox"} - handleMsgMhfOperateWarehouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfOperateWarehouse_Op3(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 3} - handleMsgMhfOperateWarehouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfOperateWarehouse_Op4(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 4} - handleMsgMhfOperateWarehouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateWarehouse_Items(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateWarehouse{AckHandle: 1, BoxType: 0, BoxIndex: 0} - handleMsgMhfEnumerateWarehouse(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfEnumerateWarehouse_Equipment(t *testing.T) { - srv := createMockServer() - srv.houseRepo = newMockHouseRepoForItems() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfEnumerateWarehouse{AckHandle: 1, BoxType: 1, BoxIndex: 0} - handleMsgMhfEnumerateWarehouse(s, pkt) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_house_test.go b/server/channelserver/handlers_house_test.go index f9eff1f3a..7a4bbc286 100644 --- a/server/channelserver/handlers_house_test.go +++ b/server/channelserver/handlers_house_test.go @@ -1147,3 +1147,261 @@ func BenchmarkWarehouseEquipmentMerge(b *testing.B) { _ = finalEquip // Use finalEquip to avoid unused variable warning } } + +func TestHandleMsgMhfEnumerateHouse_Method3_SearchByName(t *testing.T) { + srv := createMockServer() + srv.erupeConfig.RealClientMode = cfg.ZZ + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 3, Name: "TestHouse"} + handleMsgMhfEnumerateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateHouse_Method4_ByCharID(t *testing.T) { + srv := createMockServer() + srv.erupeConfig.RealClientMode = cfg.ZZ + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 4, CharID: 200} + handleMsgMhfEnumerateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateHouse_Method5_RecentVisitors(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 5} + handleMsgMhfEnumerateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateHouse_Method1_Friends(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + charRepo := newMockCharacterRepo() + charRepo.strings["friends"] = "" + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 1} + handleMsgMhfEnumerateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateHouse_Method2_GuildMembers(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + guild := &Guild{ID: 1} + srv.guildRepo = &mockGuildRepo{guild: guild} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 2} + handleMsgMhfEnumerateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateHouse_Method2_NoGuild(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + srv.guildRepo = &mockGuildRepo{getErr: errNotFound} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateHouse{AckHandle: 1, Method: 2} + handleMsgMhfEnumerateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfSaveDecoMyset_ShortPayload(t *testing.T) { + srv := createMockServer() + srv.charRepo = newMockCharacterRepo() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfSaveDecoMyset{AckHandle: 1, RawDataPayload: []byte{0x00, 0x01}} + handleMsgMhfSaveDecoMyset(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfSaveDecoMyset_WithData(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + // Pre-populate with version byte + 0 sets + charRepo.columns["decomyset"] = []byte{0x01, 0x00} + srv.charRepo = charRepo + srv.erupeConfig.RealClientMode = cfg.ZZ + + s := createMockSession(100, srv) + + // Build payload: version byte + 1 set with index 0 + 76 bytes of data + payload := make([]byte, 3+2+76) + payload[0] = 0x01 // version + payload[1] = 0x01 // count + payload[2] = 0x00 // padding + + pkt := &mhfpacket.MsgMhfSaveDecoMyset{AckHandle: 1, RawDataPayload: payload} + handleMsgMhfSaveDecoMyset(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfInfoTournament_Type2(t *testing.T) { + srv := createMockServer() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfInfoTournament{AckHandle: 1, QueryType: 2} + handleMsgMhfInfoTournament(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateInterior_Normal(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateInterior{AckHandle: 1, InteriorData: make([]byte, 20)} + handleMsgMhfUpdateInterior(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateInterior_TooLarge(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateInterior{AckHandle: 1, InteriorData: make([]byte, 100)} + handleMsgMhfUpdateInterior(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateMyhouseInfo_Normal(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{AckHandle: 1, Data: make([]byte, 9)} + handleMsgMhfUpdateMyhouseInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateMyhouseInfo_TooLarge(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateMyhouseInfo{AckHandle: 1, Data: make([]byte, 600)} + handleMsgMhfUpdateMyhouseInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetMyhouseInfo(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetMyhouseInfo{AckHandle: 1} + handleMsgMhfGetMyhouseInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateTitle(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateTitle{AckHandle: 1} + handleMsgMhfEnumerateTitle(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfAcquireTitle(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfAcquireTitle{AckHandle: 1, TitleIDs: []uint16{1, 2, 3}} + handleMsgMhfAcquireTitle(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfUpdateHouse(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateHouse{AckHandle: 1, State: 2, Password: "1234"} + handleMsgMhfUpdateHouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfOperateWarehouse_Op0(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 0} + handleMsgMhfOperateWarehouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfOperateWarehouse_Op1(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 1} + handleMsgMhfOperateWarehouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfOperateWarehouse_Op2_Rename(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 2, BoxType: 0, BoxIndex: 1, Name: "MyBox"} + handleMsgMhfOperateWarehouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfOperateWarehouse_Op3(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 3} + handleMsgMhfOperateWarehouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfOperateWarehouse_Op4(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfOperateWarehouse{AckHandle: 1, Operation: 4} + handleMsgMhfOperateWarehouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateWarehouse_Items(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateWarehouse{AckHandle: 1, BoxType: 0, BoxIndex: 0} + handleMsgMhfEnumerateWarehouse(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfEnumerateWarehouse_Equipment(t *testing.T) { + srv := createMockServer() + srv.houseRepo = newMockHouseRepoForItems() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfEnumerateWarehouse{AckHandle: 1, BoxType: 1, BoxIndex: 0} + handleMsgMhfEnumerateWarehouse(s, pkt) + <-s.sendPackets +} diff --git a/server/channelserver/handlers_items_test.go b/server/channelserver/handlers_items_test.go index b880ddb63..00d85a371 100644 --- a/server/channelserver/handlers_items_test.go +++ b/server/channelserver/handlers_items_test.go @@ -322,6 +322,51 @@ func TestExchangeWeeklyStamp_ExchangeError(t *testing.T) { } } +// Tests consolidated from handlers_coverage3_test.go + +func TestNonTrivialHandlers_NoDB_Items(t *testing.T) { + server := createMockServer() + + t.Run("handleMsgMhfTransferItem", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfTransferItem(session, &mhfpacket.MsgMhfTransferItem{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfEnumerateOrder", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfEnumerateOrder(session, &mhfpacket.MsgMhfEnumerateOrder{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfEnumeratePrice", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfEnumeratePrice(session, &mhfpacket.MsgMhfEnumeratePrice{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + func TestExchangeWeeklyStamp_Yearly(t *testing.T) { server := createMockServer() stampMock := &mockStampRepoForItems{ @@ -362,3 +407,78 @@ func TestExchangeWeeklyStamp_Yearly(t *testing.T) { t.Error("No response packet queued") } } + +// Tests consolidated from handlers_core_test.go + +func TestHandleMsgMhfGetExtraInfo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfGetExtraInfo panicked: %v", r) + } + }() + + handleMsgMhfGetExtraInfo(session, nil) +} + +func TestHandleMsgMhfTransferItem(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfTransferItem{ + AckHandle: 12345, + } + + handleMsgMhfTransferItem(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfEnumeratePrice(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumeratePrice{ + AckHandle: 12345, + } + + handleMsgMhfEnumeratePrice(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfEnumerateOrder(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateOrder{ + AckHandle: 12345, + } + + handleMsgMhfEnumerateOrder(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} diff --git a/server/channelserver/handlers_mercenary_coverage_test.go b/server/channelserver/handlers_mercenary_coverage_test.go deleted file mode 100644 index 98f3e8dfe..000000000 --- a/server/channelserver/handlers_mercenary_coverage_test.go +++ /dev/null @@ -1,256 +0,0 @@ -package channelserver - -import ( - "testing" - - cfg "erupe-ce/config" - "erupe-ce/network/mhfpacket" -) - -func TestHandleMsgMhfLoadPartner(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfLoadPartner{AckHandle: 1} - handleMsgMhfLoadPartner(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSavePartner(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfSavePartner{AckHandle: 1, RawDataPayload: []byte{1, 2, 3, 4}} - handleMsgMhfSavePartner(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfLoadHunterNavi_G8(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - server.erupeConfig.RealClientMode = cfg.G10 - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfLoadHunterNavi{AckHandle: 1} - handleMsgMhfLoadHunterNavi(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfLoadHunterNavi_G7(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - server.erupeConfig.RealClientMode = cfg.G7 - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfLoadHunterNavi{AckHandle: 1} - handleMsgMhfLoadHunterNavi(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSaveHunterNavi_NoDiff(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - data := make([]byte, 100) - pkt := &mhfpacket.MsgMhfSaveHunterNavi{ - AckHandle: 1, - IsDataDiff: false, - RawDataPayload: data, - } - handleMsgMhfSaveHunterNavi(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSaveHunterNavi_Diff(t *testing.T) { - server := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.columns["hunternavi"] = make([]byte, 552) - server.charRepo = charRepo - server.erupeConfig.RealClientMode = cfg.G10 - session := createMockSession(100, server) - - // Create a valid diff payload (deltacomp format: pairs of offset+data) - // A simple diff: zero length means no changes - diffData := make([]byte, 4) // minimal diff - pkt := &mhfpacket.MsgMhfSaveHunterNavi{ - AckHandle: 1, - IsDataDiff: true, - RawDataPayload: diffData, - } - handleMsgMhfSaveHunterNavi(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSaveHunterNavi_OversizedPayload(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - data := make([]byte, 5000) // > 4096 - pkt := &mhfpacket.MsgMhfSaveHunterNavi{ - AckHandle: 1, - IsDataDiff: false, - RawDataPayload: data, - } - handleMsgMhfSaveHunterNavi(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfCreateMercenary_Success(t *testing.T) { - server := createMockServer() - server.mercenaryRepo = &mockMercenaryRepo{nextRastaID: 42} - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfCreateMercenary{AckHandle: 1} - handleMsgMhfCreateMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfCreateMercenary_Error(t *testing.T) { - server := createMockServer() - server.mercenaryRepo = &mockMercenaryRepo{rastaIDErr: errNotFound} - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfCreateMercenary{AckHandle: 1} - handleMsgMhfCreateMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSaveMercenary_Normal(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - mercData := make([]byte, 100) - // Write a uint32 index at the start - mercData[0] = 0 - mercData[1] = 0 - mercData[2] = 0 - mercData[3] = 1 - pkt := &mhfpacket.MsgMhfSaveMercenary{ - AckHandle: 1, - GCP: 500, - PactMercID: 10, - MercData: mercData, - } - handleMsgMhfSaveMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfSaveMercenary_Oversized(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfSaveMercenary{ - AckHandle: 1, - MercData: make([]byte, 70000), - } - handleMsgMhfSaveMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfReadMercenaryM_EmptyData(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfReadMercenaryM{AckHandle: 1, CharID: 200} - handleMsgMhfReadMercenaryM(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfReadMercenaryM_WithData(t *testing.T) { - server := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.columns["savemercenary"] = []byte{0x01, 0x02, 0x03, 0x04} - server.charRepo = charRepo - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfReadMercenaryM{AckHandle: 1, CharID: 100} - handleMsgMhfReadMercenaryM(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfContractMercenary_Op0(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfContractMercenary{AckHandle: 1, Op: 0, CID: 200, PactMercID: 42} - handleMsgMhfContractMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfContractMercenary_Op1(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfContractMercenary{AckHandle: 1, Op: 1} - handleMsgMhfContractMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfContractMercenary_Op2(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfContractMercenary{AckHandle: 1, Op: 2, CID: 200} - handleMsgMhfContractMercenary(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfReadMercenaryW_NoPact(t *testing.T) { - server := createMockServer() - charRepo := newMockCharacterRepo() - server.charRepo = charRepo - server.mercenaryRepo = &mockMercenaryRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfReadMercenaryW{AckHandle: 1, Op: 0} - handleMsgMhfReadMercenaryW(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfReadMercenaryW_WithPact(t *testing.T) { - server := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.ints["pact_id"] = 42 - server.charRepo = charRepo - server.mercenaryRepo = &mockMercenaryRepo{} - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfReadMercenaryW{AckHandle: 1, Op: 0} - handleMsgMhfReadMercenaryW(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfReadMercenaryW_Op2(t *testing.T) { - server := createMockServer() - charRepo := newMockCharacterRepo() - server.charRepo = charRepo - server.mercenaryRepo = &mockMercenaryRepo{} - session := createMockSession(100, server) - - // Op 2 skips loan enumeration - pkt := &mhfpacket.MsgMhfReadMercenaryW{AckHandle: 1, Op: 2} - handleMsgMhfReadMercenaryW(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgMhfLoadOtomoAirou(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfLoadOtomoAirou{AckHandle: 1} - handleMsgMhfLoadOtomoAirou(session, pkt) - <-session.sendPackets -} diff --git a/server/channelserver/handlers_mercenary_test.go b/server/channelserver/handlers_mercenary_test.go index 2bb4ed2b7..74d963379 100644 --- a/server/channelserver/handlers_mercenary_test.go +++ b/server/channelserver/handlers_mercenary_test.go @@ -7,6 +7,7 @@ import ( "erupe-ce/common/byteframe" "erupe-ce/network/mhfpacket" + cfg "erupe-ce/config" ) func TestHandleMsgMhfLoadLegendDispatch(t *testing.T) { @@ -232,6 +233,119 @@ func TestGetAirouDetails_CatNamePadding(t *testing.T) { } } +func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs1(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfMercenaryHuntdata{ + AckHandle: 12345, + RequestType: 1, + } + + handleMsgMhfMercenaryHuntdata(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs0(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfMercenaryHuntdata{ + AckHandle: 12345, + RequestType: 0, + } + + handleMsgMhfMercenaryHuntdata(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs2(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfMercenaryHuntdata{ + AckHandle: 12345, + RequestType: 2, + } + + handleMsgMhfMercenaryHuntdata(session, pkt) + + // RequestType=2 takes the else branch (same as 0) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfLoadLegendDispatch_Response(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadLegendDispatch{ + AckHandle: 22222, + } + + handleMsgMhfLoadLegendDispatch(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestAirouStruct(t *testing.T) { + cat := Airou{ + ID: 42, + Name: []byte("TestCat"), + Task: 4, + Personality: 2, + Class: 1, + Experience: 1500, + WeaponType: 6, + WeaponID: 100, + } + + if cat.ID != 42 { + t.Errorf("ID = %d, want 42", cat.ID) + } + if cat.Task != 4 { + t.Errorf("Task = %d, want 4", cat.Task) + } + if cat.Experience != 1500 { + t.Errorf("Experience = %d, want 1500", cat.Experience) + } + if cat.WeaponType != 6 { + t.Errorf("WeaponType = %d, want 6", cat.WeaponType) + } + if cat.WeaponID != 100 { + t.Errorf("WeaponID = %d, want 100", cat.WeaponID) + } +} + // TestHandleMsgMhfMercenaryHuntdata_Unk0_1 tests with Unk0=1 (returns 1 byte) func TestHandleMsgMhfMercenaryHuntdata_Unk0_1(t *testing.T) { server := createMockServer() @@ -296,3 +410,251 @@ func TestHandleMsgMhfEnumerateMercenaryLog(t *testing.T) { t.Error("No response packet queued") } } + +func TestHandleMsgMhfLoadPartner(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfLoadPartner{AckHandle: 1} + handleMsgMhfLoadPartner(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSavePartner(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfSavePartner{AckHandle: 1, RawDataPayload: []byte{1, 2, 3, 4}} + handleMsgMhfSavePartner(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfLoadHunterNavi_G8(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + server.erupeConfig.RealClientMode = cfg.G10 + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfLoadHunterNavi{AckHandle: 1} + handleMsgMhfLoadHunterNavi(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfLoadHunterNavi_G7(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + server.erupeConfig.RealClientMode = cfg.G7 + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfLoadHunterNavi{AckHandle: 1} + handleMsgMhfLoadHunterNavi(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSaveHunterNavi_NoDiff(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + data := make([]byte, 100) + pkt := &mhfpacket.MsgMhfSaveHunterNavi{ + AckHandle: 1, + IsDataDiff: false, + RawDataPayload: data, + } + handleMsgMhfSaveHunterNavi(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSaveHunterNavi_Diff(t *testing.T) { + server := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.columns["hunternavi"] = make([]byte, 552) + server.charRepo = charRepo + server.erupeConfig.RealClientMode = cfg.G10 + session := createMockSession(100, server) + + // Create a valid diff payload (deltacomp format: pairs of offset+data) + // A simple diff: zero length means no changes + diffData := make([]byte, 4) // minimal diff + pkt := &mhfpacket.MsgMhfSaveHunterNavi{ + AckHandle: 1, + IsDataDiff: true, + RawDataPayload: diffData, + } + handleMsgMhfSaveHunterNavi(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSaveHunterNavi_OversizedPayload(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + data := make([]byte, 5000) // > 4096 + pkt := &mhfpacket.MsgMhfSaveHunterNavi{ + AckHandle: 1, + IsDataDiff: false, + RawDataPayload: data, + } + handleMsgMhfSaveHunterNavi(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfCreateMercenary_Success(t *testing.T) { + server := createMockServer() + server.mercenaryRepo = &mockMercenaryRepo{nextRastaID: 42} + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfCreateMercenary{AckHandle: 1} + handleMsgMhfCreateMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfCreateMercenary_Error(t *testing.T) { + server := createMockServer() + server.mercenaryRepo = &mockMercenaryRepo{rastaIDErr: errNotFound} + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfCreateMercenary{AckHandle: 1} + handleMsgMhfCreateMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSaveMercenary_Normal(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + mercData := make([]byte, 100) + // Write a uint32 index at the start + mercData[0] = 0 + mercData[1] = 0 + mercData[2] = 0 + mercData[3] = 1 + pkt := &mhfpacket.MsgMhfSaveMercenary{ + AckHandle: 1, + GCP: 500, + PactMercID: 10, + MercData: mercData, + } + handleMsgMhfSaveMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfSaveMercenary_Oversized(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfSaveMercenary{ + AckHandle: 1, + MercData: make([]byte, 70000), + } + handleMsgMhfSaveMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfReadMercenaryM_EmptyData(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfReadMercenaryM{AckHandle: 1, CharID: 200} + handleMsgMhfReadMercenaryM(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfReadMercenaryM_WithData(t *testing.T) { + server := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.columns["savemercenary"] = []byte{0x01, 0x02, 0x03, 0x04} + server.charRepo = charRepo + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfReadMercenaryM{AckHandle: 1, CharID: 100} + handleMsgMhfReadMercenaryM(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfContractMercenary_Op0(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfContractMercenary{AckHandle: 1, Op: 0, CID: 200, PactMercID: 42} + handleMsgMhfContractMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfContractMercenary_Op1(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfContractMercenary{AckHandle: 1, Op: 1} + handleMsgMhfContractMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfContractMercenary_Op2(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfContractMercenary{AckHandle: 1, Op: 2, CID: 200} + handleMsgMhfContractMercenary(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfReadMercenaryW_NoPact(t *testing.T) { + server := createMockServer() + charRepo := newMockCharacterRepo() + server.charRepo = charRepo + server.mercenaryRepo = &mockMercenaryRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfReadMercenaryW{AckHandle: 1, Op: 0} + handleMsgMhfReadMercenaryW(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfReadMercenaryW_WithPact(t *testing.T) { + server := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.ints["pact_id"] = 42 + server.charRepo = charRepo + server.mercenaryRepo = &mockMercenaryRepo{} + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfReadMercenaryW{AckHandle: 1, Op: 0} + handleMsgMhfReadMercenaryW(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfReadMercenaryW_Op2(t *testing.T) { + server := createMockServer() + charRepo := newMockCharacterRepo() + server.charRepo = charRepo + server.mercenaryRepo = &mockMercenaryRepo{} + session := createMockSession(100, server) + + // Op 2 skips loan enumeration + pkt := &mhfpacket.MsgMhfReadMercenaryW{AckHandle: 1, Op: 2} + handleMsgMhfReadMercenaryW(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgMhfLoadOtomoAirou(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfLoadOtomoAirou{AckHandle: 1} + handleMsgMhfLoadOtomoAirou(session, pkt) + <-session.sendPackets +} diff --git a/server/channelserver/handlers_misc_coverage_test.go b/server/channelserver/handlers_misc_coverage_test.go deleted file mode 100644 index b8af7316b..000000000 --- a/server/channelserver/handlers_misc_coverage_test.go +++ /dev/null @@ -1,313 +0,0 @@ -package channelserver - -import ( - "erupe-ce/network/mhfpacket" - "testing" -) - -// --- Enhanced Minidata tests (in-memory store, no DB) --- - -func TestHandleMsgMhfGetEnhancedMinidata_NotFound(t *testing.T) { - srv := createMockServer() - srv.minidata = NewMinidataStore() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetEnhancedMinidata{AckHandle: 1, CharID: 999} - handleMsgMhfGetEnhancedMinidata(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfGetEnhancedMinidata_Found(t *testing.T) { - srv := createMockServer() - srv.minidata = NewMinidataStore() - srv.minidata.Set(42, []byte{0xDE, 0xAD, 0xBE, 0xEF}) - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetEnhancedMinidata{AckHandle: 1, CharID: 42} - handleMsgMhfGetEnhancedMinidata(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfSetEnhancedMinidata(t *testing.T) { - srv := createMockServer() - srv.minidata = NewMinidataStore() - s := createMockSession(100, srv) - - payload := []byte{0x01, 0x02, 0x03} - pkt := &mhfpacket.MsgMhfSetEnhancedMinidata{AckHandle: 1, RawDataPayload: payload} - handleMsgMhfSetEnhancedMinidata(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - data, ok := srv.minidata.Get(100) - if !ok { - t.Fatal("Minidata not stored") - } - if len(data) != 3 || data[0] != 0x01 { - t.Errorf("Unexpected stored data: %v", data) - } -} - -// --- Trend Weapon tests --- - -func TestHandleMsgMhfGetTrendWeapon_Empty(t *testing.T) { - srv := createMockServer() - srv.miscRepo = &mockMiscRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetTrendWeapon{AckHandle: 1} - handleMsgMhfGetTrendWeapon(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfGetTrendWeapon_WithWeapons(t *testing.T) { - srv := createMockServer() - srv.miscRepo = &mockMiscRepo{ - trendWeapons: []uint16{100, 200, 300}, - } - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetTrendWeapon{AckHandle: 1} - handleMsgMhfGetTrendWeapon(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfUpdateUseTrendWeaponLog(t *testing.T) { - srv := createMockServer() - srv.miscRepo = &mockMiscRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateUseTrendWeaponLog{AckHandle: 1, WeaponType: 3, WeaponID: 500} - handleMsgMhfUpdateUseTrendWeaponLog(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } -} - -// --- Etc Points tests --- - -func TestHandleMsgMhfGetEtcPoints(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.etcBonusQuests = 100 - charRepo.etcDailyQuests = 50 - charRepo.etcPromoPoints = 25 - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetEtcPoints{AckHandle: 1} - handleMsgMhfGetEtcPoints(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfUpdateEtcPoint(t *testing.T) { - tests := []struct { - name string - pointType uint8 - delta int16 - column string - }{ - {"bonus_quests", 0, 5, "bonus_quests"}, - {"daily_quests", 1, 3, "daily_quests"}, - {"promo_points", 2, 1, "promo_points"}, - {"invalid_type", 99, 1, ""}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateEtcPoint{ - AckHandle: 1, - PointType: tt.pointType, - Delta: tt.delta, - } - handleMsgMhfUpdateEtcPoint(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - if tt.column != "" { - val := charRepo.ints[tt.column] - if val != int(tt.delta) { - t.Errorf("Expected %s=%d, got %d", tt.column, tt.delta, val) - } - } - }) - } -} - -func TestHandleMsgMhfUpdateEtcPoint_NegativeDelta(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.ints["bonus_quests"] = 10 - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateEtcPoint{AckHandle: 1, PointType: 0, Delta: -5} - handleMsgMhfUpdateEtcPoint(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - if charRepo.ints["bonus_quests"] != 5 { - t.Errorf("Expected bonus_quests=5, got %d", charRepo.ints["bonus_quests"]) - } -} - -func TestHandleMsgMhfUpdateEtcPoint_ClampToZero(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - charRepo.ints["bonus_quests"] = 3 - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateEtcPoint{AckHandle: 1, PointType: 0, Delta: -10} - handleMsgMhfUpdateEtcPoint(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - if charRepo.ints["bonus_quests"] != 0 { - t.Errorf("Expected bonus_quests=0, got %d", charRepo.ints["bonus_quests"]) - } -} - -// --- Equip Skin History tests --- - -func TestHandleMsgMhfGetEquipSkinHist(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetEquipSkinHist{AckHandle: 1} - handleMsgMhfGetEquipSkinHist(s, pkt) - - select { - case p := <-s.sendPackets: - if len(p.data) == 0 { - t.Fatal("Expected non-empty response") - } - default: - t.Fatal("No response packet queued") - } -} - -func TestHandleMsgMhfUpdateEquipSkinHist_Valid(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateEquipSkinHist{AckHandle: 1, ArmourID: 10001, MogType: 0} - handleMsgMhfUpdateEquipSkinHist(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - if _, ok := charRepo.columns["skin_hist"]; !ok { - t.Error("Expected skin_hist to be saved") - } -} - -func TestHandleMsgMhfUpdateEquipSkinHist_LowArmourID(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateEquipSkinHist{AckHandle: 1, ArmourID: 5000, MogType: 0} - handleMsgMhfUpdateEquipSkinHist(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - if _, ok := charRepo.columns["skin_hist"]; ok { - t.Error("Expected skin_hist NOT to be saved for low ArmourID") - } -} - -func TestHandleMsgMhfUpdateEquipSkinHist_HighMogType(t *testing.T) { - srv := createMockServer() - charRepo := newMockCharacterRepo() - srv.charRepo = charRepo - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfUpdateEquipSkinHist{AckHandle: 1, ArmourID: 10001, MogType: 5} - handleMsgMhfUpdateEquipSkinHist(s, pkt) - - select { - case <-s.sendPackets: - default: - t.Fatal("No response packet queued") - } - - if _, ok := charRepo.columns["skin_hist"]; ok { - t.Error("Expected skin_hist NOT to be saved for high MogType") - } -} diff --git a/server/channelserver/handlers_misc_test.go b/server/channelserver/handlers_misc_test.go index 26fbd67d5..71baac561 100644 --- a/server/channelserver/handlers_misc_test.go +++ b/server/channelserver/handlers_misc_test.go @@ -3,6 +3,7 @@ package channelserver import ( "testing" + cfg "erupe-ce/config" "erupe-ce/network/mhfpacket" ) @@ -559,6 +560,38 @@ func TestHandleMsgMhfGetLobbyCrowd(t *testing.T) { } } +// ============================================================================= +// equipSkinHistSize: pure function, tests all 3 config branches +// ============================================================================= + +func TestEquipSkinHistSize_Default(t *testing.T) { + got := equipSkinHistSize(cfg.ZZ) + if got != 3200 { + t.Errorf("equipSkinHistSize(ZZ) = %d, want 3200", got) + } +} + +func TestEquipSkinHistSize_Z2(t *testing.T) { + got := equipSkinHistSize(cfg.Z2) + if got != 2560 { + t.Errorf("equipSkinHistSize(Z2) = %d, want 2560", got) + } +} + +func TestEquipSkinHistSize_Z1(t *testing.T) { + got := equipSkinHistSize(cfg.Z1) + if got != 1280 { + t.Errorf("equipSkinHistSize(Z1) = %d, want 1280", got) + } +} + +func TestEquipSkinHistSize_OlderMode(t *testing.T) { + got := equipSkinHistSize(cfg.G1) + if got != 1280 { + t.Errorf("equipSkinHistSize(G1) = %d, want 1280", got) + } +} + // Distribution struct tests func TestDistributionStruct(t *testing.T) { dist := Distribution{ @@ -599,3 +632,421 @@ func TestDistributionItemStruct(t *testing.T) { t.Errorf("ItemID = %d, want 1234", item.ItemID) } } + +// --- Enhanced Minidata tests (in-memory store, no DB) --- + +func TestHandleMsgMhfGetEnhancedMinidata_NotFound(t *testing.T) { + srv := createMockServer() + srv.minidata = NewMinidataStore() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetEnhancedMinidata{AckHandle: 1, CharID: 999} + handleMsgMhfGetEnhancedMinidata(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetEnhancedMinidata_Found(t *testing.T) { + srv := createMockServer() + srv.minidata = NewMinidataStore() + srv.minidata.Set(42, []byte{0xDE, 0xAD, 0xBE, 0xEF}) + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetEnhancedMinidata{AckHandle: 1, CharID: 42} + handleMsgMhfGetEnhancedMinidata(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfSetEnhancedMinidata(t *testing.T) { + srv := createMockServer() + srv.minidata = NewMinidataStore() + s := createMockSession(100, srv) + + payload := []byte{0x01, 0x02, 0x03} + pkt := &mhfpacket.MsgMhfSetEnhancedMinidata{AckHandle: 1, RawDataPayload: payload} + handleMsgMhfSetEnhancedMinidata(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + data, ok := srv.minidata.Get(100) + if !ok { + t.Fatal("Minidata not stored") + } + if len(data) != 3 || data[0] != 0x01 { + t.Errorf("Unexpected stored data: %v", data) + } +} + +// --- Trend Weapon tests --- + +func TestHandleMsgMhfGetTrendWeapon_Empty(t *testing.T) { + srv := createMockServer() + srv.miscRepo = &mockMiscRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetTrendWeapon{AckHandle: 1} + handleMsgMhfGetTrendWeapon(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfGetTrendWeapon_WithWeapons(t *testing.T) { + srv := createMockServer() + srv.miscRepo = &mockMiscRepo{ + trendWeapons: []uint16{100, 200, 300}, + } + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetTrendWeapon{AckHandle: 1} + handleMsgMhfGetTrendWeapon(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfUpdateUseTrendWeaponLog(t *testing.T) { + srv := createMockServer() + srv.miscRepo = &mockMiscRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateUseTrendWeaponLog{AckHandle: 1, WeaponType: 3, WeaponID: 500} + handleMsgMhfUpdateUseTrendWeaponLog(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } +} + +// --- Etc Points tests --- + +func TestHandleMsgMhfGetEtcPoints(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.etcBonusQuests = 100 + charRepo.etcDailyQuests = 50 + charRepo.etcPromoPoints = 25 + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetEtcPoints{AckHandle: 1} + handleMsgMhfGetEtcPoints(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfUpdateEtcPoint(t *testing.T) { + tests := []struct { + name string + pointType uint8 + delta int16 + column string + }{ + {"bonus_quests", 0, 5, "bonus_quests"}, + {"daily_quests", 1, 3, "daily_quests"}, + {"promo_points", 2, 1, "promo_points"}, + {"invalid_type", 99, 1, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateEtcPoint{ + AckHandle: 1, + PointType: tt.pointType, + Delta: tt.delta, + } + handleMsgMhfUpdateEtcPoint(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + if tt.column != "" { + val := charRepo.ints[tt.column] + if val != int(tt.delta) { + t.Errorf("Expected %s=%d, got %d", tt.column, tt.delta, val) + } + } + }) + } +} + +func TestHandleMsgMhfUpdateEtcPoint_NegativeDelta(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.ints["bonus_quests"] = 10 + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateEtcPoint{AckHandle: 1, PointType: 0, Delta: -5} + handleMsgMhfUpdateEtcPoint(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + if charRepo.ints["bonus_quests"] != 5 { + t.Errorf("Expected bonus_quests=5, got %d", charRepo.ints["bonus_quests"]) + } +} + +func TestHandleMsgMhfUpdateEtcPoint_ClampToZero(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + charRepo.ints["bonus_quests"] = 3 + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateEtcPoint{AckHandle: 1, PointType: 0, Delta: -10} + handleMsgMhfUpdateEtcPoint(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + if charRepo.ints["bonus_quests"] != 0 { + t.Errorf("Expected bonus_quests=0, got %d", charRepo.ints["bonus_quests"]) + } +} + +// --- Equip Skin History tests --- + +func TestHandleMsgMhfGetEquipSkinHist(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetEquipSkinHist{AckHandle: 1} + handleMsgMhfGetEquipSkinHist(s, pkt) + + select { + case p := <-s.sendPackets: + if len(p.data) == 0 { + t.Fatal("Expected non-empty response") + } + default: + t.Fatal("No response packet queued") + } +} + +func TestHandleMsgMhfUpdateEquipSkinHist_Valid(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateEquipSkinHist{AckHandle: 1, ArmourID: 10001, MogType: 0} + handleMsgMhfUpdateEquipSkinHist(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + if _, ok := charRepo.columns["skin_hist"]; !ok { + t.Error("Expected skin_hist to be saved") + } +} + +func TestHandleMsgMhfUpdateEquipSkinHist_LowArmourID(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateEquipSkinHist{AckHandle: 1, ArmourID: 5000, MogType: 0} + handleMsgMhfUpdateEquipSkinHist(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + if _, ok := charRepo.columns["skin_hist"]; ok { + t.Error("Expected skin_hist NOT to be saved for low ArmourID") + } +} + +func TestHandleMsgMhfUpdateEquipSkinHist_HighMogType(t *testing.T) { + srv := createMockServer() + charRepo := newMockCharacterRepo() + srv.charRepo = charRepo + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfUpdateEquipSkinHist{AckHandle: 1, ArmourID: 10001, MogType: 5} + handleMsgMhfUpdateEquipSkinHist(s, pkt) + + select { + case <-s.sendPackets: + default: + t.Fatal("No response packet queued") + } + + if _, ok := charRepo.columns["skin_hist"]; ok { + t.Error("Expected skin_hist NOT to be saved for high MogType") + } +} + +// Tests consolidated from handlers_coverage3_test.go + +func TestNonTrivialHandlers_NoDB_Misc(t *testing.T) { + server := createMockServer() + + t.Run("handleMsgMhfGetEarthStatus", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetEarthStatus(session, &mhfpacket.MsgMhfGetEarthStatus{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfGetEarthValue_Type1", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetEarthValue(session, &mhfpacket.MsgMhfGetEarthValue{AckHandle: 1, ReqType: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfGetEarthValue_Type2", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetEarthValue(session, &mhfpacket.MsgMhfGetEarthValue{AckHandle: 1, ReqType: 2}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfGetEarthValue_Type3", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetEarthValue(session, &mhfpacket.MsgMhfGetEarthValue{AckHandle: 1, ReqType: 3}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfGetUdShopCoin", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetUdShopCoin(session, &mhfpacket.MsgMhfGetUdShopCoin{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfGetLobbyCrowd", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetLobbyCrowd(session, &mhfpacket.MsgMhfGetLobbyCrowd{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + +func TestEmptyHandlers_MiscFiles_Misc(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgMhfGetCogInfo", func() { handleMsgMhfGetCogInfo(session, nil) }}, + {"handleMsgMhfUseUdShopCoin", func() { handleMsgMhfUseUdShopCoin(session, nil) }}, + {"handleMsgMhfGetDailyMissionMaster", func() { handleMsgMhfGetDailyMissionMaster(session, nil) }}, + {"handleMsgMhfGetDailyMissionPersonal", func() { handleMsgMhfGetDailyMissionPersonal(session, nil) }}, + {"handleMsgMhfSetDailyMissionPersonal", func() { handleMsgMhfSetDailyMissionPersonal(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} diff --git a/server/channelserver/handlers_object_test.go b/server/channelserver/handlers_object_test.go index e6858552f..6c63088a1 100644 --- a/server/channelserver/handlers_object_test.go +++ b/server/channelserver/handlers_object_test.go @@ -369,3 +369,60 @@ func TestObjectHandlers_SequentialPositionUpdate(t *testing.T) { obj.x, obj.y, obj.z) } } + +// Tests consolidated from handlers_coverage3_test.go + +func TestEmptyHandlers_ObjectGo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgSysDeleteObject", func() { handleMsgSysDeleteObject(session, nil) }}, + {"handleMsgSysRotateObject", func() { handleMsgSysRotateObject(session, nil) }}, + {"handleMsgSysDuplicateObject", func() { handleMsgSysDuplicateObject(session, nil) }}, + {"handleMsgSysGetObjectBinary", func() { handleMsgSysGetObjectBinary(session, nil) }}, + {"handleMsgSysGetObjectOwner", func() { handleMsgSysGetObjectOwner(session, nil) }}, + {"handleMsgSysUpdateObjectBinary", func() { handleMsgSysUpdateObjectBinary(session, nil) }}, + {"handleMsgSysCleanupObject", func() { handleMsgSysCleanupObject(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} + +func TestEmptyHandlers_MiscFiles_Object(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgSysAddObject", func() { handleMsgSysAddObject(session, nil) }}, + {"handleMsgSysDelObject", func() { handleMsgSysDelObject(session, nil) }}, + {"handleMsgSysDispObject", func() { handleMsgSysDispObject(session, nil) }}, + {"handleMsgSysHideObject", func() { handleMsgSysHideObject(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} diff --git a/server/channelserver/handlers_quest_coverage_test.go b/server/channelserver/handlers_quest_coverage_test.go deleted file mode 100644 index d60e28eba..000000000 --- a/server/channelserver/handlers_quest_coverage_test.go +++ /dev/null @@ -1,40 +0,0 @@ -package channelserver - -import ( - "testing" - - "erupe-ce/network/mhfpacket" -) - -func TestHandleMsgMhfLoadFavoriteQuest(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfLoadFavoriteQuest{AckHandle: 1} - handleMsgMhfLoadFavoriteQuest(session, pkt) - - select { - case <-session.sendPackets: - default: - t.Error("expected response") - } -} - -func TestHandleMsgMhfSaveFavoriteQuest(t *testing.T) { - server := createMockServer() - server.charRepo = newMockCharacterRepo() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgMhfSaveFavoriteQuest{ - AckHandle: 1, - Data: []byte{0x01, 0x00, 0x01, 0x00, 0x01}, - } - handleMsgMhfSaveFavoriteQuest(session, pkt) - - select { - case <-session.sendPackets: - default: - t.Error("expected response") - } -} diff --git a/server/channelserver/handlers_quest_test.go b/server/channelserver/handlers_quest_test.go index e6f6a5d6a..16f3def5b 100644 --- a/server/channelserver/handlers_quest_test.go +++ b/server/channelserver/handlers_quest_test.go @@ -782,3 +782,36 @@ func TestHandleMsgSysGetFile_ExistingQuestFile(t *testing.T) { t.Errorf("expected success ack (ErrorCode=0) for existing quest file, got ErrorCode=%d", errorCode) } } + +func TestHandleMsgMhfLoadFavoriteQuest(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfLoadFavoriteQuest{AckHandle: 1} + handleMsgMhfLoadFavoriteQuest(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("expected response") + } +} + +func TestHandleMsgMhfSaveFavoriteQuest(t *testing.T) { + server := createMockServer() + server.charRepo = newMockCharacterRepo() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgMhfSaveFavoriteQuest{ + AckHandle: 1, + Data: []byte{0x01, 0x00, 0x01, 0x00, 0x01}, + } + handleMsgMhfSaveFavoriteQuest(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("expected response") + } +} diff --git a/server/channelserver/handlers_rengoku_test.go b/server/channelserver/handlers_rengoku_test.go index 605068e26..61679d651 100644 --- a/server/channelserver/handlers_rengoku_test.go +++ b/server/channelserver/handlers_rengoku_test.go @@ -51,3 +51,79 @@ func TestRengokuScoreStruct_DefaultValues(t *testing.T) { t.Errorf("Default Score should be 0, got %d", score.Score) } } + +func TestHandleMsgMhfGetRengokuRankingRank_ResponseData(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{ + AckHandle: 55555, + } + + handleMsgMhfGetRengokuRankingRank(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestRengokuScoreStruct_Fields(t *testing.T) { + score := RengokuScore{ + Name: "Hunter", + Score: 99999, + } + + if score.Name != "Hunter" { + t.Errorf("Name = %s, want Hunter", score.Name) + } + if score.Score != 99999 { + t.Errorf("Score = %d, want 99999", score.Score) + } +} + +// TestHandleMsgMhfGetRengokuRankingRank_DifferentAck verifies rengoku ranking +// works with different ack handles. +func TestHandleMsgMhfGetRengokuRankingRank_DifferentAck(t *testing.T) { + server := createMockServer() + + ackHandles := []uint32{0, 1, 54321, 0xDEADBEEF} + for _, ack := range ackHandles { + session := createMockSession(1, server) + pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: ack} + + handleMsgMhfGetRengokuRankingRank(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("AckHandle=%d: Response packet should have data", ack) + } + default: + t.Errorf("AckHandle=%d: No response packet queued", ack) + } + } +} + +// Tests consolidated from handlers_coverage3_test.go + +func TestNonTrivialHandlers_RengokuGo(t *testing.T) { + server := createMockServer() + + t.Run("handleMsgMhfGetRengokuRankingRank", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetRengokuRankingRank(session, &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} diff --git a/server/channelserver/handlers_reward_test.go b/server/channelserver/handlers_reward_test.go index ff2770eb0..028b20865 100644 --- a/server/channelserver/handlers_reward_test.go +++ b/server/channelserver/handlers_reward_test.go @@ -124,3 +124,92 @@ func TestHandleMsgMhfAcceptReadReward(t *testing.T) { handleMsgMhfAcceptReadReward(session, nil) } + +// Tests consolidated from handlers_coverage3_test.go + +func TestSimpleAckHandlers_RewardGo(t *testing.T) { + server := createMockServer() + + tests := []struct { + name string + fn func(s *Session) + }{ + {"handleMsgMhfGetRewardSong", func(s *Session) { + handleMsgMhfGetRewardSong(s, &mhfpacket.MsgMhfGetRewardSong{AckHandle: 1}) + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + session := createMockSession(1, server) + tt.fn(session) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("%s: response should have data", tt.name) + } + default: + t.Errorf("%s: no response queued", tt.name) + } + }) + } +} + +func TestNonTrivialHandlers_RewardGo(t *testing.T) { + server := createMockServer() + + tests := []struct { + name string + fn func(s *Session) + }{ + {"handleMsgMhfGetAdditionalBeatReward", func(s *Session) { + handleMsgMhfGetAdditionalBeatReward(s, &mhfpacket.MsgMhfGetAdditionalBeatReward{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdRankingRewardList", func(s *Session) { + handleMsgMhfGetUdRankingRewardList(s, &mhfpacket.MsgMhfGetUdRankingRewardList{AckHandle: 1}) + }}, + {"handleMsgMhfAcquireMonthlyReward", func(s *Session) { + handleMsgMhfAcquireMonthlyReward(s, &mhfpacket.MsgMhfAcquireMonthlyReward{AckHandle: 1}) + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + session := createMockSession(1, server) + tt.fn(session) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("%s: response should have data", tt.name) + } + default: + t.Errorf("%s: no response queued", tt.name) + } + }) + } +} + +func TestEmptyHandlers_MiscFiles_Reward(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgMhfUseRewardSong", func() { handleMsgMhfUseRewardSong(session, nil) }}, + {"handleMsgMhfAddRewardSongCount", func() { handleMsgMhfAddRewardSongCount(session, nil) }}, + {"handleMsgMhfAcceptReadReward", func() { handleMsgMhfAcceptReadReward(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} diff --git a/server/channelserver/handlers_seibattle_test.go b/server/channelserver/handlers_seibattle_test.go index 6e02141b2..6b0ac9e2c 100644 --- a/server/channelserver/handlers_seibattle_test.go +++ b/server/channelserver/handlers_seibattle_test.go @@ -209,6 +209,55 @@ func TestHandleMsgMhfReadBeatLevelMyRanking_EmptyResponse(t *testing.T) { } } +// Tests consolidated from handlers_coverage3_test.go + +func TestNonTrivialHandlers_NoDB_Seibattle(t *testing.T) { + server := createMockServer() + + t.Run("handleMsgMhfGetSeibattle", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfGetSeibattle(session, &mhfpacket.MsgMhfGetSeibattle{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfUpdateBeatLevel", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfUpdateBeatLevel(session, &mhfpacket.MsgMhfUpdateBeatLevel{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgMhfReadBeatLevel", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgMhfReadBeatLevel(session, &mhfpacket.MsgMhfReadBeatLevel{ + AckHandle: 1, + ValidIDCount: 2, + IDs: [16]uint32{100, 200}, + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + func TestHandleMsgMhfReadLastWeekBeatRanking_DataSize(t *testing.T) { server := createMockServer() session := createMockSession(1, server) diff --git a/server/channelserver/handlers_semaphore_test.go b/server/channelserver/handlers_semaphore_test.go index 206694394..dc5f2002a 100644 --- a/server/channelserver/handlers_semaphore_test.go +++ b/server/channelserver/handlers_semaphore_test.go @@ -418,6 +418,172 @@ func TestSemaphoreHandlers_SequentialAcquire(t *testing.T) { } } +// Tests consolidated from handlers_coverage3_test.go + +func TestSimpleAckHandlers_SemaphoreGo(t *testing.T) { + server := createMockServer() + + t.Run("handleMsgSysCreateSemaphore", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysCreateSemaphore(session, &mhfpacket.MsgSysCreateSemaphore{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("handleMsgSysCreateSemaphore: response should have data") + } + default: + t.Error("handleMsgSysCreateSemaphore: no response queued") + } + }) +} + +func TestHandleMsgSysCreateAcquireSemaphore_Coverage3(t *testing.T) { + server := createMockServer() + server.semaphore = make(map[string]*Semaphore) + + t.Run("creates_new_semaphore", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysCreateAcquireSemaphore(session, &mhfpacket.MsgSysCreateAcquireSemaphore{ + AckHandle: 1, + SemaphoreID: "test_sema_c3_1", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + if _, exists := server.semaphore["test_sema_c3_1"]; !exists { + t.Error("semaphore should have been created in server map") + } + }) + + t.Run("acquires_existing_semaphore", func(t *testing.T) { + session := createMockSession(2, server) + handleMsgSysCreateAcquireSemaphore(session, &mhfpacket.MsgSysCreateAcquireSemaphore{ + AckHandle: 2, + SemaphoreID: "test_sema_c3_1", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("creates_ravi_semaphore", func(t *testing.T) { + session := createMockSession(3, server) + handleMsgSysCreateAcquireSemaphore(session, &mhfpacket.MsgSysCreateAcquireSemaphore{ + AckHandle: 3, + SemaphoreID: "hs_l0u3B51_c3", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + if _, exists := server.semaphore["hs_l0u3B51_c3"]; !exists { + t.Error("ravi semaphore should have been created") + } + }) +} + +func TestHandleMsgSysCheckSemaphore_Coverage3(t *testing.T) { + server := createMockServer() + server.semaphore = make(map[string]*Semaphore) + + t.Run("semaphore_not_exists", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysCheckSemaphore(session, &mhfpacket.MsgSysCheckSemaphore{ + AckHandle: 1, + SemaphoreID: "nonexistent_c3", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("semaphore_exists", func(t *testing.T) { + session := createMockSession(1, server) + server.semaphore["existing_sema_c3"] = NewSemaphore(session, "existing_sema_c3", 1) + handleMsgSysCheckSemaphore(session, &mhfpacket.MsgSysCheckSemaphore{ + AckHandle: 1, + SemaphoreID: "existing_sema_c3", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + +func TestHandleMsgSysAcquireSemaphore_Coverage3(t *testing.T) { + server := createMockServer() + server.semaphore = make(map[string]*Semaphore) + + t.Run("semaphore_exists", func(t *testing.T) { + session := createMockSession(1, server) + server.semaphore["acquire_sema_c3"] = NewSemaphore(session, "acquire_sema_c3", 1) + handleMsgSysAcquireSemaphore(session, &mhfpacket.MsgSysAcquireSemaphore{ + AckHandle: 1, + SemaphoreID: "acquire_sema_c3", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("semaphore_not_exists", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysAcquireSemaphore(session, &mhfpacket.MsgSysAcquireSemaphore{ + AckHandle: 1, + SemaphoreID: "nonexistent_sema_c3", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + +func TestEmptyHandlers_MiscFiles_Semaphore(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysReleaseSemaphore panicked: %v", r) + } + }() + handleMsgSysReleaseSemaphore(session, nil) +} + func TestSemaphoreHandlers_MultipleCheck(t *testing.T) { server := createMockServer() server.semaphore = make(map[string]*Semaphore) diff --git a/server/channelserver/handlers_session_test.go b/server/channelserver/handlers_session_test.go index 74e595896..d08e66530 100644 --- a/server/channelserver/handlers_session_test.go +++ b/server/channelserver/handlers_session_test.go @@ -3,6 +3,7 @@ package channelserver import ( "encoding/binary" "errors" + "sync" "testing" "erupe-ce/common/byteframe" @@ -361,6 +362,116 @@ func TestHandleMsgMhfAnnounce_Session(t *testing.T) { } } +// TestHandleMsgSysPing_DifferentAckHandles verifies ping works with various ack handles. +func TestHandleMsgSysPing_DifferentAckHandles(t *testing.T) { + server := createMockServer() + + ackHandles := []uint32{0, 1, 99999, 0xFFFFFFFF} + for _, ack := range ackHandles { + session := createMockSession(1, server) + pkt := &mhfpacket.MsgSysPing{AckHandle: ack} + + handleMsgSysPing(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("AckHandle=%d: Response packet should have data", ack) + } + default: + t.Errorf("AckHandle=%d: No response packet queued", ack) + } + } +} + +// TestHandleMsgSysTerminalLog_NoEntries verifies the handler works with nil entries. +func TestHandleMsgSysTerminalLog_NoEntries(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTerminalLog{ + AckHandle: 99999, + LogID: 0, + Entries: nil, + } + + handleMsgSysTerminalLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +// TestHandleMsgSysTerminalLog_ManyEntries verifies the handler with many log entries. +func TestHandleMsgSysTerminalLog_ManyEntries(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + entries := make([]mhfpacket.TerminalLogEntry, 20) + for i := range entries { + entries[i] = mhfpacket.TerminalLogEntry{ + Index: uint32(i), + Type1: uint8(i % 256), + Type2: uint8((i + 1) % 256), + } + } + + pkt := &mhfpacket.MsgSysTerminalLog{ + AckHandle: 55555, + LogID: 42, + Entries: entries, + } + + handleMsgSysTerminalLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +// TestHandleMsgSysTime_MultipleCalls verifies calling time handler repeatedly. +func TestHandleMsgSysTime_MultipleCalls(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTime{ + GetRemoteTime: false, + Timestamp: 0, + } + + for i := 0; i < 5; i++ { + handleMsgSysTime(session, pkt) + } + + // Should have 5 queued responses + count := 0 + for { + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + count++ + default: + goto done + } + } +done: + if count != 5 { + t.Errorf("Expected 5 queued responses, got %d", count) + } +} + // mockCharRepoGetUserIDErr wraps mockCharacterRepo to return an error from GetUserID type mockCharRepoGetUserIDErr struct { *mockCharacterRepo @@ -370,3 +481,1110 @@ type mockCharRepoGetUserIDErr struct { func (m *mockCharRepoGetUserIDErr) GetUserID(_ uint32) (uint32, error) { return 0, m.getUserIDErr } + +// Tests consolidated from handlers_core_test.go + +func TestHandleMsgHead(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgHead panicked: %v", r) + } + }() + + handleMsgHead(session, nil) +} + +func TestHandleMsgSysExtendThreshold(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysExtendThreshold panicked: %v", r) + } + }() + + handleMsgSysExtendThreshold(session, nil) +} + +func TestHandleMsgSysEnd(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysEnd panicked: %v", r) + } + }() + + handleMsgSysEnd(session, nil) +} + +func TestHandleMsgSysNop(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysNop panicked: %v", r) + } + }() + + handleMsgSysNop(session, nil) +} + +func TestHandleMsgSysAck(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysAck panicked: %v", r) + } + }() + + handleMsgSysAck(session, nil) +} + +func TestHandleMsgCaExchangeItem(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgCaExchangeItem panicked: %v", r) + } + }() + + handleMsgCaExchangeItem(session, nil) +} + +func TestHandleMsgMhfServerCommand(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfServerCommand panicked: %v", r) + } + }() + + handleMsgMhfServerCommand(session, nil) +} + +func TestHandleMsgMhfSetLoginwindow(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfSetLoginwindow panicked: %v", r) + } + }() + + handleMsgMhfSetLoginwindow(session, nil) +} + +func TestHandleMsgSysTransBinary(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysTransBinary panicked: %v", r) + } + }() + + handleMsgSysTransBinary(session, nil) +} + +func TestHandleMsgSysCollectBinary(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysCollectBinary panicked: %v", r) + } + }() + + handleMsgSysCollectBinary(session, nil) +} + +func TestHandleMsgSysGetState(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysGetState panicked: %v", r) + } + }() + + handleMsgSysGetState(session, nil) +} + +func TestHandleMsgSysSerialize(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysSerialize panicked: %v", r) + } + }() + + handleMsgSysSerialize(session, nil) +} + +func TestHandleMsgSysEnumlobby(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysEnumlobby panicked: %v", r) + } + }() + + handleMsgSysEnumlobby(session, nil) +} + +func TestHandleMsgSysEnumuser(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysEnumuser panicked: %v", r) + } + }() + + handleMsgSysEnumuser(session, nil) +} + +func TestHandleMsgSysInfokyserver(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysInfokyserver panicked: %v", r) + } + }() + + handleMsgSysInfokyserver(session, nil) +} + +func TestHandleMsgMhfGetCaUniqueID(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgMhfGetCaUniqueID panicked: %v", r) + } + }() + + handleMsgMhfGetCaUniqueID(session, nil) +} + +func TestHandleMsgSysTerminalLog(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTerminalLog{ + AckHandle: 12345, + LogID: 100, + Entries: []mhfpacket.TerminalLogEntry{}, + } + + handleMsgSysTerminalLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysTerminalLog_WithEntries(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTerminalLog{ + AckHandle: 12345, + LogID: 100, + Entries: []mhfpacket.TerminalLogEntry{ + {Type1: 1, Type2: 2}, + {Type1: 3, Type2: 4}, + }, + } + + handleMsgSysTerminalLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysPing(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysPing{ + AckHandle: 12345, + } + + handleMsgSysPing(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysTime(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTime{ + GetRemoteTime: true, + } + + handleMsgSysTime(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysIssueLogkey(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysIssueLogkey{ + AckHandle: 12345, + } + + handleMsgSysIssueLogkey(session, pkt) + + // Verify logkey was set + if len(session.logKey) != 16 { + t.Errorf("logKey length = %d, want 16", len(session.logKey)) + } + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysRecordLog(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + // Setup stage + stage := NewStage("test_stage") + session.stage = stage + stage.reservedClientSlots[session.charID] = true + + pkt := &mhfpacket.MsgSysRecordLog{ + AckHandle: 12345, + Data: make([]byte, 256), // Must be large enough for ByteFrame reads (32 offset + 176 uint8s) + } + + handleMsgSysRecordLog(session, pkt) + + // Verify charID removed from reserved slots + if _, exists := stage.reservedClientSlots[session.charID]; exists { + t.Error("charID should be removed from reserved slots") + } + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysUnlockGlobalSema(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysUnlockGlobalSema{ + AckHandle: 12345, + } + + handleMsgSysUnlockGlobalSema(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysSetStatus(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysSetStatus panicked: %v", r) + } + }() + + handleMsgSysSetStatus(session, nil) +} + +func TestHandleMsgSysEcho(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysEcho panicked: %v", r) + } + }() + + handleMsgSysEcho(session, nil) +} + +func TestHandleMsgSysUpdateRight(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysUpdateRight panicked: %v", r) + } + }() + + handleMsgSysUpdateRight(session, nil) +} + +func TestHandleMsgSysAuthQuery(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysAuthQuery panicked: %v", r) + } + }() + + handleMsgSysAuthQuery(session, nil) +} + +func TestHandleMsgSysAuthTerminal(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + defer func() { + if r := recover(); r != nil { + t.Errorf("handleMsgSysAuthTerminal panicked: %v", r) + } + }() + + handleMsgSysAuthTerminal(session, nil) +} + +func TestHandleMsgSysLockGlobalSema_NoMatch(t *testing.T) { + server := createMockServer() + server.GlobalID = "test-server" + server.Registry = NewLocalChannelRegistry([]*Server{}) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysLockGlobalSema{ + AckHandle: 12345, + UserIDString: "user123", + ServerChannelIDString: "channel1", + } + + handleMsgSysLockGlobalSema(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysLockGlobalSema_WithChannel(t *testing.T) { + server := createMockServer() + server.GlobalID = "test-server" + + // Create a mock channel with stages + channel := &Server{ + GlobalID: "other-server", + } + channel.stages.Store("stage_user123", NewStage("stage_user123")) + server.Registry = NewLocalChannelRegistry([]*Server{channel}) + + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysLockGlobalSema{ + AckHandle: 12345, + UserIDString: "user123", + ServerChannelIDString: "channel1", + } + + handleMsgSysLockGlobalSema(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysLockGlobalSema_SameServer(t *testing.T) { + server := createMockServer() + server.GlobalID = "test-server" + + // Create a mock channel with same GlobalID + channel := &Server{ + GlobalID: "test-server", + } + channel.stages.Store("stage_user456", NewStage("stage_user456")) + server.Registry = NewLocalChannelRegistry([]*Server{channel}) + + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysLockGlobalSema{ + AckHandle: 12345, + UserIDString: "user456", + ServerChannelIDString: "channel2", + } + + handleMsgSysLockGlobalSema(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgMhfAnnounce(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfAnnounce{ + AckHandle: 12345, + IPAddress: 0x7F000001, // 127.0.0.1 + Port: 54001, + StageID: []byte("test_stage"), + Data: byteframe.NewByteFrameFromBytes([]byte{0x00}), + } + + handleMsgMhfAnnounce(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysRightsReload(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysRightsReload{ + AckHandle: 12345, + } + + // This will panic due to nil db, which is expected in test + defer func() { + if r := recover(); r != nil { + t.Log("Expected panic due to nil database in test") + } + }() + + handleMsgSysRightsReload(session, pkt) +} + +// Tests consolidated from handlers_coverage3_test.go + +func TestEmptyHandlers_HandlersGo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgSysEcho", func() { handleMsgSysEcho(session, nil) }}, + {"handleMsgSysUpdateRight", func() { handleMsgSysUpdateRight(session, nil) }}, + {"handleMsgSysAuthQuery", func() { handleMsgSysAuthQuery(session, nil) }}, + {"handleMsgSysAuthTerminal", func() { handleMsgSysAuthTerminal(session, nil) }}, + {"handleMsgCaExchangeItem", func() { handleMsgCaExchangeItem(session, nil) }}, + {"handleMsgMhfServerCommand", func() { handleMsgMhfServerCommand(session, nil) }}, + {"handleMsgMhfSetLoginwindow", func() { handleMsgMhfSetLoginwindow(session, nil) }}, + {"handleMsgSysTransBinary", func() { handleMsgSysTransBinary(session, nil) }}, + {"handleMsgSysCollectBinary", func() { handleMsgSysCollectBinary(session, nil) }}, + {"handleMsgSysGetState", func() { handleMsgSysGetState(session, nil) }}, + {"handleMsgSysSerialize", func() { handleMsgSysSerialize(session, nil) }}, + {"handleMsgSysEnumlobby", func() { handleMsgSysEnumlobby(session, nil) }}, + {"handleMsgSysEnumuser", func() { handleMsgSysEnumuser(session, nil) }}, + {"handleMsgSysInfokyserver", func() { handleMsgSysInfokyserver(session, nil) }}, + {"handleMsgMhfGetCaUniqueID", func() { handleMsgMhfGetCaUniqueID(session, nil) }}, + {"handleMsgMhfGetExtraInfo", func() { handleMsgMhfGetExtraInfo(session, nil) }}, + {"handleMsgSysSetStatus", func() { handleMsgSysSetStatus(session, nil) }}, + {"handleMsgMhfStampcardPrize", func() { handleMsgMhfStampcardPrize(session, nil) }}, + {"handleMsgMhfKickExportForce", func() { handleMsgMhfKickExportForce(session, nil) }}, + {"handleMsgMhfRegistSpabiTime", func() { handleMsgMhfRegistSpabiTime(session, nil) }}, + {"handleMsgMhfDebugPostValue", func() { handleMsgMhfDebugPostValue(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} + +func TestEmptyHandlers_Concurrent(t *testing.T) { + server := createMockServer() + + handlers := []func(*Session, mhfpacket.MHFPacket){ + handleMsgSysEcho, + handleMsgSysUpdateRight, + handleMsgSysAuthQuery, + handleMsgSysAuthTerminal, + handleMsgCaExchangeItem, + handleMsgMhfServerCommand, + handleMsgMhfSetLoginwindow, + handleMsgSysTransBinary, + handleMsgSysCollectBinary, + handleMsgSysGetState, + handleMsgSysSerialize, + handleMsgSysEnumlobby, + handleMsgSysEnumuser, + handleMsgSysInfokyserver, + handleMsgMhfGetCaUniqueID, + handleMsgMhfGetExtraInfo, + handleMsgSysSetStatus, + handleMsgSysDeleteObject, + handleMsgSysRotateObject, + handleMsgSysDuplicateObject, + handleMsgSysGetObjectBinary, + handleMsgSysGetObjectOwner, + handleMsgSysUpdateObjectBinary, + handleMsgSysCleanupObject, + handleMsgMhfShutClient, + handleMsgSysHideClient, + handleMsgSysStageDestruct, + } + + var wg sync.WaitGroup + for _, h := range handlers { + for i := 0; i < 10; i++ { + wg.Add(1) + go func(handler func(*Session, mhfpacket.MHFPacket)) { + defer wg.Done() + session := createMockSession(1, server) + handler(session, nil) + }(h) + } + } + wg.Wait() +} + +func TestSimpleHandlers_PingAndTime(t *testing.T) { + server := createMockServer() + + t.Run("handleMsgSysPing", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysPing(session, &mhfpacket.MsgSysPing{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) + + t.Run("handleMsgSysTime", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysTime(session, &mhfpacket.MsgSysTime{}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + +func TestHandleMsgSysIssueLogkey_Coverage3(t *testing.T) { + server := createMockServer() + + t.Run("generates_logkey", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysIssueLogkey(session, &mhfpacket.MsgSysIssueLogkey{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + if session.logKey == nil { + t.Error("logKey should be set after IssueLogkey") + } + if len(session.logKey) != 16 { + t.Errorf("logKey length = %d, want 16", len(session.logKey)) + } + }) +} + +func TestHandleMsgSysUnlockGlobalSema_Coverage3(t *testing.T) { + server := createMockServer() + + t.Run("produces_response", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysUnlockGlobalSema(session, &mhfpacket.MsgSysUnlockGlobalSema{AckHandle: 1}) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + +func TestHandleMsgSysLockGlobalSema_Coverage3(t *testing.T) { + server := createMockServer() + server.Registry = NewLocalChannelRegistry(make([]*Server, 0)) + + t.Run("no_channels_returns_response", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysLockGlobalSema(session, &mhfpacket.MsgSysLockGlobalSema{ + AckHandle: 1, + UserIDString: "testuser", + ServerChannelIDString: "ch1", + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + }) +} + +func TestHandlersConcurrentInvocations(t *testing.T) { + server := createMockServer() + + done := make(chan struct{}) + const numGoroutines = 10 + + for i := 0; i < numGoroutines; i++ { + go func(id uint32) { + defer func() { + if r := recover(); r != nil { + t.Errorf("goroutine %d panicked: %v", id, r) + } + done <- struct{}{} + }() + + session := createMockSession(id, server) + + // Run several handlers concurrently + handleMsgSysPing(session, &mhfpacket.MsgSysPing{AckHandle: id}) + <-session.sendPackets + + handleMsgSysTime(session, &mhfpacket.MsgSysTime{GetRemoteTime: true}) + <-session.sendPackets + + handleMsgSysIssueLogkey(session, &mhfpacket.MsgSysIssueLogkey{AckHandle: id}) + <-session.sendPackets + + handleMsgMhfMercenaryHuntdata(session, &mhfpacket.MsgMhfMercenaryHuntdata{AckHandle: id, RequestType: 1}) + <-session.sendPackets + + handleMsgMhfEnumerateMercenaryLog(session, &mhfpacket.MsgMhfEnumerateMercenaryLog{AckHandle: id}) + <-session.sendPackets + }(uint32(i + 100)) + } + + for i := 0; i < numGoroutines; i++ { + <-done + } +} + +func TestHandleMsgSysRecordLog_RemovesReservation(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + stage := NewStage("test_stage_record") + session.stage = stage + stage.reservedClientSlots[session.charID] = true + + pkt := &mhfpacket.MsgSysRecordLog{ + AckHandle: 55555, + Data: make([]byte, 256), + } + + handleMsgSysRecordLog(session, pkt) + + if _, exists := stage.reservedClientSlots[session.charID]; exists { + t.Error("charID should be removed from reserved slots after record log") + } + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysRecordLog_NoExistingReservation(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + stage := NewStage("test_stage_no_reservation") + session.stage = stage + // No reservation exists for this charID + + pkt := &mhfpacket.MsgSysRecordLog{ + AckHandle: 55556, + Data: make([]byte, 256), + } + + // Should not panic even if charID is not in reservedClientSlots + handleMsgSysRecordLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysUnlockGlobalSema_Response(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysUnlockGlobalSema{ + AckHandle: 66666, + } + + handleMsgSysUnlockGlobalSema(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysTerminalLog_MultipleEntries(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTerminalLog{ + AckHandle: 12345, + LogID: 200, + Entries: []mhfpacket.TerminalLogEntry{ + {Type1: 10, Type2: 20}, + {Type1: 11, Type2: 21}, + {Type1: 12, Type2: 22}, + }, + } + + handleMsgSysTerminalLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysTerminalLog_ZeroLogID(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTerminalLog{ + AckHandle: 12345, + LogID: 0, + Entries: []mhfpacket.TerminalLogEntry{}, + } + + handleMsgSysTerminalLog(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysPing_DifferentAckHandle(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysPing{ + AckHandle: 0xFFFFFFFF, + } + + handleMsgSysPing(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysTime_GetRemoteTimeFalse(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysTime{ + GetRemoteTime: false, + } + + handleMsgSysTime(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysIssueLogkey_LogKeyGenerated(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgSysIssueLogkey{ + AckHandle: 77777, + } + + handleMsgSysIssueLogkey(session, pkt) + + // Verify that the logKey was set on the session + session.Lock() + keyLen := len(session.logKey) + session.Unlock() + + if keyLen != 16 { + t.Errorf("logKey length = %d, want 16", keyLen) + } + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response packet should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestHandleMsgSysIssueLogkey_Uniqueness(t *testing.T) { + server := createMockServer() + + // Generate two logkeys and verify they differ + session1 := createMockSession(1, server) + session2 := createMockSession(2, server) + + pkt1 := &mhfpacket.MsgSysIssueLogkey{AckHandle: 1} + pkt2 := &mhfpacket.MsgSysIssueLogkey{AckHandle: 2} + + handleMsgSysIssueLogkey(session1, pkt1) + handleMsgSysIssueLogkey(session2, pkt2) + + // Drain send packets + <-session1.sendPackets + <-session2.sendPackets + + session1.Lock() + key1 := make([]byte, len(session1.logKey)) + copy(key1, session1.logKey) + session1.Unlock() + + session2.Lock() + key2 := make([]byte, len(session2.logKey)) + copy(key2, session2.logKey) + session2.Unlock() + + if len(key1) != 16 || len(key2) != 16 { + t.Fatalf("logKeys should be 16 bytes each, got %d and %d", len(key1), len(key2)) + } + + same := true + for i := range key1 { + if key1[i] != key2[i] { + same = false + break + } + } + if same { + t.Error("Two generated logkeys should differ (extremely unlikely to be the same)") + } +} + +func TestMultipleHandlersOnSameSession(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + // Call multiple handlers in sequence + handleMsgSysPing(session, &mhfpacket.MsgSysPing{AckHandle: 1}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from Ping handler") + } + + handleMsgSysTime(session, &mhfpacket.MsgSysTime{GetRemoteTime: true}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from Time handler") + } + + handleMsgMhfRegisterEvent(session, &mhfpacket.MsgMhfRegisterEvent{AckHandle: 2, WorldID: 5, LandID: 10}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from RegisterEvent handler") + } + + handleMsgMhfReleaseEvent(session, &mhfpacket.MsgMhfReleaseEvent{AckHandle: 3}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from ReleaseEvent handler") + } + + handleMsgMhfEnumerateEvent(session, &mhfpacket.MsgMhfEnumerateEvent{AckHandle: 4}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from EnumerateEvent handler") + } + + handleMsgMhfSetCaAchievementHist(session, &mhfpacket.MsgMhfSetCaAchievementHist{AckHandle: 5}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from SetCaAchievementHist handler") + } + + handleMsgMhfGetRengokuRankingRank(session, &mhfpacket.MsgMhfGetRengokuRankingRank{AckHandle: 6}) + select { + case <-session.sendPackets: + default: + t.Fatal("Expected packet from GetRengokuRankingRank handler") + } +} + +func TestEmptyHandlers_MiscFiles_Session(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgHead", func() { handleMsgHead(session, nil) }}, + {"handleMsgSysExtendThreshold", func() { handleMsgSysExtendThreshold(session, nil) }}, + {"handleMsgSysEnd", func() { handleMsgSysEnd(session, nil) }}, + {"handleMsgSysNop", func() { handleMsgSysNop(session, nil) }}, + {"handleMsgSysAck", func() { handleMsgSysAck(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} diff --git a/server/channelserver/handlers_stage_coverage_test.go b/server/channelserver/handlers_stage_coverage_test.go deleted file mode 100644 index ccdb48245..000000000 --- a/server/channelserver/handlers_stage_coverage_test.go +++ /dev/null @@ -1,342 +0,0 @@ -package channelserver - -import ( - "testing" - - "erupe-ce/network/mhfpacket" -) - -func TestHandleMsgSysReserveStage_NewSlot(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: make(map[uint32]bool), - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - maxPlayers: 4, - } - server.stages.Store("test_stage", stage) - - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} - handleMsgSysReserveStage(session, pkt) - - select { - case <-session.sendPackets: - default: - t.Error("expected response") - } - - if _, exists := stage.reservedClientSlots[100]; !exists { - t.Error("charID should be in reserved slots") - } -} - -func TestHandleMsgSysReserveStage_AlreadyReservedReady1(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: map[uint32]bool{100: true}, - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - maxPlayers: 4, - } - server.stages.Store("test_stage", stage) - - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} - handleMsgSysReserveStage(session, pkt) - <-session.sendPackets - - if stage.reservedClientSlots[100] != false { - t.Error("ready=1 should set slot to false") - } -} - -func TestHandleMsgSysReserveStage_AlreadyReservedReady17(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: map[uint32]bool{100: false}, - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - maxPlayers: 4, - } - server.stages.Store("test_stage", stage) - - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 17} - handleMsgSysReserveStage(session, pkt) - <-session.sendPackets - - if stage.reservedClientSlots[100] != true { - t.Error("ready=17 should set slot to true") - } -} - -func TestHandleMsgSysReserveStage_Locked(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: make(map[uint32]bool), - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - maxPlayers: 4, - locked: true, - } - server.stages.Store("test_stage", stage) - - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} - handleMsgSysReserveStage(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgSysReserveStage_PasswordMismatch(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: make(map[uint32]bool), - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - maxPlayers: 4, - password: "secret", - } - server.stages.Store("test_stage", stage) - - session.stagePass = "wrong" - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} - handleMsgSysReserveStage(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgSysReserveStage_Full(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: map[uint32]bool{200: false, 300: false}, - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - maxPlayers: 2, - } - server.stages.Store("test_stage", stage) - - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} - handleMsgSysReserveStage(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgSysReserveStage_StageNotFound(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "nonexistent", Ready: 1} - handleMsgSysReserveStage(session, pkt) - <-session.sendPackets -} - -func TestHandleMsgSysUnreserveStage_WithReservation(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: map[uint32]bool{100: false}, - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - } - session.reservationStage = stage - - pkt := &mhfpacket.MsgSysUnreserveStage{} - handleMsgSysUnreserveStage(session, pkt) - - if session.reservationStage != nil { - t.Error("reservation should be cleared") - } - if _, exists := stage.reservedClientSlots[100]; exists { - t.Error("charID should be removed from reserved slots") - } -} - -func TestHandleMsgSysUnreserveStage_NoReservation(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgSysUnreserveStage{} - handleMsgSysUnreserveStage(session, pkt) - // Should not panic -} - -func TestHandleMsgSysSetStagePass_Host(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: map[uint32]bool{100: false}, - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - } - session.reservationStage = stage - - pkt := &mhfpacket.MsgSysSetStagePass{Password: "mypass"} - handleMsgSysSetStagePass(session, pkt) - - if stage.password != "mypass" { - t.Errorf("stage password = %q, want %q", stage.password, "mypass") - } -} - -func TestHandleMsgSysSetStagePass_NonHost(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgSysSetStagePass{Password: "mypass"} - handleMsgSysSetStagePass(session, pkt) - - if session.stagePass != "mypass" { - t.Errorf("session stagePass = %q, want %q", session.stagePass, "mypass") - } -} - -func TestHandleMsgSysSetAndGetStageBinary(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: make(map[uint32]bool), - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - } - server.stages.Store("test_stage", stage) - - // Set binary - setPkt := &mhfpacket.MsgSysSetStageBinary{ - BinaryType0: 1, - BinaryType1: 2, - StageID: "test_stage", - RawDataPayload: []byte{0xDE, 0xAD, 0xBE, 0xEF}, - } - handleMsgSysSetStageBinary(session, setPkt) - - // Get binary - getPkt := &mhfpacket.MsgSysGetStageBinary{ - AckHandle: 1, - BinaryType0: 1, - BinaryType1: 2, - StageID: "test_stage", - } - handleMsgSysGetStageBinary(session, getPkt) - <-session.sendPackets -} - -func TestHandleMsgSysGetStageBinary_Type1Equals4Fallback(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: make(map[uint32]bool), - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - } - server.stages.Store("test_stage", stage) - - getPkt := &mhfpacket.MsgSysGetStageBinary{ - AckHandle: 1, - BinaryType0: 0, - BinaryType1: 4, - StageID: "test_stage", - } - handleMsgSysGetStageBinary(session, getPkt) - <-session.sendPackets -} - -func TestHandleMsgSysGetStageBinary_MissingBinary(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: make(map[uint32]bool), - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - } - server.stages.Store("test_stage", stage) - - getPkt := &mhfpacket.MsgSysGetStageBinary{ - AckHandle: 1, - BinaryType0: 9, - BinaryType1: 9, - StageID: "test_stage", - } - handleMsgSysGetStageBinary(session, getPkt) - <-session.sendPackets -} - -func TestHandleMsgSysGetStageBinary_MissingStage(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - getPkt := &mhfpacket.MsgSysGetStageBinary{ - AckHandle: 1, - BinaryType0: 0, - BinaryType1: 0, - StageID: "nonexistent", - } - handleMsgSysGetStageBinary(session, getPkt) - <-session.sendPackets -} - -func TestHandleMsgSysSetStageBinary_MissingStage(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgSysSetStageBinary{ - BinaryType0: 1, - BinaryType1: 2, - StageID: "nonexistent", - RawDataPayload: []byte{1, 2, 3}, - } - handleMsgSysSetStageBinary(session, pkt) - // Should not panic, just logs warning -} - -func TestHandleMsgSysUnlockStage_WithReservation(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - stage := &Stage{ - id: "test_stage", - reservedClientSlots: map[uint32]bool{100: false}, - rawBinaryData: make(map[stageBinaryKey][]byte), - clients: make(map[*Session]uint32), - } - server.stages.Store("test_stage", stage) - session.reservationStage = stage - - pkt := &mhfpacket.MsgSysUnlockStage{} - handleMsgSysUnlockStage(session, pkt) - - if _, exists := server.stages.Get("test_stage"); exists { - t.Error("stage should have been deleted") - } -} - -func TestHandleMsgSysUnlockStage_NoReservation(t *testing.T) { - server := createMockServer() - session := createMockSession(100, server) - - pkt := &mhfpacket.MsgSysUnlockStage{} - handleMsgSysUnlockStage(session, pkt) - // Should not panic -} diff --git a/server/channelserver/handlers_stage_test.go b/server/channelserver/handlers_stage_test.go index 93cf5dafd..f9e9be8ca 100644 --- a/server/channelserver/handlers_stage_test.go +++ b/server/channelserver/handlers_stage_test.go @@ -684,3 +684,405 @@ func TestRaceConditionStageObjectsIteration(t *testing.T) { t.Log(raceTestCompletionMsg) } + +func TestHandleMsgSysReserveStage_NewSlot(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: make(map[uint32]bool), + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + maxPlayers: 4, + } + server.stages.Store("test_stage", stage) + + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} + handleMsgSysReserveStage(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("expected response") + } + + if _, exists := stage.reservedClientSlots[100]; !exists { + t.Error("charID should be in reserved slots") + } +} + +func TestHandleMsgSysReserveStage_AlreadyReservedReady1(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: map[uint32]bool{100: true}, + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + maxPlayers: 4, + } + server.stages.Store("test_stage", stage) + + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} + handleMsgSysReserveStage(session, pkt) + <-session.sendPackets + + if stage.reservedClientSlots[100] != false { + t.Error("ready=1 should set slot to false") + } +} + +func TestHandleMsgSysReserveStage_AlreadyReservedReady17(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: map[uint32]bool{100: false}, + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + maxPlayers: 4, + } + server.stages.Store("test_stage", stage) + + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 17} + handleMsgSysReserveStage(session, pkt) + <-session.sendPackets + + if stage.reservedClientSlots[100] != true { + t.Error("ready=17 should set slot to true") + } +} + +func TestHandleMsgSysReserveStage_Locked(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: make(map[uint32]bool), + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + maxPlayers: 4, + locked: true, + } + server.stages.Store("test_stage", stage) + + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} + handleMsgSysReserveStage(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgSysReserveStage_PasswordMismatch(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: make(map[uint32]bool), + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + maxPlayers: 4, + password: "secret", + } + server.stages.Store("test_stage", stage) + + session.stagePass = "wrong" + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} + handleMsgSysReserveStage(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgSysReserveStage_Full(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: map[uint32]bool{200: false, 300: false}, + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + maxPlayers: 2, + } + server.stages.Store("test_stage", stage) + + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "test_stage", Ready: 1} + handleMsgSysReserveStage(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgSysReserveStage_StageNotFound(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgSysReserveStage{AckHandle: 1, StageID: "nonexistent", Ready: 1} + handleMsgSysReserveStage(session, pkt) + <-session.sendPackets +} + +func TestHandleMsgSysUnreserveStage_WithReservation(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: map[uint32]bool{100: false}, + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + } + session.reservationStage = stage + + pkt := &mhfpacket.MsgSysUnreserveStage{} + handleMsgSysUnreserveStage(session, pkt) + + if session.reservationStage != nil { + t.Error("reservation should be cleared") + } + if _, exists := stage.reservedClientSlots[100]; exists { + t.Error("charID should be removed from reserved slots") + } +} + +func TestHandleMsgSysUnreserveStage_NoReservation(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgSysUnreserveStage{} + handleMsgSysUnreserveStage(session, pkt) + // Should not panic +} + +func TestHandleMsgSysSetStagePass_Host(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: map[uint32]bool{100: false}, + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + } + session.reservationStage = stage + + pkt := &mhfpacket.MsgSysSetStagePass{Password: "mypass"} + handleMsgSysSetStagePass(session, pkt) + + if stage.password != "mypass" { + t.Errorf("stage password = %q, want %q", stage.password, "mypass") + } +} + +func TestHandleMsgSysSetStagePass_NonHost(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgSysSetStagePass{Password: "mypass"} + handleMsgSysSetStagePass(session, pkt) + + if session.stagePass != "mypass" { + t.Errorf("session stagePass = %q, want %q", session.stagePass, "mypass") + } +} + +func TestHandleMsgSysSetAndGetStageBinary(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: make(map[uint32]bool), + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + } + server.stages.Store("test_stage", stage) + + // Set binary + setPkt := &mhfpacket.MsgSysSetStageBinary{ + BinaryType0: 1, + BinaryType1: 2, + StageID: "test_stage", + RawDataPayload: []byte{0xDE, 0xAD, 0xBE, 0xEF}, + } + handleMsgSysSetStageBinary(session, setPkt) + + // Get binary + getPkt := &mhfpacket.MsgSysGetStageBinary{ + AckHandle: 1, + BinaryType0: 1, + BinaryType1: 2, + StageID: "test_stage", + } + handleMsgSysGetStageBinary(session, getPkt) + <-session.sendPackets +} + +func TestHandleMsgSysGetStageBinary_Type1Equals4Fallback(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: make(map[uint32]bool), + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + } + server.stages.Store("test_stage", stage) + + getPkt := &mhfpacket.MsgSysGetStageBinary{ + AckHandle: 1, + BinaryType0: 0, + BinaryType1: 4, + StageID: "test_stage", + } + handleMsgSysGetStageBinary(session, getPkt) + <-session.sendPackets +} + +func TestHandleMsgSysGetStageBinary_MissingBinary(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: make(map[uint32]bool), + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + } + server.stages.Store("test_stage", stage) + + getPkt := &mhfpacket.MsgSysGetStageBinary{ + AckHandle: 1, + BinaryType0: 9, + BinaryType1: 9, + StageID: "test_stage", + } + handleMsgSysGetStageBinary(session, getPkt) + <-session.sendPackets +} + +func TestHandleMsgSysGetStageBinary_MissingStage(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + getPkt := &mhfpacket.MsgSysGetStageBinary{ + AckHandle: 1, + BinaryType0: 0, + BinaryType1: 0, + StageID: "nonexistent", + } + handleMsgSysGetStageBinary(session, getPkt) + <-session.sendPackets +} + +func TestHandleMsgSysSetStageBinary_MissingStage(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgSysSetStageBinary{ + BinaryType0: 1, + BinaryType1: 2, + StageID: "nonexistent", + RawDataPayload: []byte{1, 2, 3}, + } + handleMsgSysSetStageBinary(session, pkt) + // Should not panic, just logs warning +} + +func TestHandleMsgSysUnlockStage_WithReservation(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + stage := &Stage{ + id: "test_stage", + reservedClientSlots: map[uint32]bool{100: false}, + rawBinaryData: make(map[stageBinaryKey][]byte), + clients: make(map[*Session]uint32), + } + server.stages.Store("test_stage", stage) + session.reservationStage = stage + + pkt := &mhfpacket.MsgSysUnlockStage{} + handleMsgSysUnlockStage(session, pkt) + + if _, exists := server.stages.Get("test_stage"); exists { + t.Error("stage should have been deleted") + } +} + +func TestHandleMsgSysUnlockStage_NoReservation(t *testing.T) { + server := createMockServer() + session := createMockSession(100, server) + + pkt := &mhfpacket.MsgSysUnlockStage{} + handleMsgSysUnlockStage(session, pkt) + // Should not panic +} + +// Tests consolidated from handlers_coverage3_test.go + +func TestEmptyHandlers_StageGo(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgSysStageDestruct", func() { handleMsgSysStageDestruct(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} + +func TestHandleMsgSysCreateStage_Coverage3(t *testing.T) { + server := createMockServer() + + t.Run("creates_new_stage", func(t *testing.T) { + session := createMockSession(1, server) + handleMsgSysCreateStage(session, &mhfpacket.MsgSysCreateStage{ + AckHandle: 1, + StageID: "test_create_stage_c3", + PlayerCount: 4, + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data") + } + default: + t.Error("no response queued") + } + if _, exists := server.stages.Get("test_create_stage_c3"); !exists { + t.Error("stage should have been created") + } + }) + + t.Run("duplicate_stage_fails", func(t *testing.T) { + session := createMockSession(1, server) + // Stage already exists from the previous test + handleMsgSysCreateStage(session, &mhfpacket.MsgSysCreateStage{ + AckHandle: 2, + StageID: "test_create_stage_c3", + PlayerCount: 4, + }) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("response should have data even on failure") + } + default: + t.Error("no response queued") + } + }) +} diff --git a/server/channelserver/handlers_tactics_test.go b/server/channelserver/handlers_tactics_test.go index d3cb5e73c..136f86316 100644 --- a/server/channelserver/handlers_tactics_test.go +++ b/server/channelserver/handlers_tactics_test.go @@ -191,3 +191,103 @@ func TestHandleMsgMhfGetUdTacticsLog(t *testing.T) { handleMsgMhfGetUdTacticsLog(session, nil) } + +// Tests consolidated from handlers_coverage3_test.go + +func TestSimpleAckHandlers_TacticsGo(t *testing.T) { + server := createMockServer() + + tests := []struct { + name string + fn func(s *Session) + }{ + {"handleMsgMhfAddUdTacticsPoint", func(s *Session) { + handleMsgMhfAddUdTacticsPoint(s, &mhfpacket.MsgMhfAddUdTacticsPoint{AckHandle: 1}) + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + session := createMockSession(1, server) + tt.fn(session) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("%s: response should have data", tt.name) + } + default: + t.Errorf("%s: no response queued", tt.name) + } + }) + } +} + +func TestNonTrivialHandlers_TacticsGo(t *testing.T) { + server := createMockServer() + + tests := []struct { + name string + fn func(s *Session) + }{ + {"handleMsgMhfGetUdTacticsPoint", func(s *Session) { + handleMsgMhfGetUdTacticsPoint(s, &mhfpacket.MsgMhfGetUdTacticsPoint{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdTacticsRewardList", func(s *Session) { + handleMsgMhfGetUdTacticsRewardList(s, &mhfpacket.MsgMhfGetUdTacticsRewardList{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdTacticsFollower", func(s *Session) { + handleMsgMhfGetUdTacticsFollower(s, &mhfpacket.MsgMhfGetUdTacticsFollower{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdTacticsBonusQuest", func(s *Session) { + handleMsgMhfGetUdTacticsBonusQuest(s, &mhfpacket.MsgMhfGetUdTacticsBonusQuest{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdTacticsFirstQuestBonus", func(s *Session) { + handleMsgMhfGetUdTacticsFirstQuestBonus(s, &mhfpacket.MsgMhfGetUdTacticsFirstQuestBonus{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdTacticsRemainingPoint", func(s *Session) { + handleMsgMhfGetUdTacticsRemainingPoint(s, &mhfpacket.MsgMhfGetUdTacticsRemainingPoint{AckHandle: 1}) + }}, + {"handleMsgMhfGetUdTacticsRanking", func(s *Session) { + handleMsgMhfGetUdTacticsRanking(s, &mhfpacket.MsgMhfGetUdTacticsRanking{AckHandle: 1}) + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + session := createMockSession(1, server) + tt.fn(session) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("%s: response should have data", tt.name) + } + default: + t.Errorf("%s: no response queued", tt.name) + } + }) + } +} + +func TestEmptyHandlers_MiscFiles_Tactics(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + tests := []struct { + name string + fn func() + }{ + {"handleMsgMhfSetUdTacticsFollower", func() { handleMsgMhfSetUdTacticsFollower(session, nil) }}, + {"handleMsgMhfGetUdTacticsLog", func() { handleMsgMhfGetUdTacticsLog(session, nil) }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Errorf("%s panicked: %v", tt.name, r) + } + }() + tt.fn() + }) + } +} diff --git a/server/channelserver/handlers_tower_extra_coverage_test.go b/server/channelserver/handlers_tower_extra_coverage_test.go deleted file mode 100644 index 5e221e490..000000000 --- a/server/channelserver/handlers_tower_extra_coverage_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package channelserver - -import ( - "erupe-ce/network/mhfpacket" - "testing" -) - -func TestHandleMsgMhfGetTenrouirai_Type2_Rewards(t *testing.T) { - srv := createMockServer() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 2} - handleMsgMhfGetTenrouirai(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetTenrouirai_Type4_Progress(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - ensureTowerService(srv) - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 4, GuildID: 1} - handleMsgMhfGetTenrouirai(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetTenrouirai_Type5_Scores(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 5, GuildID: 1, MissionIndex: 0} - handleMsgMhfGetTenrouirai(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfGetTenrouirai_Type6_RP(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 6, GuildID: 1} - handleMsgMhfGetTenrouirai(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfPostTowerInfo_SkillUpdate(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 2, Skill: 3, Cost: -10} - handleMsgMhfPostTowerInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfPostTowerInfo_ProgressUpdate(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 1, TR: 5, TRP: 100, Cost: -20, Block1: 1} - handleMsgMhfPostTowerInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfPostTowerInfo_ProgressType7(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 7, TR: 10, TRP: 200} - handleMsgMhfPostTowerInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfPostTowerInfo_QuestToolsDebug(t *testing.T) { - srv := createMockServer() - srv.towerRepo = &mockTowerRepo{} - srv.erupeConfig.DebugOptions.QuestTools = true - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 2, Skill: 1} - handleMsgMhfPostTowerInfo(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfPostTenrouirai_Op1(t *testing.T) { - srv := createMockServer() - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostTenrouirai{AckHandle: 1, Op: 1} - handleMsgMhfPostTenrouirai(s, pkt) - <-s.sendPackets -} - -func TestHandleMsgMhfPostTenrouirai_QuestToolsDebug(t *testing.T) { - srv := createMockServer() - srv.erupeConfig.DebugOptions.QuestTools = true - s := createMockSession(100, srv) - - pkt := &mhfpacket.MsgMhfPostTenrouirai{AckHandle: 1, Op: 1, Floors: 10, Slays: 5} - handleMsgMhfPostTenrouirai(s, pkt) - <-s.sendPackets -} diff --git a/server/channelserver/handlers_tower_test.go b/server/channelserver/handlers_tower_test.go index 35d42ebaa..61d099e5a 100644 --- a/server/channelserver/handlers_tower_test.go +++ b/server/channelserver/handlers_tower_test.go @@ -154,3 +154,142 @@ func TestHandleMsgMhfPresentBox(t *testing.T) { t.Error("No response packet queued") } } + +func TestHandleMsgMhfGetTenrouirai_Type2_Rewards(t *testing.T) { + srv := createMockServer() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 2} + handleMsgMhfGetTenrouirai(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetTenrouirai_Type4_Progress(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + ensureTowerService(srv) + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 4, GuildID: 1} + handleMsgMhfGetTenrouirai(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetTenrouirai_Type5_Scores(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 5, GuildID: 1, MissionIndex: 0} + handleMsgMhfGetTenrouirai(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfGetTenrouirai_Type6_RP(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, DataType: 6, GuildID: 1} + handleMsgMhfGetTenrouirai(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfPostTowerInfo_SkillUpdate(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 2, Skill: 3, Cost: -10} + handleMsgMhfPostTowerInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfPostTowerInfo_ProgressUpdate(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 1, TR: 5, TRP: 100, Cost: -20, Block1: 1} + handleMsgMhfPostTowerInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfPostTowerInfo_ProgressType7(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 7, TR: 10, TRP: 200} + handleMsgMhfPostTowerInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfPostTowerInfo_QuestToolsDebug(t *testing.T) { + srv := createMockServer() + srv.towerRepo = &mockTowerRepo{} + srv.erupeConfig.DebugOptions.QuestTools = true + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostTowerInfo{AckHandle: 1, InfoType: 2, Skill: 1} + handleMsgMhfPostTowerInfo(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfPostTenrouirai_Op1(t *testing.T) { + srv := createMockServer() + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostTenrouirai{AckHandle: 1, Op: 1} + handleMsgMhfPostTenrouirai(s, pkt) + <-s.sendPackets +} + +func TestHandleMsgMhfPostTenrouirai_QuestToolsDebug(t *testing.T) { + srv := createMockServer() + srv.erupeConfig.DebugOptions.QuestTools = true + s := createMockSession(100, srv) + + pkt := &mhfpacket.MsgMhfPostTenrouirai{AckHandle: 1, Op: 1, Floors: 10, Slays: 5} + handleMsgMhfPostTenrouirai(s, pkt) + <-s.sendPackets +} + +// Tests consolidated from handlers_coverage3_test.go + +func TestNonTrivialHandlers_TowerGo(t *testing.T) { + server := createMockServer() + + tests := []struct { + name string + fn func(s *Session) + }{ + {"handleMsgMhfGetTenrouirai_Type1_C3", func(s *Session) { + handleMsgMhfGetTenrouirai(s, &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, Unk0: 1}) + }}, + {"handleMsgMhfGetTenrouirai_Unknown_C3", func(s *Session) { + handleMsgMhfGetTenrouirai(s, &mhfpacket.MsgMhfGetTenrouirai{AckHandle: 1, Unk0: 0, DataType: 0}) + }}, + {"handleMsgMhfGetWeeklySeibatuRankingReward_C3", func(s *Session) { + handleMsgMhfGetWeeklySeibatuRankingReward(s, &mhfpacket.MsgMhfGetWeeklySeibatuRankingReward{AckHandle: 1}) + }}, + {"handleMsgMhfPresentBox_C3", func(s *Session) { + handleMsgMhfPresentBox(s, &mhfpacket.MsgMhfPresentBox{AckHandle: 1}) + }}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + session := createMockSession(1, server) + tt.fn(session) + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Errorf("%s: response should have data", tt.name) + } + default: + t.Errorf("%s: no response queued", tt.name) + } + }) + } +} diff --git a/server/channelserver/sys_time_test.go b/server/channelserver/sys_time_test.go new file mode 100644 index 000000000..0df2eaee9 --- /dev/null +++ b/server/channelserver/sys_time_test.go @@ -0,0 +1,13 @@ +package channelserver + +import "testing" + +func TestTimeGameAbsolute(t *testing.T) { + result := TimeGameAbsolute() + + // TimeGameAbsolute returns (adjustedUnix - 2160) % 5760 + // Result should be in range [0, 5760) + if result >= 5760 { + t.Errorf("TimeGameAbsolute() = %d, should be < 5760", result) + } +}