mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
Extract all direct database calls from entranceserver (2 calls) and API server (17 calls) into typed repository interfaces with PostgreSQL implementations, matching the pattern established in signserver and channelserver. Entranceserver: EntranceServerRepo, EntranceSessionRepo API server: APIUserRepo, APICharacterRepo, APISessionRepo Also fix the 3 remaining fmt.Sprintf calls inside logger invocations in handlers_commands.go and handlers_stage.go, replacing them with structured zap fields. Unskip 5 TestNewAuthData* tests that previously required a real database — they now run with mock repos.
265 lines
7.0 KiB
Go
265 lines
7.0 KiB
Go
package entranceserver
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
cfg "erupe-ce/config"
|
|
)
|
|
|
|
// TestEncodeServerInfo_EmptyClanMemberLimits verifies the crash is FIXED when ClanMemberLimits is empty
|
|
// Previously panicked: runtime error: index out of range [-1]
|
|
// From erupe.log.1:659922
|
|
// After fix: Should handle empty array gracefully with default value (60)
|
|
func TestEncodeServerInfo_EmptyClanMemberLimits(t *testing.T) {
|
|
config := &cfg.Config{
|
|
RealClientMode: cfg.Z1,
|
|
Host: "127.0.0.1",
|
|
Entrance: cfg.Entrance{
|
|
Enabled: true,
|
|
Port: 53310,
|
|
Entries: []cfg.EntranceServerInfo{
|
|
{
|
|
Name: "TestServer",
|
|
Description: "Test",
|
|
IP: "127.0.0.1",
|
|
Type: 0,
|
|
Recommended: 0,
|
|
AllowedClientFlags: 0xFFFFFFFF,
|
|
Channels: []cfg.EntranceChannelInfo{
|
|
{
|
|
Port: 54001,
|
|
MaxPlayers: 100,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GameplayOptions: cfg.GameplayOptions{
|
|
ClanMemberLimits: [][]uint8{}, // Empty array - should now use default (60) instead of panicking
|
|
},
|
|
}
|
|
|
|
server := &Server{
|
|
logger: zap.NewNop(),
|
|
erupeConfig: config,
|
|
}
|
|
|
|
// Set up defer to catch ANY panic - we should NOT get array bounds panic anymore
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
// If panic occurs, it should NOT be from array access
|
|
panicStr := fmt.Sprintf("%v", r)
|
|
if strings.Contains(panicStr, "index out of range") {
|
|
t.Errorf("Array bounds panic NOT fixed! Still getting: %v", r)
|
|
} else {
|
|
// Other panic is acceptable (network, DB, etc) - we only care about array bounds
|
|
t.Logf("Non-array-bounds panic (acceptable): %v", r)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// This should NOT panic on array bounds anymore - should use default value 60
|
|
result := encodeServerInfo(config, server, true)
|
|
if len(result) > 0 {
|
|
t.Log("✅ encodeServerInfo handled empty ClanMemberLimits without array bounds panic")
|
|
}
|
|
}
|
|
|
|
// TestClanMemberLimitsBoundsChecking verifies bounds checking logic for ClanMemberLimits
|
|
// Tests the specific logic that was fixed without needing full database setup
|
|
func TestClanMemberLimitsBoundsChecking(t *testing.T) {
|
|
// Test the bounds checking logic directly
|
|
testCases := []struct {
|
|
name string
|
|
clanMemberLimits [][]uint8
|
|
expectedValue uint8
|
|
expectDefault bool
|
|
}{
|
|
{"empty array", [][]uint8{}, 60, true},
|
|
{"single row with 2 columns", [][]uint8{{1, 50}}, 50, false},
|
|
{"single row with 1 column", [][]uint8{{1}}, 60, true},
|
|
{"multiple rows, last has 2 columns", [][]uint8{{1, 10}, {2, 20}, {3, 60}}, 60, false},
|
|
{"multiple rows, last has 1 column", [][]uint8{{1, 10}, {2, 20}, {3}}, 60, true},
|
|
{"multiple rows with valid data", [][]uint8{{1, 10}, {2, 20}, {3, 30}, {4, 40}, {5, 50}}, 50, false},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Replicate the bounds checking logic from the fix
|
|
var maxClanMembers uint8 = 60
|
|
if len(tc.clanMemberLimits) > 0 {
|
|
lastRow := tc.clanMemberLimits[len(tc.clanMemberLimits)-1]
|
|
if len(lastRow) > 1 {
|
|
maxClanMembers = lastRow[1]
|
|
}
|
|
}
|
|
|
|
// Verify correct behavior
|
|
if maxClanMembers != tc.expectedValue {
|
|
t.Errorf("Expected value %d, got %d", tc.expectedValue, maxClanMembers)
|
|
}
|
|
|
|
if tc.expectDefault && maxClanMembers != 60 {
|
|
t.Errorf("Expected default value 60, got %d", maxClanMembers)
|
|
}
|
|
|
|
t.Logf("✅ %s: Safe bounds access, value = %d", tc.name, maxClanMembers)
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
// TestEncodeServerInfo_WithMockRepo tests encodeServerInfo with a mock server repo
|
|
func TestEncodeServerInfo_WithMockRepo(t *testing.T) {
|
|
config := &cfg.Config{
|
|
RealClientMode: cfg.Z1,
|
|
Host: "127.0.0.1",
|
|
Entrance: cfg.Entrance{
|
|
Enabled: true,
|
|
Port: 53310,
|
|
Entries: []cfg.EntranceServerInfo{
|
|
{
|
|
Name: "TestServer",
|
|
Description: "Test",
|
|
IP: "127.0.0.1",
|
|
Type: 0,
|
|
Recommended: 0,
|
|
AllowedClientFlags: 0xFFFFFFFF,
|
|
Channels: []cfg.EntranceChannelInfo{
|
|
{
|
|
Port: 54001,
|
|
MaxPlayers: 100,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GameplayOptions: cfg.GameplayOptions{
|
|
ClanMemberLimits: [][]uint8{{1, 60}},
|
|
},
|
|
}
|
|
|
|
server := &Server{
|
|
logger: zap.NewNop(),
|
|
erupeConfig: config,
|
|
serverRepo: &mockEntranceServerRepo{currentPlayers: 42},
|
|
}
|
|
|
|
result := encodeServerInfo(config, server, true)
|
|
if len(result) == 0 {
|
|
t.Error("encodeServerInfo returned empty result")
|
|
}
|
|
}
|
|
|
|
// TestMakeUsrResp_WithMockRepo tests makeUsrResp with a mock session repo
|
|
func TestMakeUsrResp_WithMockRepo(t *testing.T) {
|
|
config := &cfg.Config{
|
|
RealClientMode: cfg.Z1,
|
|
}
|
|
|
|
server := &Server{
|
|
logger: zap.NewNop(),
|
|
erupeConfig: config,
|
|
sessionRepo: &mockEntranceSessionRepo{serverID: 1234},
|
|
}
|
|
|
|
// Build a minimal USR request packet:
|
|
// 4 bytes ALL+ prefix, 1 byte 0x00, 2 bytes entry count, then 4 bytes per entry (char ID)
|
|
pkt := []byte{
|
|
'A', 'L', 'L', '+',
|
|
0x00,
|
|
0x00, 0x01, // 1 entry
|
|
0x00, 0x00, 0x00, 0x01, // char_id = 1
|
|
}
|
|
|
|
result := makeUsrResp(pkt, server)
|
|
if len(result) == 0 {
|
|
t.Error("makeUsrResp returned empty result")
|
|
}
|
|
}
|
|
|
|
// TestMakeUsrResp_NilSessionRepo tests makeUsrResp when sessionRepo is nil
|
|
func TestMakeUsrResp_NilSessionRepo(t *testing.T) {
|
|
config := &cfg.Config{
|
|
RealClientMode: cfg.Z1,
|
|
}
|
|
|
|
server := &Server{
|
|
logger: zap.NewNop(),
|
|
erupeConfig: config,
|
|
}
|
|
|
|
pkt := []byte{
|
|
'A', 'L', 'L', '+',
|
|
0x00,
|
|
0x00, 0x01,
|
|
0x00, 0x00, 0x00, 0x01,
|
|
}
|
|
|
|
result := makeUsrResp(pkt, server)
|
|
if len(result) == 0 {
|
|
t.Error("makeUsrResp returned empty result")
|
|
}
|
|
}
|
|
|
|
// TestEncodeServerInfo_MissingSecondColumnClanMemberLimits tests accessing [last][1] when [last] is too small
|
|
// Previously panicked: runtime error: index out of range [1]
|
|
// After fix: Should handle missing column gracefully with default value (60)
|
|
func TestEncodeServerInfo_MissingSecondColumnClanMemberLimits(t *testing.T) {
|
|
config := &cfg.Config{
|
|
RealClientMode: cfg.Z1,
|
|
Host: "127.0.0.1",
|
|
Entrance: cfg.Entrance{
|
|
Enabled: true,
|
|
Port: 53310,
|
|
Entries: []cfg.EntranceServerInfo{
|
|
{
|
|
Name: "TestServer",
|
|
Description: "Test",
|
|
IP: "127.0.0.1",
|
|
Type: 0,
|
|
Recommended: 0,
|
|
AllowedClientFlags: 0xFFFFFFFF,
|
|
Channels: []cfg.EntranceChannelInfo{
|
|
{
|
|
Port: 54001,
|
|
MaxPlayers: 100,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
GameplayOptions: cfg.GameplayOptions{
|
|
ClanMemberLimits: [][]uint8{
|
|
{1}, // Only 1 element, code used to panic accessing [1]
|
|
},
|
|
},
|
|
}
|
|
|
|
server := &Server{
|
|
logger: zap.NewNop(),
|
|
erupeConfig: config,
|
|
}
|
|
|
|
defer func() {
|
|
if r := recover(); r != nil {
|
|
panicStr := fmt.Sprintf("%v", r)
|
|
if strings.Contains(panicStr, "index out of range") {
|
|
t.Errorf("Array bounds panic NOT fixed! Still getting: %v", r)
|
|
} else {
|
|
t.Logf("Non-array-bounds panic (acceptable): %v", r)
|
|
}
|
|
}
|
|
}()
|
|
|
|
// This should NOT panic on array bounds anymore - should use default value 60
|
|
result := encodeServerInfo(config, server, true)
|
|
if len(result) > 0 {
|
|
t.Log("✅ encodeServerInfo handled missing ClanMemberLimits column without array bounds panic")
|
|
}
|
|
}
|