test: expand channelserver coverage from 20% to 25%

Add tests for:
- Discord handlers (getPlayerSlice, getCharacterList)
- House handlers (boxToBytes, HouseData, Title structs)
- Mail struct tests
- Mercenary handlers (Partner, HunterNavi structs)
- Shop/Gacha handlers (writeShopItems, ShopItem, Gacha structs)
- Additional handler coverage for guild, tower, and simple handlers
- Stage handler tests for binary operations and enumeration
- Channel server tests for BroadcastMHF and session management
This commit is contained in:
Houmgaor
2026-02-02 16:02:01 +01:00
parent 2d8f1d3b41
commit 7e9440d8cc
14 changed files with 2162 additions and 0 deletions

View File

@@ -7,6 +7,7 @@ import (
"time"
"erupe-ce/config"
"erupe-ce/network/mhfpacket"
"go.uber.org/zap"
)
@@ -482,3 +483,265 @@ func TestConfigStruct(t *testing.T) {
t.Error("Config Enable should be true")
}
}
func TestServerBroadcastMHF_NoSessions(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
// Should not panic with no sessions
pkt := &mhfpacket.MsgSysAck{
AckHandle: 1,
IsBufferResponse: false,
ErrorCode: 0,
AckData: []byte{0x00},
}
s.BroadcastMHF(pkt, nil)
}
func TestServerBroadcastMHF_WithSession(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
session := createMockSession(1, s)
s.sessions[nil] = session
pkt := &mhfpacket.MsgSysAck{
AckHandle: 1,
IsBufferResponse: false,
ErrorCode: 0,
AckData: []byte{0x00},
}
s.BroadcastMHF(pkt, nil)
// Check if packet was queued
select {
case p := <-session.sendPackets:
if p.data == nil {
t.Error("Packet data should not be nil")
}
default:
t.Error("No packet queued to session")
}
}
func TestServerBroadcastMHF_SkipsOrigin(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
session1 := createMockSession(1, s)
session2 := createMockSession(2, s)
s.sessions[nil] = session1
s.sessions[session2.rawConn] = session2
pkt := &mhfpacket.MsgSysAck{
AckHandle: 1,
IsBufferResponse: false,
ErrorCode: 0,
AckData: []byte{0x00},
}
// Broadcast from session1 - should skip session1
s.BroadcastMHF(pkt, session1)
// session1 should not receive the packet
select {
case <-session1.sendPackets:
t.Error("Origin session should not receive broadcast")
default:
// Good - no packet for origin
}
}
func TestServerFindSessionByCharID_Found(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
s.Channels = []*Server{s}
session := createMockSession(12345, s)
s.sessions[nil] = session
// Search for existing character
found := s.FindSessionByCharID(12345)
if found == nil {
t.Error("Expected to find session")
}
if found != session {
t.Error("Found wrong session")
}
}
func TestServerFindObjectByChar_Found(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
// Add a stage with an object
stage := NewStage("test_obj_stage")
obj := &Object{
id: 1,
ownerCharID: 12345,
x: 100.0,
y: 200.0,
z: 300.0,
}
stage.objects[obj.ownerCharID] = obj
s.stages["test_obj_stage"] = stage
// Search for existing object
found := s.FindObjectByChar(12345)
if found == nil {
t.Error("Expected to find object")
}
if found != obj {
t.Error("Found wrong object")
}
}
func TestServerConcurrentBinaryPartsAccess(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
var wg sync.WaitGroup
// Concurrent writers
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for j := 0; j < 10; j++ {
partID := userBinaryPartID{charID: uint32(id), index: uint8(j % 4)}
s.userBinaryPartsLock.Lock()
s.userBinaryParts[partID] = []byte{byte(id), byte(j)}
s.userBinaryPartsLock.Unlock()
}
}(i)
}
// Concurrent readers
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 20; j++ {
s.userBinaryPartsLock.RLock()
_ = len(s.userBinaryParts)
s.userBinaryPartsLock.RUnlock()
}
}()
}
wg.Wait()
}
func TestServerNextSemaphoreID(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
// Get multiple IDs and verify they're unique
ids := make(map[uint32]bool)
for i := 0; i < 10; i++ {
id := s.NextSemaphoreID()
if ids[id] {
t.Errorf("Duplicate semaphore ID: %d", id)
}
ids[id] = true
// IDs should be >= 7 (reserved indexes skipped)
if id < 7 {
t.Errorf("Semaphore ID %d should be >= 7", id)
}
}
}
func TestServerNextSemaphoreID_WrapAround(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
// Set semaphoreIndex to near max uint32 to test wrap-around
s.semaphoreIndex = ^uint32(0) - 1 // Max uint32 - 1
id1 := s.NextSemaphoreID()
id2 := s.NextSemaphoreID()
// After wrap-around, should skip to 7
if id1 < 7 && id1 != 0 {
t.Errorf("After wrap-around, first ID should be >= 7, got %d", id1)
}
if id1 == id2 {
t.Error("IDs should be different")
}
}
func TestServerNextSemaphoreID_SkipsExisting(t *testing.T) {
logger, _ := zap.NewDevelopment()
cfg := &Config{
ID: 1,
Logger: logger,
ErupeConfig: &config.Config{DevMode: true},
}
s := NewServer(cfg)
// Pre-populate with some semaphores
for i := uint32(7); i <= 15; i++ {
s.semaphore["test_"+string(rune('a'+i))] = &Semaphore{id: i}
}
// Get a new ID - should skip the existing ones
id := s.NextSemaphoreID()
// Verify ID is not one of the existing ones
for _, sema := range s.semaphore {
if sema.id == id {
t.Errorf("NextSemaphoreID returned existing ID: %d", id)
}
}
}