Files
Erupe/network/mhfpacket/msg_opcode_coverage_test.go
Houmgaor a752c5187e fix(channelserver): send ACK on empty Raviente register payload
handleMsgSysOperateRegister returned without sending an ACK when the
payload was empty, causing the client to softlock waiting for a response.
Send doAckBufSucceed with nil data on the early-return path to match
the success-path ACK type.

Also update tests to expect error returns instead of panics from
unimplemented Build/Parse stubs (matching prior panic→error refactor),
and mark resolved anti-patterns in docs.
2026-02-20 20:05:52 +01:00

305 lines
11 KiB
Go

package mhfpacket
import (
"strings"
"testing"
"erupe-ce/common/byteframe"
_config "erupe-ce/config"
"erupe-ce/network/clientctx"
)
// callBuildSafe calls Build on the packet, recovering from panics.
// Returns the error from Build, or nil if it panicked (panic is acceptable
// for "Not implemented" stubs).
func callBuildSafe(pkt MHFPacket, bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) (err error, panicked bool) {
defer func() {
if r := recover(); r != nil {
panicked = true
}
}()
err = pkt.Build(bf, ctx)
return err, false
}
// callParseSafe calls Parse on the packet, recovering from panics.
func callParseSafe(pkt MHFPacket, bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) (err error, panicked bool) {
defer func() {
if r := recover(); r != nil {
panicked = true
}
}()
err = pkt.Parse(bf, ctx)
return err, false
}
// TestBuildCoverage_NotImplemented exercises Build() on packet types whose Build
// method is not yet covered. These stubs either return errors.New("NOT IMPLEMENTED")
// or panic("Not implemented"). Both are acceptable outcomes that indicate the
// method was reached.
func TestBuildCoverage_NotImplemented(t *testing.T) {
tests := []struct {
name string
pkt MHFPacket
}{
// msg_ca_exchange_item.go
{"MsgCaExchangeItem", &MsgCaExchangeItem{}},
// msg_head.go
{"MsgHead", &MsgHead{}},
// msg_mhf_acquire_cafe_item.go
{"MsgMhfAcquireCafeItem", &MsgMhfAcquireCafeItem{}},
// msg_mhf_acquire_monthly_item.go
{"MsgMhfAcquireMonthlyItem", &MsgMhfAcquireMonthlyItem{}},
// msg_mhf_acquire_ud_item.go
{"MsgMhfAcquireUdItem", &MsgMhfAcquireUdItem{}},
// msg_mhf_announce.go
{"MsgMhfAnnounce", &MsgMhfAnnounce{}},
// msg_mhf_check_monthly_item.go
{"MsgMhfCheckMonthlyItem", &MsgMhfCheckMonthlyItem{}},
// msg_mhf_check_weekly_stamp.go
{"MsgMhfCheckWeeklyStamp", &MsgMhfCheckWeeklyStamp{}},
// msg_mhf_enumerate_festa_member.go
{"MsgMhfEnumerateFestaMember", &MsgMhfEnumerateFestaMember{}},
// msg_mhf_enumerate_inv_guild.go
{"MsgMhfEnumerateInvGuild", &MsgMhfEnumerateInvGuild{}},
// msg_mhf_enumerate_item.go
{"MsgMhfEnumerateItem", &MsgMhfEnumerateItem{}},
// msg_mhf_enumerate_order.go
{"MsgMhfEnumerateOrder", &MsgMhfEnumerateOrder{}},
// msg_mhf_enumerate_quest.go
{"MsgMhfEnumerateQuest", &MsgMhfEnumerateQuest{}},
// msg_mhf_enumerate_ranking.go
{"MsgMhfEnumerateRanking", &MsgMhfEnumerateRanking{}},
// msg_mhf_enumerate_shop.go
{"MsgMhfEnumerateShop", &MsgMhfEnumerateShop{}},
// msg_mhf_enumerate_warehouse.go
{"MsgMhfEnumerateWarehouse", &MsgMhfEnumerateWarehouse{}},
// msg_mhf_exchange_fpoint_2_item.go
{"MsgMhfExchangeFpoint2Item", &MsgMhfExchangeFpoint2Item{}},
// msg_mhf_exchange_item_2_fpoint.go
{"MsgMhfExchangeItem2Fpoint", &MsgMhfExchangeItem2Fpoint{}},
// msg_mhf_exchange_weekly_stamp.go
{"MsgMhfExchangeWeeklyStamp", &MsgMhfExchangeWeeklyStamp{}},
// msg_mhf_generate_ud_guild_map.go
{"MsgMhfGenerateUdGuildMap", &MsgMhfGenerateUdGuildMap{}},
// msg_mhf_get_boost_time.go
{"MsgMhfGetBoostTime", &MsgMhfGetBoostTime{}},
// msg_mhf_get_boost_time_limit.go
{"MsgMhfGetBoostTimeLimit", &MsgMhfGetBoostTimeLimit{}},
// msg_mhf_get_cafe_duration.go
{"MsgMhfGetCafeDuration", &MsgMhfGetCafeDuration{}},
// msg_mhf_get_cafe_duration_bonus_info.go
{"MsgMhfGetCafeDurationBonusInfo", &MsgMhfGetCafeDurationBonusInfo{}},
// msg_mhf_get_cog_info.go
{"MsgMhfGetCogInfo", &MsgMhfGetCogInfo{}},
// msg_mhf_get_gacha_point.go
{"MsgMhfGetGachaPoint", &MsgMhfGetGachaPoint{}},
// msg_mhf_get_gem_info.go
{"MsgMhfGetGemInfo", &MsgMhfGetGemInfo{}},
// msg_mhf_get_kiju_info.go
{"MsgMhfGetKijuInfo", &MsgMhfGetKijuInfo{}},
// msg_mhf_get_myhouse_info.go
{"MsgMhfGetMyhouseInfo", &MsgMhfGetMyhouseInfo{}},
// msg_mhf_get_notice.go
{"MsgMhfGetNotice", &MsgMhfGetNotice{}},
// msg_mhf_get_tower_info.go
{"MsgMhfGetTowerInfo", &MsgMhfGetTowerInfo{}},
// msg_mhf_get_ud_info.go
{"MsgMhfGetUdInfo", &MsgMhfGetUdInfo{}},
// msg_mhf_get_ud_schedule.go
{"MsgMhfGetUdSchedule", &MsgMhfGetUdSchedule{}},
// msg_mhf_get_weekly_schedule.go
{"MsgMhfGetWeeklySchedule", &MsgMhfGetWeeklySchedule{}},
// msg_mhf_guild_huntdata.go
{"MsgMhfGuildHuntdata", &MsgMhfGuildHuntdata{}},
// msg_mhf_info_joint.go
{"MsgMhfInfoJoint", &MsgMhfInfoJoint{}},
// msg_mhf_load_deco_myset.go
{"MsgMhfLoadDecoMyset", &MsgMhfLoadDecoMyset{}},
// msg_mhf_load_guild_adventure.go
{"MsgMhfLoadGuildAdventure", &MsgMhfLoadGuildAdventure{}},
// msg_mhf_load_guild_cooking.go
{"MsgMhfLoadGuildCooking", &MsgMhfLoadGuildCooking{}},
// msg_mhf_load_hunter_navi.go
{"MsgMhfLoadHunterNavi", &MsgMhfLoadHunterNavi{}},
// msg_mhf_load_otomo_airou.go
{"MsgMhfLoadOtomoAirou", &MsgMhfLoadOtomoAirou{}},
// msg_mhf_load_partner.go
{"MsgMhfLoadPartner", &MsgMhfLoadPartner{}},
// msg_mhf_load_plate_box.go
{"MsgMhfLoadPlateBox", &MsgMhfLoadPlateBox{}},
// msg_mhf_load_plate_data.go
{"MsgMhfLoadPlateData", &MsgMhfLoadPlateData{}},
// msg_mhf_post_notice.go
{"MsgMhfPostNotice", &MsgMhfPostNotice{}},
// msg_mhf_post_tower_info.go
{"MsgMhfPostTowerInfo", &MsgMhfPostTowerInfo{}},
// msg_mhf_reserve10f.go
{"MsgMhfReserve10F", &MsgMhfReserve10F{}},
// msg_mhf_server_command.go
{"MsgMhfServerCommand", &MsgMhfServerCommand{}},
// msg_mhf_set_loginwindow.go
{"MsgMhfSetLoginwindow", &MsgMhfSetLoginwindow{}},
// msg_mhf_shut_client.go
{"MsgMhfShutClient", &MsgMhfShutClient{}},
// msg_mhf_stampcard_stamp.go
{"MsgMhfStampcardStamp", &MsgMhfStampcardStamp{}},
// msg_sys_add_object.go
{"MsgSysAddObject", &MsgSysAddObject{}},
// msg_sys_back_stage.go
{"MsgSysBackStage", &MsgSysBackStage{}},
// msg_sys_cast_binary.go
{"MsgSysCastBinary", &MsgSysCastBinary{}},
// msg_sys_create_semaphore.go
{"MsgSysCreateSemaphore", &MsgSysCreateSemaphore{}},
// msg_sys_create_stage.go
{"MsgSysCreateStage", &MsgSysCreateStage{}},
// msg_sys_del_object.go
{"MsgSysDelObject", &MsgSysDelObject{}},
// msg_sys_disp_object.go
{"MsgSysDispObject", &MsgSysDispObject{}},
// msg_sys_echo.go
{"MsgSysEcho", &MsgSysEcho{}},
// msg_sys_enter_stage.go
{"MsgSysEnterStage", &MsgSysEnterStage{}},
// msg_sys_enumerate_client.go
{"MsgSysEnumerateClient", &MsgSysEnumerateClient{}},
// msg_sys_extend_threshold.go
{"MsgSysExtendThreshold", &MsgSysExtendThreshold{}},
// msg_sys_get_stage_binary.go
{"MsgSysGetStageBinary", &MsgSysGetStageBinary{}},
// msg_sys_hide_object.go
{"MsgSysHideObject", &MsgSysHideObject{}},
// msg_sys_leave_stage.go
{"MsgSysLeaveStage", &MsgSysLeaveStage{}},
// msg_sys_lock_stage.go
{"MsgSysLockStage", &MsgSysLockStage{}},
// msg_sys_login.go
{"MsgSysLogin", &MsgSysLogin{}},
// msg_sys_move_stage.go
{"MsgSysMoveStage", &MsgSysMoveStage{}},
// msg_sys_set_stage_binary.go
{"MsgSysSetStageBinary", &MsgSysSetStageBinary{}},
// msg_sys_set_stage_pass.go
{"MsgSysSetStagePass", &MsgSysSetStagePass{}},
// msg_sys_set_status.go
{"MsgSysSetStatus", &MsgSysSetStatus{}},
// msg_sys_wait_stage_binary.go
{"MsgSysWaitStageBinary", &MsgSysWaitStageBinary{}},
// Reserve files - sys reserves
{"MsgSysReserve01", &MsgSysReserve01{}},
{"MsgSysReserve02", &MsgSysReserve02{}},
{"MsgSysReserve03", &MsgSysReserve03{}},
{"MsgSysReserve04", &MsgSysReserve04{}},
{"MsgSysReserve05", &MsgSysReserve05{}},
{"MsgSysReserve06", &MsgSysReserve06{}},
{"MsgSysReserve07", &MsgSysReserve07{}},
{"MsgSysReserve0C", &MsgSysReserve0C{}},
{"MsgSysReserve0D", &MsgSysReserve0D{}},
{"MsgSysReserve0E", &MsgSysReserve0E{}},
{"MsgSysReserve4A", &MsgSysReserve4A{}},
{"MsgSysReserve4B", &MsgSysReserve4B{}},
{"MsgSysReserve4C", &MsgSysReserve4C{}},
{"MsgSysReserve4D", &MsgSysReserve4D{}},
{"MsgSysReserve4E", &MsgSysReserve4E{}},
{"MsgSysReserve4F", &MsgSysReserve4F{}},
{"MsgSysReserve55", &MsgSysReserve55{}},
{"MsgSysReserve56", &MsgSysReserve56{}},
{"MsgSysReserve57", &MsgSysReserve57{}},
{"MsgSysReserve5C", &MsgSysReserve5C{}},
{"MsgSysReserve5E", &MsgSysReserve5E{}},
{"MsgSysReserve5F", &MsgSysReserve5F{}},
{"MsgSysReserve71", &MsgSysReserve71{}},
{"MsgSysReserve72", &MsgSysReserve72{}},
{"MsgSysReserve73", &MsgSysReserve73{}},
{"MsgSysReserve74", &MsgSysReserve74{}},
{"MsgSysReserve75", &MsgSysReserve75{}},
{"MsgSysReserve76", &MsgSysReserve76{}},
{"MsgSysReserve77", &MsgSysReserve77{}},
{"MsgSysReserve78", &MsgSysReserve78{}},
{"MsgSysReserve79", &MsgSysReserve79{}},
{"MsgSysReserve7A", &MsgSysReserve7A{}},
{"MsgSysReserve7B", &MsgSysReserve7B{}},
{"MsgSysReserve7C", &MsgSysReserve7C{}},
{"MsgSysReserve7E", &MsgSysReserve7E{}},
{"MsgSysReserve180", &MsgSysReserve180{}},
{"MsgSysReserve188", &MsgSysReserve188{}},
{"MsgSysReserve18B", &MsgSysReserve18B{}},
{"MsgSysReserve18E", &MsgSysReserve18E{}},
{"MsgSysReserve18F", &MsgSysReserve18F{}},
{"MsgSysReserve192", &MsgSysReserve192{}},
{"MsgSysReserve193", &MsgSysReserve193{}},
{"MsgSysReserve194", &MsgSysReserve194{}},
{"MsgSysReserve19B", &MsgSysReserve19B{}},
{"MsgSysReserve19E", &MsgSysReserve19E{}},
{"MsgSysReserve19F", &MsgSysReserve19F{}},
{"MsgSysReserve1A4", &MsgSysReserve1A4{}},
{"MsgSysReserve1A6", &MsgSysReserve1A6{}},
{"MsgSysReserve1A7", &MsgSysReserve1A7{}},
{"MsgSysReserve1A8", &MsgSysReserve1A8{}},
{"MsgSysReserve1A9", &MsgSysReserve1A9{}},
{"MsgSysReserve1AA", &MsgSysReserve1AA{}},
{"MsgSysReserve1AB", &MsgSysReserve1AB{}},
{"MsgSysReserve1AC", &MsgSysReserve1AC{}},
{"MsgSysReserve1AD", &MsgSysReserve1AD{}},
{"MsgSysReserve1AE", &MsgSysReserve1AE{}},
{"MsgSysReserve1AF", &MsgSysReserve1AF{}},
}
ctx := &clientctx.ClientContext{RealClientMode: _config.ZZ}
bf := byteframe.NewByteFrame()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err, panicked := callBuildSafe(tt.pkt, bf, ctx)
if panicked {
// Build panicked with "Not implemented" - this is acceptable
// and still exercises the code path for coverage.
return
}
if err == nil {
// Build succeeded (some packets may have implemented Build)
return
}
// Build returned an error, which is expected for NOT IMPLEMENTED stubs
errMsg := err.Error()
if errMsg != "NOT IMPLEMENTED" && !strings.Contains(errMsg, "not implemented") {
t.Errorf("Build() returned unexpected error: %v", err)
}
})
}
}
// TestParseCoverage_NotImplemented exercises Parse() on packet types whose Parse
// method returns "NOT IMPLEMENTED" and is not yet covered by existing tests.
func TestParseCoverage_NotImplemented(t *testing.T) {
tests := []struct {
name string
pkt MHFPacket
}{
// msg_mhf_acquire_tournament.go - Parse returns NOT IMPLEMENTED
{"MsgMhfAcquireTournament", &MsgMhfAcquireTournament{}},
// msg_mhf_entry_tournament.go - Parse returns NOT IMPLEMENTED
{"MsgMhfEntryTournament", &MsgMhfEntryTournament{}},
// msg_mhf_update_guild.go - Parse returns NOT IMPLEMENTED
{"MsgMhfUpdateGuild", &MsgMhfUpdateGuild{}},
}
ctx := &clientctx.ClientContext{RealClientMode: _config.ZZ}
bf := byteframe.NewByteFrame()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err, panicked := callParseSafe(tt.pkt, bf, ctx)
if panicked {
return
}
if err == nil {
return
}
if err.Error() != "NOT IMPLEMENTED" {
t.Errorf("Parse() returned unexpected error: %v", err)
}
})
}
}