From cdc4cd9ba3a4555249c88ba609f456499fc36615 Mon Sep 17 00:00:00 2001 From: Houmgaor Date: Thu, 26 Feb 2026 22:28:32 +0100 Subject: [PATCH] test(channelserver): add handler coverage tests for misc, cafe, festa, event Add four new test files covering previously-untested handler functions to raise total coverage from 57.7% to 60.0%: - handlers_misc_coverage_test.go: minidata, trend weapons, etc points, equip skin history - handlers_cafe_coverage_test.go: cafe duration bonuses, daily cafe, cafe duration - handlers_festa_coverage_test.go: mezfes data, festa voting, entry, charge, prizes, state queries, member enumeration - handlers_event_coverage_test.go: weekly schedule, login boost, scenario data, friends/blacklist operations Also make mockCharacterRepo.ReadEtcPoints configurable to support etc points handler tests. --- .../handlers_cafe_coverage_test.go | 213 ++++++++++++ .../handlers_event_coverage_test.go | 226 +++++++++++++ .../handlers_festa_coverage_test.go | 301 +++++++++++++++++ .../handlers_misc_coverage_test.go | 313 ++++++++++++++++++ server/channelserver/repo_mocks_test.go | 8 +- 5 files changed, 1060 insertions(+), 1 deletion(-) create mode 100644 server/channelserver/handlers_cafe_coverage_test.go create mode 100644 server/channelserver/handlers_event_coverage_test.go create mode 100644 server/channelserver/handlers_festa_coverage_test.go create mode 100644 server/channelserver/handlers_misc_coverage_test.go diff --git a/server/channelserver/handlers_cafe_coverage_test.go b/server/channelserver/handlers_cafe_coverage_test.go new file mode 100644 index 000000000..08db261c9 --- /dev/null +++ b/server/channelserver/handlers_cafe_coverage_test.go @@ -0,0 +1,213 @@ +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_event_coverage_test.go b/server/channelserver/handlers_event_coverage_test.go new file mode 100644 index 000000000..bbcdd79d2 --- /dev/null +++ b/server/channelserver/handlers_event_coverage_test.go @@ -0,0 +1,226 @@ +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_festa_coverage_test.go b/server/channelserver/handlers_festa_coverage_test.go new file mode 100644 index 000000000..3c7d19412 --- /dev/null +++ b/server/channelserver/handlers_festa_coverage_test.go @@ -0,0 +1,301 @@ +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_misc_coverage_test.go b/server/channelserver/handlers_misc_coverage_test.go new file mode 100644 index 000000000..b8af7316b --- /dev/null +++ b/server/channelserver/handlers_misc_coverage_test.go @@ -0,0 +1,313 @@ +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/repo_mocks_test.go b/server/channelserver/repo_mocks_test.go index 467a6120e..15f991741 100644 --- a/server/channelserver/repo_mocks_test.go +++ b/server/channelserver/repo_mocks_test.go @@ -121,6 +121,12 @@ type mockCharacterRepo struct { loadSaveDataNew bool loadSaveDataName string loadSaveDataErr error + + // ReadEtcPoints mock fields + etcBonusQuests uint32 + etcDailyQuests uint32 + etcPromoPoints uint32 + etcPointsErr error } func newMockCharacterRepo() *mockCharacterRepo { @@ -205,7 +211,7 @@ func (m *mockCharacterRepo) SetDeleted(_ uint32) error func (m *mockCharacterRepo) UpdateDailyCafe(_ uint32, _ time.Time, _, _ uint32) error { return nil } func (m *mockCharacterRepo) ResetDailyQuests(_ uint32) error { return nil } func (m *mockCharacterRepo) ReadEtcPoints(_ uint32) (uint32, uint32, uint32, error) { - return 0, 0, 0, nil + return m.etcBonusQuests, m.etcDailyQuests, m.etcPromoPoints, m.etcPointsErr } func (m *mockCharacterRepo) ResetCafeTime(_ uint32, _ time.Time) error { return nil } func (m *mockCharacterRepo) UpdateGuildPostChecked(_ uint32) error { return nil }