tests(network): adds tests for network features (except mhfpacket).

This commit is contained in:
Houmgaor
2025-10-27 12:18:41 +01:00
parent 25d218fbcd
commit 127d3af167
6 changed files with 1901 additions and 0 deletions

View File

@@ -0,0 +1,380 @@
package binpacket
import (
"bytes"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"testing"
)
func TestMsgBinChat_Opcode(t *testing.T) {
msg := &MsgBinChat{}
if msg.Opcode() != network.MSG_SYS_CAST_BINARY {
t.Errorf("Opcode() = %v, want %v", msg.Opcode(), network.MSG_SYS_CAST_BINARY)
}
}
func TestMsgBinChat_Build(t *testing.T) {
tests := []struct {
name string
msg *MsgBinChat
wantErr bool
validate func(*testing.T, []byte)
}{
{
name: "basic message",
msg: &MsgBinChat{
Unk0: 0x01,
Type: ChatTypeWorld,
Flags: 0x0000,
Message: "Hello",
SenderName: "Player1",
},
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) == 0 {
t.Error("Build() returned empty data")
}
// Verify the structure starts with Unk0, Type, Flags
if data[0] != 0x01 {
t.Errorf("Unk0 = 0x%X, want 0x01", data[0])
}
if data[1] != byte(ChatTypeWorld) {
t.Errorf("Type = 0x%X, want 0x%X", data[1], byte(ChatTypeWorld))
}
},
},
{
name: "all chat types",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeStage,
Flags: 0x1234,
Message: "Test",
SenderName: "Sender",
},
wantErr: false,
},
{
name: "empty message",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeGuild,
Flags: 0x0000,
Message: "",
SenderName: "Player",
},
wantErr: false,
},
{
name: "empty sender",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeParty,
Flags: 0x0000,
Message: "Hello",
SenderName: "",
},
wantErr: false,
},
{
name: "long message",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeWhisper,
Flags: 0x0000,
Message: "This is a very long message that contains a lot of text to test the handling of longer strings in the binary packet format.",
SenderName: "LongNamePlayer",
},
wantErr: false,
},
{
name: "special characters",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeAlliance,
Flags: 0x0000,
Message: "Hello!@#$%^&*()",
SenderName: "Player_123",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bf := byteframe.NewByteFrame()
err := tt.msg.Build(bf)
if (err != nil) != tt.wantErr {
t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
data := bf.Data()
if tt.validate != nil {
tt.validate(t, data)
}
}
})
}
}
func TestMsgBinChat_Parse(t *testing.T) {
tests := []struct {
name string
data []byte
want *MsgBinChat
wantErr bool
}{
{
name: "basic message",
data: []byte{
0x01, // Unk0
0x00, // Type (ChatTypeWorld)
0x00, 0x00, // Flags
0x00, 0x08, // lenSenderName (8)
0x00, 0x06, // lenMessage (6)
// Message: "Hello" + null terminator (SJIS compatible ASCII)
0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x00,
// SenderName: "Player1" + null terminator
0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x31, 0x00,
},
want: &MsgBinChat{
Unk0: 0x01,
Type: ChatTypeWorld,
Flags: 0x0000,
Message: "Hello",
SenderName: "Player1",
},
wantErr: false,
},
{
name: "different chat type",
data: []byte{
0x00, // Unk0
0x02, // Type (ChatTypeGuild)
0x12, 0x34, // Flags
0x00, 0x05, // lenSenderName
0x00, 0x03, // lenMessage
// Message: "Hi" + null
0x48, 0x69, 0x00,
// SenderName: "Bob" + null + padding
0x42, 0x6F, 0x62, 0x00, 0x00,
},
want: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeGuild,
Flags: 0x1234,
Message: "Hi",
SenderName: "Bob",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bf := byteframe.NewByteFrameFromBytes(tt.data)
msg := &MsgBinChat{}
err := msg.Parse(bf)
if (err != nil) != tt.wantErr {
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
if msg.Unk0 != tt.want.Unk0 {
t.Errorf("Unk0 = 0x%X, want 0x%X", msg.Unk0, tt.want.Unk0)
}
if msg.Type != tt.want.Type {
t.Errorf("Type = %v, want %v", msg.Type, tt.want.Type)
}
if msg.Flags != tt.want.Flags {
t.Errorf("Flags = 0x%X, want 0x%X", msg.Flags, tt.want.Flags)
}
if msg.Message != tt.want.Message {
t.Errorf("Message = %q, want %q", msg.Message, tt.want.Message)
}
if msg.SenderName != tt.want.SenderName {
t.Errorf("SenderName = %q, want %q", msg.SenderName, tt.want.SenderName)
}
}
})
}
}
func TestMsgBinChat_RoundTrip(t *testing.T) {
tests := []struct {
name string
msg *MsgBinChat
}{
{
name: "world chat",
msg: &MsgBinChat{
Unk0: 0x01,
Type: ChatTypeWorld,
Flags: 0x0000,
Message: "Hello World",
SenderName: "TestPlayer",
},
},
{
name: "stage chat",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeStage,
Flags: 0x1234,
Message: "Stage message",
SenderName: "Player2",
},
},
{
name: "guild chat",
msg: &MsgBinChat{
Unk0: 0x02,
Type: ChatTypeGuild,
Flags: 0xFFFF,
Message: "Guild announcement",
SenderName: "GuildMaster",
},
},
{
name: "alliance chat",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeAlliance,
Flags: 0x0001,
Message: "Alliance msg",
SenderName: "AllyLeader",
},
},
{
name: "party chat",
msg: &MsgBinChat{
Unk0: 0x01,
Type: ChatTypeParty,
Flags: 0x0000,
Message: "Party up!",
SenderName: "PartyLeader",
},
},
{
name: "whisper",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeWhisper,
Flags: 0x0002,
Message: "Secret message",
SenderName: "Whisperer",
},
},
{
name: "empty strings",
msg: &MsgBinChat{
Unk0: 0x00,
Type: ChatTypeWorld,
Flags: 0x0000,
Message: "",
SenderName: "",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Build
bf := byteframe.NewByteFrame()
err := tt.msg.Build(bf)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
// Parse
parsedMsg := &MsgBinChat{}
parsedBf := byteframe.NewByteFrameFromBytes(bf.Data())
err = parsedMsg.Parse(parsedBf)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// Compare
if parsedMsg.Unk0 != tt.msg.Unk0 {
t.Errorf("Unk0 = 0x%X, want 0x%X", parsedMsg.Unk0, tt.msg.Unk0)
}
if parsedMsg.Type != tt.msg.Type {
t.Errorf("Type = %v, want %v", parsedMsg.Type, tt.msg.Type)
}
if parsedMsg.Flags != tt.msg.Flags {
t.Errorf("Flags = 0x%X, want 0x%X", parsedMsg.Flags, tt.msg.Flags)
}
if parsedMsg.Message != tt.msg.Message {
t.Errorf("Message = %q, want %q", parsedMsg.Message, tt.msg.Message)
}
if parsedMsg.SenderName != tt.msg.SenderName {
t.Errorf("SenderName = %q, want %q", parsedMsg.SenderName, tt.msg.SenderName)
}
})
}
}
func TestChatType_Values(t *testing.T) {
tests := []struct {
chatType ChatType
expected uint8
}{
{ChatTypeWorld, 0},
{ChatTypeStage, 1},
{ChatTypeGuild, 2},
{ChatTypeAlliance, 3},
{ChatTypeParty, 4},
{ChatTypeWhisper, 5},
}
for _, tt := range tests {
if uint8(tt.chatType) != tt.expected {
t.Errorf("ChatType value = %d, want %d", uint8(tt.chatType), tt.expected)
}
}
}
func TestMsgBinChat_BuildParseConsistency(t *testing.T) {
// Test that Build and Parse are consistent with each other
// by building, parsing, building again, and comparing
original := &MsgBinChat{
Unk0: 0x01,
Type: ChatTypeWorld,
Flags: 0x1234,
Message: "Test message",
SenderName: "TestSender",
}
// First build
bf1 := byteframe.NewByteFrame()
err := original.Build(bf1)
if err != nil {
t.Fatalf("First Build() error = %v", err)
}
// Parse
parsed := &MsgBinChat{}
parsedBf := byteframe.NewByteFrameFromBytes(bf1.Data())
err = parsed.Parse(parsedBf)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// Second build
bf2 := byteframe.NewByteFrame()
err = parsed.Build(bf2)
if err != nil {
t.Fatalf("Second Build() error = %v", err)
}
// Compare the two builds
if !bytes.Equal(bf1.Data(), bf2.Data()) {
t.Errorf("Build-Parse-Build inconsistency:\nFirst: %v\nSecond: %v", bf1.Data(), bf2.Data())
}
}

View File

@@ -0,0 +1,219 @@
package binpacket
import (
"erupe-ce/common/byteframe"
"erupe-ce/network"
"testing"
)
func TestMsgBinMailNotify_Opcode(t *testing.T) {
msg := MsgBinMailNotify{}
if msg.Opcode() != network.MSG_SYS_CASTED_BINARY {
t.Errorf("Opcode() = %v, want %v", msg.Opcode(), network.MSG_SYS_CASTED_BINARY)
}
}
func TestMsgBinMailNotify_Build(t *testing.T) {
tests := []struct {
name string
senderName string
wantErr bool
validate func(*testing.T, []byte)
}{
{
name: "basic sender name",
senderName: "Player1",
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) == 0 {
t.Error("Build() returned empty data")
}
// First byte should be 0x01 (Unk)
if data[0] != 0x01 {
t.Errorf("First byte = 0x%X, want 0x01", data[0])
}
// Total length should be 1 (Unk) + 21 (padded string)
expectedLen := 1 + 21
if len(data) != expectedLen {
t.Errorf("data length = %d, want %d", len(data), expectedLen)
}
},
},
{
name: "empty sender name",
senderName: "",
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) != 22 { // 1 + 21
t.Errorf("data length = %d, want 22", len(data))
}
},
},
{
name: "long sender name",
senderName: "VeryLongPlayerNameThatExceeds21Characters",
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) != 22 { // 1 + 21 (truncated/padded)
t.Errorf("data length = %d, want 22", len(data))
}
},
},
{
name: "exactly 21 characters",
senderName: "ExactlyTwentyOneChar1",
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) != 22 {
t.Errorf("data length = %d, want 22", len(data))
}
},
},
{
name: "special characters",
senderName: "Player_123",
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) != 22 {
t.Errorf("data length = %d, want 22", len(data))
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msg := MsgBinMailNotify{
SenderName: tt.senderName,
}
bf := byteframe.NewByteFrame()
err := msg.Build(bf)
if (err != nil) != tt.wantErr {
t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && tt.validate != nil {
tt.validate(t, bf.Data())
}
})
}
}
func TestMsgBinMailNotify_Parse_Panics(t *testing.T) {
// Document that Parse() is not implemented and panics
msg := MsgBinMailNotify{}
bf := byteframe.NewByteFrame()
defer func() {
if r := recover(); r == nil {
t.Error("Parse() did not panic, but should panic with 'implement me'")
}
}()
// This should panic
_ = msg.Parse(bf)
}
func TestMsgBinMailNotify_BuildMultiple(t *testing.T) {
// Test building multiple messages to ensure no state pollution
names := []string{"Player1", "Player2", "Player3"}
for _, name := range names {
msg := MsgBinMailNotify{SenderName: name}
bf := byteframe.NewByteFrame()
err := msg.Build(bf)
if err != nil {
t.Errorf("Build(%s) error = %v", name, err)
}
data := bf.Data()
if len(data) != 22 {
t.Errorf("Build(%s) length = %d, want 22", name, len(data))
}
}
}
func TestMsgBinMailNotify_PaddingBehavior(t *testing.T) {
// Test that the padded string is always 21 bytes
tests := []struct {
name string
senderName string
}{
{"short", "A"},
{"medium", "PlayerName"},
{"long", "VeryVeryLongPlayerName"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
msg := MsgBinMailNotify{SenderName: tt.senderName}
bf := byteframe.NewByteFrame()
err := msg.Build(bf)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
data := bf.Data()
// Skip first byte (Unk), check remaining 21 bytes
if len(data) < 22 {
t.Fatalf("data too short: %d bytes", len(data))
}
paddedString := data[1:22]
if len(paddedString) != 21 {
t.Errorf("padded string length = %d, want 21", len(paddedString))
}
})
}
}
func TestMsgBinMailNotify_BuildStructure(t *testing.T) {
// Test the structure of the built data
msg := MsgBinMailNotify{SenderName: "Test"}
bf := byteframe.NewByteFrame()
err := msg.Build(bf)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
data := bf.Data()
// Check structure: 1 byte Unk + 21 bytes padded string = 22 bytes total
if len(data) != 22 {
t.Errorf("data length = %d, want 22", len(data))
}
// First byte should be 0x01
if data[0] != 0x01 {
t.Errorf("Unk byte = 0x%X, want 0x01", data[0])
}
// The rest (21 bytes) should contain the sender name (SJIS encoded) and padding
// We can't verify exact content without knowing SJIS encoding details,
// but we can verify length
paddedPortion := data[1:]
if len(paddedPortion) != 21 {
t.Errorf("padded portion length = %d, want 21", len(paddedPortion))
}
}
func TestMsgBinMailNotify_ValueSemantics(t *testing.T) {
// Test that MsgBinMailNotify uses value semantics (not pointer receiver for Opcode)
msg := MsgBinMailNotify{SenderName: "Test"}
// Should work with value
opcode := msg.Opcode()
if opcode != network.MSG_SYS_CASTED_BINARY {
t.Errorf("Opcode() = %v, want %v", opcode, network.MSG_SYS_CASTED_BINARY)
}
// Should also work with pointer (Go allows this)
msgPtr := &MsgBinMailNotify{SenderName: "Test"}
opcode2 := msgPtr.Opcode()
if opcode2 != network.MSG_SYS_CASTED_BINARY {
t.Errorf("Opcode() on pointer = %v, want %v", opcode2, network.MSG_SYS_CASTED_BINARY)
}
}

View File

@@ -0,0 +1,404 @@
package binpacket
import (
"bytes"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"testing"
)
func TestMsgBinTargeted_Opcode(t *testing.T) {
msg := &MsgBinTargeted{}
if msg.Opcode() != network.MSG_SYS_CAST_BINARY {
t.Errorf("Opcode() = %v, want %v", msg.Opcode(), network.MSG_SYS_CAST_BINARY)
}
}
func TestMsgBinTargeted_Build(t *testing.T) {
tests := []struct {
name string
msg *MsgBinTargeted
wantErr bool
validate func(*testing.T, []byte)
}{
{
name: "single target with payload",
msg: &MsgBinTargeted{
TargetCount: 1,
TargetCharIDs: []uint32{12345},
RawDataPayload: []byte{0x01, 0x02, 0x03, 0x04},
},
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) < 2+4+4 { // 2 bytes count + 4 bytes ID + 4 bytes payload
t.Errorf("data length = %d, want at least %d", len(data), 2+4+4)
}
},
},
{
name: "multiple targets",
msg: &MsgBinTargeted{
TargetCount: 3,
TargetCharIDs: []uint32{100, 200, 300},
RawDataPayload: []byte{0xAA, 0xBB},
},
wantErr: false,
validate: func(t *testing.T, data []byte) {
expectedLen := 2 + (3 * 4) + 2 // count + 3 IDs + payload
if len(data) != expectedLen {
t.Errorf("data length = %d, want %d", len(data), expectedLen)
}
},
},
{
name: "zero targets",
msg: &MsgBinTargeted{
TargetCount: 0,
TargetCharIDs: []uint32{},
RawDataPayload: []byte{0xFF},
},
wantErr: false,
validate: func(t *testing.T, data []byte) {
if len(data) < 2+1 { // count + payload
t.Errorf("data length = %d, want at least %d", len(data), 2+1)
}
},
},
{
name: "empty payload",
msg: &MsgBinTargeted{
TargetCount: 1,
TargetCharIDs: []uint32{999},
RawDataPayload: []byte{},
},
wantErr: false,
validate: func(t *testing.T, data []byte) {
expectedLen := 2 + 4 // count + 1 ID
if len(data) != expectedLen {
t.Errorf("data length = %d, want %d", len(data), expectedLen)
}
},
},
{
name: "large payload",
msg: &MsgBinTargeted{
TargetCount: 2,
TargetCharIDs: []uint32{1000, 2000},
RawDataPayload: bytes.Repeat([]byte{0xCC}, 256),
},
wantErr: false,
},
{
name: "max uint32 target IDs",
msg: &MsgBinTargeted{
TargetCount: 2,
TargetCharIDs: []uint32{0xFFFFFFFF, 0x12345678},
RawDataPayload: []byte{0x01},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bf := byteframe.NewByteFrame()
err := tt.msg.Build(bf)
if (err != nil) != tt.wantErr {
t.Errorf("Build() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
data := bf.Data()
if tt.validate != nil {
tt.validate(t, data)
}
}
})
}
}
func TestMsgBinTargeted_Parse(t *testing.T) {
tests := []struct {
name string
data []byte
want *MsgBinTargeted
wantErr bool
}{
{
name: "single target",
data: []byte{
0x00, 0x01, // TargetCount = 1
0x00, 0x00, 0x30, 0x39, // TargetCharID = 12345
0xAA, 0xBB, 0xCC, // RawDataPayload
},
want: &MsgBinTargeted{
TargetCount: 1,
TargetCharIDs: []uint32{12345},
RawDataPayload: []byte{0xAA, 0xBB, 0xCC},
},
wantErr: false,
},
{
name: "multiple targets",
data: []byte{
0x00, 0x03, // TargetCount = 3
0x00, 0x00, 0x00, 0x64, // Target 1 = 100
0x00, 0x00, 0x00, 0xC8, // Target 2 = 200
0x00, 0x00, 0x01, 0x2C, // Target 3 = 300
0x01, 0x02, // RawDataPayload
},
want: &MsgBinTargeted{
TargetCount: 3,
TargetCharIDs: []uint32{100, 200, 300},
RawDataPayload: []byte{0x01, 0x02},
},
wantErr: false,
},
{
name: "zero targets",
data: []byte{
0x00, 0x00, // TargetCount = 0
0xFF, 0xFF, // RawDataPayload
},
want: &MsgBinTargeted{
TargetCount: 0,
TargetCharIDs: []uint32{},
RawDataPayload: []byte{0xFF, 0xFF},
},
wantErr: false,
},
{
name: "no payload",
data: []byte{
0x00, 0x01, // TargetCount = 1
0x00, 0x00, 0x03, 0xE7, // Target = 999
},
want: &MsgBinTargeted{
TargetCount: 1,
TargetCharIDs: []uint32{999},
RawDataPayload: []byte{},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
bf := byteframe.NewByteFrameFromBytes(tt.data)
msg := &MsgBinTargeted{}
err := msg.Parse(bf)
if (err != nil) != tt.wantErr {
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
if msg.TargetCount != tt.want.TargetCount {
t.Errorf("TargetCount = %d, want %d", msg.TargetCount, tt.want.TargetCount)
}
if len(msg.TargetCharIDs) != len(tt.want.TargetCharIDs) {
t.Errorf("len(TargetCharIDs) = %d, want %d", len(msg.TargetCharIDs), len(tt.want.TargetCharIDs))
} else {
for i, id := range msg.TargetCharIDs {
if id != tt.want.TargetCharIDs[i] {
t.Errorf("TargetCharIDs[%d] = %d, want %d", i, id, tt.want.TargetCharIDs[i])
}
}
}
if !bytes.Equal(msg.RawDataPayload, tt.want.RawDataPayload) {
t.Errorf("RawDataPayload = %v, want %v", msg.RawDataPayload, tt.want.RawDataPayload)
}
}
})
}
}
func TestMsgBinTargeted_RoundTrip(t *testing.T) {
tests := []struct {
name string
msg *MsgBinTargeted
}{
{
name: "single target",
msg: &MsgBinTargeted{
TargetCount: 1,
TargetCharIDs: []uint32{12345},
RawDataPayload: []byte{0x01, 0x02, 0x03},
},
},
{
name: "multiple targets",
msg: &MsgBinTargeted{
TargetCount: 5,
TargetCharIDs: []uint32{100, 200, 300, 400, 500},
RawDataPayload: []byte{0xAA, 0xBB, 0xCC, 0xDD},
},
},
{
name: "zero targets",
msg: &MsgBinTargeted{
TargetCount: 0,
TargetCharIDs: []uint32{},
RawDataPayload: []byte{0xFF},
},
},
{
name: "empty payload",
msg: &MsgBinTargeted{
TargetCount: 2,
TargetCharIDs: []uint32{1000, 2000},
RawDataPayload: []byte{},
},
},
{
name: "large IDs and payload",
msg: &MsgBinTargeted{
TargetCount: 3,
TargetCharIDs: []uint32{0xFFFFFFFF, 0x12345678, 0xABCDEF00},
RawDataPayload: bytes.Repeat([]byte{0xDD}, 128),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Build
bf := byteframe.NewByteFrame()
err := tt.msg.Build(bf)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
// Parse
parsedMsg := &MsgBinTargeted{}
parsedBf := byteframe.NewByteFrameFromBytes(bf.Data())
err = parsedMsg.Parse(parsedBf)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// Compare
if parsedMsg.TargetCount != tt.msg.TargetCount {
t.Errorf("TargetCount = %d, want %d", parsedMsg.TargetCount, tt.msg.TargetCount)
}
if len(parsedMsg.TargetCharIDs) != len(tt.msg.TargetCharIDs) {
t.Errorf("len(TargetCharIDs) = %d, want %d", len(parsedMsg.TargetCharIDs), len(tt.msg.TargetCharIDs))
} else {
for i, id := range parsedMsg.TargetCharIDs {
if id != tt.msg.TargetCharIDs[i] {
t.Errorf("TargetCharIDs[%d] = %d, want %d", i, id, tt.msg.TargetCharIDs[i])
}
}
}
if !bytes.Equal(parsedMsg.RawDataPayload, tt.msg.RawDataPayload) {
t.Errorf("RawDataPayload length mismatch: got %d, want %d", len(parsedMsg.RawDataPayload), len(tt.msg.RawDataPayload))
}
})
}
}
func TestMsgBinTargeted_TargetCountMismatch(t *testing.T) {
// Test that TargetCount and actual array length don't have to match
// The Build function uses the TargetCount field
msg := &MsgBinTargeted{
TargetCount: 2, // Says 2
TargetCharIDs: []uint32{100, 200, 300}, // But has 3
RawDataPayload: []byte{0x01},
}
bf := byteframe.NewByteFrame()
err := msg.Build(bf)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
// Parse should read exactly 2 IDs as specified by TargetCount
parsedMsg := &MsgBinTargeted{}
parsedBf := byteframe.NewByteFrameFromBytes(bf.Data())
err = parsedMsg.Parse(parsedBf)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
if parsedMsg.TargetCount != 2 {
t.Errorf("TargetCount = %d, want 2", parsedMsg.TargetCount)
}
if len(parsedMsg.TargetCharIDs) != 2 {
t.Errorf("len(TargetCharIDs) = %d, want 2", len(parsedMsg.TargetCharIDs))
}
}
func TestMsgBinTargeted_BuildParseConsistency(t *testing.T) {
original := &MsgBinTargeted{
TargetCount: 3,
TargetCharIDs: []uint32{111, 222, 333},
RawDataPayload: []byte{0x11, 0x22, 0x33, 0x44},
}
// First build
bf1 := byteframe.NewByteFrame()
err := original.Build(bf1)
if err != nil {
t.Fatalf("First Build() error = %v", err)
}
// Parse
parsed := &MsgBinTargeted{}
parsedBf := byteframe.NewByteFrameFromBytes(bf1.Data())
err = parsed.Parse(parsedBf)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
// Second build
bf2 := byteframe.NewByteFrame()
err = parsed.Build(bf2)
if err != nil {
t.Fatalf("Second Build() error = %v", err)
}
// Compare the two builds
if !bytes.Equal(bf1.Data(), bf2.Data()) {
t.Errorf("Build-Parse-Build inconsistency:\nFirst: %v\nSecond: %v", bf1.Data(), bf2.Data())
}
}
func TestMsgBinTargeted_PayloadForwarding(t *testing.T) {
// Test that RawDataPayload is correctly preserved
// This is important as it forwards another binpacket
originalPayload := []byte{
0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80,
0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0, 0xF0, 0xFF,
}
msg := &MsgBinTargeted{
TargetCount: 1,
TargetCharIDs: []uint32{999},
RawDataPayload: originalPayload,
}
bf := byteframe.NewByteFrame()
err := msg.Build(bf)
if err != nil {
t.Fatalf("Build() error = %v", err)
}
parsed := &MsgBinTargeted{}
parsedBf := byteframe.NewByteFrameFromBytes(bf.Data())
err = parsed.Parse(parsedBf)
if err != nil {
t.Fatalf("Parse() error = %v", err)
}
if !bytes.Equal(parsed.RawDataPayload, originalPayload) {
t.Errorf("Payload not preserved:\ngot: %v\nwant: %v", parsed.RawDataPayload, originalPayload)
}
}