From 4b24489ebee363de2ae8976edc2d76e507800af4 Mon Sep 17 00:00:00 2001 From: Houmgaor Date: Fri, 27 Feb 2026 11:33:13 +0100 Subject: [PATCH] test(channelserver): add handler coverage tests for scout, house, items, rengoku, tower --- .../handlers_guild_scout_test.go | 229 ++++++++++ server/channelserver/handlers_house_test.go | 193 ++++++++ server/channelserver/handlers_items_test.go | 80 ++++ server/channelserver/handlers_rengoku_test.go | 411 ++++++++++++++++++ server/channelserver/handlers_tower_test.go | 250 +++++++++++ 5 files changed, 1163 insertions(+) diff --git a/server/channelserver/handlers_guild_scout_test.go b/server/channelserver/handlers_guild_scout_test.go index 6cd445034..82e981619 100644 --- a/server/channelserver/handlers_guild_scout_test.go +++ b/server/channelserver/handlers_guild_scout_test.go @@ -265,3 +265,232 @@ func TestSetRejectGuildScout_DBError(t *testing.T) { t.Error("No response packet queued") } } + +// --- handleMsgMhfPostGuildScout tests --- + +func TestPostGuildScout_Success(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + membership: &GuildMember{GuildID: 10, Recruiter: true}, + } + guildMock.guild = &Guild{ID: 10, Name: "TestGuild"} + guildMock.guild.LeaderCharID = 1 + server.guildRepo = guildMock + server.mailRepo = &mockMailRepo{} + ensureGuildService(server) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostGuildScout{ + AckHandle: 100, + CharID: 42, + } + + handleMsgMhfPostGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestPostGuildScout_AlreadyInvited(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + membership: &GuildMember{GuildID: 10, Recruiter: true}, + createAppErr: ErrAlreadyInvited, + } + guildMock.guild = &Guild{ID: 10, Name: "TestGuild"} + guildMock.guild.LeaderCharID = 1 + server.guildRepo = guildMock + server.mailRepo = &mockMailRepo{} + ensureGuildService(server) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostGuildScout{ + AckHandle: 100, + CharID: 42, + } + + handleMsgMhfPostGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestPostGuildScout_Error(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + getMemberErr: errNotFound, + } + server.guildRepo = guildMock + server.mailRepo = &mockMailRepo{} + ensureGuildService(server) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostGuildScout{ + AckHandle: 100, + CharID: 42, + } + + handleMsgMhfPostGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfCancelGuildScout tests --- + +func TestCancelGuildScout_Success(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + membership: &GuildMember{GuildID: 10, Recruiter: true}, + } + guildMock.guild = &Guild{ID: 10, Name: "TestGuild"} + server.guildRepo = guildMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfCancelGuildScout{ + AckHandle: 100, + InvitationID: 42, + } + + handleMsgMhfCancelGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestCancelGuildScout_NoMembership(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + getMemberErr: errNotFound, + } + server.guildRepo = guildMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfCancelGuildScout{ + AckHandle: 100, + InvitationID: 42, + } + + handleMsgMhfCancelGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestCancelGuildScout_NilMembership(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + membership: nil, + } + server.guildRepo = guildMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfCancelGuildScout{ + AckHandle: 100, + InvitationID: 42, + } + + handleMsgMhfCancelGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestCancelGuildScout_GuildNotFound(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{ + membership: &GuildMember{GuildID: 99, Recruiter: true}, + getErr: errNotFound, + } + server.guildRepo = guildMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfCancelGuildScout{ + AckHandle: 100, + InvitationID: 42, + } + + handleMsgMhfCancelGuildScout(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfGetGuildScoutList tests --- + +func TestGetGuildScoutList_NoGuildNoPrevID(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{} // GetByCharID returns nil + server.guildRepo = guildMock + session := createMockSession(1, server) + session.prevGuildID = 0 + + pkt := &mhfpacket.MsgMhfGetGuildScoutList{AckHandle: 100} + + handleMsgMhfGetGuildScoutList(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetGuildScoutList_NilGuildWithPrevID_GetByIDFails(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{} // GetByCharID returns nil, GetByID for prevGuildID returns not found + server.guildRepo = guildMock + session := createMockSession(1, server) + session.prevGuildID = 99 // non-zero triggers else branch + + pkt := &mhfpacket.MsgMhfGetGuildScoutList{AckHandle: 100} + + handleMsgMhfGetGuildScoutList(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetGuildScoutList_WithGuild(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 10, Name: "TestGuild"} + guildMock := &mockGuildRepo{} + guildMock.guild = guild + server.guildRepo = guildMock + session := createMockSession(1, server) + session.prevGuildID = 10 + + pkt := &mhfpacket.MsgMhfGetGuildScoutList{AckHandle: 100} + + handleMsgMhfGetGuildScoutList(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} diff --git a/server/channelserver/handlers_house_test.go b/server/channelserver/handlers_house_test.go index 7a4bbc286..fef931951 100644 --- a/server/channelserver/handlers_house_test.go +++ b/server/channelserver/handlers_house_test.go @@ -1386,6 +1386,199 @@ func TestHandleMsgMhfOperateWarehouse_Op4(t *testing.T) { <-s.sendPackets } +// --- handleMsgMhfLoadHouse tests --- + +func TestLoadHouse_OwnHouse(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + server.charRepo = newMockCharacterRepo() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 9} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestLoadHouse_OthersHouse(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + server.charRepo = newMockCharacterRepo() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 2, Destination: 3} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestLoadHouse_Bookshelf(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + server.charRepo = newMockCharacterRepo() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 4} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestLoadHouse_Gallery(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 5} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestLoadHouse_Tore(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 8} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestLoadHouse_Garden(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + server.goocooRepo = newMockGoocooRepo() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 10} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestLoadHouse_UnknownDestination(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadHouse{AckHandle: 100, CharID: 1, Destination: 99} + handleMsgMhfLoadHouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfLoadDecoMyset tests --- + +func TestLoadDecoMyset(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + server.charRepo = charMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadDecoMyset{AckHandle: 100} + handleMsgMhfLoadDecoMyset(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response should have default data") + } + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfUpdateWarehouse tests --- + +func TestUpdateWarehouse_EmptyItems(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfUpdateWarehouse{ + AckHandle: 100, + BoxType: 0, + BoxIndex: 0, + UpdatedItems: []mhfitem.MHFItemStack{}, + } + handleMsgMhfUpdateWarehouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestUpdateWarehouse_EmptyEquipment(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfUpdateWarehouse{ + AckHandle: 100, + BoxType: 1, + BoxIndex: 0, + UpdatedEquipment: []mhfitem.MHFEquipment{}, + } + handleMsgMhfUpdateWarehouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestUpdateWarehouse_InvalidIndex(t *testing.T) { + server := createMockServer() + server.houseRepo = newMockHouseRepoForItems() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfUpdateWarehouse{ + AckHandle: 100, + BoxType: 0, + BoxIndex: 15, // > 10 + } + handleMsgMhfUpdateWarehouse(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + func TestHandleMsgMhfEnumerateWarehouse_Items(t *testing.T) { srv := createMockServer() srv.houseRepo = newMockHouseRepoForItems() diff --git a/server/channelserver/handlers_items_test.go b/server/channelserver/handlers_items_test.go index 00d85a371..48d68a40e 100644 --- a/server/channelserver/handlers_items_test.go +++ b/server/channelserver/handlers_items_test.go @@ -408,6 +408,86 @@ func TestExchangeWeeklyStamp_Yearly(t *testing.T) { } } +// --- handleMsgMhfEnumerateUnionItem tests --- + +func TestEnumerateUnionItem_WithItems(t *testing.T) { + server := createMockServer() + + // Build serialized item box with 1 item + bf := byteframe.NewByteFrame() + bf.WriteUint16(1) // numStacks + bf.WriteUint16(0) // unused + bf.WriteUint32(100) // warehouseID + bf.WriteUint16(500) // itemID + bf.WriteUint16(3) // quantity + bf.WriteUint32(0) // unk0 + + userMock := &mockUserRepoForItems{itemBoxData: bf.Data()} + server.userRepo = userMock + session := createMockSession(1, server) + session.userID = 1 + + pkt := &mhfpacket.MsgMhfEnumerateUnionItem{AckHandle: 100} + handleMsgMhfEnumerateUnionItem(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestEnumerateUnionItem_Empty(t *testing.T) { + server := createMockServer() + userMock := &mockUserRepoForItems{itemBoxData: nil} + server.userRepo = userMock + session := createMockSession(1, server) + session.userID = 1 + + pkt := &mhfpacket.MsgMhfEnumerateUnionItem{AckHandle: 100} + handleMsgMhfEnumerateUnionItem(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfUpdateUnionItem tests --- + +func TestUpdateUnionItem(t *testing.T) { + server := createMockServer() + + bf := byteframe.NewByteFrame() + bf.WriteUint16(1) // numStacks + bf.WriteUint16(0) // unused + bf.WriteUint32(100) // warehouseID + bf.WriteUint16(500) // itemID + bf.WriteUint16(3) // quantity + bf.WriteUint32(0) // unk0 + + userMock := &mockUserRepoForItems{itemBoxData: bf.Data()} + server.userRepo = userMock + session := createMockSession(1, server) + session.userID = 1 + + pkt := &mhfpacket.MsgMhfUpdateUnionItem{ + AckHandle: 100, + UpdatedItems: []mhfitem.MHFItemStack{}, + } + handleMsgMhfUpdateUnionItem(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + // Tests consolidated from handlers_core_test.go func TestHandleMsgMhfGetExtraInfo(t *testing.T) { diff --git a/server/channelserver/handlers_rengoku_test.go b/server/channelserver/handlers_rengoku_test.go index 61679d651..dfcd77351 100644 --- a/server/channelserver/handlers_rengoku_test.go +++ b/server/channelserver/handlers_rengoku_test.go @@ -109,6 +109,417 @@ func TestHandleMsgMhfGetRengokuRankingRank_DifferentAck(t *testing.T) { } } +// --- handleMsgMhfSaveRengokuData tests --- + +func TestSaveRengokuData_TooSmall(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + server.charRepo = charMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfSaveRengokuData{ + AckHandle: 100, + RawDataPayload: make([]byte, 10), // too small + } + + handleMsgMhfSaveRengokuData(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestSaveRengokuData_TooLarge(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + server.charRepo = charMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfSaveRengokuData{ + AckHandle: 100, + RawDataPayload: make([]byte, 5000), // too large + } + + handleMsgMhfSaveRengokuData(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestSaveRengokuData_NormalSave(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + server.charRepo = charMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + // Build valid payload (>= rengokuMinPayloadSize=91 bytes) + payload := make([]byte, 100) + // Set sentinel to non-zero so it's not rejected + payload[0] = 0x00 + payload[1] = 0x00 + payload[2] = 0x00 + payload[3] = 0x01 + // Set some skill data so it's not zeroed + payload[rengokuSkillSlotsStart] = 1 + + pkt := &mhfpacket.MsgMhfSaveRengokuData{ + AckHandle: 100, + RawDataPayload: payload, + } + + handleMsgMhfSaveRengokuData(session, pkt) + + if charMock.columns["rengokudata"] == nil { + t.Error("rengokudata should be saved") + } + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestSaveRengokuData_SkillMerge(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + + // Set up existing data with skills + existing := make([]byte, 100) + existing[0] = 0x00 + existing[1] = 0x00 + existing[2] = 0x00 + existing[3] = 0x01 + existing[rengokuSkillSlotsStart] = 5 // has skill + existing[rengokuSkillValuesStart] = 3 + charMock.columns["rengokudata"] = existing + + server.charRepo = charMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + // Build payload with zeroed skills but has points (triggers merge) + payload := make([]byte, 100) + payload[0] = 0x00 + payload[1] = 0x00 + payload[2] = 0x00 + payload[3] = 0x01 + // Skills are zeroed (default) + // But points are set + payload[rengokuPointsStart] = 10 + + pkt := &mhfpacket.MsgMhfSaveRengokuData{ + AckHandle: 100, + RawDataPayload: payload, + } + + handleMsgMhfSaveRengokuData(session, pkt) + + saved := charMock.columns["rengokudata"] + if saved == nil { + t.Fatal("rengokudata should be saved") + } + // Skills should be merged from existing + if saved[rengokuSkillSlotsStart] != 5 { + t.Errorf("Skill slot should be merged from existing, got %d", saved[rengokuSkillSlotsStart]) + } + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestSaveRengokuData_SentinelRejection(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + + // Set up existing data with non-zero sentinel + existing := make([]byte, 100) + existing[0] = 0x00 + existing[1] = 0x00 + existing[2] = 0x00 + existing[3] = 0x01 + existing[rengokuSkillSlotsStart] = 1 + charMock.columns["rengokudata"] = existing + + server.charRepo = charMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + // Build payload with zero sentinel (should be rejected) + payload := make([]byte, 100) + // sentinel is 0 (all zeros) + payload[rengokuSkillSlotsStart] = 1 // non-zeroed skills to skip merge path + + pkt := &mhfpacket.MsgMhfSaveRengokuData{ + AckHandle: 100, + RawDataPayload: payload, + } + + handleMsgMhfSaveRengokuData(session, pkt) + + // Existing data should be preserved (not overwritten) + if charMock.columns["rengokudata"][3] != 0x01 { + t.Error("Existing rengoku data should not be overwritten") + } + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestSaveRengokuData_SaveError(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + charMock.saveErr = errNotFound + server.charRepo = charMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + payload := make([]byte, 100) + payload[3] = 0x01 + payload[rengokuSkillSlotsStart] = 1 + + pkt := &mhfpacket.MsgMhfSaveRengokuData{ + AckHandle: 100, + RawDataPayload: payload, + } + + handleMsgMhfSaveRengokuData(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfLoadRengokuData tests --- + +func TestLoadRengokuData_WithData(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + charMock.columns["rengokudata"] = []byte{0x01, 0x02, 0x03, 0x04} + server.charRepo = charMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadRengokuData{AckHandle: 100} + + handleMsgMhfLoadRengokuData(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestLoadRengokuData_EmptyData(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + // No rengokudata column set - returns nil/empty + server.charRepo = charMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadRengokuData{AckHandle: 100} + + handleMsgMhfLoadRengokuData(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Default response should have data") + } + default: + t.Error("No response packet queued") + } +} + +func TestLoadRengokuData_DBError(t *testing.T) { + server := createMockServer() + charMock := newMockCharacterRepo() + charMock.loadColumnErr = errNotFound + server.charRepo = charMock + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfLoadRengokuData{AckHandle: 100} + + handleMsgMhfLoadRengokuData(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Default response should have data on error") + } + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfEnumerateRengokuRanking tests --- + +func TestEnumerateRengokuRanking_NoGuild_Leaderboard2(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{} // GetByCharID returns nil + server.guildRepo = guildMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{ + AckHandle: 100, + Leaderboard: 2, // guild leaderboard, requires guild + } + + handleMsgMhfEnumerateRengokuRanking(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestEnumerateRengokuRanking_NoGuild_Leaderboard3(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{} + server.guildRepo = guildMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{ + AckHandle: 100, + Leaderboard: 3, + } + + handleMsgMhfEnumerateRengokuRanking(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestEnumerateRengokuRanking_WithGuild(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 10, Name: "TestGuild"} + guildMock := &mockGuildRepo{} + guildMock.guild = guild + server.guildRepo = guildMock + server.rengokuRepo = &mockRengokuRepo{ + ranking: []RengokuScore{ + {Name: "Player1", Score: 100}, + {Name: "Player2", Score: 50}, + }, + } + session := createMockSession(1, server) + session.Name = "Player1" + + pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{ + AckHandle: 100, + Leaderboard: 2, + } + + handleMsgMhfEnumerateRengokuRanking(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response should have ranking data") + } + default: + t.Error("No response packet queued") + } +} + +func TestEnumerateRengokuRanking_SoloLeaderboard(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{} + server.guildRepo = guildMock + server.rengokuRepo = &mockRengokuRepo{ + ranking: []RengokuScore{ + {Name: "Player1", Score: 200}, + }, + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{ + AckHandle: 100, + Leaderboard: 0, // solo, no guild required + } + + handleMsgMhfEnumerateRengokuRanking(session, pkt) + + select { + case p := <-session.sendPackets: + if len(p.data) == 0 { + t.Error("Response should have ranking data") + } + default: + t.Error("No response packet queued") + } +} + +func TestEnumerateRengokuRanking_QueryError(t *testing.T) { + server := createMockServer() + guildMock := &mockGuildRepo{} + server.guildRepo = guildMock + server.rengokuRepo = &mockRengokuRepo{rankingErr: errNotFound} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{ + AckHandle: 100, + Leaderboard: 0, + } + + handleMsgMhfEnumerateRengokuRanking(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestEnumerateRengokuRanking_Applicant(t *testing.T) { + server := createMockServer() + guild := &Guild{ID: 10, Name: "TestGuild"} + guildMock := &mockGuildRepo{hasAppResult: true} + guildMock.guild = guild + server.guildRepo = guildMock + server.rengokuRepo = &mockRengokuRepo{} + session := createMockSession(1, server) + + // Leaderboard 6 requires guild, but applicant should be treated as no guild + pkt := &mhfpacket.MsgMhfEnumerateRengokuRanking{ + AckHandle: 100, + Leaderboard: 6, + } + + handleMsgMhfEnumerateRengokuRanking(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + // Tests consolidated from handlers_coverage3_test.go func TestNonTrivialHandlers_RengokuGo(t *testing.T) { diff --git a/server/channelserver/handlers_tower_test.go b/server/channelserver/handlers_tower_test.go index 61d099e5a..b859743cf 100644 --- a/server/channelserver/handlers_tower_test.go +++ b/server/channelserver/handlers_tower_test.go @@ -1,6 +1,7 @@ package channelserver import ( + "strings" "testing" "erupe-ce/network/mhfpacket" @@ -255,6 +256,255 @@ func TestHandleMsgMhfPostTenrouirai_QuestToolsDebug(t *testing.T) { <-s.sendPackets } +// --- EmptyTowerCSV tests --- + +func TestEmptyTowerCSV(t *testing.T) { + result := EmptyTowerCSV(3) + if result != "0,0,0" { + t.Errorf("EmptyTowerCSV(3) = %q, want %q", result, "0,0,0") + } + + result = EmptyTowerCSV(1) + if result != "0" { + t.Errorf("EmptyTowerCSV(1) = %q, want %q", result, "0") + } + + result = EmptyTowerCSV(5) + parts := strings.Split(result, ",") + if len(parts) != 5 { + t.Errorf("EmptyTowerCSV(5) has %d parts, want 5", len(parts)) + } +} + +// --- handleMsgMhfGetTowerInfo tests --- + +func TestGetTowerInfo_InfoType1_TRP(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{ + towerData: TowerData{TR: 10, TRP: 100}, + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetTowerInfo{AckHandle: 100, InfoType: 1} + handleMsgMhfGetTowerInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetTowerInfo_InfoType2_Skills(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{ + towerData: TowerData{TSP: 50}, + skills: "1,2,3", + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetTowerInfo{AckHandle: 100, InfoType: 2} + handleMsgMhfGetTowerInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetTowerInfo_InfoType3_Level(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{ + towerData: TowerData{Block1: 5, Block2: 3}, + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetTowerInfo{AckHandle: 100, InfoType: 3} + handleMsgMhfGetTowerInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetTowerInfo_InfoType4_History(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetTowerInfo{AckHandle: 100, InfoType: 4} + handleMsgMhfGetTowerInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetTowerInfo_InfoType5_Level(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{ + towerData: TowerData{Block1: 10, Block2: 7}, + } + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetTowerInfo{AckHandle: 100, InfoType: 5} + handleMsgMhfGetTowerInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetTowerInfo_DBError(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{towerDataErr: errNotFound} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetTowerInfo{AckHandle: 100, InfoType: 1} + handleMsgMhfGetTowerInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfGetGemInfo tests --- + +func TestGetGemInfo_QueryType1_Gems(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{gems: "1,2,3,4,5"} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetGemInfo{AckHandle: 100, QueryType: 1} + handleMsgMhfGetGemInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetGemInfo_QueryType2_History(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{gems: "0,0,0"} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetGemInfo{AckHandle: 100, QueryType: 2} + handleMsgMhfGetGemInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestGetGemInfo_NoGems(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{gems: ""} + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetGemInfo{AckHandle: 100, QueryType: 1} + handleMsgMhfGetGemInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfPostGemInfo tests --- + +func TestPostGemInfo_AddGem(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{gems: "0,0,0,0,0"} + ensureTowerService(server) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostGemInfo{AckHandle: 100, Op: 1, Gem: 0x0101, Quantity: 5} + handleMsgMhfPostGemInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestPostGemInfo_Transfer(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{gems: "0,0,0,0,0"} + ensureTowerService(server) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostGemInfo{AckHandle: 100, Op: 2, Gem: 0x0101, Quantity: 1} + handleMsgMhfPostGemInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestPostGemInfo_DebugMode(t *testing.T) { + server := createMockServer() + server.towerRepo = &mockTowerRepo{gems: "0,0,0,0,0"} + server.erupeConfig.DebugOptions.QuestTools = true + ensureTowerService(server) + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostGemInfo{AckHandle: 100, Op: 1, Gem: 0x0101, Quantity: 3} + handleMsgMhfPostGemInfo(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +// --- handleMsgMhfGetNotice / handleMsgMhfPostNotice tests --- + +func TestGetNotice(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfGetNotice{AckHandle: 100} + handleMsgMhfGetNotice(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + +func TestPostNotice(t *testing.T) { + server := createMockServer() + session := createMockSession(1, server) + + pkt := &mhfpacket.MsgMhfPostNotice{AckHandle: 100} + handleMsgMhfPostNotice(session, pkt) + + select { + case <-session.sendPackets: + default: + t.Error("No response packet queued") + } +} + // Tests consolidated from handlers_coverage3_test.go func TestNonTrivialHandlers_TowerGo(t *testing.T) {