Files
Erupe/server/channelserver/handlers_session_test.go
Houmgaor d0837e779c refactor(channelserver): consolidate tests into matching source test files
Move ~300 test functions from 21 catch-all files (handlers_core_test.go,
handlers_coverage*_test.go, *_coverage_test.go) into the *_test.go file
matching each handler's source file. This makes tests discoverable by
convention: tests for handlers_guild.go live in handlers_guild_test.go.

New files: handlers_guild_mission_test.go, sys_time_test.go.
No test logic changed — pure file reorganization.
2026-02-26 23:41:44 +01:00

1591 lines
37 KiB
Go

package channelserver
import (
"encoding/binary"
"errors"
"sync"
"testing"
"erupe-ce/common/byteframe"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
)
func TestHandleMsgSysTerminalLog_ReturnsLogIDPlusOne(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgSysTerminalLog{
AckHandle: 100,
LogID: 5,
Entries: []mhfpacket.TerminalLogEntry{
{Type1: 1, Type2: 2, Unk0: 3, Unk1: 4, Unk2: 5, Unk3: 6},
},
}
handleMsgSysTerminalLog(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) < 4 {
t.Fatal("Response too short")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysLogin_Success(t *testing.T) {
server := createMockServer()
server.erupeConfig.DebugOptions.DisableTokenCheck = true
server.userBinary = NewUserBinaryStore()
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
sessionRepo := &mockSessionRepo{}
server.sessionRepo = sessionRepo
userRepo := &mockUserRepoGacha{}
server.userRepo = userRepo
session := createMockSession(0, server)
pkt := &mhfpacket.MsgSysLogin{
AckHandle: 100,
CharID0: 42,
LoginTokenString: "test-token",
}
handleMsgSysLogin(session, pkt)
if session.charID != 42 {
t.Errorf("Expected charID 42, got %d", session.charID)
}
if session.token != "test-token" {
t.Errorf("Expected token 'test-token', got %q", session.token)
}
if sessionRepo.boundToken != "test-token" {
t.Errorf("Expected BindSession called with 'test-token', got %q", sessionRepo.boundToken)
}
select {
case <-session.sendPackets:
// success
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysLogin_GetUserIDError(t *testing.T) {
server := createMockServer()
server.erupeConfig.DebugOptions.DisableTokenCheck = true
charRepo := newMockCharacterRepo()
server.charRepo = &mockCharRepoGetUserIDErr{
mockCharacterRepo: charRepo,
getUserIDErr: errors.New("user not found"),
}
sessionRepo := &mockSessionRepo{}
server.sessionRepo = sessionRepo
userRepo := &mockUserRepoGacha{}
server.userRepo = userRepo
session := createMockSession(0, server)
pkt := &mhfpacket.MsgSysLogin{
AckHandle: 100,
CharID0: 42,
LoginTokenString: "test-token",
}
handleMsgSysLogin(session, pkt)
select {
case <-session.sendPackets:
// got a response (fail ACK)
default:
t.Error("No response packet queued on GetUserID error")
}
}
func TestHandleMsgSysLogin_BindSessionError(t *testing.T) {
server := createMockServer()
server.erupeConfig.DebugOptions.DisableTokenCheck = true
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
sessionRepo := &mockSessionRepo{bindErr: errors.New("bind failed")}
server.sessionRepo = sessionRepo
userRepo := &mockUserRepoGacha{}
server.userRepo = userRepo
session := createMockSession(0, server)
pkt := &mhfpacket.MsgSysLogin{
AckHandle: 100,
CharID0: 42,
LoginTokenString: "test-token",
}
handleMsgSysLogin(session, pkt)
select {
case <-session.sendPackets:
// got a response (fail ACK)
default:
t.Error("No response packet queued on BindSession error")
}
}
func TestHandleMsgSysLogin_SetLastCharacterError(t *testing.T) {
server := createMockServer()
server.erupeConfig.DebugOptions.DisableTokenCheck = true
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
sessionRepo := &mockSessionRepo{}
server.sessionRepo = sessionRepo
userRepo := &mockUserRepoGacha{setLastCharErr: errors.New("set failed")}
server.userRepo = userRepo
session := createMockSession(0, server)
pkt := &mhfpacket.MsgSysLogin{
AckHandle: 100,
CharID0: 42,
LoginTokenString: "test-token",
}
handleMsgSysLogin(session, pkt)
select {
case <-session.sendPackets:
// got a response (fail ACK)
default:
t.Error("No response packet queued on SetLastCharacter error")
}
}
func TestHandleMsgSysPing_Session(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgSysPing{AckHandle: 100}
handleMsgSysPing(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Empty response")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysIssueLogkey_GeneratesKey(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgSysIssueLogkey{AckHandle: 100}
handleMsgSysIssueLogkey(session, pkt)
if len(session.logKey) != 16 {
t.Errorf("Expected 16-byte log key, got %d bytes", len(session.logKey))
}
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Empty response")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysRecordLog_ZZMode(t *testing.T) {
server := createMockServer()
server.erupeConfig.RealClientMode = cfg.ZZ
server.userBinary = NewUserBinaryStore()
guildRepo := &mockGuildRepo{}
server.guildRepo = guildRepo
session := createMockSession(1, server)
// Create a stage for the session (handler accesses s.stage.reservedClientSlots)
stage := &Stage{
id: "testStage",
clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]bool),
}
stage.reservedClientSlots[1] = true
session.stage = stage
// Build kill log data: 32 header bytes + 176 monster bytes
data := make([]byte, 32+176)
// Set monster index 5 to have 2 kills (a large monster per mhfmon)
data[32+5] = 2
pkt := &mhfpacket.MsgSysRecordLog{
AckHandle: 100,
Data: data,
}
handleMsgSysRecordLog(session, pkt)
// Check that reserved slot was cleaned up
if _, exists := stage.reservedClientSlots[1]; exists {
t.Error("Expected reserved client slot to be removed")
}
select {
case <-session.sendPackets:
// success
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysLockGlobalSema_LocalChannel(t *testing.T) {
server := createMockServer()
server.GlobalID = "ch1"
session := createMockSession(1, server)
pkt := &mhfpacket.MsgSysLockGlobalSema{
AckHandle: 100,
UserIDString: "someStage",
ServerChannelIDString: "ch1",
}
handleMsgSysLockGlobalSema(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Empty response")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysLockGlobalSema_RemoteMatch(t *testing.T) {
server := createMockServer()
server.GlobalID = "ch1"
otherChannel := createMockServer()
otherChannel.GlobalID = "ch2"
otherChannel.stages.Store("prefix_testStage", &Stage{
id: "prefix_testStage",
clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]bool),
})
server.Registry = NewLocalChannelRegistry([]*Server{server, otherChannel})
session := createMockSession(1, server)
pkt := &mhfpacket.MsgSysLockGlobalSema{
AckHandle: 100,
UserIDString: "testStage",
ServerChannelIDString: "ch1",
}
handleMsgSysLockGlobalSema(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Empty response")
}
_ = byteframe.NewByteFrameFromBytes(p.data)
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysUnlockGlobalSema_Session(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgSysUnlockGlobalSema{AckHandle: 100}
handleMsgSysUnlockGlobalSema(session, pkt)
select {
case <-session.sendPackets:
// success
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgSysRightsReload_Session(t *testing.T) {
server := createMockServer()
userRepo := &mockUserRepoGacha{rights: 0x02}
server.userRepo = userRepo
session := createMockSession(1, server)
session.userID = 1
pkt := &mhfpacket.MsgSysRightsReload{AckHandle: 100}
handleMsgSysRightsReload(session, pkt)
select {
case <-session.sendPackets:
// success
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfAnnounce_Session(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
dataBf := byteframe.NewByteFrame()
dataBf.WriteUint8(2) // type = berserk
pkt := &mhfpacket.MsgMhfAnnounce{
AckHandle: 100,
IPAddress: binary.LittleEndian.Uint32([]byte{127, 0, 0, 1}),
Port: 54001,
StageID: make([]byte, 32),
Data: byteframe.NewByteFrameFromBytes(dataBf.Data()),
}
handleMsgMhfAnnounce(session, pkt)
select {
case <-session.sendPackets:
// success
default:
t.Error("No response packet queued")
}
}
// 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
getUserIDErr error
}
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()
})
}
}