mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
Add comprehensive tests across multiple packages: - mhfpacket: Add tests for 300+ packet opcodes, system packets, MHF packets, and detailed parsing tests (6.4% -> 38.8%) - timeserver: Add tests for all time functions (0% -> 97.4%) - deltacomp: Add edge case tests for compression functions - entranceserver: Add server creation tests - binpacket: Add mail notify panic test - config: Add Mode.String() tests - signserver: Expand server tests
431 lines
9.7 KiB
Go
431 lines
9.7 KiB
Go
package binpacket
|
|
|
|
import (
|
|
"bytes"
|
|
"testing"
|
|
|
|
"erupe-ce/common/byteframe"
|
|
"erupe-ce/network"
|
|
)
|
|
|
|
func TestMsgBinTargetedOpcode(t *testing.T) {
|
|
m := &MsgBinTargeted{}
|
|
if m.Opcode() != network.MSG_SYS_CAST_BINARY {
|
|
t.Errorf("MsgBinTargeted.Opcode() = %v, want MSG_SYS_CAST_BINARY", m.Opcode())
|
|
}
|
|
}
|
|
|
|
func TestMsgBinTargetedParseEmpty(t *testing.T) {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint16(0) // TargetCount = 0
|
|
|
|
bf.Seek(0, 0)
|
|
|
|
m := &MsgBinTargeted{}
|
|
err := m.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
if m.TargetCount != 0 {
|
|
t.Errorf("TargetCount = %d, want 0", m.TargetCount)
|
|
}
|
|
if len(m.TargetCharIDs) != 0 {
|
|
t.Errorf("TargetCharIDs len = %d, want 0", len(m.TargetCharIDs))
|
|
}
|
|
}
|
|
|
|
func TestMsgBinTargetedParseSingleTarget(t *testing.T) {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint16(1) // TargetCount = 1
|
|
bf.WriteUint32(0x12345678) // TargetCharID
|
|
bf.WriteBytes([]byte{0xDE, 0xAD, 0xBE, 0xEF})
|
|
|
|
bf.Seek(0, 0)
|
|
|
|
m := &MsgBinTargeted{}
|
|
err := m.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
if m.TargetCount != 1 {
|
|
t.Errorf("TargetCount = %d, want 1", m.TargetCount)
|
|
}
|
|
if len(m.TargetCharIDs) != 1 {
|
|
t.Errorf("TargetCharIDs len = %d, want 1", len(m.TargetCharIDs))
|
|
}
|
|
if m.TargetCharIDs[0] != 0x12345678 {
|
|
t.Errorf("TargetCharIDs[0] = %x, want 0x12345678", m.TargetCharIDs[0])
|
|
}
|
|
if !bytes.Equal(m.RawDataPayload, []byte{0xDE, 0xAD, 0xBE, 0xEF}) {
|
|
t.Errorf("RawDataPayload = %v, want [0xDE, 0xAD, 0xBE, 0xEF]", m.RawDataPayload)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinTargetedParseMultipleTargets(t *testing.T) {
|
|
bf := byteframe.NewByteFrame()
|
|
bf.WriteUint16(3) // TargetCount = 3
|
|
bf.WriteUint32(100)
|
|
bf.WriteUint32(200)
|
|
bf.WriteUint32(300)
|
|
bf.WriteBytes([]byte{0x01, 0x02, 0x03})
|
|
|
|
bf.Seek(0, 0)
|
|
|
|
m := &MsgBinTargeted{}
|
|
err := m.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
if m.TargetCount != 3 {
|
|
t.Errorf("TargetCount = %d, want 3", m.TargetCount)
|
|
}
|
|
if len(m.TargetCharIDs) != 3 {
|
|
t.Errorf("TargetCharIDs len = %d, want 3", len(m.TargetCharIDs))
|
|
}
|
|
if m.TargetCharIDs[0] != 100 || m.TargetCharIDs[1] != 200 || m.TargetCharIDs[2] != 300 {
|
|
t.Errorf("TargetCharIDs = %v, want [100, 200, 300]", m.TargetCharIDs)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinTargetedBuild(t *testing.T) {
|
|
m := &MsgBinTargeted{
|
|
TargetCount: 2,
|
|
TargetCharIDs: []uint32{0x11111111, 0x22222222},
|
|
RawDataPayload: []byte{0xAA, 0xBB},
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
err := m.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
expected := []byte{
|
|
0x00, 0x02, // TargetCount
|
|
0x11, 0x11, 0x11, 0x11, // TargetCharIDs[0]
|
|
0x22, 0x22, 0x22, 0x22, // TargetCharIDs[1]
|
|
0xAA, 0xBB, // RawDataPayload
|
|
}
|
|
|
|
if !bytes.Equal(bf.Data(), expected) {
|
|
t.Errorf("Build() = %v, want %v", bf.Data(), expected)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinTargetedRoundTrip(t *testing.T) {
|
|
original := &MsgBinTargeted{
|
|
TargetCount: 3,
|
|
TargetCharIDs: []uint32{1000, 2000, 3000},
|
|
RawDataPayload: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
|
|
}
|
|
|
|
// Build
|
|
bf := byteframe.NewByteFrame()
|
|
err := original.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
// Parse
|
|
bf.Seek(0, 0)
|
|
parsed := &MsgBinTargeted{}
|
|
err = parsed.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
// Compare
|
|
if parsed.TargetCount != original.TargetCount {
|
|
t.Errorf("TargetCount = %d, want %d", parsed.TargetCount, original.TargetCount)
|
|
}
|
|
if len(parsed.TargetCharIDs) != len(original.TargetCharIDs) {
|
|
t.Errorf("TargetCharIDs len = %d, want %d", len(parsed.TargetCharIDs), len(original.TargetCharIDs))
|
|
}
|
|
for i := range original.TargetCharIDs {
|
|
if parsed.TargetCharIDs[i] != original.TargetCharIDs[i] {
|
|
t.Errorf("TargetCharIDs[%d] = %d, want %d", i, parsed.TargetCharIDs[i], original.TargetCharIDs[i])
|
|
}
|
|
}
|
|
if !bytes.Equal(parsed.RawDataPayload, original.RawDataPayload) {
|
|
t.Errorf("RawDataPayload = %v, want %v", parsed.RawDataPayload, original.RawDataPayload)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinMailNotifyOpcode(t *testing.T) {
|
|
m := MsgBinMailNotify{}
|
|
if m.Opcode() != network.MSG_SYS_CASTED_BINARY {
|
|
t.Errorf("MsgBinMailNotify.Opcode() = %v, want MSG_SYS_CASTED_BINARY", m.Opcode())
|
|
}
|
|
}
|
|
|
|
func TestMsgBinMailNotifyBuild(t *testing.T) {
|
|
m := MsgBinMailNotify{
|
|
SenderName: "TestPlayer",
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
err := m.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
data := bf.Data()
|
|
|
|
// First byte should be 0x01 (Unk)
|
|
if data[0] != 0x01 {
|
|
t.Errorf("First byte = %x, want 0x01", data[0])
|
|
}
|
|
|
|
// Total length should be 1 (Unk) + 21 (padded name) = 22
|
|
if len(data) != 22 {
|
|
t.Errorf("Data len = %d, want 22", len(data))
|
|
}
|
|
}
|
|
|
|
func TestMsgBinMailNotifyBuildEmptyName(t *testing.T) {
|
|
m := MsgBinMailNotify{
|
|
SenderName: "",
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
err := m.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
if len(bf.Data()) != 22 {
|
|
t.Errorf("Data len = %d, want 22", len(bf.Data()))
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatOpcode(t *testing.T) {
|
|
m := &MsgBinChat{}
|
|
if m.Opcode() != network.MSG_SYS_CAST_BINARY {
|
|
t.Errorf("MsgBinChat.Opcode() = %v, want MSG_SYS_CAST_BINARY", m.Opcode())
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatTypes(t *testing.T) {
|
|
tests := []struct {
|
|
chatType ChatType
|
|
value uint8
|
|
}{
|
|
{ChatTypeLocal, 1},
|
|
{ChatTypeGuild, 2},
|
|
{ChatTypeAlliance, 3},
|
|
{ChatTypeParty, 4},
|
|
{ChatTypeWhisper, 5},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
if uint8(tt.chatType) != tt.value {
|
|
t.Errorf("ChatType %v = %d, want %d", tt.chatType, uint8(tt.chatType), tt.value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatBuildParse(t *testing.T) {
|
|
original := &MsgBinChat{
|
|
Unk0: 0x00,
|
|
Type: ChatTypeLocal,
|
|
Flags: 0x0000,
|
|
Message: "Hello",
|
|
SenderName: "Player",
|
|
}
|
|
|
|
// Build
|
|
bf := byteframe.NewByteFrame()
|
|
err := original.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
// Parse
|
|
bf.Seek(0, 0)
|
|
parsed := &MsgBinChat{}
|
|
err = parsed.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
// Compare
|
|
if parsed.Unk0 != original.Unk0 {
|
|
t.Errorf("Unk0 = %d, want %d", parsed.Unk0, original.Unk0)
|
|
}
|
|
if parsed.Type != original.Type {
|
|
t.Errorf("Type = %d, want %d", parsed.Type, original.Type)
|
|
}
|
|
if parsed.Flags != original.Flags {
|
|
t.Errorf("Flags = %d, want %d", parsed.Flags, original.Flags)
|
|
}
|
|
if parsed.Message != original.Message {
|
|
t.Errorf("Message = %q, want %q", parsed.Message, original.Message)
|
|
}
|
|
if parsed.SenderName != original.SenderName {
|
|
t.Errorf("SenderName = %q, want %q", parsed.SenderName, original.SenderName)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatBuildParseJapanese(t *testing.T) {
|
|
original := &MsgBinChat{
|
|
Unk0: 0x00,
|
|
Type: ChatTypeGuild,
|
|
Flags: 0x0001,
|
|
Message: "こんにちは",
|
|
SenderName: "テスト",
|
|
}
|
|
|
|
// Build
|
|
bf := byteframe.NewByteFrame()
|
|
err := original.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
// Parse
|
|
bf.Seek(0, 0)
|
|
parsed := &MsgBinChat{}
|
|
err = parsed.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
if parsed.Message != original.Message {
|
|
t.Errorf("Message = %q, want %q", parsed.Message, original.Message)
|
|
}
|
|
if parsed.SenderName != original.SenderName {
|
|
t.Errorf("SenderName = %q, want %q", parsed.SenderName, original.SenderName)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatBuildParseEmpty(t *testing.T) {
|
|
original := &MsgBinChat{
|
|
Unk0: 0x00,
|
|
Type: ChatTypeParty,
|
|
Flags: 0x0000,
|
|
Message: "",
|
|
SenderName: "",
|
|
}
|
|
|
|
// Build
|
|
bf := byteframe.NewByteFrame()
|
|
err := original.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
// Parse
|
|
bf.Seek(0, 0)
|
|
parsed := &MsgBinChat{}
|
|
err = parsed.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
if parsed.Message != "" {
|
|
t.Errorf("Message = %q, want empty", parsed.Message)
|
|
}
|
|
if parsed.SenderName != "" {
|
|
t.Errorf("SenderName = %q, want empty", parsed.SenderName)
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatBuildFormat(t *testing.T) {
|
|
m := &MsgBinChat{
|
|
Unk0: 0x12,
|
|
Type: ChatTypeWhisper,
|
|
Flags: 0x3456,
|
|
Message: "Hi",
|
|
SenderName: "A",
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
err := m.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
data := bf.Data()
|
|
|
|
// Verify header structure
|
|
if data[0] != 0x12 {
|
|
t.Errorf("Unk0 = %x, want 0x12", data[0])
|
|
}
|
|
if data[1] != uint8(ChatTypeWhisper) {
|
|
t.Errorf("Type = %x, want %x", data[1], uint8(ChatTypeWhisper))
|
|
}
|
|
// Flags at bytes 2-3 (big endian)
|
|
if data[2] != 0x34 || data[3] != 0x56 {
|
|
t.Errorf("Flags = %x%x, want 3456", data[2], data[3])
|
|
}
|
|
}
|
|
|
|
func TestMsgBinChatAllTypes(t *testing.T) {
|
|
types := []ChatType{
|
|
ChatTypeLocal,
|
|
ChatTypeGuild,
|
|
ChatTypeAlliance,
|
|
ChatTypeParty,
|
|
ChatTypeWhisper,
|
|
}
|
|
|
|
for _, chatType := range types {
|
|
t.Run("", func(t *testing.T) {
|
|
original := &MsgBinChat{
|
|
Type: chatType,
|
|
Message: "Test",
|
|
SenderName: "Player",
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
err := original.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
bf.Seek(0, 0)
|
|
parsed := &MsgBinChat{}
|
|
err = parsed.Parse(bf)
|
|
if err != nil {
|
|
t.Fatalf("Parse() error = %v", err)
|
|
}
|
|
|
|
if parsed.Type != chatType {
|
|
t.Errorf("Type = %d, want %d", parsed.Type, chatType)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMsgBinMailNotifyParsePanics(t *testing.T) {
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Error("Parse() should panic with 'implement me'")
|
|
}
|
|
}()
|
|
|
|
m := MsgBinMailNotify{}
|
|
bf := byteframe.NewByteFrame()
|
|
_ = m.Parse(bf)
|
|
}
|
|
|
|
func TestMsgBinMailNotifyBuildLongName(t *testing.T) {
|
|
m := MsgBinMailNotify{
|
|
SenderName: "ThisIsAVeryLongPlayerNameThatExceeds21Characters",
|
|
}
|
|
|
|
bf := byteframe.NewByteFrame()
|
|
err := m.Build(bf)
|
|
if err != nil {
|
|
t.Fatalf("Build() error = %v", err)
|
|
}
|
|
|
|
// Data should still be 22 bytes (1 + 21)
|
|
if len(bf.Data()) != 22 {
|
|
t.Errorf("Data len = %d, want 22", len(bf.Data()))
|
|
}
|
|
}
|