mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
Add comprehensive tests for: - Pure time functions in channelserver (sys_time_test.go) - Stage-related packet parsing (msg_sys_stage_test.go) - Acquire packet family parsing (msg_mhf_acquire_test.go) - Extended mhfpacket tests for login, logout, and stage packets - Entrance server makeHeader structure and checksum tests - SignV2 server request/response JSON structure tests
402 lines
11 KiB
Go
402 lines
11 KiB
Go
package entranceserver
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
)
|
|
|
|
func TestMakeHeader(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
respType string
|
|
entryCount uint16
|
|
key byte
|
|
}{
|
|
{"empty data", []byte{}, "SV2", 0, 0x00},
|
|
{"single byte", []byte{0x01}, "SVR", 1, 0x00},
|
|
{"multiple bytes", []byte{0x01, 0x02, 0x03, 0x04}, "SV2", 2, 0x00},
|
|
{"with key", []byte{0xDE, 0xAD, 0xBE, 0xEF}, "USR", 5, 0x42},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := makeHeader(tt.data, tt.respType, tt.entryCount, tt.key)
|
|
|
|
// Result should not be empty
|
|
if len(result) == 0 {
|
|
t.Error("makeHeader() returned empty result")
|
|
}
|
|
|
|
// First byte should be the key
|
|
if result[0] != tt.key {
|
|
t.Errorf("makeHeader() first byte = %x, want %x", result[0], tt.key)
|
|
}
|
|
|
|
// Result should be longer than just the key
|
|
if len(result) <= 1 {
|
|
t.Error("makeHeader() result too short")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderEncryption(t *testing.T) {
|
|
data := []byte{0x01, 0x02, 0x03, 0x04}
|
|
|
|
result1 := makeHeader(data, "SV2", 1, 0x00)
|
|
result2 := makeHeader(data, "SV2", 1, 0x01)
|
|
|
|
// Different keys should produce different encrypted output
|
|
if bytes.Equal(result1, result2) {
|
|
t.Error("makeHeader() with different keys should produce different output")
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderRespTypes(t *testing.T) {
|
|
data := []byte{0x01}
|
|
|
|
// Test different response types produce valid output
|
|
types := []string{"SV2", "SVR", "USR"}
|
|
|
|
for _, respType := range types {
|
|
t.Run(respType, func(t *testing.T) {
|
|
result := makeHeader(data, respType, 1, 0x00)
|
|
if len(result) == 0 {
|
|
t.Errorf("makeHeader() with type %s returned empty result", respType)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderEmptyData(t *testing.T) {
|
|
// Empty data should still produce a valid (shorter) header
|
|
result := makeHeader([]byte{}, "SV2", 0, 0x00)
|
|
|
|
if len(result) == 0 {
|
|
t.Error("makeHeader() with empty data returned empty result")
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderLargeData(t *testing.T) {
|
|
// Test with larger data
|
|
data := make([]byte, 1000)
|
|
for i := range data {
|
|
data[i] = byte(i % 256)
|
|
}
|
|
|
|
result := makeHeader(data, "SV2", 100, 0x55)
|
|
|
|
if len(result) == 0 {
|
|
t.Error("makeHeader() with large data returned empty result")
|
|
}
|
|
|
|
// Result should be data + overhead
|
|
if len(result) <= len(data) {
|
|
t.Error("makeHeader() result should be larger than input data due to header")
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderEntryCount(t *testing.T) {
|
|
data := []byte{0x01, 0x02}
|
|
|
|
// Different entry counts should work
|
|
for _, count := range []uint16{0, 1, 10, 100, 65535} {
|
|
result := makeHeader(data, "SV2", count, 0x00)
|
|
if len(result) == 0 {
|
|
t.Errorf("makeHeader() with entryCount=%d returned empty result", count)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderDecryptable(t *testing.T) {
|
|
data := []byte{0x01, 0x02, 0x03, 0x04}
|
|
key := byte(0x00)
|
|
|
|
result := makeHeader(data, "SV2", 1, key)
|
|
|
|
// Remove key byte and decrypt
|
|
encrypted := result[1:]
|
|
decrypted := DecryptBin8(encrypted, key)
|
|
|
|
// Decrypted data should start with "SV2"
|
|
if len(decrypted) >= 3 && string(decrypted[:3]) != "SV2" {
|
|
t.Errorf("makeHeader() decrypted data should start with SV2, got %s", string(decrypted[:3]))
|
|
}
|
|
}
|
|
|
|
func TestMakeHeaderConsistency(t *testing.T) {
|
|
data := []byte{0x01, 0x02, 0x03}
|
|
key := byte(0x10)
|
|
|
|
// Same input should produce same output
|
|
result1 := makeHeader(data, "SV2", 5, key)
|
|
result2 := makeHeader(data, "SV2", 5, key)
|
|
|
|
if !bytes.Equal(result1, result2) {
|
|
t.Error("makeHeader() with same input should produce same output")
|
|
}
|
|
}
|
|
|
|
// TestEncodeServerInfoClanMemberLimit documents the hardcoded clan member limit.
|
|
//
|
|
// CURRENT BEHAVIOR:
|
|
//
|
|
// bf.WriteUint32(0x0000003C) // Hardcoded to 60
|
|
//
|
|
// EXPECTED BEHAVIOR (after fix commit 7d760bd):
|
|
//
|
|
// bf.WriteUint32(uint32(s.erupeConfig.GameplayOptions.ClanMemberLimits[len(s.erupeConfig.GameplayOptions.ClanMemberLimits)-1][1]))
|
|
// This reads the maximum clan member limit from the last entry of ClanMemberLimits config.
|
|
//
|
|
// Note: The ClanMemberLimits config field doesn't exist in this branch yet.
|
|
// This test documents the expected value (60 = 0x3C) that will be read from config.
|
|
func TestEncodeServerInfoClanMemberLimit(t *testing.T) {
|
|
// The hardcoded value is 60 (0x3C)
|
|
hardcodedLimit := uint32(0x0000003C)
|
|
|
|
if hardcodedLimit != 60 {
|
|
t.Errorf("hardcoded clan member limit = %d, expected 60", hardcodedLimit)
|
|
}
|
|
|
|
t.Logf("Current implementation uses hardcoded clan member limit: %d", hardcodedLimit)
|
|
t.Logf("After fix, this will be read from config.GameplayOptions.ClanMemberLimits")
|
|
}
|
|
|
|
// TestMakeHeaderDataIntegrity verifies that data passed to makeHeader is preserved
|
|
// through encryption/decryption.
|
|
func TestMakeHeaderDataIntegrity(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
data []byte
|
|
respType string
|
|
count uint16
|
|
key byte
|
|
}{
|
|
{"empty SV2", []byte{}, "SV2", 0, 0x00},
|
|
{"simple SVR", []byte{0x01, 0x02}, "SVR", 1, 0x00},
|
|
{"with key", []byte{0xDE, 0xAD, 0xBE, 0xEF}, "SV2", 2, 0x42},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := makeHeader(tc.data, tc.respType, tc.count, tc.key)
|
|
|
|
// Result should have key as first byte
|
|
if len(result) == 0 {
|
|
t.Fatal("makeHeader returned empty result")
|
|
}
|
|
if result[0] != tc.key {
|
|
t.Errorf("first byte = 0x%X, want 0x%X (key)", result[0], tc.key)
|
|
}
|
|
|
|
// Decrypt and verify response type
|
|
if len(result) > 1 {
|
|
decrypted := DecryptBin8(result[1:], tc.key)
|
|
if len(decrypted) >= 3 {
|
|
gotType := string(decrypted[:3])
|
|
if gotType != tc.respType {
|
|
t.Errorf("decrypted respType = %s, want %s", gotType, tc.respType)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMakeHeaderStructure verifies the internal structure of makeHeader output
|
|
func TestMakeHeaderStructure(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
respType string
|
|
entryCount uint16
|
|
key byte
|
|
}{
|
|
{"SV2 response", []byte{0x01, 0x02, 0x03, 0x04}, "SV2", 5, 0x00},
|
|
{"SVR response", []byte{0xAA, 0xBB}, "SVR", 10, 0x10},
|
|
{"USR response", []byte{0x00}, "USR", 1, 0xFF},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := makeHeader(tt.data, tt.respType, tt.entryCount, tt.key)
|
|
|
|
// Result should not be empty
|
|
if len(result) == 0 {
|
|
t.Fatal("makeHeader returned empty result")
|
|
}
|
|
|
|
// First byte should be the key
|
|
if result[0] != tt.key {
|
|
t.Errorf("first byte = 0x%X, want 0x%X", result[0], tt.key)
|
|
}
|
|
|
|
// Decrypt the rest
|
|
encrypted := result[1:]
|
|
decrypted := DecryptBin8(encrypted, tt.key)
|
|
|
|
// First 3 bytes should be respType
|
|
if len(decrypted) < 3 {
|
|
t.Fatal("decrypted data too short for respType")
|
|
}
|
|
if string(decrypted[:3]) != tt.respType {
|
|
t.Errorf("respType = %s, want %s", string(decrypted[:3]), tt.respType)
|
|
}
|
|
|
|
// Next 2 bytes should be entry count (big endian)
|
|
if len(decrypted) < 5 {
|
|
t.Fatal("decrypted data too short for entry count")
|
|
}
|
|
gotCount := uint16(decrypted[3])<<8 | uint16(decrypted[4])
|
|
if gotCount != tt.entryCount {
|
|
t.Errorf("entryCount = %d, want %d", gotCount, tt.entryCount)
|
|
}
|
|
|
|
// Next 2 bytes should be data length (big endian)
|
|
if len(decrypted) < 7 {
|
|
t.Fatal("decrypted data too short for data length")
|
|
}
|
|
gotLen := uint16(decrypted[5])<<8 | uint16(decrypted[6])
|
|
if gotLen != uint16(len(tt.data)) {
|
|
t.Errorf("dataLen = %d, want %d", gotLen, len(tt.data))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMakeHeaderChecksum verifies that checksum is correctly calculated
|
|
func TestMakeHeaderChecksum(t *testing.T) {
|
|
data := []byte{0x01, 0x02, 0x03, 0x04, 0x05}
|
|
key := byte(0x00)
|
|
|
|
result := makeHeader(data, "SV2", 1, key)
|
|
|
|
// Decrypt
|
|
decrypted := DecryptBin8(result[1:], key)
|
|
|
|
// After respType(3) + entryCount(2) + dataLen(2) = 7 bytes
|
|
// Next 4 bytes should be checksum
|
|
if len(decrypted) < 11 {
|
|
t.Fatal("decrypted data too short for checksum")
|
|
}
|
|
|
|
expectedChecksum := CalcSum32(data)
|
|
gotChecksum := uint32(decrypted[7])<<24 | uint32(decrypted[8])<<16 | uint32(decrypted[9])<<8 | uint32(decrypted[10])
|
|
|
|
if gotChecksum != expectedChecksum {
|
|
t.Errorf("checksum = 0x%X, want 0x%X", gotChecksum, expectedChecksum)
|
|
}
|
|
}
|
|
|
|
// TestMakeHeaderDataPreservation verifies original data is preserved in output
|
|
func TestMakeHeaderDataPreservation(t *testing.T) {
|
|
originalData := []byte{0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE}
|
|
key := byte(0x00)
|
|
|
|
result := makeHeader(originalData, "SV2", 1, key)
|
|
|
|
// Decrypt
|
|
decrypted := DecryptBin8(result[1:], key)
|
|
|
|
// Header: respType(3) + entryCount(2) + dataLen(2) + checksum(4) = 11 bytes
|
|
// Data starts at offset 11
|
|
if len(decrypted) < 11+len(originalData) {
|
|
t.Fatalf("decrypted data too short: got %d, want at least %d", len(decrypted), 11+len(originalData))
|
|
}
|
|
|
|
recoveredData := decrypted[11 : 11+len(originalData)]
|
|
if !bytes.Equal(recoveredData, originalData) {
|
|
t.Errorf("recovered data = %X, want %X", recoveredData, originalData)
|
|
}
|
|
}
|
|
|
|
// TestMakeHeaderEmptyDataNoChecksum verifies empty data doesn't include checksum
|
|
func TestMakeHeaderEmptyDataNoChecksum(t *testing.T) {
|
|
result := makeHeader([]byte{}, "SV2", 0, 0x00)
|
|
|
|
// Decrypt
|
|
decrypted := DecryptBin8(result[1:], 0x00)
|
|
|
|
// Header without data: respType(3) + entryCount(2) + dataLen(2) = 7 bytes
|
|
// No checksum for empty data
|
|
if len(decrypted) != 7 {
|
|
t.Errorf("decrypted length = %d, want 7 (no checksum for empty data)", len(decrypted))
|
|
}
|
|
|
|
// Verify data length is 0
|
|
gotLen := uint16(decrypted[5])<<8 | uint16(decrypted[6])
|
|
if gotLen != 0 {
|
|
t.Errorf("dataLen = %d, want 0", gotLen)
|
|
}
|
|
}
|
|
|
|
// TestMakeHeaderKeyVariation verifies different keys produce different output
|
|
func TestMakeHeaderKeyVariation(t *testing.T) {
|
|
data := []byte{0x01, 0x02, 0x03}
|
|
|
|
result1 := makeHeader(data, "SV2", 1, 0x00)
|
|
result2 := makeHeader(data, "SV2", 1, 0x55)
|
|
result3 := makeHeader(data, "SV2", 1, 0xAA)
|
|
|
|
// All results should have different first bytes (the key)
|
|
if result1[0] == result2[0] || result2[0] == result3[0] {
|
|
t.Error("different keys should produce different first bytes")
|
|
}
|
|
|
|
// Encrypted portions should also differ
|
|
if bytes.Equal(result1[1:], result2[1:]) {
|
|
t.Error("different keys should produce different encrypted data")
|
|
}
|
|
if bytes.Equal(result2[1:], result3[1:]) {
|
|
t.Error("different keys should produce different encrypted data")
|
|
}
|
|
}
|
|
|
|
// TestCalcSum32EdgeCases tests edge cases for the checksum function
|
|
func TestCalcSum32EdgeCases(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
data []byte
|
|
}{
|
|
{"single byte", []byte{0x00}},
|
|
{"all zeros", make([]byte, 10)},
|
|
{"all ones", bytes.Repeat([]byte{0xFF}, 10)},
|
|
{"alternating", []byte{0xAA, 0x55, 0xAA, 0x55}},
|
|
{"sequential", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Should not panic
|
|
result := CalcSum32(tt.data)
|
|
|
|
// Result should be deterministic
|
|
result2 := CalcSum32(tt.data)
|
|
if result != result2 {
|
|
t.Errorf("CalcSum32 not deterministic: got %X and %X", result, result2)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestCalcSum32Uniqueness verifies different inputs produce different checksums
|
|
func TestCalcSum32Uniqueness(t *testing.T) {
|
|
inputs := [][]byte{
|
|
{0x01},
|
|
{0x02},
|
|
{0x01, 0x02},
|
|
{0x02, 0x01},
|
|
{0x01, 0x02, 0x03},
|
|
}
|
|
|
|
checksums := make(map[uint32]int)
|
|
for i, input := range inputs {
|
|
sum := CalcSum32(input)
|
|
if prevIdx, exists := checksums[sum]; exists {
|
|
t.Errorf("collision: input %d and %d both produce checksum 0x%X", prevIdx, i, sum)
|
|
}
|
|
checksums[sum] = i
|
|
}
|
|
}
|