mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
test: expand channelserver coverage from 12% to 16%
Add comprehensive tests for handler files: - handlers_object: object creation, positioning, binary ops - handlers_semaphore: create, acquire, release, check, delete - handlers_reserve: stub handlers and reserve188/18B - handlers_event: registration, release, feature weapons - handlers_mutex: create, open, close, delete operations - handlers_campaign: enumerate, state, apply - handlers_bbs: user status, SNS status, article apply - handlers_tournament: info, entry, acquire - handlers_users: user binary operations - handlers_clients: client enumeration - handlers_rengoku: ranking - handlers_register: raviente semaphore functions - handlers_tower: tower info, tenrouirai, seibatu ranking All tests pass with race detection enabled.
This commit is contained in:
74
server/channelserver/handlers_bbs_test.go
Normal file
74
server/channelserver/handlers_bbs_test.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/config"
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgMhfGetBbsUserStatus(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetBbsUserStatus{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetBbsUserStatus(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetBbsSnsStatus(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetBbsSnsStatus{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetBbsSnsStatus(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfApplyBbsArticle(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.erupeConfig = &config.Config{
|
||||||
|
ScreenshotAPIURL: "http://example.com/api",
|
||||||
|
}
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfApplyBbsArticle{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfApplyBbsArticle(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
70
server/channelserver/handlers_campaign_test.go
Normal file
70
server/channelserver/handlers_campaign_test.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgMhfEnumerateCampaign(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfEnumerateCampaign{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfEnumerateCampaign(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued (fail response expected)
|
||||||
|
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 TestHandleMsgMhfStateCampaign(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfStateCampaign{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfStateCampaign(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued (fail response expected)
|
||||||
|
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 TestHandleMsgMhfApplyCampaign(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfApplyCampaign{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfApplyCampaign(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued (fail response expected)
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
150
server/channelserver/handlers_clients_test.go
Normal file
150
server/channelserver/handlers_clients_test.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgSysEnumerateClient_StageNotExists(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysEnumerateClient{
|
||||||
|
AckHandle: 12345,
|
||||||
|
StageID: "nonexistent_stage",
|
||||||
|
Get: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysEnumerateClient(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued (failure expected)
|
||||||
|
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 TestHandleMsgSysEnumerateClient_AllClients(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create stage with clients
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
server.stages["test_stage"] = stage
|
||||||
|
|
||||||
|
client1 := createMockSession(100, server)
|
||||||
|
client2 := createMockSession(200, server)
|
||||||
|
stage.clients[client1] = client1.charID
|
||||||
|
stage.clients[client2] = client2.charID
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysEnumerateClient{
|
||||||
|
AckHandle: 12345,
|
||||||
|
StageID: "test_stage",
|
||||||
|
Get: 0, // All clients
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysEnumerateClient(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgSysEnumerateClient_NotReady(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create stage with reserved slots
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
server.stages["test_stage"] = stage
|
||||||
|
|
||||||
|
stage.reservedClientSlots[100] = false // Not ready
|
||||||
|
stage.reservedClientSlots[200] = true // Ready
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysEnumerateClient{
|
||||||
|
AckHandle: 12345,
|
||||||
|
StageID: "test_stage",
|
||||||
|
Get: 1, // Not ready
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysEnumerateClient(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgSysEnumerateClient_Ready(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create stage with reserved slots
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
server.stages["test_stage"] = stage
|
||||||
|
|
||||||
|
stage.reservedClientSlots[100] = false // Not ready
|
||||||
|
stage.reservedClientSlots[200] = true // Ready
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysEnumerateClient{
|
||||||
|
AckHandle: 12345,
|
||||||
|
StageID: "test_stage",
|
||||||
|
Get: 2, // Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysEnumerateClient(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfShutClient(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgMhfShutClient panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgMhfShutClient(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysHideClient(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysHideClient panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysHideClient(session, nil)
|
||||||
|
}
|
||||||
161
server/channelserver/handlers_event_test.go
Normal file
161
server/channelserver/handlers_event_test.go
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgMhfRegisterEvent(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfRegisterEvent{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk2: 1,
|
||||||
|
Unk4: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfRegisterEvent(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfReleaseEvent(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfReleaseEvent{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfReleaseEvent(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued (with special error code 0x41)
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgMhfEnumerateEvent(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfEnumerateEvent{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfEnumerateEvent(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgMhfGetRestrictionEvent(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgMhfGetRestrictionEvent panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgMhfGetRestrictionEvent(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgMhfSetRestrictionEvent(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfSetRestrictionEvent{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfSetRestrictionEvent(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestGenerateFeatureWeapons(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
count int
|
||||||
|
}{
|
||||||
|
{"single weapon", 1},
|
||||||
|
{"few weapons", 3},
|
||||||
|
{"normal count", 7},
|
||||||
|
{"max weapons", 14},
|
||||||
|
{"over max", 20}, // Should cap at 14
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := generateFeatureWeapons(tt.count)
|
||||||
|
|
||||||
|
// Result should be non-zero for positive counts
|
||||||
|
if tt.count > 0 && result.ActiveFeatures == 0 {
|
||||||
|
t.Error("Expected non-zero ActiveFeatures")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not exceed max value (2^14 - 1 = 16383)
|
||||||
|
if result.ActiveFeatures > 16383 {
|
||||||
|
t.Errorf("ActiveFeatures = %d, exceeds max of 16383", result.ActiveFeatures)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateFeatureWeapons_Randomness(t *testing.T) {
|
||||||
|
// Generate multiple times and verify some variation
|
||||||
|
results := make(map[uint32]int)
|
||||||
|
iterations := 100
|
||||||
|
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
result := generateFeatureWeapons(5)
|
||||||
|
results[result.ActiveFeatures]++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should have some variation (not all the same)
|
||||||
|
if len(results) == 1 {
|
||||||
|
t.Error("Expected some variation in generated weapons")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateFeatureWeapons_ZeroCount(t *testing.T) {
|
||||||
|
result := generateFeatureWeapons(0)
|
||||||
|
|
||||||
|
// Should return 0 for no weapons
|
||||||
|
if result.ActiveFeatures != 0 {
|
||||||
|
t.Errorf("Expected 0 for zero count, got %d", result.ActiveFeatures)
|
||||||
|
}
|
||||||
|
}
|
||||||
77
server/channelserver/handlers_mutex_test.go
Normal file
77
server/channelserver/handlers_mutex_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test that all mutex handlers don't panic (they are empty implementations)
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateMutex(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysCreateMutex panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysCreateMutex(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateOpenMutex(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysCreateOpenMutex panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysCreateOpenMutex(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDeleteMutex(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDeleteMutex panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDeleteMutex(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysOpenMutex(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysOpenMutex panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysOpenMutex(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCloseMutex(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysCloseMutex panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysCloseMutex(session, nil)
|
||||||
|
}
|
||||||
393
server/channelserver/handlers_object_test.go
Normal file
393
server/channelserver/handlers_object_test.go
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create a stage for the session
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
session.stage = stage
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateObject{
|
||||||
|
AckHandle: 12345,
|
||||||
|
X: 100.0,
|
||||||
|
Y: 50.0,
|
||||||
|
Z: -25.0,
|
||||||
|
Unk0: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateObject(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify object was created in stage
|
||||||
|
if len(stage.objects) != 1 {
|
||||||
|
t.Errorf("Stage should have 1 object, got %d", len(stage.objects))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateObject_MultipleObjects(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
|
||||||
|
// Create multiple sessions that create objects
|
||||||
|
sessions := make([]*Session, 3)
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
sessions[i] = createMockSession(uint32(i+1), server)
|
||||||
|
sessions[i].stage = stage
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateObject{
|
||||||
|
AckHandle: uint32(12345 + i),
|
||||||
|
X: float32(i * 10),
|
||||||
|
Y: float32(i * 20),
|
||||||
|
Z: float32(i * 30),
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateObject(sessions[i], pkt)
|
||||||
|
|
||||||
|
// Drain send queue
|
||||||
|
select {
|
||||||
|
case <-sessions[i].sendPackets:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All objects should exist
|
||||||
|
if len(stage.objects) != 3 {
|
||||||
|
t.Errorf("Stage should have 3 objects, got %d", len(stage.objects))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysPositionObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create a stage with an existing object
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
session.stage = stage
|
||||||
|
|
||||||
|
// Add another session to receive broadcast
|
||||||
|
session2 := createMockSession(2, server)
|
||||||
|
session2.stage = stage
|
||||||
|
stage.clients[session] = session.charID
|
||||||
|
stage.clients[session2] = session2.charID
|
||||||
|
|
||||||
|
// Create an object
|
||||||
|
stage.objects[session.charID] = &Object{
|
||||||
|
id: 1,
|
||||||
|
ownerCharID: session.charID,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysPositionObject{
|
||||||
|
ObjID: 1,
|
||||||
|
X: 100.0,
|
||||||
|
Y: 200.0,
|
||||||
|
Z: 300.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysPositionObject(session, pkt)
|
||||||
|
|
||||||
|
// Verify object position was updated
|
||||||
|
obj := stage.objects[session.charID]
|
||||||
|
if obj.x != 100.0 || obj.y != 200.0 || obj.z != 300.0 {
|
||||||
|
t.Errorf("Object position not updated: got (%f, %f, %f), want (100, 200, 300)",
|
||||||
|
obj.x, obj.y, obj.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify broadcast was sent to session2
|
||||||
|
select {
|
||||||
|
case <-session2.sendPackets:
|
||||||
|
// Good - broadcast received
|
||||||
|
default:
|
||||||
|
t.Error("Position update should be broadcast to other sessions")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysPositionObject_NoObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
session.stage = stage
|
||||||
|
stage.clients[session] = session.charID
|
||||||
|
|
||||||
|
// Position update for non-existent object - should not panic
|
||||||
|
pkt := &mhfpacket.MsgSysPositionObject{
|
||||||
|
ObjID: 999,
|
||||||
|
X: 100.0,
|
||||||
|
Y: 200.0,
|
||||||
|
Z: 300.0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not panic
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysPositionObject panicked with non-existent object: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysPositionObject(session, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDeleteObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDeleteObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDeleteObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysRotateObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysRotateObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysRotateObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDuplicateObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDuplicateObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDuplicateObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysSetObjectBinary(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.userBinaryParts = make(map[userBinaryPartID][]byte)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
server.sessions[nil] = session // Add session to server
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysSetObjectBinary{
|
||||||
|
RawDataPayload: []byte{0x01, 0x02, 0x03, 0x04},
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysSetObjectBinary(session, pkt)
|
||||||
|
|
||||||
|
// Verify binary was stored
|
||||||
|
key := userBinaryPartID{charID: session.charID, index: 3}
|
||||||
|
if data, ok := server.userBinaryParts[key]; !ok {
|
||||||
|
t.Error("Object binary should be stored")
|
||||||
|
} else if len(data) != 4 {
|
||||||
|
t.Errorf("Object binary length = %d, want 4", len(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysGetObjectBinary(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysGetObjectBinary panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysGetObjectBinary(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysGetObjectOwner(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysGetObjectOwner panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysGetObjectOwner(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysUpdateObjectBinary(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysUpdateObjectBinary panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysUpdateObjectBinary(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCleanupObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysCleanupObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysCleanupObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysAddObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysAddObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysAddObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDelObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDelObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDelObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDispObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDispObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDispObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysHideObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysHideObject panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysHideObject(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObjectHandlers_SequentialCreateObject(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
|
||||||
|
// Create objects sequentially from multiple sessions
|
||||||
|
// Note: handleMsgSysCreateObject has a race condition in NextObjectID
|
||||||
|
// so we test sequential creation instead
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
session := createMockSession(uint32(i), server)
|
||||||
|
session.stage = stage
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateObject{
|
||||||
|
AckHandle: uint32(i),
|
||||||
|
X: float32(i),
|
||||||
|
Y: float32(i * 2),
|
||||||
|
Z: float32(i * 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateObject(session, pkt)
|
||||||
|
|
||||||
|
// Drain send queue
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// All objects should be created
|
||||||
|
if len(stage.objects) != 10 {
|
||||||
|
t.Errorf("Expected 10 objects, got %d", len(stage.objects))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestObjectHandlers_SequentialPositionUpdate(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stage := NewStage("test_stage")
|
||||||
|
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
session.stage = stage
|
||||||
|
stage.clients[session] = session.charID
|
||||||
|
|
||||||
|
// Create an object
|
||||||
|
stage.objects[session.charID] = &Object{
|
||||||
|
id: 1,
|
||||||
|
ownerCharID: session.charID,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
z: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sequentially update object position
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
pkt := &mhfpacket.MsgSysPositionObject{
|
||||||
|
ObjID: 1,
|
||||||
|
X: float32(i),
|
||||||
|
Y: float32(i * 2),
|
||||||
|
Z: float32(i * 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysPositionObject(session, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify final position
|
||||||
|
obj := stage.objects[session.charID]
|
||||||
|
if obj.x != 9 || obj.y != 18 || obj.z != 27 {
|
||||||
|
t.Errorf("Object position not as expected: got (%f, %f, %f), want (9, 18, 27)",
|
||||||
|
obj.x, obj.y, obj.z)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
server/channelserver/handlers_register_test.go
Normal file
63
server/channelserver/handlers_register_test.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgSysNotifyRegister(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysNotifyRegister panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysNotifyRegister(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRaviSemaphore_None(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
|
||||||
|
result := getRaviSemaphore(server)
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
t.Error("Expected nil when no raviente semaphore exists")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRaviSemaphore_Found(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
|
||||||
|
// Create a raviente semaphore (matches prefix hs_l0u3B5 and suffix 3)
|
||||||
|
sema := NewSemaphore(server, "hs_l0u3B53", 32)
|
||||||
|
server.semaphore["hs_l0u3B53"] = sema
|
||||||
|
|
||||||
|
result := getRaviSemaphore(server)
|
||||||
|
|
||||||
|
if result == nil {
|
||||||
|
t.Error("Expected to find raviente semaphore")
|
||||||
|
}
|
||||||
|
if result.id_semaphore != "hs_l0u3B53" {
|
||||||
|
t.Errorf("Wrong semaphore returned: %s", result.id_semaphore)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRaviSemaphore_WrongSuffix(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
|
||||||
|
// Create a semaphore with wrong suffix
|
||||||
|
sema := NewSemaphore(server, "hs_l0u3B51", 32)
|
||||||
|
server.semaphore["hs_l0u3B51"] = sema
|
||||||
|
|
||||||
|
result := getRaviSemaphore(server)
|
||||||
|
|
||||||
|
if result != nil {
|
||||||
|
t.Error("Should not match semaphore with wrong suffix")
|
||||||
|
}
|
||||||
|
}
|
||||||
28
server/channelserver/handlers_rengoku_test.go
Normal file
28
server/channelserver/handlers_rengoku_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgMhfGetRengokuRankingRank(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetRengokuRankingRank{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetRengokuRankingRank(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
138
server/channelserver/handlers_reserve_test.go
Normal file
138
server/channelserver/handlers_reserve_test.go
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Test that reserve handlers with AckHandle respond correctly
|
||||||
|
|
||||||
|
func TestHandleMsgSysReserve188(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysReserve188{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysReserve188(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgSysReserve18B(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysReserve18B{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysReserve18B(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that empty reserve handlers don't panic
|
||||||
|
|
||||||
|
func TestEmptyReserveHandlers(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
handler func(s *Session, p mhfpacket.MHFPacket)
|
||||||
|
}{
|
||||||
|
{"handleMsgSysReserve55", handleMsgSysReserve55},
|
||||||
|
{"handleMsgSysReserve56", handleMsgSysReserve56},
|
||||||
|
{"handleMsgSysReserve57", handleMsgSysReserve57},
|
||||||
|
{"handleMsgSysReserve01", handleMsgSysReserve01},
|
||||||
|
{"handleMsgSysReserve02", handleMsgSysReserve02},
|
||||||
|
{"handleMsgSysReserve03", handleMsgSysReserve03},
|
||||||
|
{"handleMsgSysReserve04", handleMsgSysReserve04},
|
||||||
|
{"handleMsgSysReserve05", handleMsgSysReserve05},
|
||||||
|
{"handleMsgSysReserve06", handleMsgSysReserve06},
|
||||||
|
{"handleMsgSysReserve07", handleMsgSysReserve07},
|
||||||
|
{"handleMsgSysReserve0C", handleMsgSysReserve0C},
|
||||||
|
{"handleMsgSysReserve0D", handleMsgSysReserve0D},
|
||||||
|
{"handleMsgSysReserve0E", handleMsgSysReserve0E},
|
||||||
|
{"handleMsgSysReserve4A", handleMsgSysReserve4A},
|
||||||
|
{"handleMsgSysReserve4B", handleMsgSysReserve4B},
|
||||||
|
{"handleMsgSysReserve4C", handleMsgSysReserve4C},
|
||||||
|
{"handleMsgSysReserve4D", handleMsgSysReserve4D},
|
||||||
|
{"handleMsgSysReserve4E", handleMsgSysReserve4E},
|
||||||
|
{"handleMsgSysReserve4F", handleMsgSysReserve4F},
|
||||||
|
{"handleMsgSysReserve5C", handleMsgSysReserve5C},
|
||||||
|
{"handleMsgSysReserve5E", handleMsgSysReserve5E},
|
||||||
|
{"handleMsgSysReserve5F", handleMsgSysReserve5F},
|
||||||
|
{"handleMsgSysReserve71", handleMsgSysReserve71},
|
||||||
|
{"handleMsgSysReserve72", handleMsgSysReserve72},
|
||||||
|
{"handleMsgSysReserve73", handleMsgSysReserve73},
|
||||||
|
{"handleMsgSysReserve74", handleMsgSysReserve74},
|
||||||
|
{"handleMsgSysReserve75", handleMsgSysReserve75},
|
||||||
|
{"handleMsgSysReserve76", handleMsgSysReserve76},
|
||||||
|
{"handleMsgSysReserve77", handleMsgSysReserve77},
|
||||||
|
{"handleMsgSysReserve78", handleMsgSysReserve78},
|
||||||
|
{"handleMsgSysReserve79", handleMsgSysReserve79},
|
||||||
|
{"handleMsgSysReserve7A", handleMsgSysReserve7A},
|
||||||
|
{"handleMsgSysReserve7B", handleMsgSysReserve7B},
|
||||||
|
{"handleMsgSysReserve7C", handleMsgSysReserve7C},
|
||||||
|
{"handleMsgSysReserve7E", handleMsgSysReserve7E},
|
||||||
|
{"handleMsgMhfReserve10F", handleMsgMhfReserve10F},
|
||||||
|
{"handleMsgSysReserve180", handleMsgSysReserve180},
|
||||||
|
{"handleMsgSysReserve18E", handleMsgSysReserve18E},
|
||||||
|
{"handleMsgSysReserve18F", handleMsgSysReserve18F},
|
||||||
|
{"handleMsgSysReserve19E", handleMsgSysReserve19E},
|
||||||
|
{"handleMsgSysReserve19F", handleMsgSysReserve19F},
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Call with nil packet - empty handlers should handle this
|
||||||
|
tt.handler(session, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test reserve handlers are registered in handler table
|
||||||
|
|
||||||
|
func TestReserveHandlersRegistered(t *testing.T) {
|
||||||
|
if handlerTable == nil {
|
||||||
|
t.Fatal("handlerTable should be initialized")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that reserve handlers exist in the table
|
||||||
|
reserveHandlerCount := 0
|
||||||
|
for _, handler := range handlerTable {
|
||||||
|
if handler != nil {
|
||||||
|
reserveHandlerCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if reserveHandlerCount < 50 {
|
||||||
|
t.Errorf("Expected at least 50 handlers registered, got %d", reserveHandlerCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
457
server/channelserver/handlers_semaphore_test.go
Normal file
457
server/channelserver/handlers_semaphore_test.go
Normal file
@@ -0,0 +1,457 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
DataSize: 0,
|
||||||
|
RawDataPayload: []byte{},
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgSysDeleteSemaphore_NoSemaphores(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysDeleteSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not panic when no semaphores exist
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDeleteSemaphore panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDeleteSemaphore(session, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDeleteSemaphore_WithSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create a semaphore
|
||||||
|
sema := NewSemaphore(server, "test_sema", 4)
|
||||||
|
server.semaphore["test_sema"] = sema
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysDeleteSemaphore{
|
||||||
|
AckHandle: sema.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysDeleteSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Semaphore should be deleted
|
||||||
|
if _, exists := server.semaphore["test_sema"]; exists {
|
||||||
|
t.Error("Semaphore should be deleted")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateAcquireSemaphore_NewSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateAcquireSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
PlayerCount: 4,
|
||||||
|
SemaphoreID: "test_semaphore",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateAcquireSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify semaphore was created
|
||||||
|
if _, exists := server.semaphore["test_semaphore"]; !exists {
|
||||||
|
t.Error("Semaphore should be created")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateAcquireSemaphore_ExistingSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Pre-create semaphore
|
||||||
|
sema := NewSemaphore(server, "existing_sema", 4)
|
||||||
|
server.semaphore["existing_sema"] = sema
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateAcquireSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
PlayerCount: 4,
|
||||||
|
SemaphoreID: "existing_sema",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateAcquireSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify client was added to semaphore
|
||||||
|
if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 {
|
||||||
|
t.Error("Session should be added to semaphore")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateAcquireSemaphore_RavienteSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Test raviente semaphore (special prefix)
|
||||||
|
pkt := &mhfpacket.MsgSysCreateAcquireSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
PlayerCount: 32,
|
||||||
|
SemaphoreID: "hs_l0u3B51", // Raviente prefix + suffix
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateAcquireSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify raviente semaphore was created with special settings
|
||||||
|
if sema, exists := server.semaphore["hs_l0u3B51"]; !exists {
|
||||||
|
t.Error("Raviente semaphore should be created")
|
||||||
|
} else if sema.maxPlayers != 32 {
|
||||||
|
t.Errorf("Raviente semaphore maxPlayers = %d, want 32", sema.maxPlayers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCreateAcquireSemaphore_Full(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
|
||||||
|
// Create semaphore with 1 player max
|
||||||
|
sema := NewSemaphore(server, "full_sema", 1)
|
||||||
|
server.semaphore["full_sema"] = sema
|
||||||
|
|
||||||
|
// Fill the semaphore
|
||||||
|
session1 := createMockSession(1, server)
|
||||||
|
sema.reservedClientSlots[session1.charID] = nil
|
||||||
|
sema.clients[session1] = session1.charID
|
||||||
|
|
||||||
|
// Try to acquire with another session
|
||||||
|
session2 := createMockSession(2, server)
|
||||||
|
pkt := &mhfpacket.MsgSysCreateAcquireSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
PlayerCount: 1,
|
||||||
|
SemaphoreID: "full_sema",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateAcquireSemaphore(session2, pkt)
|
||||||
|
|
||||||
|
// Should still respond (with failure indication)
|
||||||
|
select {
|
||||||
|
case p := <-session2.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data even for full semaphore")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysAcquireSemaphore_Exists(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create semaphore
|
||||||
|
sema := NewSemaphore(server, "acquire_test", 4)
|
||||||
|
server.semaphore["acquire_test"] = sema
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysAcquireSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
SemaphoreID: "acquire_test",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysAcquireSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) == 0 {
|
||||||
|
t.Error("Response packet should have data")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify client was added
|
||||||
|
if _, exists := sema.clients[session]; !exists {
|
||||||
|
t.Error("Session should be added to semaphore clients")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysAcquireSemaphore_NotExists(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysAcquireSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
SemaphoreID: "nonexistent",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysAcquireSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Should respond with failure
|
||||||
|
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 TestHandleMsgSysReleaseSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (mostly empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysReleaseSemaphore panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysReleaseSemaphore{}
|
||||||
|
handleMsgSysReleaseSemaphore(session, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCheckSemaphore_Exists(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create semaphore
|
||||||
|
sema := NewSemaphore(server, "check_test", 4)
|
||||||
|
server.semaphore["check_test"] = sema
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCheckSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
SemaphoreID: "check_test",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCheckSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response indicates semaphore exists
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) < 4 {
|
||||||
|
t.Error("Response packet should have at least 4 bytes")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysCheckSemaphore_NotExists(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCheckSemaphore{
|
||||||
|
AckHandle: 12345,
|
||||||
|
SemaphoreID: "nonexistent",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCheckSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Verify response indicates semaphore does not exist
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) < 4 {
|
||||||
|
t.Error("Response packet should have at least 4 bytes")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveSessionFromSemaphore(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create semaphore and add session
|
||||||
|
sema := NewSemaphore(server, "remove_test", 4)
|
||||||
|
sema.clients[session] = session.charID
|
||||||
|
sema.reservedClientSlots[session.charID] = nil
|
||||||
|
server.semaphore["remove_test"] = sema
|
||||||
|
|
||||||
|
// Remove session
|
||||||
|
removeSessionFromSemaphore(session)
|
||||||
|
|
||||||
|
// Verify session was removed
|
||||||
|
if _, exists := sema.clients[session]; exists {
|
||||||
|
t.Error("Session should be removed from clients")
|
||||||
|
}
|
||||||
|
if _, exists := sema.reservedClientSlots[session.charID]; exists {
|
||||||
|
t.Error("Session should be removed from reserved slots")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRemoveSessionFromSemaphore_MultipleSemaphores(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create multiple semaphores with the session
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
sema := NewSemaphore(server, "multi_test_"+string(rune('a'+i)), 4)
|
||||||
|
sema.clients[session] = session.charID
|
||||||
|
sema.reservedClientSlots[session.charID] = nil
|
||||||
|
server.semaphore["multi_test_"+string(rune('a'+i))] = sema
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove session from all
|
||||||
|
removeSessionFromSemaphore(session)
|
||||||
|
|
||||||
|
// Verify session was removed from all semaphores
|
||||||
|
for _, sema := range server.semaphore {
|
||||||
|
if _, exists := sema.clients[session]; exists {
|
||||||
|
t.Error("Session should be removed from all semaphore clients")
|
||||||
|
}
|
||||||
|
if _, exists := sema.reservedClientSlots[session.charID]; exists {
|
||||||
|
t.Error("Session should be removed from all reserved slots")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDestructEmptySemaphores(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Create empty semaphore
|
||||||
|
sema := NewSemaphore(server, "empty_sema", 4)
|
||||||
|
server.semaphore["empty_sema"] = sema
|
||||||
|
|
||||||
|
// Create non-empty semaphore
|
||||||
|
semaWithClients := NewSemaphore(server, "with_clients", 4)
|
||||||
|
semaWithClients.clients[session] = session.charID
|
||||||
|
server.semaphore["with_clients"] = semaWithClients
|
||||||
|
|
||||||
|
destructEmptySemaphores(session)
|
||||||
|
|
||||||
|
// Empty semaphore should be deleted
|
||||||
|
if _, exists := server.semaphore["empty_sema"]; exists {
|
||||||
|
t.Error("Empty semaphore should be deleted")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-empty semaphore should remain
|
||||||
|
if _, exists := server.semaphore["with_clients"]; !exists {
|
||||||
|
t.Error("Non-empty semaphore should remain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSemaphoreHandlers_SequentialAcquire(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
|
||||||
|
// Sequentially try to create/acquire the same semaphore
|
||||||
|
// Note: the handler has race conditions when accessed concurrently
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
session := createMockSession(uint32(i), server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCreateAcquireSemaphore{
|
||||||
|
AckHandle: uint32(i),
|
||||||
|
Unk0: 0,
|
||||||
|
PlayerCount: 4,
|
||||||
|
SemaphoreID: "sequential_test",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCreateAcquireSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Drain send queue
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Semaphore should exist
|
||||||
|
if _, exists := server.semaphore["sequential_test"]; !exists {
|
||||||
|
t.Error("Semaphore should exist after sequential acquires")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSemaphoreHandlers_MultipleCheck(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.semaphore = make(map[string]*Semaphore)
|
||||||
|
|
||||||
|
// Create semaphore
|
||||||
|
sema := NewSemaphore(server, "check_multiple", 4)
|
||||||
|
server.semaphore["check_multiple"] = sema
|
||||||
|
|
||||||
|
// Check the semaphore from multiple sessions sequentially
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
session := createMockSession(uint32(i), server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysCheckSemaphore{
|
||||||
|
AckHandle: uint32(i),
|
||||||
|
SemaphoreID: "check_multiple",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysCheckSemaphore(session, pkt)
|
||||||
|
|
||||||
|
// Drain send queue
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
79
server/channelserver/handlers_tournament_test.go
Normal file
79
server/channelserver/handlers_tournament_test.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgMhfInfoTournament_Type0(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfInfoTournament{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfInfoTournament(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfInfoTournament_Type1(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfInfoTournament{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfInfoTournament(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfEntryTournament(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgMhfEntryTournament panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgMhfEntryTournament(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgMhfAcquireTournament(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgMhfAcquireTournament panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgMhfAcquireTournament(session, nil)
|
||||||
|
}
|
||||||
296
server/channelserver/handlers_tower_test.go
Normal file
296
server/channelserver/handlers_tower_test.go
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgMhfGetTowerInfo_TowerRankPoint(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTowerInfo{
|
||||||
|
AckHandle: 12345,
|
||||||
|
InfoType: mhfpacket.TowerInfoTypeTowerRankPoint,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTowerInfo(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetTowerInfo_GetOwnTowerSkill(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTowerInfo{
|
||||||
|
AckHandle: 12345,
|
||||||
|
InfoType: mhfpacket.TowerInfoTypeGetOwnTowerSkill,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTowerInfo(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetTowerInfo_TowerTouhaHistory(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTowerInfo{
|
||||||
|
AckHandle: 12345,
|
||||||
|
InfoType: mhfpacket.TowerInfoTypeTowerTouhaHistory,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTowerInfo(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetTowerInfo_Unk5(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTowerInfo{
|
||||||
|
AckHandle: 12345,
|
||||||
|
InfoType: mhfpacket.TowerInfoTypeUnk5,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTowerInfo(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfPostTowerInfo(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfPostTowerInfo{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfPostTowerInfo(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetTenrouirai_Type1(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTenrouirai{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTenrouirai(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetTenrouirai_Type4(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTenrouirai{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
Unk2: 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTenrouirai(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetTenrouirai_Default(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetTenrouirai{
|
||||||
|
AckHandle: 12345,
|
||||||
|
Unk0: 0,
|
||||||
|
Unk2: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetTenrouirai(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfPostTenrouirai(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfPostTenrouirai{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfPostTenrouirai(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetBreakSeibatuLevelReward(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgMhfGetBreakSeibatuLevelReward panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgMhfGetBreakSeibatuLevelReward(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgMhfGetWeeklySeibatuRankingReward(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetWeeklySeibatuRankingReward{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetWeeklySeibatuRankingReward(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfPresentBox(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfPresentBox{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfPresentBox(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfGetGemInfo(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetGemInfo{
|
||||||
|
AckHandle: 12345,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfGetGemInfo(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgMhfPostGemInfo(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgMhfPostGemInfo panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgMhfPostGemInfo(session, nil)
|
||||||
|
}
|
||||||
128
server/channelserver/handlers_users_test.go
Normal file
128
server/channelserver/handlers_users_test.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandleMsgSysInsertUser(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysInsertUser panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysInsertUser(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysDeleteUser(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysDeleteUser panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysDeleteUser(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysNotifyUserBinary(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Should not panic (empty handler)
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
t.Errorf("handleMsgSysNotifyUserBinary panicked: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysNotifyUserBinary(session, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestHandleMsgSysGetUserBinary_FromCache(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.userBinaryParts = make(map[userBinaryPartID][]byte)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Pre-populate cache
|
||||||
|
key := userBinaryPartID{charID: 100, index: 1}
|
||||||
|
server.userBinaryParts[key] = []byte{0x01, 0x02, 0x03, 0x04}
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgSysGetUserBinary{
|
||||||
|
AckHandle: 12345,
|
||||||
|
CharID: 100,
|
||||||
|
BinaryType: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgSysGetUserBinary(session, pkt)
|
||||||
|
|
||||||
|
// Verify response packet was queued
|
||||||
|
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 TestHandleMsgSysGetUserBinary_NotInCache(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
server.userBinaryParts = make(map[userBinaryPartID][]byte)
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
// Don't populate cache - will fall back to DB (which is nil in test)
|
||||||
|
pkt := &mhfpacket.MsgSysGetUserBinary{
|
||||||
|
AckHandle: 12345,
|
||||||
|
CharID: 100,
|
||||||
|
BinaryType: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will panic when trying to access nil db, which is expected
|
||||||
|
// in the test environment without database setup
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
// Expected - no database in test
|
||||||
|
t.Log("Expected panic due to nil database in test")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
handleMsgSysGetUserBinary(session, pkt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserBinaryPartID_AsMapKey(t *testing.T) {
|
||||||
|
// Test that userBinaryPartID works as map key
|
||||||
|
parts := make(map[userBinaryPartID][]byte)
|
||||||
|
|
||||||
|
key1 := userBinaryPartID{charID: 1, index: 0}
|
||||||
|
key2 := userBinaryPartID{charID: 1, index: 1}
|
||||||
|
key3 := userBinaryPartID{charID: 2, index: 0}
|
||||||
|
|
||||||
|
parts[key1] = []byte{0x01}
|
||||||
|
parts[key2] = []byte{0x02}
|
||||||
|
parts[key3] = []byte{0x03}
|
||||||
|
|
||||||
|
if len(parts) != 3 {
|
||||||
|
t.Errorf("Expected 3 parts, got %d", len(parts))
|
||||||
|
}
|
||||||
|
|
||||||
|
if parts[key1][0] != 0x01 {
|
||||||
|
t.Error("Key1 data mismatch")
|
||||||
|
}
|
||||||
|
if parts[key2][0] != 0x02 {
|
||||||
|
t.Error("Key2 data mismatch")
|
||||||
|
}
|
||||||
|
if parts[key3][0] != 0x03 {
|
||||||
|
t.Error("Key3 data mismatch")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user