mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
test: backport remaining test files from v9.2.x-stable
Import 18 network packet test files and 5 server infrastructure test files, adapted for main branch APIs: fix config import alias (_config), remove non-existent DevMode field, use global handlerTable instead of per-server handlers map, and correct validateToken mock expectations to include both token and tokenID arguments. Adds go-sqlmock dependency for database mocking in signserver tests.
This commit is contained in:
1
go.mod
1
go.mod
@@ -15,6 +15,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.1 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
|
||||
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
|
||||
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -143,6 +145,7 @@ github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Cc
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
||||
430
network/binpacket/binpacket_test.go
Normal file
430
network/binpacket/binpacket_test.go
Normal file
@@ -0,0 +1,430 @@
|
||||
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
|
||||
}{
|
||||
{ChatTypeStage, 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: ChatTypeStage,
|
||||
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{
|
||||
ChatTypeStage,
|
||||
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()))
|
||||
}
|
||||
}
|
||||
828
network/mhfpacket/mhfpacket_test.go
Normal file
828
network/mhfpacket/mhfpacket_test.go
Normal file
@@ -0,0 +1,828 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
func TestMHFPacketInterface(t *testing.T) {
|
||||
// Verify that packets implement the MHFPacket interface
|
||||
var _ MHFPacket = &MsgSysPing{}
|
||||
var _ MHFPacket = &MsgSysTime{}
|
||||
var _ MHFPacket = &MsgSysNop{}
|
||||
var _ MHFPacket = &MsgSysEnd{}
|
||||
var _ MHFPacket = &MsgSysLogin{}
|
||||
var _ MHFPacket = &MsgSysLogout{}
|
||||
}
|
||||
|
||||
func TestFromOpcodeReturnsCorrectType(t *testing.T) {
|
||||
tests := []struct {
|
||||
opcode network.PacketID
|
||||
wantType string
|
||||
}{
|
||||
{network.MSG_HEAD, "*mhfpacket.MsgHead"},
|
||||
{network.MSG_SYS_PING, "*mhfpacket.MsgSysPing"},
|
||||
{network.MSG_SYS_TIME, "*mhfpacket.MsgSysTime"},
|
||||
{network.MSG_SYS_NOP, "*mhfpacket.MsgSysNop"},
|
||||
{network.MSG_SYS_END, "*mhfpacket.MsgSysEnd"},
|
||||
{network.MSG_SYS_ACK, "*mhfpacket.MsgSysAck"},
|
||||
{network.MSG_SYS_LOGIN, "*mhfpacket.MsgSysLogin"},
|
||||
{network.MSG_SYS_LOGOUT, "*mhfpacket.MsgSysLogout"},
|
||||
{network.MSG_SYS_CREATE_STAGE, "*mhfpacket.MsgSysCreateStage"},
|
||||
{network.MSG_SYS_ENTER_STAGE, "*mhfpacket.MsgSysEnterStage"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(tt.opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", tt.opcode)
|
||||
return
|
||||
}
|
||||
if pkt.Opcode() != tt.opcode {
|
||||
t.Errorf("Opcode() = %s, want %s", pkt.Opcode(), tt.opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromOpcodeUnknown(t *testing.T) {
|
||||
// Test with an invalid opcode
|
||||
pkt := FromOpcode(network.PacketID(0xFFFF))
|
||||
if pkt != nil {
|
||||
t.Error("FromOpcode(0xFFFF) should return nil for unknown opcode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysPingRoundTrip(t *testing.T) {
|
||||
original := &MsgSysPing{
|
||||
AckHandle: 0x12345678,
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Parse
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysPing{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
// Compare
|
||||
if parsed.AckHandle != original.AckHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", parsed.AckHandle, original.AckHandle)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysTimeRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
getRemoteTime bool
|
||||
timestamp uint32
|
||||
}{
|
||||
{"no remote time", false, 1577105879},
|
||||
{"with remote time", true, 1609459200},
|
||||
{"zero timestamp", false, 0},
|
||||
{"max timestamp", true, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
original := &MsgSysTime{
|
||||
GetRemoteTime: tt.getRemoteTime,
|
||||
Timestamp: tt.timestamp,
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Parse
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysTime{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
// Compare
|
||||
if parsed.GetRemoteTime != original.GetRemoteTime {
|
||||
t.Errorf("GetRemoteTime = %v, want %v", parsed.GetRemoteTime, original.GetRemoteTime)
|
||||
}
|
||||
if parsed.Timestamp != original.Timestamp {
|
||||
t.Errorf("Timestamp = %d, want %d", parsed.Timestamp, original.Timestamp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysPingOpcode(t *testing.T) {
|
||||
pkt := &MsgSysPing{}
|
||||
if pkt.Opcode() != network.MSG_SYS_PING {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_PING", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysTimeOpcode(t *testing.T) {
|
||||
pkt := &MsgSysTime{}
|
||||
if pkt.Opcode() != network.MSG_SYS_TIME {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_TIME", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromOpcodeSystemPackets(t *testing.T) {
|
||||
// Test all system packet opcodes return non-nil
|
||||
systemOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_reserve01,
|
||||
network.MSG_SYS_reserve02,
|
||||
network.MSG_SYS_reserve03,
|
||||
network.MSG_SYS_reserve04,
|
||||
network.MSG_SYS_reserve05,
|
||||
network.MSG_SYS_reserve06,
|
||||
network.MSG_SYS_reserve07,
|
||||
network.MSG_SYS_ADD_OBJECT,
|
||||
network.MSG_SYS_DEL_OBJECT,
|
||||
network.MSG_SYS_DISP_OBJECT,
|
||||
network.MSG_SYS_HIDE_OBJECT,
|
||||
network.MSG_SYS_END,
|
||||
network.MSG_SYS_NOP,
|
||||
network.MSG_SYS_ACK,
|
||||
network.MSG_SYS_LOGIN,
|
||||
network.MSG_SYS_LOGOUT,
|
||||
network.MSG_SYS_SET_STATUS,
|
||||
network.MSG_SYS_PING,
|
||||
network.MSG_SYS_TIME,
|
||||
}
|
||||
|
||||
for _, opcode := range systemOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromOpcodeStagePackets(t *testing.T) {
|
||||
stageOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_CREATE_STAGE,
|
||||
network.MSG_SYS_STAGE_DESTRUCT,
|
||||
network.MSG_SYS_ENTER_STAGE,
|
||||
network.MSG_SYS_BACK_STAGE,
|
||||
network.MSG_SYS_MOVE_STAGE,
|
||||
network.MSG_SYS_LEAVE_STAGE,
|
||||
network.MSG_SYS_LOCK_STAGE,
|
||||
network.MSG_SYS_UNLOCK_STAGE,
|
||||
network.MSG_SYS_RESERVE_STAGE,
|
||||
network.MSG_SYS_UNRESERVE_STAGE,
|
||||
network.MSG_SYS_SET_STAGE_PASS,
|
||||
}
|
||||
|
||||
for _, opcode := range stageOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpcodeMatches(t *testing.T) {
|
||||
// Verify that packets return the same opcode they were created from
|
||||
tests := []network.PacketID{
|
||||
network.MSG_HEAD,
|
||||
network.MSG_SYS_PING,
|
||||
network.MSG_SYS_TIME,
|
||||
network.MSG_SYS_END,
|
||||
network.MSG_SYS_NOP,
|
||||
network.MSG_SYS_ACK,
|
||||
network.MSG_SYS_LOGIN,
|
||||
network.MSG_SYS_CREATE_STAGE,
|
||||
}
|
||||
|
||||
for _, opcode := range tests {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Skip("opcode not implemented")
|
||||
}
|
||||
if pkt.Opcode() != opcode {
|
||||
t.Errorf("Opcode() = %s, want %s", pkt.Opcode(), opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParserInterface(t *testing.T) {
|
||||
// Verify Parser interface works
|
||||
var p Parser = &MsgSysPing{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(123)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
err := p.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Errorf("Parse() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuilderInterface(t *testing.T) {
|
||||
// Verify Builder interface works
|
||||
var b Builder = &MsgSysPing{AckHandle: 456}
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
err := b.Build(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Errorf("Build() error = %v", err)
|
||||
}
|
||||
if len(bf.Data()) == 0 {
|
||||
t.Error("Build() should write data")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpcoderInterface(t *testing.T) {
|
||||
// Verify Opcoder interface works
|
||||
var o Opcoder = &MsgSysPing{}
|
||||
opcode := o.Opcode()
|
||||
|
||||
if opcode != network.MSG_SYS_PING {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_PING", opcode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientContextNilSafe(t *testing.T) {
|
||||
// Some packets may need to handle nil ClientContext
|
||||
pkt := &MsgSysPing{AckHandle: 123}
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
// This should not panic even with nil context (implementation dependent)
|
||||
// Note: The actual behavior depends on implementation
|
||||
err := pkt.Build(bf, nil)
|
||||
if err != nil {
|
||||
// Error is acceptable if nil context is not supported
|
||||
t.Logf("Build() with nil context returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysPingBuildFormat(t *testing.T) {
|
||||
pkt := &MsgSysPing{AckHandle: 0x12345678}
|
||||
bf := byteframe.NewByteFrame()
|
||||
pkt.Build(bf, &clientctx.ClientContext{})
|
||||
|
||||
data := bf.Data()
|
||||
if len(data) != 4 {
|
||||
t.Errorf("Build() data len = %d, want 4", len(data))
|
||||
}
|
||||
|
||||
// Verify big-endian format (default)
|
||||
if data[0] != 0x12 || data[1] != 0x34 || data[2] != 0x56 || data[3] != 0x78 {
|
||||
t.Errorf("Build() data = %x, want 12345678", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysTimeBuildFormat(t *testing.T) {
|
||||
pkt := &MsgSysTime{
|
||||
GetRemoteTime: true,
|
||||
Timestamp: 0xDEADBEEF,
|
||||
}
|
||||
bf := byteframe.NewByteFrame()
|
||||
pkt.Build(bf, &clientctx.ClientContext{})
|
||||
|
||||
data := bf.Data()
|
||||
if len(data) != 5 {
|
||||
t.Errorf("Build() data len = %d, want 5 (1 bool + 4 uint32)", len(data))
|
||||
}
|
||||
|
||||
// First byte is bool (1 = true)
|
||||
if data[0] != 1 {
|
||||
t.Errorf("GetRemoteTime byte = %d, want 1", data[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysNop(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_SYS_NOP)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_SYS_NOP) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_SYS_NOP {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_NOP", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysEnd(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_SYS_END)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_SYS_END) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_SYS_END {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_END", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgHead(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_HEAD)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_HEAD) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_HEAD {
|
||||
t.Errorf("Opcode() = %s, want MSG_HEAD", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysAck(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_SYS_ACK)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_SYS_ACK) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_SYS_ACK {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ACK", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestBinaryPackets(t *testing.T) {
|
||||
binaryOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_CAST_BINARY,
|
||||
network.MSG_SYS_CASTED_BINARY,
|
||||
network.MSG_SYS_SET_STAGE_BINARY,
|
||||
network.MSG_SYS_GET_STAGE_BINARY,
|
||||
network.MSG_SYS_WAIT_STAGE_BINARY,
|
||||
}
|
||||
|
||||
for _, opcode := range binaryOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnumeratePackets(t *testing.T) {
|
||||
enumOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_ENUMERATE_CLIENT,
|
||||
network.MSG_SYS_ENUMERATE_STAGE,
|
||||
}
|
||||
|
||||
for _, opcode := range enumOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSemaphorePackets(t *testing.T) {
|
||||
semaOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_CREATE_ACQUIRE_SEMAPHORE,
|
||||
network.MSG_SYS_ACQUIRE_SEMAPHORE,
|
||||
network.MSG_SYS_RELEASE_SEMAPHORE,
|
||||
network.MSG_SYS_CHECK_SEMAPHORE,
|
||||
}
|
||||
|
||||
for _, opcode := range semaOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestObjectPackets(t *testing.T) {
|
||||
objOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_ADD_OBJECT,
|
||||
network.MSG_SYS_DEL_OBJECT,
|
||||
network.MSG_SYS_DISP_OBJECT,
|
||||
network.MSG_SYS_HIDE_OBJECT,
|
||||
}
|
||||
|
||||
for _, opcode := range objOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogPackets(t *testing.T) {
|
||||
logOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_TERMINAL_LOG,
|
||||
network.MSG_SYS_ISSUE_LOGKEY,
|
||||
network.MSG_SYS_RECORD_LOG,
|
||||
}
|
||||
|
||||
for _, opcode := range logOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMHFSaveLoad(t *testing.T) {
|
||||
saveLoadOpcodes := []network.PacketID{
|
||||
network.MSG_MHF_SAVEDATA,
|
||||
network.MSG_MHF_LOADDATA,
|
||||
}
|
||||
|
||||
for _, opcode := range saveLoadOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Errorf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysCreateStageParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantHandle uint32
|
||||
wantUnk0 uint8
|
||||
wantPlayers uint8
|
||||
wantStageID string
|
||||
}{
|
||||
{
|
||||
name: "simple stage",
|
||||
data: append([]byte{0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x05}, append([]byte("test"), 0x00)...),
|
||||
wantHandle: 1,
|
||||
wantUnk0: 2,
|
||||
wantPlayers: 4,
|
||||
wantStageID: "test",
|
||||
},
|
||||
{
|
||||
name: "empty stage ID",
|
||||
data: []byte{0x12, 0x34, 0x56, 0x78, 0x01, 0x02, 0x00},
|
||||
wantHandle: 0x12345678,
|
||||
wantUnk0: 1,
|
||||
wantPlayers: 2,
|
||||
wantStageID: "",
|
||||
},
|
||||
{
|
||||
name: "with null terminator",
|
||||
data: append([]byte{0x00, 0x00, 0x00, 0x0A, 0x01, 0x01, 0x08}, append([]byte("stage01"), 0x00)...),
|
||||
wantHandle: 10,
|
||||
wantUnk0: 1,
|
||||
wantPlayers: 1,
|
||||
wantStageID: "stage01",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCreateStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.wantHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.wantHandle)
|
||||
}
|
||||
if pkt.Unk0 != tt.wantUnk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.wantUnk0)
|
||||
}
|
||||
if pkt.PlayerCount != tt.wantPlayers {
|
||||
t.Errorf("PlayerCount = %d, want %d", pkt.PlayerCount, tt.wantPlayers)
|
||||
}
|
||||
if pkt.StageID != tt.wantStageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.wantStageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysEnterStageParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantHandle uint32
|
||||
wantUnk bool
|
||||
wantStageID string
|
||||
}{
|
||||
{
|
||||
name: "enter mezeporta",
|
||||
data: append([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x0F}, append([]byte("sl1Ns200p0a0u0"), 0x00)...),
|
||||
wantHandle: 1,
|
||||
wantUnk: false,
|
||||
wantStageID: "sl1Ns200p0a0u0",
|
||||
},
|
||||
{
|
||||
name: "with unk bool set",
|
||||
data: append([]byte{0xAB, 0xCD, 0xEF, 0x12, 0x01, 0x05}, append([]byte("room1"), 0x00)...),
|
||||
wantHandle: 0xABCDEF12,
|
||||
wantUnk: true,
|
||||
wantStageID: "room1",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysEnterStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.wantHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.wantHandle)
|
||||
}
|
||||
if pkt.Unk != tt.wantUnk {
|
||||
t.Errorf("Unk = %v, want %v", pkt.Unk, tt.wantUnk)
|
||||
}
|
||||
if pkt.StageID != tt.wantStageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.wantStageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysMoveStageParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantHandle uint32
|
||||
wantUnkBool uint8
|
||||
wantStageID string
|
||||
}{
|
||||
{
|
||||
name: "move to quest stage",
|
||||
data: append([]byte{0x00, 0x00, 0x12, 0x34, 0x00, 0x06}, []byte("quest1")...),
|
||||
wantHandle: 0x1234,
|
||||
wantUnkBool: 0,
|
||||
wantStageID: "quest1",
|
||||
},
|
||||
{
|
||||
name: "with null in string",
|
||||
data: append([]byte{0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x08}, append([]byte("stage"), []byte{0x00, 0x00, 0x00}...)...),
|
||||
wantHandle: 0xFFFFFFFF,
|
||||
wantUnkBool: 1,
|
||||
wantStageID: "stage",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysMoveStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.wantHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.wantHandle)
|
||||
}
|
||||
if pkt.UnkBool != tt.wantUnkBool {
|
||||
t.Errorf("UnkBool = %d, want %d", pkt.UnkBool, tt.wantUnkBool)
|
||||
}
|
||||
if pkt.StageID != tt.wantStageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.wantStageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysLockStageParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantHandle uint32
|
||||
wantStageID string
|
||||
}{
|
||||
{
|
||||
name: "lock stage",
|
||||
data: append([]byte{0x00, 0x00, 0x00, 0x05, 0x01, 0x01, 0x06}, append([]byte("room01"), 0x00)...),
|
||||
wantHandle: 5,
|
||||
wantStageID: "room01",
|
||||
},
|
||||
{
|
||||
name: "different unk values",
|
||||
data: append([]byte{0x12, 0x34, 0x56, 0x78, 0x02, 0x03, 0x04}, append([]byte("test"), 0x00)...),
|
||||
wantHandle: 0x12345678,
|
||||
wantStageID: "test",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLockStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.wantHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.wantHandle)
|
||||
}
|
||||
if pkt.StageID != tt.wantStageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.wantStageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysUnlockStageRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
unk0 uint16
|
||||
}{
|
||||
{"zero value", 0},
|
||||
{"typical value", 1},
|
||||
{"max value", 0xFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build (returns NOT IMPLEMENTED)
|
||||
original := &MsgSysUnlockStage{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err == nil {
|
||||
t.Fatal("Build() expected NOT IMPLEMENTED error")
|
||||
}
|
||||
|
||||
// Parse should consume a uint16 without error
|
||||
bf = byteframe.NewByteFrame()
|
||||
bf.WriteUint16(tt.unk0)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysUnlockStage{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysBackStageParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantHandle uint32
|
||||
}{
|
||||
{"simple handle", []byte{0x00, 0x00, 0x00, 0x01}, 1},
|
||||
{"large handle", []byte{0xDE, 0xAD, 0xBE, 0xEF}, 0xDEADBEEF},
|
||||
{"zero handle", []byte{0x00, 0x00, 0x00, 0x00}, 0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysBackStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.wantHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.wantHandle)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysLogoutParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
wantUnk0 uint8
|
||||
}{
|
||||
{"typical logout", []byte{0x01}, 1},
|
||||
{"zero value", []byte{0x00}, 0},
|
||||
{"max value", []byte{0xFF}, 255},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLogout{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.Unk0 != tt.wantUnk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.wantUnk0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysLoginParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
charID0 uint32
|
||||
loginTokenNumber uint32
|
||||
hardcodedZero0 uint16
|
||||
requestVersion uint16
|
||||
charID1 uint32
|
||||
hardcodedZero1 uint16
|
||||
tokenStrLen uint16
|
||||
tokenString string
|
||||
}{
|
||||
{
|
||||
name: "typical login",
|
||||
ackHandle: 1,
|
||||
charID0: 12345,
|
||||
loginTokenNumber: 67890,
|
||||
hardcodedZero0: 0,
|
||||
requestVersion: 1,
|
||||
charID1: 12345,
|
||||
hardcodedZero1: 0,
|
||||
tokenStrLen: 0x11,
|
||||
tokenString: "abc123token",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint32(tt.charID0)
|
||||
bf.WriteUint32(tt.loginTokenNumber)
|
||||
bf.WriteUint16(tt.hardcodedZero0)
|
||||
bf.WriteUint16(tt.requestVersion)
|
||||
bf.WriteUint32(tt.charID1)
|
||||
bf.WriteUint16(tt.hardcodedZero1)
|
||||
bf.WriteUint16(tt.tokenStrLen)
|
||||
bf.WriteBytes(append([]byte(tt.tokenString), 0x00)) // null terminated
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLogin{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.CharID0 != tt.charID0 {
|
||||
t.Errorf("CharID0 = %d, want %d", pkt.CharID0, tt.charID0)
|
||||
}
|
||||
if pkt.LoginTokenNumber != tt.loginTokenNumber {
|
||||
t.Errorf("LoginTokenNumber = %d, want %d", pkt.LoginTokenNumber, tt.loginTokenNumber)
|
||||
}
|
||||
if pkt.RequestVersion != tt.requestVersion {
|
||||
t.Errorf("RequestVersion = %d, want %d", pkt.RequestVersion, tt.requestVersion)
|
||||
}
|
||||
if pkt.LoginTokenString != tt.tokenString {
|
||||
t.Errorf("LoginTokenString = %q, want %q", pkt.LoginTokenString, tt.tokenString)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
2229
network/mhfpacket/msg_batch_parse_test.go
Normal file
2229
network/mhfpacket/msg_batch_parse_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1413
network/mhfpacket/msg_build_test.go
Normal file
1413
network/mhfpacket/msg_build_test.go
Normal file
File diff suppressed because it is too large
Load Diff
1150
network/mhfpacket/msg_comprehensive_test.go
Normal file
1150
network/mhfpacket/msg_comprehensive_test.go
Normal file
File diff suppressed because it is too large
Load Diff
180
network/mhfpacket/msg_mhf_acquire_cafe_item_test.go
Normal file
180
network/mhfpacket/msg_mhf_acquire_cafe_item_test.go
Normal file
@@ -0,0 +1,180 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
_config "erupe-ce/config"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Initialize ErupeConfig for tests that access it
|
||||
_config.ErupeConfig = &_config.Config{
|
||||
RealClientMode: _config.ZZ, // Default to ZZ for tests
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireCafeItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfAcquireCafeItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ACQUIRE_CAFE_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ACQUIRE_CAFE_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireCafeItemParse(t *testing.T) {
|
||||
// Test basic parsing with current implementation (always reads uint32 for PointCost)
|
||||
// Current code: m.PointCost = bf.ReadUint32() (no client mode check)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint16(1) // ItemType
|
||||
bf.WriteUint16(100) // ItemID
|
||||
bf.WriteUint16(5) // Quant
|
||||
bf.WriteUint32(1000) // PointCost (uint32)
|
||||
bf.WriteUint16(0) // Unk0
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireCafeItem{}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
err := pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.ItemType != 1 {
|
||||
t.Errorf("ItemType = %d, want 1", pkt.ItemType)
|
||||
}
|
||||
if pkt.ItemID != 100 {
|
||||
t.Errorf("ItemID = %d, want 100", pkt.ItemID)
|
||||
}
|
||||
if pkt.Quant != 5 {
|
||||
t.Errorf("Quant = %d, want 5", pkt.Quant)
|
||||
}
|
||||
if pkt.PointCost != 1000 {
|
||||
t.Errorf("PointCost = %d, want 1000", pkt.PointCost)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAcquireCafeItemParseUint32PointCost documents the current behavior.
|
||||
//
|
||||
// CURRENT BEHAVIOR: Always reads PointCost as uint32.
|
||||
//
|
||||
// EXPECTED BEHAVIOR AFTER FIX (commit 3d0114c):
|
||||
// - G6+: Read PointCost as uint32
|
||||
// - G1-G5.2: Read PointCost as uint16
|
||||
//
|
||||
// This test verifies current uint32 parsing works correctly.
|
||||
// After the fix is applied, this test should still pass for G6+ clients.
|
||||
func TestMsgMhfAcquireCafeItemParseUint32PointCost(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pointCost uint32
|
||||
wantCost uint32
|
||||
}{
|
||||
{"small cost", 100, 100},
|
||||
{"medium cost", 5000, 5000},
|
||||
{"large cost exceeding uint16", 70000, 70000},
|
||||
{"max uint32", 0xFFFFFFFF, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAAAABBBB) // AckHandle
|
||||
bf.WriteUint16(1) // ItemType
|
||||
bf.WriteUint16(200) // ItemID
|
||||
bf.WriteUint16(10) // Quant
|
||||
bf.WriteUint32(tt.pointCost)
|
||||
bf.WriteUint16(0) // Unk0
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireCafeItem{}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
err := pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.PointCost != tt.wantCost {
|
||||
t.Errorf("PointCost = %d, want %d", pkt.PointCost, tt.wantCost)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAcquireCafeItemParseFieldOrder verifies the exact field order in parsing.
|
||||
// This is important because the fix changes when PointCost is read (uint16 vs uint32).
|
||||
func TestMsgMhfAcquireCafeItemParseFieldOrder(t *testing.T) {
|
||||
// Build a packet with known values
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x11223344) // AckHandle (offset 0-3)
|
||||
bf.WriteUint16(0x5566) // ItemType (offset 4-5)
|
||||
bf.WriteUint16(0x7788) // ItemID (offset 6-7)
|
||||
bf.WriteUint16(0x99AA) // Quant (offset 8-9)
|
||||
bf.WriteUint32(0xBBCCDDEE) // PointCost (offset 10-13)
|
||||
bf.WriteUint16(0xFF00) // Unk0 (offset 14-15)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireCafeItem{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x11223344 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x11223344", pkt.AckHandle)
|
||||
}
|
||||
if pkt.ItemType != 0x5566 {
|
||||
t.Errorf("ItemType = 0x%X, want 0x5566", pkt.ItemType)
|
||||
}
|
||||
if pkt.ItemID != 0x7788 {
|
||||
t.Errorf("ItemID = 0x%X, want 0x7788", pkt.ItemID)
|
||||
}
|
||||
if pkt.Quant != 0x99AA {
|
||||
t.Errorf("Quant = 0x%X, want 0x99AA", pkt.Quant)
|
||||
}
|
||||
if pkt.PointCost != 0xBBCCDDEE {
|
||||
t.Errorf("PointCost = 0x%X, want 0xBBCCDDEE", pkt.PointCost)
|
||||
}
|
||||
if pkt.Unk0 != 0xFF00 {
|
||||
t.Errorf("Unk0 = 0x%X, want 0xFF00", pkt.Unk0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireCafeItemBuildNotImplemented(t *testing.T) {
|
||||
pkt := &MsgMhfAcquireCafeItem{
|
||||
AckHandle: 123,
|
||||
ItemType: 1,
|
||||
ItemID: 100,
|
||||
Quant: 5,
|
||||
PointCost: 1000,
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
err := pkt.Build(bf, ctx)
|
||||
if err == nil {
|
||||
t.Error("Build() should return error (NOT IMPLEMENTED)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireCafeItemFromOpcode(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_MHF_ACQUIRE_CAFE_ITEM)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_MHF_ACQUIRE_CAFE_ITEM) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_MHF_ACQUIRE_CAFE_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ACQUIRE_CAFE_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
263
network/mhfpacket/msg_mhf_acquire_test.go
Normal file
263
network/mhfpacket/msg_mhf_acquire_test.go
Normal file
@@ -0,0 +1,263 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
func TestAcquirePacketOpcodes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
expect network.PacketID
|
||||
}{
|
||||
{"MsgMhfAcquireGuildTresure", &MsgMhfAcquireGuildTresure{}, network.MSG_MHF_ACQUIRE_GUILD_TRESURE},
|
||||
{"MsgMhfAcquireTitle", &MsgMhfAcquireTitle{}, network.MSG_MHF_ACQUIRE_TITLE},
|
||||
{"MsgMhfAcquireDistItem", &MsgMhfAcquireDistItem{}, network.MSG_MHF_ACQUIRE_DIST_ITEM},
|
||||
{"MsgMhfAcquireMonthlyItem", &MsgMhfAcquireMonthlyItem{}, network.MSG_MHF_ACQUIRE_MONTHLY_ITEM},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.pkt.Opcode(); got != tt.expect {
|
||||
t.Errorf("Opcode() = %v, want %v", got, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireGuildTresureParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
huntID uint32
|
||||
unk bool
|
||||
}{
|
||||
{"basic acquisition", 1, 12345, false},
|
||||
{"large hunt ID", 0xABCDEF12, 0xFFFFFFFF, true},
|
||||
{"zero values", 0, 0, false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint32(tt.huntID)
|
||||
bf.WriteBool(tt.unk)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireGuildTresure{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.HuntID != tt.huntID {
|
||||
t.Errorf("HuntID = %d, want %d", pkt.HuntID, tt.huntID)
|
||||
}
|
||||
if pkt.Unk != tt.unk {
|
||||
t.Errorf("Unk = %v, want %v", pkt.Unk, tt.unk)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireTitleParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
titleIDs []uint16
|
||||
}{
|
||||
{"acquire title 1", 1, []uint16{1}},
|
||||
{"acquire titles 100 200", 0x12345678, []uint16{100, 200}},
|
||||
{"no titles", 0xFFFFFFFF, []uint16{}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint16(uint16(len(tt.titleIDs))) // count
|
||||
bf.WriteUint16(0) // zeroed
|
||||
for _, id := range tt.titleIDs {
|
||||
bf.WriteUint16(id)
|
||||
}
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireTitle{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if len(pkt.TitleIDs) != len(tt.titleIDs) {
|
||||
t.Errorf("TitleIDs len = %d, want %d", len(pkt.TitleIDs), len(tt.titleIDs))
|
||||
}
|
||||
for i, id := range tt.titleIDs {
|
||||
if i < len(pkt.TitleIDs) && pkt.TitleIDs[i] != id {
|
||||
t.Errorf("TitleIDs[%d] = %d, want %d", i, pkt.TitleIDs[i], id)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireDistItemParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
distributionType uint8
|
||||
distributionID uint32
|
||||
}{
|
||||
{"type 0", 1, 0, 12345},
|
||||
{"type 1", 0xABCD, 1, 67890},
|
||||
{"max values", 0xFFFFFFFF, 0xFF, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint8(tt.distributionType)
|
||||
bf.WriteUint32(tt.distributionID)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireDistItem{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.DistributionType != tt.distributionType {
|
||||
t.Errorf("DistributionType = %d, want %d", pkt.DistributionType, tt.distributionType)
|
||||
}
|
||||
if pkt.DistributionID != tt.distributionID {
|
||||
t.Errorf("DistributionID = %d, want %d", pkt.DistributionID, tt.distributionID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfAcquireMonthlyItemParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
unk0 uint8
|
||||
unk1 uint8
|
||||
unk2 uint16
|
||||
unk3 uint32
|
||||
}{
|
||||
{"basic", 1, 0, 0, 0, 0},
|
||||
{"with values", 100, 10, 20, 30, 40},
|
||||
{"max values", 0xFFFFFFFF, 0xFF, 0xFF, 0xFFFF, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint8(tt.unk0)
|
||||
bf.WriteUint8(tt.unk1)
|
||||
bf.WriteUint16(tt.unk2)
|
||||
bf.WriteUint32(tt.unk3)
|
||||
bf.WriteUint32(0) // Zeroed (consumed by Parse)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireMonthlyItem{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.Unk0 != tt.unk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.unk0)
|
||||
}
|
||||
if pkt.Unk1 != tt.unk1 {
|
||||
t.Errorf("Unk1 = %d, want %d", pkt.Unk1, tt.unk1)
|
||||
}
|
||||
if pkt.Unk2 != tt.unk2 {
|
||||
t.Errorf("Unk2 = %d, want %d", pkt.Unk2, tt.unk2)
|
||||
}
|
||||
if pkt.Unk3 != tt.unk3 {
|
||||
t.Errorf("Unk3 = %d, want %d", pkt.Unk3, tt.unk3)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcquirePacketsFromOpcode(t *testing.T) {
|
||||
acquireOpcodes := []network.PacketID{
|
||||
network.MSG_MHF_ACQUIRE_GUILD_TRESURE,
|
||||
network.MSG_MHF_ACQUIRE_TITLE,
|
||||
network.MSG_MHF_ACQUIRE_DIST_ITEM,
|
||||
network.MSG_MHF_ACQUIRE_MONTHLY_ITEM,
|
||||
}
|
||||
|
||||
for _, opcode := range acquireOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Fatalf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
if pkt.Opcode() != opcode {
|
||||
t.Errorf("Opcode() = %s, want %s", pkt.Opcode(), opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcquirePacketEdgeCases(t *testing.T) {
|
||||
t.Run("guild tresure with max hunt ID", func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint32(0xFFFFFFFF)
|
||||
bf.WriteBool(true)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireGuildTresure{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.HuntID != 0xFFFFFFFF {
|
||||
t.Errorf("HuntID = %d, want %d", pkt.HuntID, 0xFFFFFFFF)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("dist item with all types", func(t *testing.T) {
|
||||
for i := uint8(0); i < 5; i++ {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint8(i)
|
||||
bf.WriteUint32(12345)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAcquireDistItem{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v for type %d", err, i)
|
||||
}
|
||||
|
||||
if pkt.DistributionType != i {
|
||||
t.Errorf("DistributionType = %d, want %d", pkt.DistributionType, i)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
364
network/mhfpacket/msg_mhf_guacot_test.go
Normal file
364
network/mhfpacket/msg_mhf_guacot_test.go
Normal file
@@ -0,0 +1,364 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
)
|
||||
|
||||
func TestMsgMhfUpdateGuacotOpcode_Guacot(t *testing.T) {
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
if pkt.Opcode() != network.MSG_MHF_UPDATE_GUACOT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_UPDATE_GUACOT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfEnumerateGuacotOpcode_Guacot(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_GUACOT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_GUACOT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_SingleEntry(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
// Goocoo entry
|
||||
bf.WriteUint32(2) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(int16(i + 1)) // Data1
|
||||
}
|
||||
bf.WriteUint32(100) // Data2[0]
|
||||
bf.WriteUint32(200) // Data2[1]
|
||||
bf.WriteUint8(5) // Name length
|
||||
bf.WriteBytes([]byte("Porky"))
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if pkt.EntryCount != 1 {
|
||||
t.Errorf("EntryCount = %d, want 1", pkt.EntryCount)
|
||||
}
|
||||
if len(pkt.Goocoos) != 1 {
|
||||
t.Fatalf("len(Goocoos) = %d, want 1", len(pkt.Goocoos))
|
||||
}
|
||||
|
||||
g := pkt.Goocoos[0]
|
||||
if g.Index != 2 {
|
||||
t.Errorf("Index = %d, want 2", g.Index)
|
||||
}
|
||||
if len(g.Data1) != 22 {
|
||||
t.Fatalf("len(Data1) = %d, want 22", len(g.Data1))
|
||||
}
|
||||
for i := 0; i < 22; i++ {
|
||||
if g.Data1[i] != int16(i+1) {
|
||||
t.Errorf("Data1[%d] = %d, want %d", i, g.Data1[i], i+1)
|
||||
}
|
||||
}
|
||||
if len(g.Data2) != 2 {
|
||||
t.Fatalf("len(Data2) = %d, want 2", len(g.Data2))
|
||||
}
|
||||
if g.Data2[0] != 100 {
|
||||
t.Errorf("Data2[0] = %d, want 100", g.Data2[0])
|
||||
}
|
||||
if g.Data2[1] != 200 {
|
||||
t.Errorf("Data2[1] = %d, want 200", g.Data2[1])
|
||||
}
|
||||
if string(g.Name) != "Porky" {
|
||||
t.Errorf("Name = %q, want %q", string(g.Name), "Porky")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_MultipleEntries(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(3) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
for idx := uint32(0); idx < 3; idx++ {
|
||||
bf.WriteUint32(idx) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(int16(idx*100 + uint32(i)))
|
||||
}
|
||||
bf.WriteUint32(idx * 10) // Data2[0]
|
||||
bf.WriteUint32(idx * 20) // Data2[1]
|
||||
name := []byte("Pog")
|
||||
bf.WriteUint8(uint8(len(name)))
|
||||
bf.WriteBytes(name)
|
||||
}
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if len(pkt.Goocoos) != 3 {
|
||||
t.Fatalf("len(Goocoos) = %d, want 3", len(pkt.Goocoos))
|
||||
}
|
||||
for idx := uint32(0); idx < 3; idx++ {
|
||||
g := pkt.Goocoos[idx]
|
||||
if g.Index != idx {
|
||||
t.Errorf("Goocoos[%d].Index = %d, want %d", idx, g.Index, idx)
|
||||
}
|
||||
if g.Data1[0] != int16(idx*100) {
|
||||
t.Errorf("Goocoos[%d].Data1[0] = %d, want %d", idx, g.Data1[0], idx*100)
|
||||
}
|
||||
if g.Data2[0] != idx*10 {
|
||||
t.Errorf("Goocoos[%d].Data2[0] = %d, want %d", idx, g.Data2[0], idx*10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_ZeroEntries(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(42) // AckHandle
|
||||
bf.WriteUint16(0) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if pkt.EntryCount != 0 {
|
||||
t.Errorf("EntryCount = %d, want 0", pkt.EntryCount)
|
||||
}
|
||||
if len(pkt.Goocoos) != 0 {
|
||||
t.Errorf("len(Goocoos) = %d, want 0", len(pkt.Goocoos))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_DeletionEntry(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
bf.WriteUint32(0) // Index
|
||||
// Data1[0] = 0 signals deletion
|
||||
bf.WriteInt16(0)
|
||||
for i := 1; i < 22; i++ {
|
||||
bf.WriteInt16(0)
|
||||
}
|
||||
bf.WriteUint32(0) // Data2[0]
|
||||
bf.WriteUint32(0) // Data2[1]
|
||||
bf.WriteUint8(0) // Empty name
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
g := pkt.Goocoos[0]
|
||||
if g.Data1[0] != 0 {
|
||||
t.Errorf("Data1[0] = %d, want 0 (deletion marker)", g.Data1[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotParse_EmptyName(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
bf.WriteUint32(0) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(1)
|
||||
}
|
||||
bf.WriteUint32(0) // Data2[0]
|
||||
bf.WriteUint32(0) // Data2[1]
|
||||
bf.WriteUint8(0) // Empty name
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if len(pkt.Goocoos[0].Name) != 0 {
|
||||
t.Errorf("Name length = %d, want 0", len(pkt.Goocoos[0].Name))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfEnumerateGuacotParse(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(0) // Unk0
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error: %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Unk0 != 0 {
|
||||
t.Errorf("Unk0 = %d, want 0", pkt.Unk0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfUpdateGuacotBuild_NotImplemented(t *testing.T) {
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
err := pkt.Build(byteframe.NewByteFrame(), nil)
|
||||
if err == nil {
|
||||
t.Error("Build() should return error (not implemented)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgMhfEnumerateGuacotBuild_NotImplemented(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
err := pkt.Build(byteframe.NewByteFrame(), nil)
|
||||
if err == nil {
|
||||
t.Error("Build() should return error (not implemented)")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoocooStruct_Data1Size(t *testing.T) {
|
||||
// Verify 22 int16 entries = 44 bytes of outfit/appearance data
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint16(1) // EntryCount
|
||||
bf.WriteUint16(0) // Zeroed
|
||||
|
||||
bf.WriteUint32(0) // Index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(int16(i * 3))
|
||||
}
|
||||
bf.WriteUint32(0xDEAD) // Data2[0]
|
||||
bf.WriteUint32(0xBEEF) // Data2[1]
|
||||
bf.WriteUint8(0) // No name
|
||||
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
bf.Seek(0, 0)
|
||||
_ = pkt.Parse(bf, nil)
|
||||
|
||||
g := pkt.Goocoos[0]
|
||||
|
||||
// Verify all 22 data slots are correctly read
|
||||
for i := 0; i < 22; i++ {
|
||||
expected := int16(i * 3)
|
||||
if g.Data1[i] != expected {
|
||||
t.Errorf("Data1[%d] = %d, want %d", i, g.Data1[i], expected)
|
||||
}
|
||||
}
|
||||
|
||||
if g.Data2[0] != 0xDEAD {
|
||||
t.Errorf("Data2[0] = 0x%X, want 0xDEAD", g.Data2[0])
|
||||
}
|
||||
if g.Data2[1] != 0xBEEF {
|
||||
t.Errorf("Data2[1] = 0x%X, want 0xBEEF", g.Data2[1])
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoocooSerialization_Roundtrip(t *testing.T) {
|
||||
// Simulate what handleMsgMhfUpdateGuacot does when saving to DB
|
||||
goocoo := Goocoo{
|
||||
Index: 1,
|
||||
Data1: make([]int16, 22),
|
||||
Data2: []uint32{0x1234, 0x5678},
|
||||
Name: []byte("MyPoogie"),
|
||||
}
|
||||
goocoo.Data1[0] = 5 // outfit type (non-zero = exists)
|
||||
goocoo.Data1[1] = 100 // some appearance data
|
||||
goocoo.Data1[21] = -50 // test negative int16
|
||||
|
||||
// Serialize (matches handler logic)
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(goocoo.Index)
|
||||
for i := range goocoo.Data1 {
|
||||
bf.WriteInt16(goocoo.Data1[i])
|
||||
}
|
||||
for i := range goocoo.Data2 {
|
||||
bf.WriteUint32(goocoo.Data2[i])
|
||||
}
|
||||
bf.WriteUint8(uint8(len(goocoo.Name)))
|
||||
bf.WriteBytes(goocoo.Name)
|
||||
|
||||
// Deserialize and verify
|
||||
data := bf.Data()
|
||||
rbf := byteframe.NewByteFrameFromBytes(data)
|
||||
|
||||
index := rbf.ReadUint32()
|
||||
if index != 1 {
|
||||
t.Errorf("index = %d, want 1", index)
|
||||
}
|
||||
|
||||
data1_0 := rbf.ReadInt16()
|
||||
if data1_0 != 5 {
|
||||
t.Errorf("data1[0] = %d, want 5", data1_0)
|
||||
}
|
||||
data1_1 := rbf.ReadInt16()
|
||||
if data1_1 != 100 {
|
||||
t.Errorf("data1[1] = %d, want 100", data1_1)
|
||||
}
|
||||
// Skip to data1[21]
|
||||
for i := 2; i < 21; i++ {
|
||||
rbf.ReadInt16()
|
||||
}
|
||||
data1_21 := rbf.ReadInt16()
|
||||
if data1_21 != -50 {
|
||||
t.Errorf("data1[21] = %d, want -50", data1_21)
|
||||
}
|
||||
|
||||
d2_0 := rbf.ReadUint32()
|
||||
if d2_0 != 0x1234 {
|
||||
t.Errorf("data2[0] = 0x%X, want 0x1234", d2_0)
|
||||
}
|
||||
d2_1 := rbf.ReadUint32()
|
||||
if d2_1 != 0x5678 {
|
||||
t.Errorf("data2[1] = 0x%X, want 0x5678", d2_1)
|
||||
}
|
||||
|
||||
nameLen := rbf.ReadUint8()
|
||||
if nameLen != 8 {
|
||||
t.Errorf("nameLen = %d, want 8", nameLen)
|
||||
}
|
||||
name := rbf.ReadBytes(uint(nameLen))
|
||||
if string(name) != "MyPoogie" {
|
||||
t.Errorf("name = %q, want %q", string(name), "MyPoogie")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGoocooEntrySize(t *testing.T) {
|
||||
// Each goocoo entry in the packet should be:
|
||||
// 4 (index) + 22*2 (data1) + 2*4 (data2) + 1 (name len) + N (name)
|
||||
// = 4 + 44 + 8 + 1 + N = 57 + N bytes
|
||||
name := []byte("Test")
|
||||
expectedSize := 4 + 44 + 8 + 1 + len(name)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0) // index
|
||||
for i := 0; i < 22; i++ {
|
||||
bf.WriteInt16(0)
|
||||
}
|
||||
bf.WriteUint32(0) // data2[0]
|
||||
bf.WriteUint32(0) // data2[1]
|
||||
bf.WriteUint8(uint8(len(name))) // name len
|
||||
bf.WriteBytes(name)
|
||||
|
||||
if len(bf.Data()) != expectedSize {
|
||||
t.Errorf("entry size = %d bytes, want %d bytes (57 + %d name)", len(bf.Data()), expectedSize, len(name))
|
||||
}
|
||||
}
|
||||
537
network/mhfpacket/msg_mhf_packets_test.go
Normal file
537
network/mhfpacket/msg_mhf_packets_test.go
Normal file
@@ -0,0 +1,537 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// TestMsgMhfSavedataParse tests parsing MsgMhfSavedata
|
||||
func TestMsgMhfSavedataParse(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_MHF_SAVEDATA)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_MHF_SAVEDATA) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_MHF_SAVEDATA {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SAVEDATA", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfLoaddataParse tests parsing MsgMhfLoaddata
|
||||
func TestMsgMhfLoaddataParse(t *testing.T) {
|
||||
pkt := FromOpcode(network.MSG_MHF_LOADDATA)
|
||||
if pkt == nil {
|
||||
t.Fatal("FromOpcode(MSG_MHF_LOADDATA) returned nil")
|
||||
}
|
||||
if pkt.Opcode() != network.MSG_MHF_LOADDATA {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_LOADDATA", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfListMemberOpcode tests MsgMhfListMember Opcode
|
||||
func TestMsgMhfListMemberOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfListMember{}
|
||||
if pkt.Opcode() != network.MSG_MHF_LIST_MEMBER {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_LIST_MEMBER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfOprMemberOpcode tests MsgMhfOprMember Opcode
|
||||
func TestMsgMhfOprMemberOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfOprMember{}
|
||||
if pkt.Opcode() != network.MSG_MHF_OPR_MEMBER {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_OPR_MEMBER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateDistItemOpcode tests MsgMhfEnumerateDistItem Opcode
|
||||
func TestMsgMhfEnumerateDistItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateDistItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_DIST_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_DIST_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfApplyDistItemOpcode tests MsgMhfApplyDistItem Opcode
|
||||
func TestMsgMhfApplyDistItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfApplyDistItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_APPLY_DIST_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_APPLY_DIST_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAcquireDistItemOpcode tests MsgMhfAcquireDistItem Opcode
|
||||
func TestMsgMhfAcquireDistItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfAcquireDistItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ACQUIRE_DIST_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ACQUIRE_DIST_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfGetDistDescriptionOpcode tests MsgMhfGetDistDescription Opcode
|
||||
func TestMsgMhfGetDistDescriptionOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfGetDistDescription{}
|
||||
if pkt.Opcode() != network.MSG_MHF_GET_DIST_DESCRIPTION {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_GET_DIST_DESCRIPTION", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfSendMailOpcode tests MsgMhfSendMail Opcode
|
||||
func TestMsgMhfSendMailOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfSendMail{}
|
||||
if pkt.Opcode() != network.MSG_MHF_SEND_MAIL {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SEND_MAIL", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfReadMailOpcode tests MsgMhfReadMail Opcode
|
||||
func TestMsgMhfReadMailOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfReadMail{}
|
||||
if pkt.Opcode() != network.MSG_MHF_READ_MAIL {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_READ_MAIL", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfListMailOpcode tests MsgMhfListMail Opcode
|
||||
func TestMsgMhfListMailOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfListMail{}
|
||||
if pkt.Opcode() != network.MSG_MHF_LIST_MAIL {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_LIST_MAIL", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfOprtMailOpcode tests MsgMhfOprtMail Opcode
|
||||
func TestMsgMhfOprtMailOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfOprtMail{}
|
||||
if pkt.Opcode() != network.MSG_MHF_OPRT_MAIL {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_OPRT_MAIL", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfLoadFavoriteQuestOpcode tests MsgMhfLoadFavoriteQuest Opcode
|
||||
func TestMsgMhfLoadFavoriteQuestOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfLoadFavoriteQuest{}
|
||||
if pkt.Opcode() != network.MSG_MHF_LOAD_FAVORITE_QUEST {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_LOAD_FAVORITE_QUEST", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfSaveFavoriteQuestOpcode tests MsgMhfSaveFavoriteQuest Opcode
|
||||
func TestMsgMhfSaveFavoriteQuestOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfSaveFavoriteQuest{}
|
||||
if pkt.Opcode() != network.MSG_MHF_SAVE_FAVORITE_QUEST {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SAVE_FAVORITE_QUEST", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfRegisterEventOpcode tests MsgMhfRegisterEvent Opcode
|
||||
func TestMsgMhfRegisterEventOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfRegisterEvent{}
|
||||
if pkt.Opcode() != network.MSG_MHF_REGISTER_EVENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_REGISTER_EVENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfReleaseEventOpcode tests MsgMhfReleaseEvent Opcode
|
||||
func TestMsgMhfReleaseEventOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfReleaseEvent{}
|
||||
if pkt.Opcode() != network.MSG_MHF_RELEASE_EVENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_RELEASE_EVENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfTransitMessageOpcode tests MsgMhfTransitMessage Opcode
|
||||
func TestMsgMhfTransitMessageOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfTransitMessage{}
|
||||
if pkt.Opcode() != network.MSG_MHF_TRANSIT_MESSAGE {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_TRANSIT_MESSAGE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfPresentBoxOpcode tests MsgMhfPresentBox Opcode
|
||||
func TestMsgMhfPresentBoxOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfPresentBox{}
|
||||
if pkt.Opcode() != network.MSG_MHF_PRESENT_BOX {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_PRESENT_BOX", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfServerCommandOpcode tests MsgMhfServerCommand Opcode
|
||||
func TestMsgMhfServerCommandOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfServerCommand{}
|
||||
if pkt.Opcode() != network.MSG_MHF_SERVER_COMMAND {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SERVER_COMMAND", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfShutClientOpcode tests MsgMhfShutClient Opcode
|
||||
func TestMsgMhfShutClientOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfShutClient{}
|
||||
if pkt.Opcode() != network.MSG_MHF_SHUT_CLIENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SHUT_CLIENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAnnounceOpcode tests MsgMhfAnnounce Opcode
|
||||
func TestMsgMhfAnnounceOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfAnnounce{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ANNOUNCE {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ANNOUNCE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfSetLoginwindowOpcode tests MsgMhfSetLoginwindow Opcode
|
||||
func TestMsgMhfSetLoginwindowOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfSetLoginwindow{}
|
||||
if pkt.Opcode() != network.MSG_MHF_SET_LOGINWINDOW {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SET_LOGINWINDOW", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfGetCaUniqueIDOpcode tests MsgMhfGetCaUniqueID Opcode
|
||||
func TestMsgMhfGetCaUniqueIDOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfGetCaUniqueID{}
|
||||
if pkt.Opcode() != network.MSG_MHF_GET_CA_UNIQUE_ID {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_GET_CA_UNIQUE_ID", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfSetCaAchievementOpcode tests MsgMhfSetCaAchievement Opcode
|
||||
func TestMsgMhfSetCaAchievementOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfSetCaAchievement{}
|
||||
if pkt.Opcode() != network.MSG_MHF_SET_CA_ACHIEVEMENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_SET_CA_ACHIEVEMENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCaravanMyScoreOpcode tests MsgMhfCaravanMyScore Opcode
|
||||
func TestMsgMhfCaravanMyScoreOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCaravanMyScore{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CARAVAN_MY_SCORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CARAVAN_MY_SCORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCaravanRankingOpcode tests MsgMhfCaravanRanking Opcode
|
||||
func TestMsgMhfCaravanRankingOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCaravanRanking{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CARAVAN_RANKING {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CARAVAN_RANKING", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCaravanMyRankOpcode tests MsgMhfCaravanMyRank Opcode
|
||||
func TestMsgMhfCaravanMyRankOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCaravanMyRank{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CARAVAN_MY_RANK {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CARAVAN_MY_RANK", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateQuestOpcode tests MsgMhfEnumerateQuest Opcode
|
||||
func TestMsgMhfEnumerateQuestOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateQuest{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_QUEST {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_QUEST", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateEventOpcode tests MsgMhfEnumerateEvent Opcode
|
||||
func TestMsgMhfEnumerateEventOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateEvent{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_EVENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_EVENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumeratePriceOpcode tests MsgMhfEnumeratePrice Opcode
|
||||
func TestMsgMhfEnumeratePriceOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumeratePrice{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_PRICE {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_PRICE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateRankingOpcode tests MsgMhfEnumerateRanking Opcode
|
||||
func TestMsgMhfEnumerateRankingOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateRanking{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_RANKING {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_RANKING", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateOrderOpcode tests MsgMhfEnumerateOrder Opcode
|
||||
func TestMsgMhfEnumerateOrderOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateOrder{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_ORDER {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_ORDER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateShopOpcode tests MsgMhfEnumerateShop Opcode
|
||||
func TestMsgMhfEnumerateShopOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateShop{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_SHOP {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_SHOP", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfGetExtraInfoOpcode tests MsgMhfGetExtraInfo Opcode
|
||||
func TestMsgMhfGetExtraInfoOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfGetExtraInfo{}
|
||||
if pkt.Opcode() != network.MSG_MHF_GET_EXTRA_INFO {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_GET_EXTRA_INFO", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateItemOpcode tests MsgMhfEnumerateItem Opcode
|
||||
func TestMsgMhfEnumerateItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAcquireItemOpcode tests MsgMhfAcquireItem Opcode
|
||||
func TestMsgMhfAcquireItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfAcquireItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ACQUIRE_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ACQUIRE_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfTransferItemOpcode tests MsgMhfTransferItem Opcode
|
||||
func TestMsgMhfTransferItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfTransferItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_TRANSFER_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_TRANSFER_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEntryRookieGuildOpcode tests MsgMhfEntryRookieGuild Opcode
|
||||
func TestMsgMhfEntryRookieGuildOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEntryRookieGuild{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENTRY_ROOKIE_GUILD {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENTRY_ROOKIE_GUILD", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgCaExchangeItemOpcode tests MsgCaExchangeItem Opcode
|
||||
func TestMsgCaExchangeItemOpcode(t *testing.T) {
|
||||
pkt := &MsgCaExchangeItem{}
|
||||
if pkt.Opcode() != network.MSG_CA_EXCHANGE_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_CA_EXCHANGE_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateCampaignOpcode tests MsgMhfEnumerateCampaign Opcode
|
||||
func TestMsgMhfEnumerateCampaignOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateCampaign{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_CAMPAIGN {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_CAMPAIGN", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfStateCampaignOpcode tests MsgMhfStateCampaign Opcode
|
||||
func TestMsgMhfStateCampaignOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfStateCampaign{}
|
||||
if pkt.Opcode() != network.MSG_MHF_STATE_CAMPAIGN {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_STATE_CAMPAIGN", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfApplyCampaignOpcode tests MsgMhfApplyCampaign Opcode
|
||||
func TestMsgMhfApplyCampaignOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfApplyCampaign{}
|
||||
if pkt.Opcode() != network.MSG_MHF_APPLY_CAMPAIGN {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_APPLY_CAMPAIGN", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCreateJointOpcode tests MsgMhfCreateJoint Opcode
|
||||
func TestMsgMhfCreateJointOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCreateJoint{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CREATE_JOINT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CREATE_JOINT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfOperateJointOpcode tests MsgMhfOperateJoint Opcode
|
||||
func TestMsgMhfOperateJointOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfOperateJoint{}
|
||||
if pkt.Opcode() != network.MSG_MHF_OPERATE_JOINT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_OPERATE_JOINT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfInfoJointOpcode tests MsgMhfInfoJoint Opcode
|
||||
func TestMsgMhfInfoJointOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfInfoJoint{}
|
||||
if pkt.Opcode() != network.MSG_MHF_INFO_JOINT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_INFO_JOINT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfGetCogInfoOpcode tests MsgMhfGetCogInfo Opcode
|
||||
func TestMsgMhfGetCogInfoOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfGetCogInfo{}
|
||||
if pkt.Opcode() != network.MSG_MHF_GET_COG_INFO {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_GET_COG_INFO", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCheckMonthlyItemOpcode tests MsgMhfCheckMonthlyItem Opcode
|
||||
func TestMsgMhfCheckMonthlyItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCheckMonthlyItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CHECK_MONTHLY_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CHECK_MONTHLY_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAcquireMonthlyItemOpcode tests MsgMhfAcquireMonthlyItem Opcode
|
||||
func TestMsgMhfAcquireMonthlyItemOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfAcquireMonthlyItem{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ACQUIRE_MONTHLY_ITEM {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ACQUIRE_MONTHLY_ITEM", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCheckWeeklyStampOpcode tests MsgMhfCheckWeeklyStamp Opcode
|
||||
func TestMsgMhfCheckWeeklyStampOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCheckWeeklyStamp{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CHECK_WEEKLY_STAMP {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CHECK_WEEKLY_STAMP", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfExchangeWeeklyStampOpcode tests MsgMhfExchangeWeeklyStamp Opcode
|
||||
func TestMsgMhfExchangeWeeklyStampOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfExchangeWeeklyStamp{}
|
||||
if pkt.Opcode() != network.MSG_MHF_EXCHANGE_WEEKLY_STAMP {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_EXCHANGE_WEEKLY_STAMP", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfCreateMercenaryOpcode tests MsgMhfCreateMercenary Opcode
|
||||
func TestMsgMhfCreateMercenaryOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfCreateMercenary{}
|
||||
if pkt.Opcode() != network.MSG_MHF_CREATE_MERCENARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_CREATE_MERCENARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateMercenaryLogOpcode tests MsgMhfEnumerateMercenaryLog Opcode
|
||||
func TestMsgMhfEnumerateMercenaryLogOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateMercenaryLog{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_MERCENARY_LOG {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_MERCENARY_LOG", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnumerateGuacotOpcode tests MsgMhfEnumerateGuacot Opcode
|
||||
func TestMsgMhfEnumerateGuacotOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnumerateGuacot{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENUMERATE_GUACOT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENUMERATE_GUACOT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfUpdateGuacotOpcode tests MsgMhfUpdateGuacot Opcode
|
||||
func TestMsgMhfUpdateGuacotOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfUpdateGuacot{}
|
||||
if pkt.Opcode() != network.MSG_MHF_UPDATE_GUACOT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_UPDATE_GUACOT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfEnterTournamentQuestOpcode tests MsgMhfEnterTournamentQuest Opcode
|
||||
func TestMsgMhfEnterTournamentQuestOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfEnterTournamentQuest{}
|
||||
if pkt.Opcode() != network.MSG_MHF_ENTER_TOURNAMENT_QUEST {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_ENTER_TOURNAMENT_QUEST", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfResetAchievementOpcode tests MsgMhfResetAchievement Opcode
|
||||
func TestMsgMhfResetAchievementOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfResetAchievement{}
|
||||
if pkt.Opcode() != network.MSG_MHF_RESET_ACHIEVEMENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_RESET_ACHIEVEMENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfPaymentAchievementOpcode tests MsgMhfPaymentAchievement Opcode
|
||||
func TestMsgMhfPaymentAchievementOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfPaymentAchievement{}
|
||||
if pkt.Opcode() != network.MSG_MHF_PAYMENT_ACHIEVEMENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_PAYMENT_ACHIEVEMENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfDisplayedAchievementOpcode tests MsgMhfDisplayedAchievement Opcode
|
||||
func TestMsgMhfDisplayedAchievementOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfDisplayedAchievement{}
|
||||
if pkt.Opcode() != network.MSG_MHF_DISPLAYED_ACHIEVEMENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_DISPLAYED_ACHIEVEMENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfGetBbsSnsStatusOpcode tests MsgMhfGetBbsSnsStatus Opcode
|
||||
func TestMsgMhfGetBbsSnsStatusOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfGetBbsSnsStatus{}
|
||||
if pkt.Opcode() != network.MSG_MHF_GET_BBS_SNS_STATUS {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_GET_BBS_SNS_STATUS", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfApplyBbsArticleOpcode tests MsgMhfApplyBbsArticle Opcode
|
||||
func TestMsgMhfApplyBbsArticleOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfApplyBbsArticle{}
|
||||
if pkt.Opcode() != network.MSG_MHF_APPLY_BBS_ARTICLE {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_APPLY_BBS_ARTICLE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfGetEtcPointsOpcode tests MsgMhfGetEtcPoints Opcode
|
||||
func TestMsgMhfGetEtcPointsOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfGetEtcPoints{}
|
||||
if pkt.Opcode() != network.MSG_MHF_GET_ETC_POINTS {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_GET_ETC_POINTS", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfUpdateEtcPointOpcode tests MsgMhfUpdateEtcPoint Opcode
|
||||
func TestMsgMhfUpdateEtcPointOpcode(t *testing.T) {
|
||||
pkt := &MsgMhfUpdateEtcPoint{}
|
||||
if pkt.Opcode() != network.MSG_MHF_UPDATE_ETC_POINT {
|
||||
t.Errorf("Opcode() = %s, want MSG_MHF_UPDATE_ETC_POINT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestAchievementPacketParse tests simple achievement packet parsing
|
||||
func TestAchievementPacketParse(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(5) // AchievementID
|
||||
bf.WriteUint16(100) // Unk1
|
||||
bf.WriteUint16(200) // Unk2
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAddAchievement{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AchievementID != 5 {
|
||||
t.Errorf("AchievementID = %d, want 5", pkt.AchievementID)
|
||||
}
|
||||
if pkt.Unk1 != 100 {
|
||||
t.Errorf("Unk1 = %d, want 100", pkt.Unk1)
|
||||
}
|
||||
if pkt.Unk2 != 200 {
|
||||
t.Errorf("Unk2 = %d, want 200", pkt.Unk2)
|
||||
}
|
||||
}
|
||||
301
network/mhfpacket/msg_opcode_coverage_test.go
Normal file
301
network/mhfpacket/msg_opcode_coverage_test.go
Normal file
@@ -0,0 +1,301 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// callBuildSafe calls Build on the packet, recovering from panics.
|
||||
// Returns the error from Build, or nil if it panicked (panic is acceptable
|
||||
// for "Not implemented" stubs).
|
||||
func callBuildSafe(pkt MHFPacket, bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) (err error, panicked bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panicked = true
|
||||
}
|
||||
}()
|
||||
err = pkt.Build(bf, ctx)
|
||||
return err, false
|
||||
}
|
||||
|
||||
// callParseSafe calls Parse on the packet, recovering from panics.
|
||||
func callParseSafe(pkt MHFPacket, bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) (err error, panicked bool) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
panicked = true
|
||||
}
|
||||
}()
|
||||
err = pkt.Parse(bf, ctx)
|
||||
return err, false
|
||||
}
|
||||
|
||||
// TestBuildCoverage_NotImplemented exercises Build() on packet types whose Build
|
||||
// method is not yet covered. These stubs either return errors.New("NOT IMPLEMENTED")
|
||||
// or panic("Not implemented"). Both are acceptable outcomes that indicate the
|
||||
// method was reached.
|
||||
func TestBuildCoverage_NotImplemented(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
}{
|
||||
// msg_ca_exchange_item.go
|
||||
{"MsgCaExchangeItem", &MsgCaExchangeItem{}},
|
||||
// msg_head.go
|
||||
{"MsgHead", &MsgHead{}},
|
||||
// msg_mhf_acquire_cafe_item.go
|
||||
{"MsgMhfAcquireCafeItem", &MsgMhfAcquireCafeItem{}},
|
||||
// msg_mhf_acquire_monthly_item.go
|
||||
{"MsgMhfAcquireMonthlyItem", &MsgMhfAcquireMonthlyItem{}},
|
||||
// msg_mhf_acquire_ud_item.go
|
||||
{"MsgMhfAcquireUdItem", &MsgMhfAcquireUdItem{}},
|
||||
// msg_mhf_announce.go
|
||||
{"MsgMhfAnnounce", &MsgMhfAnnounce{}},
|
||||
// msg_mhf_check_monthly_item.go
|
||||
{"MsgMhfCheckMonthlyItem", &MsgMhfCheckMonthlyItem{}},
|
||||
// msg_mhf_check_weekly_stamp.go
|
||||
{"MsgMhfCheckWeeklyStamp", &MsgMhfCheckWeeklyStamp{}},
|
||||
// msg_mhf_enumerate_festa_member.go
|
||||
{"MsgMhfEnumerateFestaMember", &MsgMhfEnumerateFestaMember{}},
|
||||
// msg_mhf_enumerate_inv_guild.go
|
||||
{"MsgMhfEnumerateInvGuild", &MsgMhfEnumerateInvGuild{}},
|
||||
// msg_mhf_enumerate_item.go
|
||||
{"MsgMhfEnumerateItem", &MsgMhfEnumerateItem{}},
|
||||
// msg_mhf_enumerate_order.go
|
||||
{"MsgMhfEnumerateOrder", &MsgMhfEnumerateOrder{}},
|
||||
// msg_mhf_enumerate_quest.go
|
||||
{"MsgMhfEnumerateQuest", &MsgMhfEnumerateQuest{}},
|
||||
// msg_mhf_enumerate_ranking.go
|
||||
{"MsgMhfEnumerateRanking", &MsgMhfEnumerateRanking{}},
|
||||
// msg_mhf_enumerate_shop.go
|
||||
{"MsgMhfEnumerateShop", &MsgMhfEnumerateShop{}},
|
||||
// msg_mhf_enumerate_warehouse.go
|
||||
{"MsgMhfEnumerateWarehouse", &MsgMhfEnumerateWarehouse{}},
|
||||
// msg_mhf_exchange_fpoint_2_item.go
|
||||
{"MsgMhfExchangeFpoint2Item", &MsgMhfExchangeFpoint2Item{}},
|
||||
// msg_mhf_exchange_item_2_fpoint.go
|
||||
{"MsgMhfExchangeItem2Fpoint", &MsgMhfExchangeItem2Fpoint{}},
|
||||
// msg_mhf_exchange_weekly_stamp.go
|
||||
{"MsgMhfExchangeWeeklyStamp", &MsgMhfExchangeWeeklyStamp{}},
|
||||
// msg_mhf_generate_ud_guild_map.go
|
||||
{"MsgMhfGenerateUdGuildMap", &MsgMhfGenerateUdGuildMap{}},
|
||||
// msg_mhf_get_boost_time.go
|
||||
{"MsgMhfGetBoostTime", &MsgMhfGetBoostTime{}},
|
||||
// msg_mhf_get_boost_time_limit.go
|
||||
{"MsgMhfGetBoostTimeLimit", &MsgMhfGetBoostTimeLimit{}},
|
||||
// msg_mhf_get_cafe_duration.go
|
||||
{"MsgMhfGetCafeDuration", &MsgMhfGetCafeDuration{}},
|
||||
// msg_mhf_get_cafe_duration_bonus_info.go
|
||||
{"MsgMhfGetCafeDurationBonusInfo", &MsgMhfGetCafeDurationBonusInfo{}},
|
||||
// msg_mhf_get_cog_info.go
|
||||
{"MsgMhfGetCogInfo", &MsgMhfGetCogInfo{}},
|
||||
// msg_mhf_get_gacha_point.go
|
||||
{"MsgMhfGetGachaPoint", &MsgMhfGetGachaPoint{}},
|
||||
// msg_mhf_get_gem_info.go
|
||||
{"MsgMhfGetGemInfo", &MsgMhfGetGemInfo{}},
|
||||
// msg_mhf_get_kiju_info.go
|
||||
{"MsgMhfGetKijuInfo", &MsgMhfGetKijuInfo{}},
|
||||
// msg_mhf_get_myhouse_info.go
|
||||
{"MsgMhfGetMyhouseInfo", &MsgMhfGetMyhouseInfo{}},
|
||||
// msg_mhf_get_notice.go
|
||||
{"MsgMhfGetNotice", &MsgMhfGetNotice{}},
|
||||
// msg_mhf_get_tower_info.go
|
||||
{"MsgMhfGetTowerInfo", &MsgMhfGetTowerInfo{}},
|
||||
// msg_mhf_get_ud_info.go
|
||||
{"MsgMhfGetUdInfo", &MsgMhfGetUdInfo{}},
|
||||
// msg_mhf_get_ud_schedule.go
|
||||
{"MsgMhfGetUdSchedule", &MsgMhfGetUdSchedule{}},
|
||||
// msg_mhf_get_weekly_schedule.go
|
||||
{"MsgMhfGetWeeklySchedule", &MsgMhfGetWeeklySchedule{}},
|
||||
// msg_mhf_guild_huntdata.go
|
||||
{"MsgMhfGuildHuntdata", &MsgMhfGuildHuntdata{}},
|
||||
// msg_mhf_info_joint.go
|
||||
{"MsgMhfInfoJoint", &MsgMhfInfoJoint{}},
|
||||
// msg_mhf_load_deco_myset.go
|
||||
{"MsgMhfLoadDecoMyset", &MsgMhfLoadDecoMyset{}},
|
||||
// msg_mhf_load_guild_adventure.go
|
||||
{"MsgMhfLoadGuildAdventure", &MsgMhfLoadGuildAdventure{}},
|
||||
// msg_mhf_load_guild_cooking.go
|
||||
{"MsgMhfLoadGuildCooking", &MsgMhfLoadGuildCooking{}},
|
||||
// msg_mhf_load_hunter_navi.go
|
||||
{"MsgMhfLoadHunterNavi", &MsgMhfLoadHunterNavi{}},
|
||||
// msg_mhf_load_otomo_airou.go
|
||||
{"MsgMhfLoadOtomoAirou", &MsgMhfLoadOtomoAirou{}},
|
||||
// msg_mhf_load_partner.go
|
||||
{"MsgMhfLoadPartner", &MsgMhfLoadPartner{}},
|
||||
// msg_mhf_load_plate_box.go
|
||||
{"MsgMhfLoadPlateBox", &MsgMhfLoadPlateBox{}},
|
||||
// msg_mhf_load_plate_data.go
|
||||
{"MsgMhfLoadPlateData", &MsgMhfLoadPlateData{}},
|
||||
// msg_mhf_post_notice.go
|
||||
{"MsgMhfPostNotice", &MsgMhfPostNotice{}},
|
||||
// msg_mhf_post_tower_info.go
|
||||
{"MsgMhfPostTowerInfo", &MsgMhfPostTowerInfo{}},
|
||||
// msg_mhf_reserve10f.go
|
||||
{"MsgMhfReserve10F", &MsgMhfReserve10F{}},
|
||||
// msg_mhf_server_command.go
|
||||
{"MsgMhfServerCommand", &MsgMhfServerCommand{}},
|
||||
// msg_mhf_set_loginwindow.go
|
||||
{"MsgMhfSetLoginwindow", &MsgMhfSetLoginwindow{}},
|
||||
// msg_mhf_shut_client.go
|
||||
{"MsgMhfShutClient", &MsgMhfShutClient{}},
|
||||
// msg_mhf_stampcard_stamp.go
|
||||
{"MsgMhfStampcardStamp", &MsgMhfStampcardStamp{}},
|
||||
// msg_sys_add_object.go
|
||||
{"MsgSysAddObject", &MsgSysAddObject{}},
|
||||
// msg_sys_back_stage.go
|
||||
{"MsgSysBackStage", &MsgSysBackStage{}},
|
||||
// msg_sys_cast_binary.go
|
||||
{"MsgSysCastBinary", &MsgSysCastBinary{}},
|
||||
// msg_sys_create_semaphore.go
|
||||
{"MsgSysCreateSemaphore", &MsgSysCreateSemaphore{}},
|
||||
// msg_sys_create_stage.go
|
||||
{"MsgSysCreateStage", &MsgSysCreateStage{}},
|
||||
// msg_sys_del_object.go
|
||||
{"MsgSysDelObject", &MsgSysDelObject{}},
|
||||
// msg_sys_disp_object.go
|
||||
{"MsgSysDispObject", &MsgSysDispObject{}},
|
||||
// msg_sys_echo.go
|
||||
{"MsgSysEcho", &MsgSysEcho{}},
|
||||
// msg_sys_enter_stage.go
|
||||
{"MsgSysEnterStage", &MsgSysEnterStage{}},
|
||||
// msg_sys_enumerate_client.go
|
||||
{"MsgSysEnumerateClient", &MsgSysEnumerateClient{}},
|
||||
// msg_sys_extend_threshold.go
|
||||
{"MsgSysExtendThreshold", &MsgSysExtendThreshold{}},
|
||||
// msg_sys_get_stage_binary.go
|
||||
{"MsgSysGetStageBinary", &MsgSysGetStageBinary{}},
|
||||
// msg_sys_hide_object.go
|
||||
{"MsgSysHideObject", &MsgSysHideObject{}},
|
||||
// msg_sys_leave_stage.go
|
||||
{"MsgSysLeaveStage", &MsgSysLeaveStage{}},
|
||||
// msg_sys_lock_stage.go
|
||||
{"MsgSysLockStage", &MsgSysLockStage{}},
|
||||
// msg_sys_login.go
|
||||
{"MsgSysLogin", &MsgSysLogin{}},
|
||||
// msg_sys_move_stage.go
|
||||
{"MsgSysMoveStage", &MsgSysMoveStage{}},
|
||||
// msg_sys_set_stage_binary.go
|
||||
{"MsgSysSetStageBinary", &MsgSysSetStageBinary{}},
|
||||
// msg_sys_set_stage_pass.go
|
||||
{"MsgSysSetStagePass", &MsgSysSetStagePass{}},
|
||||
// msg_sys_set_status.go
|
||||
{"MsgSysSetStatus", &MsgSysSetStatus{}},
|
||||
// msg_sys_wait_stage_binary.go
|
||||
{"MsgSysWaitStageBinary", &MsgSysWaitStageBinary{}},
|
||||
|
||||
// Reserve files - sys reserves
|
||||
{"MsgSysReserve01", &MsgSysReserve01{}},
|
||||
{"MsgSysReserve02", &MsgSysReserve02{}},
|
||||
{"MsgSysReserve03", &MsgSysReserve03{}},
|
||||
{"MsgSysReserve04", &MsgSysReserve04{}},
|
||||
{"MsgSysReserve05", &MsgSysReserve05{}},
|
||||
{"MsgSysReserve06", &MsgSysReserve06{}},
|
||||
{"MsgSysReserve07", &MsgSysReserve07{}},
|
||||
{"MsgSysReserve0C", &MsgSysReserve0C{}},
|
||||
{"MsgSysReserve0D", &MsgSysReserve0D{}},
|
||||
{"MsgSysReserve0E", &MsgSysReserve0E{}},
|
||||
{"MsgSysReserve4A", &MsgSysReserve4A{}},
|
||||
{"MsgSysReserve4B", &MsgSysReserve4B{}},
|
||||
{"MsgSysReserve4C", &MsgSysReserve4C{}},
|
||||
{"MsgSysReserve4D", &MsgSysReserve4D{}},
|
||||
{"MsgSysReserve4E", &MsgSysReserve4E{}},
|
||||
{"MsgSysReserve4F", &MsgSysReserve4F{}},
|
||||
{"MsgSysReserve55", &MsgSysReserve55{}},
|
||||
{"MsgSysReserve56", &MsgSysReserve56{}},
|
||||
{"MsgSysReserve57", &MsgSysReserve57{}},
|
||||
{"MsgSysReserve5C", &MsgSysReserve5C{}},
|
||||
{"MsgSysReserve5E", &MsgSysReserve5E{}},
|
||||
{"MsgSysReserve5F", &MsgSysReserve5F{}},
|
||||
{"MsgSysReserve71", &MsgSysReserve71{}},
|
||||
{"MsgSysReserve72", &MsgSysReserve72{}},
|
||||
{"MsgSysReserve73", &MsgSysReserve73{}},
|
||||
{"MsgSysReserve74", &MsgSysReserve74{}},
|
||||
{"MsgSysReserve75", &MsgSysReserve75{}},
|
||||
{"MsgSysReserve76", &MsgSysReserve76{}},
|
||||
{"MsgSysReserve77", &MsgSysReserve77{}},
|
||||
{"MsgSysReserve78", &MsgSysReserve78{}},
|
||||
{"MsgSysReserve79", &MsgSysReserve79{}},
|
||||
{"MsgSysReserve7A", &MsgSysReserve7A{}},
|
||||
{"MsgSysReserve7B", &MsgSysReserve7B{}},
|
||||
{"MsgSysReserve7C", &MsgSysReserve7C{}},
|
||||
{"MsgSysReserve7E", &MsgSysReserve7E{}},
|
||||
{"MsgSysReserve180", &MsgSysReserve180{}},
|
||||
{"MsgSysReserve188", &MsgSysReserve188{}},
|
||||
{"MsgSysReserve18B", &MsgSysReserve18B{}},
|
||||
{"MsgSysReserve18E", &MsgSysReserve18E{}},
|
||||
{"MsgSysReserve18F", &MsgSysReserve18F{}},
|
||||
{"MsgSysReserve192", &MsgSysReserve192{}},
|
||||
{"MsgSysReserve193", &MsgSysReserve193{}},
|
||||
{"MsgSysReserve194", &MsgSysReserve194{}},
|
||||
{"MsgSysReserve19B", &MsgSysReserve19B{}},
|
||||
{"MsgSysReserve19E", &MsgSysReserve19E{}},
|
||||
{"MsgSysReserve19F", &MsgSysReserve19F{}},
|
||||
{"MsgSysReserve1A4", &MsgSysReserve1A4{}},
|
||||
{"MsgSysReserve1A6", &MsgSysReserve1A6{}},
|
||||
{"MsgSysReserve1A7", &MsgSysReserve1A7{}},
|
||||
{"MsgSysReserve1A8", &MsgSysReserve1A8{}},
|
||||
{"MsgSysReserve1A9", &MsgSysReserve1A9{}},
|
||||
{"MsgSysReserve1AA", &MsgSysReserve1AA{}},
|
||||
{"MsgSysReserve1AB", &MsgSysReserve1AB{}},
|
||||
{"MsgSysReserve1AC", &MsgSysReserve1AC{}},
|
||||
{"MsgSysReserve1AD", &MsgSysReserve1AD{}},
|
||||
{"MsgSysReserve1AE", &MsgSysReserve1AE{}},
|
||||
{"MsgSysReserve1AF", &MsgSysReserve1AF{}},
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err, panicked := callBuildSafe(tt.pkt, bf, ctx)
|
||||
if panicked {
|
||||
// Build panicked with "Not implemented" - this is acceptable
|
||||
// and still exercises the code path for coverage.
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
// Build succeeded (some packets may have implemented Build)
|
||||
return
|
||||
}
|
||||
// Build returned an error, which is expected for NOT IMPLEMENTED stubs
|
||||
if err.Error() != "NOT IMPLEMENTED" {
|
||||
t.Errorf("Build() returned unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseCoverage_NotImplemented exercises Parse() on packet types whose Parse
|
||||
// method returns "NOT IMPLEMENTED" and is not yet covered by existing tests.
|
||||
func TestParseCoverage_NotImplemented(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
}{
|
||||
// msg_mhf_acquire_tournament.go - Parse returns NOT IMPLEMENTED
|
||||
{"MsgMhfAcquireTournament", &MsgMhfAcquireTournament{}},
|
||||
// msg_mhf_entry_tournament.go - Parse returns NOT IMPLEMENTED
|
||||
{"MsgMhfEntryTournament", &MsgMhfEntryTournament{}},
|
||||
// msg_mhf_update_guild.go - Parse returns NOT IMPLEMENTED
|
||||
{"MsgMhfUpdateGuild", &MsgMhfUpdateGuild{}},
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
bf := byteframe.NewByteFrame()
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err, panicked := callParseSafe(tt.pkt, bf, ctx)
|
||||
if panicked {
|
||||
return
|
||||
}
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if err.Error() != "NOT IMPLEMENTED" {
|
||||
t.Errorf("Parse() returned unexpected error: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
880
network/mhfpacket/msg_parse_large_test.go
Normal file
880
network/mhfpacket/msg_parse_large_test.go
Normal file
@@ -0,0 +1,880 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// TestParseLargeMsgSysUpdateRightBuild tests Build for MsgSysUpdateRight (no Parse implementation).
|
||||
func TestParseLargeMsgSysUpdateRightBuild(t *testing.T) {
|
||||
ctx := &clientctx.ClientContext{}
|
||||
original := &MsgSysUpdateRight{
|
||||
ClientRespAckHandle: 0x12345678,
|
||||
Bitfield: 0xDEADBEEF,
|
||||
Rights: nil,
|
||||
UnkSize: 0,
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
if err := original.Build(bf, ctx); err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify binary output manually:
|
||||
// uint32 ClientRespAckHandle + uint32 Bitfield + uint16 Rights count(0) + uint16 padding(0) + ps.Uint16 empty string(uint16(1) + 0x00)
|
||||
data := bf.Data()
|
||||
if len(data) < 12 {
|
||||
t.Fatalf("Build() wrote %d bytes, want at least 12", len(data))
|
||||
}
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
if bf.ReadUint32() != 0x12345678 {
|
||||
t.Error("ClientRespAckHandle mismatch")
|
||||
}
|
||||
if bf.ReadUint32() != 0xDEADBEEF {
|
||||
t.Error("Bitfield mismatch")
|
||||
}
|
||||
if bf.ReadUint16() != 0 {
|
||||
t.Error("Rights count should be 0")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfOperateWarehouse tests Parse for MsgMhfOperateWarehouse.
|
||||
func TestParseLargeMsgMhfOperateWarehouse(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint8(1) // Operation
|
||||
bf.WriteUint8(0) // BoxType = item
|
||||
bf.WriteUint8(2) // BoxIndex
|
||||
bf.WriteUint8(8) // lenName (unused but read)
|
||||
bf.WriteUint16(0) // Unk
|
||||
bf.WriteBytes([]byte("TestBox"))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfOperateWarehouse{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Operation != 1 {
|
||||
t.Errorf("Operation = %d, want 1", pkt.Operation)
|
||||
}
|
||||
if pkt.BoxType != 0 {
|
||||
t.Errorf("BoxType = %d, want 0", pkt.BoxType)
|
||||
}
|
||||
if pkt.BoxIndex != 2 {
|
||||
t.Errorf("BoxIndex = %d, want 2", pkt.BoxIndex)
|
||||
}
|
||||
if pkt.Name != "TestBox" {
|
||||
t.Errorf("Name = %q, want %q", pkt.Name, "TestBox")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfOperateWarehouseEquip tests Parse for MsgMhfOperateWarehouse with equip box type.
|
||||
func TestParseLargeMsgMhfOperateWarehouseEquip(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(42) // AckHandle
|
||||
bf.WriteUint8(2) // Operation
|
||||
bf.WriteUint8(1) // BoxType = equip
|
||||
bf.WriteUint8(0) // BoxIndex
|
||||
bf.WriteUint8(5) // lenName
|
||||
bf.WriteUint16(0) // Unk
|
||||
bf.WriteBytes([]byte("Arms"))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfOperateWarehouse{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.BoxType != 1 {
|
||||
t.Errorf("BoxType = %d, want 1", pkt.BoxType)
|
||||
}
|
||||
if pkt.Name != "Arms" {
|
||||
t.Errorf("Name = %q, want %q", pkt.Name, "Arms")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfLoadHouse tests Parse for MsgMhfLoadHouse.
|
||||
func TestParseLargeMsgMhfLoadHouse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
charID uint32
|
||||
destination uint8
|
||||
checkPass bool
|
||||
password string
|
||||
}{
|
||||
{"with password", 0xAABBCCDD, 12345, 1, true, "pass123"},
|
||||
{"no password", 0x11111111, 0, 0, false, ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint32(tt.charID)
|
||||
bf.WriteUint8(tt.destination)
|
||||
bf.WriteBool(tt.checkPass)
|
||||
bf.WriteUint16(0) // Unk (hardcoded 0)
|
||||
bf.WriteUint8(uint8(len(tt.password) + 1)) // Password length
|
||||
bf.WriteBytes([]byte(tt.password))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfLoadHouse{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.CharID != tt.charID {
|
||||
t.Errorf("CharID = %d, want %d", pkt.CharID, tt.charID)
|
||||
}
|
||||
if pkt.Destination != tt.destination {
|
||||
t.Errorf("Destination = %d, want %d", pkt.Destination, tt.destination)
|
||||
}
|
||||
if pkt.CheckPass != tt.checkPass {
|
||||
t.Errorf("CheckPass = %v, want %v", pkt.CheckPass, tt.checkPass)
|
||||
}
|
||||
if pkt.Password != tt.password {
|
||||
t.Errorf("Password = %q, want %q", pkt.Password, tt.password)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfSendMail tests Parse for MsgMhfSendMail.
|
||||
func TestParseLargeMsgMhfSendMail(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(99999) // RecipientID
|
||||
bf.WriteUint16(6) // SubjectLength
|
||||
bf.WriteUint16(12) // BodyLength
|
||||
bf.WriteUint32(5) // Quantity
|
||||
bf.WriteUint16(1001) // ItemID
|
||||
bf.WriteBytes([]byte("Hello"))
|
||||
bf.WriteUint8(0) // null terminator for Subject
|
||||
bf.WriteBytes([]byte("Hello World"))
|
||||
bf.WriteUint8(0) // null terminator for Body
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfSendMail{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.RecipientID != 99999 {
|
||||
t.Errorf("RecipientID = %d, want 99999", pkt.RecipientID)
|
||||
}
|
||||
if pkt.SubjectLength != 6 {
|
||||
t.Errorf("SubjectLength = %d, want 6", pkt.SubjectLength)
|
||||
}
|
||||
if pkt.BodyLength != 12 {
|
||||
t.Errorf("BodyLength = %d, want 12", pkt.BodyLength)
|
||||
}
|
||||
if pkt.Quantity != 5 {
|
||||
t.Errorf("Quantity = %d, want 5", pkt.Quantity)
|
||||
}
|
||||
if pkt.ItemID != 1001 {
|
||||
t.Errorf("ItemID = %d, want 1001", pkt.ItemID)
|
||||
}
|
||||
if pkt.Subject != "Hello" {
|
||||
t.Errorf("Subject = %q, want %q", pkt.Subject, "Hello")
|
||||
}
|
||||
if pkt.Body != "Hello World" {
|
||||
t.Errorf("Body = %q, want %q", pkt.Body, "Hello World")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfApplyBbsArticle tests Parse for MsgMhfApplyBbsArticle.
|
||||
func TestParseLargeMsgMhfApplyBbsArticle(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xCAFEBABE) // AckHandle
|
||||
bf.WriteUint32(42) // Unk0
|
||||
|
||||
// Unk1: 16 bytes
|
||||
unk1 := make([]byte, 16)
|
||||
for i := range unk1 {
|
||||
unk1[i] = byte(i + 1)
|
||||
}
|
||||
bf.WriteBytes(unk1)
|
||||
|
||||
// Name: 32 bytes (padded with nulls) - uses bfutil.UpToNull
|
||||
nameBytes := make([]byte, 32)
|
||||
copy(nameBytes, "Hunter")
|
||||
bf.WriteBytes(nameBytes)
|
||||
|
||||
// Title: 128 bytes (padded with nulls)
|
||||
titleBytes := make([]byte, 128)
|
||||
copy(titleBytes, "My Post Title")
|
||||
bf.WriteBytes(titleBytes)
|
||||
|
||||
// Description: 256 bytes (padded with nulls)
|
||||
descBytes := make([]byte, 256)
|
||||
copy(descBytes, "This is a description")
|
||||
bf.WriteBytes(descBytes)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfApplyBbsArticle{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xCAFEBABE {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xCAFEBABE", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Unk0 != 42 {
|
||||
t.Errorf("Unk0 = %d, want 42", pkt.Unk0)
|
||||
}
|
||||
if !bytes.Equal(pkt.Unk1, unk1) {
|
||||
t.Error("Unk1 mismatch")
|
||||
}
|
||||
if pkt.Name != "Hunter" {
|
||||
t.Errorf("Name = %q, want %q", pkt.Name, "Hunter")
|
||||
}
|
||||
if pkt.Title != "My Post Title" {
|
||||
t.Errorf("Title = %q, want %q", pkt.Title, "My Post Title")
|
||||
}
|
||||
if pkt.Description != "This is a description" {
|
||||
t.Errorf("Description = %q, want %q", pkt.Description, "This is a description")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfChargeFesta tests Parse for MsgMhfChargeFesta.
|
||||
func TestParseLargeMsgMhfChargeFesta(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x11223344) // AckHandle
|
||||
bf.WriteUint32(100) // FestaID
|
||||
bf.WriteUint32(200) // GuildID
|
||||
bf.WriteUint16(3) // soul count
|
||||
bf.WriteUint16(10) // soul value 1
|
||||
bf.WriteUint16(20) // soul value 2
|
||||
bf.WriteUint16(30) // soul value 3
|
||||
bf.WriteUint8(0) // Unk
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfChargeFesta{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x11223344 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x11223344", pkt.AckHandle)
|
||||
}
|
||||
if pkt.FestaID != 100 {
|
||||
t.Errorf("FestaID = %d, want 100", pkt.FestaID)
|
||||
}
|
||||
if pkt.GuildID != 200 {
|
||||
t.Errorf("GuildID = %d, want 200", pkt.GuildID)
|
||||
}
|
||||
if len(pkt.Souls) != 3 {
|
||||
t.Fatalf("Souls len = %d, want 3", len(pkt.Souls))
|
||||
}
|
||||
expectedSouls := []uint16{10, 20, 30}
|
||||
for i, v := range expectedSouls {
|
||||
if pkt.Souls[i] != v {
|
||||
t.Errorf("Souls[%d] = %d, want %d", i, pkt.Souls[i], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfChargeFestaZeroSouls tests Parse for MsgMhfChargeFesta with zero soul entries.
|
||||
func TestParseLargeMsgMhfChargeFestaZeroSouls(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint32(0) // FestaID
|
||||
bf.WriteUint32(0) // GuildID
|
||||
bf.WriteUint16(0) // soul count = 0
|
||||
bf.WriteUint8(0) // Unk
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfChargeFesta{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if len(pkt.Souls) != 0 {
|
||||
t.Errorf("Souls len = %d, want 0", len(pkt.Souls))
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfOperateJoint tests Parse for MsgMhfOperateJoint.
|
||||
// Parse reads: uint32 AckHandle, uint32 AllianceID, uint32 GuildID, uint8 Action,
|
||||
// uint8 dataLen, 4 bytes Data1, dataLen bytes Data2.
|
||||
func TestParseLargeMsgMhfOperateJoint(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(100) // AllianceID
|
||||
bf.WriteUint32(200) // GuildID
|
||||
bf.WriteUint8(0x01) // Action = OPERATE_JOINT_DISBAND
|
||||
bf.WriteUint8(3) // dataLen = 3
|
||||
bf.WriteBytes([]byte{0xAA, 0xBB, 0xCC, 0xDD}) // Data1 (always 4 bytes)
|
||||
bf.WriteBytes([]byte{0x01, 0x02, 0x03}) // Data2 (dataLen bytes)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfOperateJoint{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.AllianceID != 100 {
|
||||
t.Errorf("AllianceID = %d, want 100", pkt.AllianceID)
|
||||
}
|
||||
if pkt.GuildID != 200 {
|
||||
t.Errorf("GuildID = %d, want 200", pkt.GuildID)
|
||||
}
|
||||
if pkt.Action != OPERATE_JOINT_DISBAND {
|
||||
t.Errorf("Action = %d, want %d", pkt.Action, OPERATE_JOINT_DISBAND)
|
||||
}
|
||||
if pkt.Data1 == nil {
|
||||
t.Fatal("Data1 is nil")
|
||||
}
|
||||
if pkt.Data2 == nil {
|
||||
t.Fatal("Data2 is nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfOperationInvGuild tests Parse for MsgMhfOperationInvGuild.
|
||||
func TestParseLargeMsgMhfOperationInvGuild(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint8(1) // Operation
|
||||
bf.WriteUint8(5) // ActiveHours
|
||||
bf.WriteUint8(7) // DaysActive
|
||||
bf.WriteUint8(3) // PlayStyle
|
||||
bf.WriteUint8(2) // GuildRequest
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfOperationInvGuild{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Operation != 1 {
|
||||
t.Errorf("Operation = %d, want 1", pkt.Operation)
|
||||
}
|
||||
if pkt.ActiveHours != 5 {
|
||||
t.Errorf("ActiveHours = %d, want 5", pkt.ActiveHours)
|
||||
}
|
||||
if pkt.DaysActive != 7 {
|
||||
t.Errorf("DaysActive = %d, want 7", pkt.DaysActive)
|
||||
}
|
||||
if pkt.PlayStyle != 3 {
|
||||
t.Errorf("PlayStyle = %d, want 3", pkt.PlayStyle)
|
||||
}
|
||||
if pkt.GuildRequest != 2 {
|
||||
t.Errorf("GuildRequest = %d, want 2", pkt.GuildRequest)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfSaveMercenary tests Parse for MsgMhfSaveMercenary.
|
||||
func TestParseLargeMsgMhfSaveMercenary(t *testing.T) {
|
||||
mercData := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xCAFEBABE) // AckHandle
|
||||
bf.WriteUint32(0) // lenData (skipped)
|
||||
bf.WriteUint32(5000) // GCP
|
||||
bf.WriteUint32(42) // PactMercID
|
||||
bf.WriteUint32(uint32(len(mercData))) // dataSize
|
||||
bf.WriteUint32(0) // Merc index (skipped)
|
||||
bf.WriteBytes(mercData)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfSaveMercenary{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xCAFEBABE {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xCAFEBABE", pkt.AckHandle)
|
||||
}
|
||||
if pkt.GCP != 5000 {
|
||||
t.Errorf("GCP = %d, want 5000", pkt.GCP)
|
||||
}
|
||||
if pkt.PactMercID != 42 {
|
||||
t.Errorf("PactMercID = %d, want 42", pkt.PactMercID)
|
||||
}
|
||||
if !bytes.Equal(pkt.MercData, mercData) {
|
||||
t.Errorf("MercData = %v, want %v", pkt.MercData, mercData)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfUpdateHouse tests Parse for MsgMhfUpdateHouse.
|
||||
func TestParseLargeMsgMhfUpdateHouse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
state uint8
|
||||
password string
|
||||
}{
|
||||
{"with password", 1, "secret"},
|
||||
{"empty password", 0, ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint8(tt.state) // State
|
||||
bf.WriteUint8(1) // Unk1
|
||||
bf.WriteUint16(0) // Unk2
|
||||
bf.WriteUint8(uint8(len(tt.password) + 1)) // Password length
|
||||
bf.WriteBytes([]byte(tt.password))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfUpdateHouse{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.State != tt.state {
|
||||
t.Errorf("State = %d, want %d", pkt.State, tt.state)
|
||||
}
|
||||
if pkt.Unk1 != 1 {
|
||||
t.Errorf("Unk1 = %d, want 1", pkt.Unk1)
|
||||
}
|
||||
if pkt.Password != tt.password {
|
||||
t.Errorf("Password = %q, want %q", pkt.Password, tt.password)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgSysCreateAcquireSemaphore tests Parse for MsgSysCreateAcquireSemaphore.
|
||||
func TestParseLargeMsgSysCreateAcquireSemaphore(t *testing.T) {
|
||||
semID := "stage_001"
|
||||
semBytes := make([]byte, len(semID)+1) // include space for null if needed
|
||||
copy(semBytes, semID)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xDEADBEEF) // AckHandle
|
||||
bf.WriteUint16(100) // Unk0
|
||||
bf.WriteUint8(4) // PlayerCount
|
||||
bf.WriteUint8(uint8(len(semBytes))) // SemaphoreIDLength
|
||||
bf.WriteBytes(semBytes)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCreateAcquireSemaphore{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xDEADBEEF {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xDEADBEEF", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Unk0 != 100 {
|
||||
t.Errorf("Unk0 = %d, want 100", pkt.Unk0)
|
||||
}
|
||||
if pkt.PlayerCount != 4 {
|
||||
t.Errorf("PlayerCount = %d, want 4", pkt.PlayerCount)
|
||||
}
|
||||
if pkt.SemaphoreID != semID {
|
||||
t.Errorf("SemaphoreID = %q, want %q", pkt.SemaphoreID, semID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfOperateGuild tests Parse for MsgMhfOperateGuild.
|
||||
func TestParseLargeMsgMhfOperateGuild(t *testing.T) {
|
||||
dataPayload := []byte{0x10, 0x20, 0x30, 0x40, 0x50}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint32(999) // GuildID
|
||||
bf.WriteUint8(0x09) // Action = OperateGuildUpdateComment
|
||||
bf.WriteUint8(uint8(len(dataPayload))) // dataLen
|
||||
bf.WriteBytes([]byte{0x01, 0x02, 0x03, 0x04}) // Data1 (always 4 bytes)
|
||||
bf.WriteBytes(dataPayload) // Data2 (dataLen bytes)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfOperateGuild{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if pkt.GuildID != 999 {
|
||||
t.Errorf("GuildID = %d, want 999", pkt.GuildID)
|
||||
}
|
||||
if pkt.Action != OperateGuildUpdateComment {
|
||||
t.Errorf("Action = %d, want %d", pkt.Action, OperateGuildUpdateComment)
|
||||
}
|
||||
if pkt.Data1 == nil {
|
||||
t.Fatal("Data1 is nil")
|
||||
}
|
||||
if pkt.Data2 == nil {
|
||||
t.Fatal("Data2 is nil")
|
||||
}
|
||||
data2Bytes := pkt.Data2.Data()
|
||||
if !bytes.Equal(data2Bytes, dataPayload) {
|
||||
t.Errorf("Data2 = %v, want %v", data2Bytes, dataPayload)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfReadBeatLevel tests Parse for MsgMhfReadBeatLevel.
|
||||
func TestParseLargeMsgMhfReadBeatLevel(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(1) // Unk0
|
||||
bf.WriteUint32(4) // ValidIDCount
|
||||
|
||||
// Write 16 uint32 IDs
|
||||
ids := [16]uint32{0x74, 0x6B, 0x02, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
for _, id := range ids {
|
||||
bf.WriteUint32(id)
|
||||
}
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfReadBeatLevel{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Unk0 != 1 {
|
||||
t.Errorf("Unk0 = %d, want 1", pkt.Unk0)
|
||||
}
|
||||
if pkt.ValidIDCount != 4 {
|
||||
t.Errorf("ValidIDCount = %d, want 4", pkt.ValidIDCount)
|
||||
}
|
||||
for i, id := range ids {
|
||||
if pkt.IDs[i] != id {
|
||||
t.Errorf("IDs[%d] = 0x%X, want 0x%X", i, pkt.IDs[i], id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgSysCreateObject tests Parse for MsgSysCreateObject.
|
||||
func TestParseLargeMsgSysCreateObject(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
x, y, z float32
|
||||
unk0 uint32
|
||||
}{
|
||||
{"origin", 1, 0.0, 0.0, 0.0, 0},
|
||||
{"typical", 0x12345678, 1.5, 2.5, 3.5, 42},
|
||||
{"negative coords", 0xFFFFFFFF, -100.25, 200.75, -300.125, 0xDEADBEEF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteFloat32(tt.x)
|
||||
bf.WriteFloat32(tt.y)
|
||||
bf.WriteFloat32(tt.z)
|
||||
bf.WriteUint32(tt.unk0)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCreateObject{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.X != tt.x {
|
||||
t.Errorf("X = %f, want %f", pkt.X, tt.x)
|
||||
}
|
||||
if pkt.Y != tt.y {
|
||||
t.Errorf("Y = %f, want %f", pkt.Y, tt.y)
|
||||
}
|
||||
if pkt.Z != tt.z {
|
||||
t.Errorf("Z = %f, want %f", pkt.Z, tt.z)
|
||||
}
|
||||
if pkt.Unk0 != tt.unk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.unk0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgSysLockGlobalSema tests Parse for MsgSysLockGlobalSema.
|
||||
func TestParseLargeMsgSysLockGlobalSema(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xDEADBEEF) // AckHandle
|
||||
bf.WriteUint16(8) // UserIDLength
|
||||
bf.WriteUint16(11) // ServerChannelIDLength
|
||||
bf.WriteBytes([]byte("user123"))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.WriteBytes([]byte("channel_01"))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLockGlobalSema{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xDEADBEEF {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xDEADBEEF", pkt.AckHandle)
|
||||
}
|
||||
if pkt.UserIDLength != 8 {
|
||||
t.Errorf("UserIDLength = %d, want 8", pkt.UserIDLength)
|
||||
}
|
||||
if pkt.ServerChannelIDLength != 11 {
|
||||
t.Errorf("ServerChannelIDLength = %d, want 11", pkt.ServerChannelIDLength)
|
||||
}
|
||||
if pkt.UserIDString != "user123" {
|
||||
t.Errorf("UserIDString = %q, want %q", pkt.UserIDString, "user123")
|
||||
}
|
||||
if pkt.ServerChannelIDString != "channel_01" {
|
||||
t.Errorf("ServerChannelIDString = %q, want %q", pkt.ServerChannelIDString, "channel_01")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfCreateJoint tests Parse for MsgMhfCreateJoint.
|
||||
func TestParseLargeMsgMhfCreateJoint(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xCAFEBABE) // AckHandle
|
||||
bf.WriteUint32(500) // GuildID
|
||||
bf.WriteUint32(15) // len (unused)
|
||||
bf.WriteBytes([]byte("Alliance01"))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfCreateJoint{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xCAFEBABE {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xCAFEBABE", pkt.AckHandle)
|
||||
}
|
||||
if pkt.GuildID != 500 {
|
||||
t.Errorf("GuildID = %d, want 500", pkt.GuildID)
|
||||
}
|
||||
if pkt.Name != "Alliance01" {
|
||||
t.Errorf("Name = %q, want %q", pkt.Name, "Alliance01")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfGetUdTacticsRemainingPoint tests Parse for MsgMhfGetUdTacticsRemainingPoint.
|
||||
func TestParseLargeMsgMhfGetUdTacticsRemainingPoint(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(100) // Unk0
|
||||
bf.WriteUint32(200) // Unk1
|
||||
bf.WriteUint32(300) // Unk2
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfGetUdTacticsRemainingPoint{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Unk0 != 100 {
|
||||
t.Errorf("Unk0 = %d, want 100", pkt.Unk0)
|
||||
}
|
||||
if pkt.Unk1 != 200 {
|
||||
t.Errorf("Unk1 = %d, want 200", pkt.Unk1)
|
||||
}
|
||||
if pkt.Unk2 != 300 {
|
||||
t.Errorf("Unk2 = %d, want 300", pkt.Unk2)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfPostCafeDurationBonusReceived tests Parse for MsgMhfPostCafeDurationBonusReceived.
|
||||
func TestParseLargeMsgMhfPostCafeDurationBonusReceived(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint32(3) // count
|
||||
bf.WriteUint32(1001) // CafeBonusID[0]
|
||||
bf.WriteUint32(1002) // CafeBonusID[1]
|
||||
bf.WriteUint32(1003) // CafeBonusID[2]
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfPostCafeDurationBonusReceived{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if len(pkt.CafeBonusID) != 3 {
|
||||
t.Fatalf("CafeBonusID len = %d, want 3", len(pkt.CafeBonusID))
|
||||
}
|
||||
expected := []uint32{1001, 1002, 1003}
|
||||
for i, v := range expected {
|
||||
if pkt.CafeBonusID[i] != v {
|
||||
t.Errorf("CafeBonusID[%d] = %d, want %d", i, pkt.CafeBonusID[i], v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfPostCafeDurationBonusReceivedEmpty tests Parse with zero IDs.
|
||||
func TestParseLargeMsgMhfPostCafeDurationBonusReceivedEmpty(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint32(0) // count = 0
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfPostCafeDurationBonusReceived{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if len(pkt.CafeBonusID) != 0 {
|
||||
t.Errorf("CafeBonusID len = %d, want 0", len(pkt.CafeBonusID))
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfRegistGuildAdventureDiva tests Parse for MsgMhfRegistGuildAdventureDiva.
|
||||
func TestParseLargeMsgMhfRegistGuildAdventureDiva(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(5) // Destination
|
||||
bf.WriteUint32(1000) // Charge
|
||||
bf.WriteUint32(42) // CharID (skipped)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfRegistGuildAdventureDiva{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Destination != 5 {
|
||||
t.Errorf("Destination = %d, want 5", pkt.Destination)
|
||||
}
|
||||
if pkt.Charge != 1000 {
|
||||
t.Errorf("Charge = %d, want 1000", pkt.Charge)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfStateFestaG tests Parse for MsgMhfStateFestaG.
|
||||
func TestParseLargeMsgMhfStateFestaG(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xDEADBEEF) // AckHandle
|
||||
bf.WriteUint32(100) // FestaID
|
||||
bf.WriteUint32(200) // GuildID
|
||||
bf.WriteUint16(0) // Hardcoded 0
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfStateFestaG{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xDEADBEEF {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xDEADBEEF", pkt.AckHandle)
|
||||
}
|
||||
if pkt.FestaID != 100 {
|
||||
t.Errorf("FestaID = %d, want 100", pkt.FestaID)
|
||||
}
|
||||
if pkt.GuildID != 200 {
|
||||
t.Errorf("GuildID = %d, want 200", pkt.GuildID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgMhfStateFestaU tests Parse for MsgMhfStateFestaU.
|
||||
func TestParseLargeMsgMhfStateFestaU(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xCAFEBABE) // AckHandle
|
||||
bf.WriteUint32(300) // FestaID
|
||||
bf.WriteUint32(400) // GuildID
|
||||
bf.WriteUint16(0) // Hardcoded 0
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfStateFestaU{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xCAFEBABE {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xCAFEBABE", pkt.AckHandle)
|
||||
}
|
||||
if pkt.FestaID != 300 {
|
||||
t.Errorf("FestaID = %d, want 300", pkt.FestaID)
|
||||
}
|
||||
if pkt.GuildID != 400 {
|
||||
t.Errorf("GuildID = %d, want 400", pkt.GuildID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgSysEnumerateStage tests Parse for MsgSysEnumerateStage.
|
||||
func TestParseLargeMsgSysEnumerateStage(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x11223344) // AckHandle
|
||||
bf.WriteUint8(1) // Unk0
|
||||
bf.WriteUint8(0) // skipped byte
|
||||
bf.WriteBytes([]byte("quest_"))
|
||||
bf.WriteUint8(0) // null terminator
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysEnumerateStage{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x11223344 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x11223344", pkt.AckHandle)
|
||||
}
|
||||
if pkt.StagePrefix != "quest_" {
|
||||
t.Errorf("StagePrefix = %q, want %q", pkt.StagePrefix, "quest_")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseLargeMsgSysReserveStage tests Parse for MsgSysReserveStage.
|
||||
func TestParseLargeMsgSysReserveStage(t *testing.T) {
|
||||
stageID := "stage_42"
|
||||
stageBytes := make([]byte, len(stageID)+1) // padded with null at end
|
||||
copy(stageBytes, stageID)
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0xAABBCCDD) // AckHandle
|
||||
bf.WriteUint8(0x11) // Ready
|
||||
bf.WriteUint8(uint8(len(stageBytes))) // stageIDLength
|
||||
bf.WriteBytes(stageBytes)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysReserveStage{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0xAABBCCDD {
|
||||
t.Errorf("AckHandle = 0x%X, want 0xAABBCCDD", pkt.AckHandle)
|
||||
}
|
||||
if pkt.Ready != 0x11 {
|
||||
t.Errorf("Ready = 0x%X, want 0x11", pkt.Ready)
|
||||
}
|
||||
if pkt.StageID != stageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, stageID)
|
||||
}
|
||||
}
|
||||
776
network/mhfpacket/msg_parse_medium_test.go
Normal file
776
network/mhfpacket/msg_parse_medium_test.go
Normal file
@@ -0,0 +1,776 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// --- 5-stmt packets (medium complexity) ---
|
||||
|
||||
// TestParseMediumVoteFesta verifies Parse for MsgMhfVoteFesta.
|
||||
// Fields: AckHandle(u32), FestaID(u32), GuildID(u32), TrialID(u32)
|
||||
func TestParseMediumVoteFesta(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
festaID uint32
|
||||
guildID uint32
|
||||
trialID uint32
|
||||
}{
|
||||
{"typical", 0x11223344, 1, 500, 42},
|
||||
{"zero", 0, 0, 0, 0},
|
||||
{"max", 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
bf.WriteUint32(tt.festaID)
|
||||
bf.WriteUint32(tt.guildID)
|
||||
bf.WriteUint32(tt.trialID)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgMhfVoteFesta{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if pkt.FestaID != tt.festaID {
|
||||
t.Errorf("FestaID = %d, want %d", pkt.FestaID, tt.festaID)
|
||||
}
|
||||
if pkt.GuildID != tt.guildID {
|
||||
t.Errorf("GuildID = %d, want %d", pkt.GuildID, tt.guildID)
|
||||
}
|
||||
if pkt.TrialID != tt.trialID {
|
||||
t.Errorf("TrialID = %d, want %d", pkt.TrialID, tt.trialID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumAcquireSemaphore verifies Parse for MsgSysAcquireSemaphore.
|
||||
// Fields: AckHandle(u32), SemaphoreIDLength(u8), SemaphoreID(string via bfutil.UpToNull)
|
||||
func TestParseMediumAcquireSemaphore(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
semaphoreID string
|
||||
}{
|
||||
{"typical", 0xAABBCCDD, "quest_semaphore"},
|
||||
{"short", 1, "s"},
|
||||
{"empty", 0, ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
// SemaphoreIDLength includes the null terminator in the read
|
||||
idBytes := []byte(tt.semaphoreID)
|
||||
idBytes = append(idBytes, 0x00) // null terminator
|
||||
bf.WriteUint8(uint8(len(idBytes)))
|
||||
bf.WriteBytes(idBytes)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgSysAcquireSemaphore{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if pkt.SemaphoreID != tt.semaphoreID {
|
||||
t.Errorf("SemaphoreID = %q, want %q", pkt.SemaphoreID, tt.semaphoreID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumCheckSemaphore verifies Parse for MsgSysCheckSemaphore.
|
||||
// Fields: AckHandle(u32), semaphoreIDLength(u8), SemaphoreID(string via bfutil.UpToNull)
|
||||
func TestParseMediumCheckSemaphore(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
semaphoreID string
|
||||
}{
|
||||
{"typical", 0x12345678, "global_semaphore"},
|
||||
{"short id", 42, "x"},
|
||||
{"empty id", 0, ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
idBytes := []byte(tt.semaphoreID)
|
||||
idBytes = append(idBytes, 0x00)
|
||||
bf.WriteUint8(uint8(len(idBytes)))
|
||||
bf.WriteBytes(idBytes)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgSysCheckSemaphore{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if pkt.SemaphoreID != tt.semaphoreID {
|
||||
t.Errorf("SemaphoreID = %q, want %q", pkt.SemaphoreID, tt.semaphoreID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumGetUserBinary verifies Parse for MsgSysGetUserBinary.
|
||||
// Fields: AckHandle(u32), CharID(u32), BinaryType(u8)
|
||||
func TestParseMediumGetUserBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
charID uint32
|
||||
binaryType uint8
|
||||
}{
|
||||
{"typical", 0xDEADBEEF, 12345, 1},
|
||||
{"zero", 0, 0, 0},
|
||||
{"max", 0xFFFFFFFF, 0xFFFFFFFF, 255},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
bf.WriteUint32(tt.charID)
|
||||
bf.WriteUint8(tt.binaryType)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgSysGetUserBinary{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if pkt.CharID != tt.charID {
|
||||
t.Errorf("CharID = %d, want %d", pkt.CharID, tt.charID)
|
||||
}
|
||||
if pkt.BinaryType != tt.binaryType {
|
||||
t.Errorf("BinaryType = %d, want %d", pkt.BinaryType, tt.binaryType)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumSetObjectBinary verifies Parse for MsgSysSetObjectBinary.
|
||||
// Fields: ObjID(u32), DataSize(u16), RawDataPayload([]byte of DataSize)
|
||||
func TestParseMediumSetObjectBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
objID uint32
|
||||
payload []byte
|
||||
}{
|
||||
{"typical", 42, []byte{0x01, 0x02, 0x03, 0x04}},
|
||||
{"empty", 0, []byte{}},
|
||||
{"large", 0xCAFEBABE, []byte{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22, 0x33, 0x44}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.objID)
|
||||
bf.WriteUint16(uint16(len(tt.payload)))
|
||||
bf.WriteBytes(tt.payload)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgSysSetObjectBinary{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.ObjID != tt.objID {
|
||||
t.Errorf("ObjID = %d, want %d", pkt.ObjID, tt.objID)
|
||||
}
|
||||
if pkt.DataSize != uint16(len(tt.payload)) {
|
||||
t.Errorf("DataSize = %d, want %d", pkt.DataSize, len(tt.payload))
|
||||
}
|
||||
if !bytes.Equal(pkt.RawDataPayload, tt.payload) {
|
||||
t.Errorf("RawDataPayload = %v, want %v", pkt.RawDataPayload, tt.payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumSetUserBinary verifies Parse for MsgSysSetUserBinary.
|
||||
// Fields: BinaryType(u8), DataSize(u16), RawDataPayload([]byte of DataSize)
|
||||
func TestParseMediumSetUserBinary(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
binaryType uint8
|
||||
payload []byte
|
||||
}{
|
||||
{"typical", 1, []byte{0xDE, 0xAD, 0xBE, 0xEF}},
|
||||
{"empty", 0, []byte{}},
|
||||
{"max type", 255, []byte{0x01}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(tt.binaryType)
|
||||
bf.WriteUint16(uint16(len(tt.payload)))
|
||||
bf.WriteBytes(tt.payload)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgSysSetUserBinary{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.BinaryType != tt.binaryType {
|
||||
t.Errorf("BinaryType = %d, want %d", pkt.BinaryType, tt.binaryType)
|
||||
}
|
||||
if pkt.DataSize != uint16(len(tt.payload)) {
|
||||
t.Errorf("DataSize = %d, want %d", pkt.DataSize, len(tt.payload))
|
||||
}
|
||||
if !bytes.Equal(pkt.RawDataPayload, tt.payload) {
|
||||
t.Errorf("RawDataPayload = %v, want %v", pkt.RawDataPayload, tt.payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// --- 4-stmt packets ---
|
||||
|
||||
// TestParseMediumGetUdRanking verifies Parse for MsgMhfGetUdRanking.
|
||||
// Fields: AckHandle(u32), Unk0(u8)
|
||||
func TestParseMediumGetUdRanking(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
unk0 uint8
|
||||
}{
|
||||
{"typical", 0x11223344, 5},
|
||||
{"zero", 0, 0},
|
||||
{"max", 0xFFFFFFFF, 255},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
bf.WriteUint8(tt.unk0)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgMhfGetUdRanking{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if pkt.Unk0 != tt.unk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.unk0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumGetUdTacticsRanking verifies Parse for MsgMhfGetUdTacticsRanking.
|
||||
// Fields: AckHandle(u32), GuildID(u32)
|
||||
func TestParseMediumGetUdTacticsRanking(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
guildID uint32
|
||||
}{
|
||||
{"typical", 0xAABBCCDD, 500},
|
||||
{"zero", 0, 0},
|
||||
{"max", 0xFFFFFFFF, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
bf.WriteUint32(tt.guildID)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgMhfGetUdTacticsRanking{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if pkt.GuildID != tt.guildID {
|
||||
t.Errorf("GuildID = %d, want %d", pkt.GuildID, tt.guildID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumRegistGuildTresure verifies Parse for MsgMhfRegistGuildTresure.
|
||||
// Fields: AckHandle(u32), DataLen(u16), Data([]byte), trailing u32 (discarded)
|
||||
func TestParseMediumRegistGuildTresure(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ack uint32
|
||||
data []byte
|
||||
}{
|
||||
{"typical", 0x12345678, []byte{0x01, 0x02, 0x03}},
|
||||
{"empty data", 1, []byte{}},
|
||||
{"larger data", 0xDEADBEEF, []byte{0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ack)
|
||||
bf.WriteUint16(uint16(len(tt.data)))
|
||||
bf.WriteBytes(tt.data)
|
||||
bf.WriteUint32(0) // trailing uint32 that is read and discarded
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgMhfRegistGuildTresure{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ack)
|
||||
}
|
||||
if !bytes.Equal(pkt.Data, tt.data) {
|
||||
t.Errorf("Data = %v, want %v", pkt.Data, tt.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumUpdateMyhouseInfo verifies Parse for MsgMhfUpdateMyhouseInfo.
|
||||
// Fields: AckHandle(u32), Unk0([]byte of 0x16A bytes)
|
||||
func TestParseMediumUpdateMyhouseInfo(t *testing.T) {
|
||||
t.Run("typical", func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
ack := uint32(0xCAFEBABE)
|
||||
bf.WriteUint32(ack)
|
||||
|
||||
// 0x16A = 362 bytes
|
||||
payload := make([]byte, 0x16A)
|
||||
for i := range payload {
|
||||
payload[i] = byte(i % 256)
|
||||
}
|
||||
bf.WriteBytes(payload)
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgMhfUpdateMyhouseInfo{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
if len(pkt.Data) != 0x16A {
|
||||
t.Errorf("Unk0 length = %d, want %d", len(pkt.Data), 0x16A)
|
||||
}
|
||||
if !bytes.Equal(pkt.Data, payload) {
|
||||
t.Error("Unk0 content mismatch")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("zero values", func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0)
|
||||
bf.WriteBytes(make([]byte, 0x16A))
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
pkt := &MsgMhfUpdateMyhouseInfo{}
|
||||
if err := pkt.Parse(bf, nil); err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0", pkt.AckHandle)
|
||||
}
|
||||
if len(pkt.Data) != 0x16A {
|
||||
t.Errorf("Unk0 length = %d, want %d", len(pkt.Data), 0x16A)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// --- 3-stmt packets (AckHandle-only Parse) ---
|
||||
|
||||
// TestParseMediumAckHandleOnlyBatch tests Parse for all 3-stmt packets that only
|
||||
// read a single AckHandle uint32. These are verified to parse correctly and
|
||||
// return the expected AckHandle value.
|
||||
func TestParseMediumAckHandleOnlyBatch(t *testing.T) {
|
||||
packets := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
// getAck extracts the AckHandle from the parsed packet
|
||||
getAck func() uint32
|
||||
}{
|
||||
{
|
||||
"MsgMhfGetUdBonusQuestInfo",
|
||||
&MsgMhfGetUdBonusQuestInfo{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdDailyPresentList",
|
||||
&MsgMhfGetUdDailyPresentList{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdGuildMapInfo",
|
||||
&MsgMhfGetUdGuildMapInfo{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdMonsterPoint",
|
||||
&MsgMhfGetUdMonsterPoint{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdMyRanking",
|
||||
&MsgMhfGetUdMyRanking{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdNormaPresentList",
|
||||
&MsgMhfGetUdNormaPresentList{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdRankingRewardList",
|
||||
&MsgMhfGetUdRankingRewardList{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdSelectedColorInfo",
|
||||
&MsgMhfGetUdSelectedColorInfo{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdShopCoin",
|
||||
&MsgMhfGetUdShopCoin{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdTacticsBonusQuest",
|
||||
&MsgMhfGetUdTacticsBonusQuest{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdTacticsFirstQuestBonus",
|
||||
&MsgMhfGetUdTacticsFirstQuestBonus{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdTacticsFollower",
|
||||
&MsgMhfGetUdTacticsFollower{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdTacticsLog",
|
||||
&MsgMhfGetUdTacticsLog{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdTacticsPoint",
|
||||
&MsgMhfGetUdTacticsPoint{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfGetUdTacticsRewardList",
|
||||
&MsgMhfGetUdTacticsRewardList{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgMhfReceiveCafeDurationBonus",
|
||||
&MsgMhfReceiveCafeDurationBonus{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgSysDeleteSemaphore",
|
||||
&MsgSysDeleteSemaphore{},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"MsgSysReleaseSemaphore",
|
||||
&MsgSysReleaseSemaphore{},
|
||||
nil,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
ackValues := []uint32{0x12345678, 0, 0xFFFFFFFF, 0xDEADBEEF}
|
||||
|
||||
for _, tc := range packets {
|
||||
for _, ackVal := range ackValues {
|
||||
t.Run(tc.name+"/ack_"+ackHex(ackVal), func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(ackVal)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
err := tc.pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumAckHandleOnlyVerifyValues tests each 3-stmt AckHandle-only
|
||||
// packet individually, verifying that the AckHandle field is correctly populated.
|
||||
func TestParseMediumAckHandleOnlyVerifyValues(t *testing.T) {
|
||||
ctx := &clientctx.ClientContext{}
|
||||
ack := uint32(0xCAFEBABE)
|
||||
|
||||
makeFrame := func() *byteframe.ByteFrame {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(ack)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
return bf
|
||||
}
|
||||
|
||||
t.Run("MsgMhfGetUdBonusQuestInfo", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdBonusQuestInfo{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdDailyPresentList", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdDailyPresentList{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdGuildMapInfo", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdGuildMapInfo{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdMonsterPoint", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdMonsterPoint{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdMyRanking", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdMyRanking{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdNormaPresentList", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdNormaPresentList{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdRankingRewardList", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdRankingRewardList{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdSelectedColorInfo", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdSelectedColorInfo{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdShopCoin", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdShopCoin{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdTacticsBonusQuest", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdTacticsBonusQuest{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdTacticsFirstQuestBonus", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdTacticsFirstQuestBonus{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdTacticsFollower", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdTacticsFollower{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdTacticsLog", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdTacticsLog{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdTacticsPoint", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdTacticsPoint{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfGetUdTacticsRewardList", func(t *testing.T) {
|
||||
pkt := &MsgMhfGetUdTacticsRewardList{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgMhfReceiveCafeDurationBonus", func(t *testing.T) {
|
||||
pkt := &MsgMhfReceiveCafeDurationBonus{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgSysDeleteSemaphore", func(t *testing.T) {
|
||||
pkt := &MsgSysDeleteSemaphore{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.SemaphoreID != ack {
|
||||
t.Errorf("SemaphoreID = 0x%X, want 0x%X", pkt.SemaphoreID, ack)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("MsgSysReleaseSemaphore", func(t *testing.T) {
|
||||
pkt := &MsgSysReleaseSemaphore{}
|
||||
if err := pkt.Parse(makeFrame(), ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if pkt.AckHandle != ack {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, ack)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestParseMediumDeleteUser verifies that MsgSysDeleteUser.Parse returns
|
||||
// NOT IMPLEMENTED error (Parse is not implemented, only Build is).
|
||||
func TestParseMediumDeleteUser(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(12345)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysDeleteUser{}
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err == nil {
|
||||
t.Fatal("Parse() should return error for NOT IMPLEMENTED")
|
||||
}
|
||||
if err.Error() != "NOT IMPLEMENTED" {
|
||||
t.Errorf("Parse() error = %q, want %q", err.Error(), "NOT IMPLEMENTED")
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseMediumInsertUser verifies that MsgSysInsertUser.Parse returns
|
||||
// NOT IMPLEMENTED error (Parse is not implemented, only Build is).
|
||||
func TestParseMediumInsertUser(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(12345)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysInsertUser{}
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err == nil {
|
||||
t.Fatal("Parse() should return error for NOT IMPLEMENTED")
|
||||
}
|
||||
if err.Error() != "NOT IMPLEMENTED" {
|
||||
t.Errorf("Parse() error = %q, want %q", err.Error(), "NOT IMPLEMENTED")
|
||||
}
|
||||
}
|
||||
|
||||
// ackHex returns a hex string for a uint32 ack value, used for test naming.
|
||||
func ackHex(v uint32) string {
|
||||
const hex = "0123456789ABCDEF"
|
||||
buf := make([]byte, 8)
|
||||
for i := 7; i >= 0; i-- {
|
||||
buf[i] = hex[v&0xF]
|
||||
v >>= 4
|
||||
}
|
||||
return string(buf)
|
||||
}
|
||||
216
network/mhfpacket/msg_parse_small_test.go
Normal file
216
network/mhfpacket/msg_parse_small_test.go
Normal file
@@ -0,0 +1,216 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// TestParseSmallNotImplemented tests Parse for packets whose Parse method returns
|
||||
// "NOT IMPLEMENTED". We verify that Parse returns a non-nil error and does not panic.
|
||||
func TestParseSmallNotImplemented(t *testing.T) {
|
||||
packets := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
}{
|
||||
// MHF packets - NOT IMPLEMENTED
|
||||
{"MsgMhfAcceptReadReward", &MsgMhfAcceptReadReward{}},
|
||||
{"MsgMhfAddRewardSongCount", &MsgMhfAddRewardSongCount{}},
|
||||
{"MsgMhfDebugPostValue", &MsgMhfDebugPostValue{}},
|
||||
{"MsgMhfEnterTournamentQuest", &MsgMhfEnterTournamentQuest{}},
|
||||
{"MsgMhfGetCaAchievementHist", &MsgMhfGetCaAchievementHist{}},
|
||||
{"MsgMhfGetCaUniqueID", &MsgMhfGetCaUniqueID{}},
|
||||
{"MsgMhfGetDailyMissionMaster", &MsgMhfGetDailyMissionMaster{}},
|
||||
{"MsgMhfGetDailyMissionPersonal", &MsgMhfGetDailyMissionPersonal{}},
|
||||
{"MsgMhfGetExtraInfo", &MsgMhfGetExtraInfo{}},
|
||||
{"MsgMhfGetRestrictionEvent", &MsgMhfGetRestrictionEvent{}},
|
||||
{"MsgMhfKickExportForce", &MsgMhfKickExportForce{}},
|
||||
{"MsgMhfPaymentAchievement", &MsgMhfPaymentAchievement{}},
|
||||
{"MsgMhfPostRyoudama", &MsgMhfPostRyoudama{}},
|
||||
{"MsgMhfRegistSpabiTime", &MsgMhfRegistSpabiTime{}},
|
||||
{"MsgMhfResetAchievement", &MsgMhfResetAchievement{}},
|
||||
{"MsgMhfResetTitle", &MsgMhfResetTitle{}},
|
||||
{"MsgMhfSetCaAchievement", &MsgMhfSetCaAchievement{}},
|
||||
{"MsgMhfSetDailyMissionPersonal", &MsgMhfSetDailyMissionPersonal{}},
|
||||
{"MsgMhfSetUdTacticsFollower", &MsgMhfSetUdTacticsFollower{}},
|
||||
{"MsgMhfStampcardPrize", &MsgMhfStampcardPrize{}},
|
||||
{"MsgMhfUpdateForceGuildRank", &MsgMhfUpdateForceGuildRank{}},
|
||||
{"MsgMhfUseUdShopCoin", &MsgMhfUseUdShopCoin{}},
|
||||
|
||||
// SYS packets - NOT IMPLEMENTED
|
||||
{"MsgSysAuthData", &MsgSysAuthData{}},
|
||||
{"MsgSysAuthQuery", &MsgSysAuthQuery{}},
|
||||
{"MsgSysAuthTerminal", &MsgSysAuthTerminal{}},
|
||||
{"MsgSysCloseMutex", &MsgSysCloseMutex{}},
|
||||
{"MsgSysCollectBinary", &MsgSysCollectBinary{}},
|
||||
{"MsgSysCreateMutex", &MsgSysCreateMutex{}},
|
||||
{"MsgSysCreateOpenMutex", &MsgSysCreateOpenMutex{}},
|
||||
{"MsgSysDeleteMutex", &MsgSysDeleteMutex{}},
|
||||
{"MsgSysEnumlobby", &MsgSysEnumlobby{}},
|
||||
{"MsgSysEnumuser", &MsgSysEnumuser{}},
|
||||
{"MsgSysGetObjectBinary", &MsgSysGetObjectBinary{}},
|
||||
{"MsgSysGetState", &MsgSysGetState{}},
|
||||
{"MsgSysInfokyserver", &MsgSysInfokyserver{}},
|
||||
{"MsgSysOpenMutex", &MsgSysOpenMutex{}},
|
||||
{"MsgSysRotateObject", &MsgSysRotateObject{}},
|
||||
{"MsgSysSerialize", &MsgSysSerialize{}},
|
||||
{"MsgSysTransBinary", &MsgSysTransBinary{}},
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
for _, tc := range packets {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
// Write some padding bytes so Parse has data available if it tries to read.
|
||||
bf.WriteUint32(0)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
err := tc.pkt.Parse(bf, ctx)
|
||||
if err == nil {
|
||||
t.Fatalf("Parse() expected error for NOT IMPLEMENTED packet, got nil")
|
||||
}
|
||||
if err.Error() != "NOT IMPLEMENTED" {
|
||||
t.Fatalf("Parse() error = %q, want %q", err.Error(), "NOT IMPLEMENTED")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseSmallNoData tests Parse for packets with no fields that return nil.
|
||||
func TestParseSmallNoData(t *testing.T) {
|
||||
packets := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
}{
|
||||
{"MsgSysCleanupObject", &MsgSysCleanupObject{}},
|
||||
{"MsgSysUnreserveStage", &MsgSysUnreserveStage{}},
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
for _, tc := range packets {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := tc.pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v, want nil", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseSmallLogout tests Parse for MsgSysLogout which reads a single uint8 field.
|
||||
func TestParseSmallLogout(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
unk0 uint8
|
||||
}{
|
||||
{"hardcoded 1", 1},
|
||||
{"zero", 0},
|
||||
{"max", 255},
|
||||
}
|
||||
|
||||
ctx := &clientctx.ClientContext{}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(tt.unk0)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLogout{}
|
||||
err := pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if pkt.Unk0 != tt.unk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.unk0)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestParseSmallEnumerateHouse tests Parse for MsgMhfEnumerateHouse which reads
|
||||
// AckHandle, CharID, Method, Unk, lenName, and optional Name.
|
||||
func TestParseSmallEnumerateHouse(t *testing.T) {
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
t.Run("no name", func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x11223344) // AckHandle
|
||||
bf.WriteUint32(0xDEADBEEF) // CharID
|
||||
bf.WriteUint8(2) // Method
|
||||
bf.WriteUint16(100) // Unk
|
||||
bf.WriteUint8(0) // lenName = 0 (no name)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfEnumerateHouse{}
|
||||
err := pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if pkt.AckHandle != 0x11223344 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x11223344", pkt.AckHandle)
|
||||
}
|
||||
if pkt.CharID != 0xDEADBEEF {
|
||||
t.Errorf("CharID = 0x%X, want 0xDEADBEEF", pkt.CharID)
|
||||
}
|
||||
if pkt.Method != 2 {
|
||||
t.Errorf("Method = %d, want 2", pkt.Method)
|
||||
}
|
||||
if pkt.Name != "" {
|
||||
t.Errorf("Name = %q, want empty", pkt.Name)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with name", func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1) // AckHandle
|
||||
bf.WriteUint32(42) // CharID
|
||||
bf.WriteUint8(1) // Method
|
||||
bf.WriteUint16(200) // Unk
|
||||
// The name is SJIS null-terminated bytes. Use ASCII-compatible bytes.
|
||||
nameBytes := []byte("Test\x00")
|
||||
bf.WriteUint8(uint8(len(nameBytes))) // lenName > 0
|
||||
bf.WriteBytes(nameBytes) // null-terminated name
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfEnumerateHouse{}
|
||||
err := pkt.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
if pkt.AckHandle != 1 {
|
||||
t.Errorf("AckHandle = %d, want 1", pkt.AckHandle)
|
||||
}
|
||||
if pkt.CharID != 42 {
|
||||
t.Errorf("CharID = %d, want 42", pkt.CharID)
|
||||
}
|
||||
if pkt.Method != 1 {
|
||||
t.Errorf("Method = %d, want 1", pkt.Method)
|
||||
}
|
||||
if pkt.Name != "Test" {
|
||||
t.Errorf("Name = %q, want %q", pkt.Name, "Test")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestParseSmallNotImplementedDoesNotPanic ensures that calling Parse on NOT IMPLEMENTED
|
||||
// packets with a nil ClientContext does not cause a nil pointer dereference panic.
|
||||
func TestParseSmallNotImplementedDoesNotPanic(t *testing.T) {
|
||||
packets := []MHFPacket{
|
||||
&MsgMhfAcceptReadReward{},
|
||||
&MsgSysAuthData{},
|
||||
&MsgSysSerialize{},
|
||||
}
|
||||
|
||||
for _, pkt := range packets {
|
||||
t.Run("nil_ctx", func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := pkt.Parse(bf, nil)
|
||||
if err == nil {
|
||||
t.Fatal("expected error, got nil")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
218
network/mhfpacket/msg_parse_test.go
Normal file
218
network/mhfpacket/msg_parse_test.go
Normal file
@@ -0,0 +1,218 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// TestMsgMhfGetAchievementParse tests MsgMhfGetAchievement parsing
|
||||
func TestMsgMhfGetAchievementDetailedParse(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0x12345678) // AckHandle
|
||||
bf.WriteUint32(54321) // CharID
|
||||
bf.WriteUint32(99999) // Unk1
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfGetAchievement{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != 0x12345678 {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x12345678", pkt.AckHandle)
|
||||
}
|
||||
if pkt.CharID != 54321 {
|
||||
t.Errorf("CharID = %d, want 54321", pkt.CharID)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgMhfAddAchievementDetailedParse tests MsgMhfAddAchievement parsing
|
||||
func TestMsgMhfAddAchievementDetailedParse(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(42) // AchievementID
|
||||
bf.WriteUint16(12345) // Unk1
|
||||
bf.WriteUint16(0xFFFF) // Unk2 - max value
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgMhfAddAchievement{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AchievementID != 42 {
|
||||
t.Errorf("AchievementID = %d, want 42", pkt.AchievementID)
|
||||
}
|
||||
if pkt.Unk1 != 12345 {
|
||||
t.Errorf("Unk1 = %d, want 12345", pkt.Unk1)
|
||||
}
|
||||
if pkt.Unk2 != 0xFFFF {
|
||||
t.Errorf("Unk2 = %d, want 65535", pkt.Unk2)
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCastBinaryDetailedParse tests MsgSysCastBinary parsing with various payloads
|
||||
func TestMsgSysCastBinaryDetailedParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
unk uint32
|
||||
broadcastType uint8
|
||||
messageType uint8
|
||||
payload []byte
|
||||
}{
|
||||
{"empty payload", 0, 1, 2, []byte{}},
|
||||
{"typical payload", 0x006400C8, 0x10, 0x20, []byte{0x01, 0x02, 0x03}},
|
||||
{"chat message", 0, 0x01, 0x01, []byte("Hello, World!")},
|
||||
{"binary data", 0xFFFFFFFF, 0xFF, 0xFF, []byte{0xDE, 0xAD, 0xBE, 0xEF}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.unk)
|
||||
bf.WriteUint8(tt.broadcastType)
|
||||
bf.WriteUint8(tt.messageType)
|
||||
bf.WriteUint16(uint16(len(tt.payload)))
|
||||
bf.WriteBytes(tt.payload)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCastBinary{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.Unk != tt.unk {
|
||||
t.Errorf("Unk = %d, want %d", pkt.Unk, tt.unk)
|
||||
}
|
||||
if pkt.BroadcastType != tt.broadcastType {
|
||||
t.Errorf("BroadcastType = %d, want %d", pkt.BroadcastType, tt.broadcastType)
|
||||
}
|
||||
if pkt.MessageType != tt.messageType {
|
||||
t.Errorf("MessageType = %d, want %d", pkt.MessageType, tt.messageType)
|
||||
}
|
||||
if len(pkt.RawDataPayload) != len(tt.payload) {
|
||||
t.Errorf("RawDataPayload len = %d, want %d", len(pkt.RawDataPayload), len(tt.payload))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysLogoutParse tests MsgSysLogout parsing
|
||||
func TestMsgSysLogoutDetailedParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
unk0 uint8
|
||||
}{
|
||||
{0},
|
||||
{1},
|
||||
{100},
|
||||
{255},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint8(tt.unk0)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLogout{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.Unk0 != tt.unk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.unk0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysBackStageParse tests MsgSysBackStage parsing
|
||||
func TestMsgSysBackStageDetailedParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
ackHandle uint32
|
||||
}{
|
||||
{0},
|
||||
{1},
|
||||
{0x12345678},
|
||||
{0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysBackStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysPingParse tests MsgSysPing parsing
|
||||
func TestMsgSysPingDetailedParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
ackHandle uint32
|
||||
}{
|
||||
{0},
|
||||
{0xABCDEF12},
|
||||
{0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysPing{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = 0x%X, want 0x%X", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysTimeParse tests MsgSysTime parsing
|
||||
func TestMsgSysTimeDetailedParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
getRemoteTime bool
|
||||
timestamp uint32
|
||||
}{
|
||||
{false, 0},
|
||||
{true, 1577836800}, // 2020-01-01 00:00:00
|
||||
{false, 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteBool(tt.getRemoteTime)
|
||||
bf.WriteUint32(tt.timestamp)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysTime{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.GetRemoteTime != tt.getRemoteTime {
|
||||
t.Errorf("GetRemoteTime = %v, want %v", pkt.GetRemoteTime, tt.getRemoteTime)
|
||||
}
|
||||
if pkt.Timestamp != tt.timestamp {
|
||||
t.Errorf("Timestamp = %d, want %d", pkt.Timestamp, tt.timestamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
310
network/mhfpacket/msg_sys_core_test.go
Normal file
310
network/mhfpacket/msg_sys_core_test.go
Normal file
@@ -0,0 +1,310 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
func TestMsgSysAckRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
isBufferResponse bool
|
||||
errorCode uint8
|
||||
ackData []byte
|
||||
}{
|
||||
{
|
||||
name: "simple non-buffer response",
|
||||
ackHandle: 1,
|
||||
isBufferResponse: false,
|
||||
errorCode: 0,
|
||||
ackData: []byte{0x00, 0x00, 0x00, 0x00},
|
||||
},
|
||||
{
|
||||
name: "buffer response with small data",
|
||||
ackHandle: 0x12345678,
|
||||
isBufferResponse: true,
|
||||
errorCode: 0,
|
||||
ackData: []byte{0x01, 0x02, 0x03, 0x04, 0x05},
|
||||
},
|
||||
{
|
||||
name: "error response",
|
||||
ackHandle: 100,
|
||||
isBufferResponse: false,
|
||||
errorCode: 1,
|
||||
ackData: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
||||
},
|
||||
{
|
||||
name: "empty buffer response",
|
||||
ackHandle: 999,
|
||||
isBufferResponse: true,
|
||||
errorCode: 0,
|
||||
ackData: []byte{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
original := &MsgSysAck{
|
||||
AckHandle: tt.ackHandle,
|
||||
IsBufferResponse: tt.isBufferResponse,
|
||||
ErrorCode: tt.errorCode,
|
||||
AckData: tt.ackData,
|
||||
}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Parse
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysAck{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
// Compare
|
||||
if parsed.AckHandle != original.AckHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", parsed.AckHandle, original.AckHandle)
|
||||
}
|
||||
if parsed.IsBufferResponse != original.IsBufferResponse {
|
||||
t.Errorf("IsBufferResponse = %v, want %v", parsed.IsBufferResponse, original.IsBufferResponse)
|
||||
}
|
||||
if parsed.ErrorCode != original.ErrorCode {
|
||||
t.Errorf("ErrorCode = %d, want %d", parsed.ErrorCode, original.ErrorCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysAckLargePayload(t *testing.T) {
|
||||
// Test with payload larger than 0xFFFF to trigger extended size field
|
||||
largeData := make([]byte, 0x10000) // 65536 bytes
|
||||
for i := range largeData {
|
||||
largeData[i] = byte(i % 256)
|
||||
}
|
||||
|
||||
original := &MsgSysAck{
|
||||
AckHandle: 1,
|
||||
IsBufferResponse: true,
|
||||
ErrorCode: 0,
|
||||
AckData: largeData,
|
||||
}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Parse
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysAck{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if len(parsed.AckData) != len(largeData) {
|
||||
t.Errorf("AckData len = %d, want %d", len(parsed.AckData), len(largeData))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysAckOpcode(t *testing.T) {
|
||||
pkt := &MsgSysAck{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ACK {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ACK", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysNopRoundTrip(t *testing.T) {
|
||||
original := &MsgSysNop{}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Should write no data
|
||||
if len(bf.Data()) != 0 {
|
||||
t.Errorf("MsgSysNop.Build() wrote %d bytes, want 0", len(bf.Data()))
|
||||
}
|
||||
|
||||
// Parse (from empty buffer)
|
||||
parsed := &MsgSysNop{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysNopOpcode(t *testing.T) {
|
||||
pkt := &MsgSysNop{}
|
||||
if pkt.Opcode() != network.MSG_SYS_NOP {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_NOP", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysEndRoundTrip(t *testing.T) {
|
||||
original := &MsgSysEnd{}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
// Build
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
// Should write no data
|
||||
if len(bf.Data()) != 0 {
|
||||
t.Errorf("MsgSysEnd.Build() wrote %d bytes, want 0", len(bf.Data()))
|
||||
}
|
||||
|
||||
// Parse (from empty buffer)
|
||||
parsed := &MsgSysEnd{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysEndOpcode(t *testing.T) {
|
||||
pkt := &MsgSysEnd{}
|
||||
if pkt.Opcode() != network.MSG_SYS_END {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_END", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysAckNonBufferResponse(t *testing.T) {
|
||||
// Non-buffer response should always read/write 4 bytes of data
|
||||
original := &MsgSysAck{
|
||||
AckHandle: 1,
|
||||
IsBufferResponse: false,
|
||||
ErrorCode: 0,
|
||||
AckData: []byte{0xAA, 0xBB, 0xCC, 0xDD},
|
||||
}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysAck{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
// Non-buffer response should have exactly 4 bytes of data
|
||||
if len(parsed.AckData) != 4 {
|
||||
t.Errorf("Non-buffer AckData len = %d, want 4", len(parsed.AckData))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysAckNonBufferShortData(t *testing.T) {
|
||||
// Non-buffer response with short data should pad to 4 bytes
|
||||
original := &MsgSysAck{
|
||||
AckHandle: 1,
|
||||
IsBufferResponse: false,
|
||||
ErrorCode: 0,
|
||||
AckData: []byte{0x01}, // Only 1 byte
|
||||
}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
err := original.Build(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Build() error = %v", err)
|
||||
}
|
||||
|
||||
bf.Seek(0, io.SeekStart)
|
||||
parsed := &MsgSysAck{}
|
||||
err = parsed.Parse(bf, ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
// Should still read 4 bytes
|
||||
if len(parsed.AckData) != 4 {
|
||||
t.Errorf("AckData len = %d, want 4", len(parsed.AckData))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysAckBuildFormat(t *testing.T) {
|
||||
pkt := &MsgSysAck{
|
||||
AckHandle: 0x12345678,
|
||||
IsBufferResponse: true,
|
||||
ErrorCode: 0x55,
|
||||
AckData: []byte{0xAA, 0xBB},
|
||||
}
|
||||
ctx := &clientctx.ClientContext{}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
pkt.Build(bf, ctx)
|
||||
|
||||
data := bf.Data()
|
||||
|
||||
// Check AckHandle (big-endian)
|
||||
if data[0] != 0x12 || data[1] != 0x34 || data[2] != 0x56 || data[3] != 0x78 {
|
||||
t.Errorf("AckHandle bytes = %X, want 12345678", data[:4])
|
||||
}
|
||||
|
||||
// Check IsBufferResponse (1 = true)
|
||||
if data[4] != 1 {
|
||||
t.Errorf("IsBufferResponse byte = %d, want 1", data[4])
|
||||
}
|
||||
|
||||
// Check ErrorCode
|
||||
if data[5] != 0x55 {
|
||||
t.Errorf("ErrorCode byte = %X, want 55", data[5])
|
||||
}
|
||||
|
||||
// Check payload size (2 bytes, big-endian)
|
||||
if data[6] != 0x00 || data[7] != 0x02 {
|
||||
t.Errorf("PayloadSize bytes = %X %X, want 00 02", data[6], data[7])
|
||||
}
|
||||
|
||||
// Check actual data
|
||||
if data[8] != 0xAA || data[9] != 0xBB {
|
||||
t.Errorf("AckData bytes = %X %X, want AA BB", data[8], data[9])
|
||||
}
|
||||
}
|
||||
|
||||
func TestCorePacketsFromOpcode(t *testing.T) {
|
||||
coreOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_NOP,
|
||||
network.MSG_SYS_END,
|
||||
network.MSG_SYS_ACK,
|
||||
network.MSG_SYS_PING,
|
||||
}
|
||||
|
||||
for _, opcode := range coreOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Fatalf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
if pkt.Opcode() != opcode {
|
||||
t.Errorf("Opcode() = %s, want %s", pkt.Opcode(), opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
592
network/mhfpacket/msg_sys_packets_test.go
Normal file
592
network/mhfpacket/msg_sys_packets_test.go
Normal file
@@ -0,0 +1,592 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
// TestMsgSysCastBinaryParse tests parsing MsgSysCastBinary
|
||||
func TestMsgSysCastBinaryParse(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
unk uint32
|
||||
broadcastType uint8
|
||||
messageType uint8
|
||||
payload []byte
|
||||
}{
|
||||
{"empty payload", 0, 1, 2, []byte{}},
|
||||
{"small payload", 0x006400C8, 3, 4, []byte{0xAA, 0xBB, 0xCC}},
|
||||
{"large payload", 0xFFFFFFFF, 0xFF, 0xFF, make([]byte, 100)},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.unk)
|
||||
bf.WriteUint8(tt.broadcastType)
|
||||
bf.WriteUint8(tt.messageType)
|
||||
bf.WriteUint16(uint16(len(tt.payload)))
|
||||
bf.WriteBytes(tt.payload)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCastBinary{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.Unk != tt.unk {
|
||||
t.Errorf("Unk = %d, want %d", pkt.Unk, tt.unk)
|
||||
}
|
||||
if pkt.BroadcastType != tt.broadcastType {
|
||||
t.Errorf("BroadcastType = %d, want %d", pkt.BroadcastType, tt.broadcastType)
|
||||
}
|
||||
if pkt.MessageType != tt.messageType {
|
||||
t.Errorf("MessageType = %d, want %d", pkt.MessageType, tt.messageType)
|
||||
}
|
||||
if len(pkt.RawDataPayload) != len(tt.payload) {
|
||||
t.Errorf("RawDataPayload len = %d, want %d", len(pkt.RawDataPayload), len(tt.payload))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCastBinaryOpcode tests Opcode method
|
||||
func TestMsgSysCastBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCastBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CAST_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CAST_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCreateSemaphoreOpcode tests Opcode method
|
||||
func TestMsgSysCreateSemaphoreOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCreateSemaphore{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CREATE_SEMAPHORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CREATE_SEMAPHORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCastedBinaryOpcode tests Opcode method
|
||||
func TestMsgSysCastedBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCastedBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CASTED_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CASTED_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysSetStageBinaryOpcode tests Opcode method
|
||||
func TestMsgSysSetStageBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysSetStageBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_SET_STAGE_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_SET_STAGE_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysGetStageBinaryOpcode tests Opcode method
|
||||
func TestMsgSysGetStageBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysGetStageBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_GET_STAGE_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_GET_STAGE_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysWaitStageBinaryOpcode tests Opcode method
|
||||
func TestMsgSysWaitStageBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysWaitStageBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_WAIT_STAGE_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_WAIT_STAGE_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysEnumerateClientOpcode tests Opcode method
|
||||
func TestMsgSysEnumerateClientOpcode(t *testing.T) {
|
||||
pkt := &MsgSysEnumerateClient{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ENUMERATE_CLIENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ENUMERATE_CLIENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysEnumerateStageOpcode tests Opcode method
|
||||
func TestMsgSysEnumerateStageOpcode(t *testing.T) {
|
||||
pkt := &MsgSysEnumerateStage{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ENUMERATE_STAGE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ENUMERATE_STAGE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCreateMutexOpcode tests Opcode method
|
||||
func TestMsgSysCreateMutexOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCreateMutex{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CREATE_MUTEX {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CREATE_MUTEX", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCreateOpenMutexOpcode tests Opcode method
|
||||
func TestMsgSysCreateOpenMutexOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCreateOpenMutex{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CREATE_OPEN_MUTEX {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CREATE_OPEN_MUTEX", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDeleteMutexOpcode tests Opcode method
|
||||
func TestMsgSysDeleteMutexOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDeleteMutex{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DELETE_MUTEX {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DELETE_MUTEX", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysOpenMutexOpcode tests Opcode method
|
||||
func TestMsgSysOpenMutexOpcode(t *testing.T) {
|
||||
pkt := &MsgSysOpenMutex{}
|
||||
if pkt.Opcode() != network.MSG_SYS_OPEN_MUTEX {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_OPEN_MUTEX", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCloseMutexOpcode tests Opcode method
|
||||
func TestMsgSysCloseMutexOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCloseMutex{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CLOSE_MUTEX {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CLOSE_MUTEX", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDeleteSemaphoreOpcode tests Opcode method
|
||||
func TestMsgSysDeleteSemaphoreOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDeleteSemaphore{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DELETE_SEMAPHORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DELETE_SEMAPHORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysAcquireSemaphoreOpcode tests Opcode method
|
||||
func TestMsgSysAcquireSemaphoreOpcode(t *testing.T) {
|
||||
pkt := &MsgSysAcquireSemaphore{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ACQUIRE_SEMAPHORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ACQUIRE_SEMAPHORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysReleaseSemaphoreOpcode tests Opcode method
|
||||
func TestMsgSysReleaseSemaphoreOpcode(t *testing.T) {
|
||||
pkt := &MsgSysReleaseSemaphore{}
|
||||
if pkt.Opcode() != network.MSG_SYS_RELEASE_SEMAPHORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_RELEASE_SEMAPHORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCheckSemaphoreOpcode tests Opcode method
|
||||
func TestMsgSysCheckSemaphoreOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCheckSemaphore{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CHECK_SEMAPHORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CHECK_SEMAPHORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCreateAcquireSemaphoreOpcode tests Opcode method
|
||||
func TestMsgSysCreateAcquireSemaphoreOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCreateAcquireSemaphore{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CREATE_ACQUIRE_SEMAPHORE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CREATE_ACQUIRE_SEMAPHORE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysOperateRegisterOpcode tests Opcode method
|
||||
func TestMsgSysOperateRegisterOpcode(t *testing.T) {
|
||||
pkt := &MsgSysOperateRegister{}
|
||||
if pkt.Opcode() != network.MSG_SYS_OPERATE_REGISTER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_OPERATE_REGISTER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysLoadRegisterOpcode tests Opcode method
|
||||
func TestMsgSysLoadRegisterOpcode(t *testing.T) {
|
||||
pkt := &MsgSysLoadRegister{}
|
||||
if pkt.Opcode() != network.MSG_SYS_LOAD_REGISTER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_LOAD_REGISTER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysNotifyRegisterOpcode tests Opcode method
|
||||
func TestMsgSysNotifyRegisterOpcode(t *testing.T) {
|
||||
pkt := &MsgSysNotifyRegister{}
|
||||
if pkt.Opcode() != network.MSG_SYS_NOTIFY_REGISTER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_NOTIFY_REGISTER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCreateObjectOpcode tests Opcode method
|
||||
func TestMsgSysCreateObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCreateObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CREATE_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CREATE_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDeleteObjectOpcode tests Opcode method
|
||||
func TestMsgSysDeleteObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDeleteObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DELETE_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DELETE_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysPositionObjectOpcode tests Opcode method
|
||||
func TestMsgSysPositionObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysPositionObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_POSITION_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_POSITION_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysRotateObjectOpcode tests Opcode method
|
||||
func TestMsgSysRotateObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysRotateObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ROTATE_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ROTATE_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDuplicateObjectOpcode tests Opcode method
|
||||
func TestMsgSysDuplicateObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDuplicateObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DUPLICATE_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DUPLICATE_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysSetObjectBinaryOpcode tests Opcode method
|
||||
func TestMsgSysSetObjectBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysSetObjectBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_SET_OBJECT_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_SET_OBJECT_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysGetObjectBinaryOpcode tests Opcode method
|
||||
func TestMsgSysGetObjectBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysGetObjectBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_GET_OBJECT_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_GET_OBJECT_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysGetObjectOwnerOpcode tests Opcode method
|
||||
func TestMsgSysGetObjectOwnerOpcode(t *testing.T) {
|
||||
pkt := &MsgSysGetObjectOwner{}
|
||||
if pkt.Opcode() != network.MSG_SYS_GET_OBJECT_OWNER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_GET_OBJECT_OWNER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysUpdateObjectBinaryOpcode tests Opcode method
|
||||
func TestMsgSysUpdateObjectBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysUpdateObjectBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_UPDATE_OBJECT_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_UPDATE_OBJECT_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCleanupObjectOpcode tests Opcode method
|
||||
func TestMsgSysCleanupObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCleanupObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_CLEANUP_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_CLEANUP_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysInsertUserOpcode tests Opcode method
|
||||
func TestMsgSysInsertUserOpcode(t *testing.T) {
|
||||
pkt := &MsgSysInsertUser{}
|
||||
if pkt.Opcode() != network.MSG_SYS_INSERT_USER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_INSERT_USER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDeleteUserOpcode tests Opcode method
|
||||
func TestMsgSysDeleteUserOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDeleteUser{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DELETE_USER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DELETE_USER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysSetUserBinaryOpcode tests Opcode method
|
||||
func TestMsgSysSetUserBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysSetUserBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_SET_USER_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_SET_USER_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysGetUserBinaryOpcode tests Opcode method
|
||||
func TestMsgSysGetUserBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysGetUserBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_GET_USER_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_GET_USER_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysNotifyUserBinaryOpcode tests Opcode method
|
||||
func TestMsgSysNotifyUserBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysNotifyUserBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_NOTIFY_USER_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_NOTIFY_USER_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysUpdateRightOpcode tests Opcode method
|
||||
func TestMsgSysUpdateRightOpcode(t *testing.T) {
|
||||
pkt := &MsgSysUpdateRight{}
|
||||
if pkt.Opcode() != network.MSG_SYS_UPDATE_RIGHT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_UPDATE_RIGHT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysAuthQueryOpcode tests Opcode method
|
||||
func TestMsgSysAuthQueryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysAuthQuery{}
|
||||
if pkt.Opcode() != network.MSG_SYS_AUTH_QUERY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_AUTH_QUERY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysAuthDataOpcode tests Opcode method
|
||||
func TestMsgSysAuthDataOpcode(t *testing.T) {
|
||||
pkt := &MsgSysAuthData{}
|
||||
if pkt.Opcode() != network.MSG_SYS_AUTH_DATA {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_AUTH_DATA", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysAuthTerminalOpcode tests Opcode method
|
||||
func TestMsgSysAuthTerminalOpcode(t *testing.T) {
|
||||
pkt := &MsgSysAuthTerminal{}
|
||||
if pkt.Opcode() != network.MSG_SYS_AUTH_TERMINAL {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_AUTH_TERMINAL", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysRightsReloadOpcode tests Opcode method
|
||||
func TestMsgSysRightsReloadOpcode(t *testing.T) {
|
||||
pkt := &MsgSysRightsReload{}
|
||||
if pkt.Opcode() != network.MSG_SYS_RIGHTS_RELOAD {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_RIGHTS_RELOAD", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysTerminalLogOpcode tests Opcode method
|
||||
func TestMsgSysTerminalLogOpcode(t *testing.T) {
|
||||
pkt := &MsgSysTerminalLog{}
|
||||
if pkt.Opcode() != network.MSG_SYS_TERMINAL_LOG {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_TERMINAL_LOG", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysIssueLogkeyOpcode tests Opcode method
|
||||
func TestMsgSysIssueLogkeyOpcode(t *testing.T) {
|
||||
pkt := &MsgSysIssueLogkey{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ISSUE_LOGKEY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ISSUE_LOGKEY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysRecordLogOpcode tests Opcode method
|
||||
func TestMsgSysRecordLogOpcode(t *testing.T) {
|
||||
pkt := &MsgSysRecordLog{}
|
||||
if pkt.Opcode() != network.MSG_SYS_RECORD_LOG {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_RECORD_LOG", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysEchoOpcode tests Opcode method
|
||||
func TestMsgSysEchoOpcode(t *testing.T) {
|
||||
pkt := &MsgSysEcho{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ECHO {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ECHO", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysGetFileOpcode tests Opcode method
|
||||
func TestMsgSysGetFileOpcode(t *testing.T) {
|
||||
pkt := &MsgSysGetFile{}
|
||||
if pkt.Opcode() != network.MSG_SYS_GET_FILE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_GET_FILE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysHideClientOpcode tests Opcode method
|
||||
func TestMsgSysHideClientOpcode(t *testing.T) {
|
||||
pkt := &MsgSysHideClient{}
|
||||
if pkt.Opcode() != network.MSG_SYS_HIDE_CLIENT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_HIDE_CLIENT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysSetStatusOpcode tests Opcode method
|
||||
func TestMsgSysSetStatusOpcode(t *testing.T) {
|
||||
pkt := &MsgSysSetStatus{}
|
||||
if pkt.Opcode() != network.MSG_SYS_SET_STATUS {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_SET_STATUS", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysStageDestructOpcode tests Opcode method
|
||||
func TestMsgSysStageDestructOpcode(t *testing.T) {
|
||||
pkt := &MsgSysStageDestruct{}
|
||||
if pkt.Opcode() != network.MSG_SYS_STAGE_DESTRUCT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_STAGE_DESTRUCT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysLeaveStageOpcode tests Opcode method
|
||||
func TestMsgSysLeaveStageOpcode(t *testing.T) {
|
||||
pkt := &MsgSysLeaveStage{}
|
||||
if pkt.Opcode() != network.MSG_SYS_LEAVE_STAGE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_LEAVE_STAGE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysReserveStageOpcode tests Opcode method
|
||||
func TestMsgSysReserveStageOpcode(t *testing.T) {
|
||||
pkt := &MsgSysReserveStage{}
|
||||
if pkt.Opcode() != network.MSG_SYS_RESERVE_STAGE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_RESERVE_STAGE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysUnreserveStageOpcode tests Opcode method
|
||||
func TestMsgSysUnreserveStageOpcode(t *testing.T) {
|
||||
pkt := &MsgSysUnreserveStage{}
|
||||
if pkt.Opcode() != network.MSG_SYS_UNRESERVE_STAGE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_UNRESERVE_STAGE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysSetStagePassOpcode tests Opcode method
|
||||
func TestMsgSysSetStagePassOpcode(t *testing.T) {
|
||||
pkt := &MsgSysSetStagePass{}
|
||||
if pkt.Opcode() != network.MSG_SYS_SET_STAGE_PASS {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_SET_STAGE_PASS", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysLockGlobalSemaOpcode tests Opcode method
|
||||
func TestMsgSysLockGlobalSemaOpcode(t *testing.T) {
|
||||
pkt := &MsgSysLockGlobalSema{}
|
||||
if pkt.Opcode() != network.MSG_SYS_LOCK_GLOBAL_SEMA {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_LOCK_GLOBAL_SEMA", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysUnlockGlobalSemaOpcode tests Opcode method
|
||||
func TestMsgSysUnlockGlobalSemaOpcode(t *testing.T) {
|
||||
pkt := &MsgSysUnlockGlobalSema{}
|
||||
if pkt.Opcode() != network.MSG_SYS_UNLOCK_GLOBAL_SEMA {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_UNLOCK_GLOBAL_SEMA", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysTransBinaryOpcode tests Opcode method
|
||||
func TestMsgSysTransBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysTransBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_TRANS_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_TRANS_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysCollectBinaryOpcode tests Opcode method
|
||||
func TestMsgSysCollectBinaryOpcode(t *testing.T) {
|
||||
pkt := &MsgSysCollectBinary{}
|
||||
if pkt.Opcode() != network.MSG_SYS_COLLECT_BINARY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_COLLECT_BINARY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysGetStateOpcode tests Opcode method
|
||||
func TestMsgSysGetStateOpcode(t *testing.T) {
|
||||
pkt := &MsgSysGetState{}
|
||||
if pkt.Opcode() != network.MSG_SYS_GET_STATE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_GET_STATE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysSerializeOpcode tests Opcode method
|
||||
func TestMsgSysSerializeOpcode(t *testing.T) {
|
||||
pkt := &MsgSysSerialize{}
|
||||
if pkt.Opcode() != network.MSG_SYS_SERIALIZE {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_SERIALIZE", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysEnumlobbyOpcode tests Opcode method
|
||||
func TestMsgSysEnumlobbyOpcode(t *testing.T) {
|
||||
pkt := &MsgSysEnumlobby{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ENUMLOBBY {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ENUMLOBBY", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysEnumuserOpcode tests Opcode method
|
||||
func TestMsgSysEnumuserOpcode(t *testing.T) {
|
||||
pkt := &MsgSysEnumuser{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ENUMUSER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ENUMUSER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysInfokyserverOpcode tests Opcode method
|
||||
func TestMsgSysInfokyserverOpcode(t *testing.T) {
|
||||
pkt := &MsgSysInfokyserver{}
|
||||
if pkt.Opcode() != network.MSG_SYS_INFOKYSERVER {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_INFOKYSERVER", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysExtendThresholdOpcode tests Opcode method
|
||||
func TestMsgSysExtendThresholdOpcode(t *testing.T) {
|
||||
pkt := &MsgSysExtendThreshold{}
|
||||
if pkt.Opcode() != network.MSG_SYS_EXTEND_THRESHOLD {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_EXTEND_THRESHOLD", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysAddObjectOpcode tests Opcode method
|
||||
func TestMsgSysAddObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysAddObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_ADD_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_ADD_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDelObjectOpcode tests Opcode method
|
||||
func TestMsgSysDelObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDelObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DEL_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DEL_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysDispObjectOpcode tests Opcode method
|
||||
func TestMsgSysDispObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysDispObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_DISP_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_DISP_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
|
||||
// TestMsgSysHideObjectOpcode tests Opcode method
|
||||
func TestMsgSysHideObjectOpcode(t *testing.T) {
|
||||
pkt := &MsgSysHideObject{}
|
||||
if pkt.Opcode() != network.MSG_SYS_HIDE_OBJECT {
|
||||
t.Errorf("Opcode() = %s, want MSG_SYS_HIDE_OBJECT", pkt.Opcode())
|
||||
}
|
||||
}
|
||||
332
network/mhfpacket/msg_sys_stage_test.go
Normal file
332
network/mhfpacket/msg_sys_stage_test.go
Normal file
@@ -0,0 +1,332 @@
|
||||
package mhfpacket
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network"
|
||||
"erupe-ce/network/clientctx"
|
||||
)
|
||||
|
||||
func TestStagePacketOpcodes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pkt MHFPacket
|
||||
expect network.PacketID
|
||||
}{
|
||||
{"MsgSysCreateStage", &MsgSysCreateStage{}, network.MSG_SYS_CREATE_STAGE},
|
||||
{"MsgSysEnterStage", &MsgSysEnterStage{}, network.MSG_SYS_ENTER_STAGE},
|
||||
{"MsgSysMoveStage", &MsgSysMoveStage{}, network.MSG_SYS_MOVE_STAGE},
|
||||
{"MsgSysBackStage", &MsgSysBackStage{}, network.MSG_SYS_BACK_STAGE},
|
||||
{"MsgSysLockStage", &MsgSysLockStage{}, network.MSG_SYS_LOCK_STAGE},
|
||||
{"MsgSysUnlockStage", &MsgSysUnlockStage{}, network.MSG_SYS_UNLOCK_STAGE},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := tt.pkt.Opcode(); got != tt.expect {
|
||||
t.Errorf("Opcode() = %v, want %v", got, tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysCreateStageFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ackHandle uint32
|
||||
unk0 uint8
|
||||
playerCount uint8
|
||||
stageID string
|
||||
}{
|
||||
{"empty stage", 1, 1, 4, ""},
|
||||
{"mezeporta", 0x12345678, 2, 8, "sl1Ns200p0a0u0"},
|
||||
{"quest room", 100, 1, 4, "q1234"},
|
||||
{"max players", 0xFFFFFFFF, 2, 16, "max_stage"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.ackHandle)
|
||||
bf.WriteUint8(tt.unk0)
|
||||
bf.WriteUint8(tt.playerCount)
|
||||
stageIDBytes := []byte(tt.stageID)
|
||||
bf.WriteUint8(uint8(len(stageIDBytes)))
|
||||
bf.WriteBytes(append(stageIDBytes, 0x00))
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCreateStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.ackHandle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.ackHandle)
|
||||
}
|
||||
if pkt.Unk0 != tt.unk0 {
|
||||
t.Errorf("Unk0 = %d, want %d", pkt.Unk0, tt.unk0)
|
||||
}
|
||||
if pkt.PlayerCount != tt.playerCount {
|
||||
t.Errorf("PlayerCount = %d, want %d", pkt.PlayerCount, tt.playerCount)
|
||||
}
|
||||
if pkt.StageID != tt.stageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.stageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysEnterStageFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handle uint32
|
||||
unk bool
|
||||
stageID string
|
||||
}{
|
||||
{"enter town", 1, false, "town01"},
|
||||
{"force enter", 2, true, "quest_stage"},
|
||||
{"rasta bar", 999, false, "sl1Ns211p0a0u0"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.handle)
|
||||
bf.WriteBool(tt.unk)
|
||||
stageIDBytes := []byte(tt.stageID)
|
||||
bf.WriteUint8(uint8(len(stageIDBytes)))
|
||||
bf.WriteBytes(append(stageIDBytes, 0x00))
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysEnterStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.handle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.handle)
|
||||
}
|
||||
if pkt.Unk != tt.unk {
|
||||
t.Errorf("Unk = %v, want %v", pkt.Unk, tt.unk)
|
||||
}
|
||||
if pkt.StageID != tt.stageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.stageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysMoveStageFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handle uint32
|
||||
unkBool uint8
|
||||
stageID string
|
||||
}{
|
||||
{"move to area", 1, 0, "area01"},
|
||||
{"move to quest", 0xABCD, 1, "quest12345"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.handle)
|
||||
bf.WriteUint8(tt.unkBool)
|
||||
stageIDBytes := []byte(tt.stageID)
|
||||
bf.WriteUint8(uint8(len(stageIDBytes)))
|
||||
bf.WriteBytes(stageIDBytes)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysMoveStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.handle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.handle)
|
||||
}
|
||||
if pkt.UnkBool != tt.unkBool {
|
||||
t.Errorf("UnkBool = %d, want %d", pkt.UnkBool, tt.unkBool)
|
||||
}
|
||||
if pkt.StageID != tt.stageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.stageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysLockStageFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handle uint32
|
||||
unk0 uint8
|
||||
unk1 uint8
|
||||
stageID string
|
||||
}{
|
||||
{"lock room", 1, 1, 1, "room01"},
|
||||
{"private party", 0x1234, 1, 1, "party_stage"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.handle)
|
||||
bf.WriteUint8(tt.unk0)
|
||||
bf.WriteUint8(tt.unk1)
|
||||
stageIDBytes := []byte(tt.stageID)
|
||||
bf.WriteUint8(uint8(len(stageIDBytes)))
|
||||
bf.WriteBytes(append(stageIDBytes, 0x00))
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysLockStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.handle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.handle)
|
||||
}
|
||||
// Unk0 and Unk1 are read but discarded by Parse, so we only verify
|
||||
// that Parse consumed the bytes without error
|
||||
if pkt.StageID != tt.stageID {
|
||||
t.Errorf("StageID = %q, want %q", pkt.StageID, tt.stageID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysUnlockStageFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
unk0 uint16
|
||||
}{
|
||||
{"zero", 0},
|
||||
{"typical", 1},
|
||||
{"max", 0xFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(tt.unk0)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysUnlockStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
// MsgSysUnlockStage is an empty struct; Parse reads and discards a uint16.
|
||||
// We just verify Parse doesn't error.
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMsgSysBackStageFields(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
handle uint32
|
||||
}{
|
||||
{"small handle", 1},
|
||||
{"large handle", 0xDEADBEEF},
|
||||
{"zero", 0},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(tt.handle)
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysBackStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.AckHandle != tt.handle {
|
||||
t.Errorf("AckHandle = %d, want %d", pkt.AckHandle, tt.handle)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStageIDEdgeCases(t *testing.T) {
|
||||
t.Run("long stage ID", func(t *testing.T) {
|
||||
// Stage ID with max length (255 bytes)
|
||||
longID := make([]byte, 200)
|
||||
for i := range longID {
|
||||
longID[i] = 'a' + byte(i%26)
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint8(1)
|
||||
bf.WriteUint8(4)
|
||||
bf.WriteUint8(uint8(len(longID)))
|
||||
bf.WriteBytes(append(longID, 0x00))
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysCreateStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
if pkt.StageID != string(longID) {
|
||||
t.Errorf("StageID length = %d, want %d", len(pkt.StageID), len(longID))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("stage ID with null terminator", func(t *testing.T) {
|
||||
// String terminated with null byte
|
||||
stageID := "test\x00extra"
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(1)
|
||||
bf.WriteUint8(0)
|
||||
bf.WriteUint8(uint8(len(stageID)))
|
||||
bf.WriteBytes([]byte(stageID))
|
||||
bf.Seek(0, io.SeekStart)
|
||||
|
||||
pkt := &MsgSysEnterStage{}
|
||||
err := pkt.Parse(bf, &clientctx.ClientContext{})
|
||||
if err != nil {
|
||||
t.Fatalf("Parse() error = %v", err)
|
||||
}
|
||||
|
||||
// Should truncate at null
|
||||
if pkt.StageID != "test" {
|
||||
t.Errorf("StageID = %q, want %q (should truncate at null)", pkt.StageID, "test")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestStagePacketFromOpcode(t *testing.T) {
|
||||
stageOpcodes := []network.PacketID{
|
||||
network.MSG_SYS_CREATE_STAGE,
|
||||
network.MSG_SYS_ENTER_STAGE,
|
||||
network.MSG_SYS_BACK_STAGE,
|
||||
network.MSG_SYS_MOVE_STAGE,
|
||||
network.MSG_SYS_LOCK_STAGE,
|
||||
network.MSG_SYS_UNLOCK_STAGE,
|
||||
}
|
||||
|
||||
for _, opcode := range stageOpcodes {
|
||||
t.Run(opcode.String(), func(t *testing.T) {
|
||||
pkt := FromOpcode(opcode)
|
||||
if pkt == nil {
|
||||
t.Fatalf("FromOpcode(%s) returned nil", opcode)
|
||||
}
|
||||
if pkt.Opcode() != opcode {
|
||||
t.Errorf("Opcode() = %s, want %s", pkt.Opcode(), opcode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
211
network/packetid_test.go
Normal file
211
network/packetid_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPacketIDType(t *testing.T) {
|
||||
// PacketID is based on uint16
|
||||
var p PacketID = 0xFFFF
|
||||
if uint16(p) != 0xFFFF {
|
||||
t.Errorf("PacketID max value = %d, want %d", uint16(p), 0xFFFF)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketIDConstants(t *testing.T) {
|
||||
// Test critical packet IDs are correct
|
||||
tests := []struct {
|
||||
name string
|
||||
id PacketID
|
||||
expect uint16
|
||||
}{
|
||||
{"MSG_HEAD", MSG_HEAD, 0},
|
||||
{"MSG_SYS_END", MSG_SYS_END, 0x10},
|
||||
{"MSG_SYS_NOP", MSG_SYS_NOP, 0x11},
|
||||
{"MSG_SYS_ACK", MSG_SYS_ACK, 0x12},
|
||||
{"MSG_SYS_LOGIN", MSG_SYS_LOGIN, 0x14},
|
||||
{"MSG_SYS_LOGOUT", MSG_SYS_LOGOUT, 0x15},
|
||||
{"MSG_SYS_PING", MSG_SYS_PING, 0x17},
|
||||
{"MSG_SYS_TIME", MSG_SYS_TIME, 0x1A},
|
||||
{"MSG_SYS_CREATE_STAGE", MSG_SYS_CREATE_STAGE, 0x20},
|
||||
{"MSG_SYS_ENTER_STAGE", MSG_SYS_ENTER_STAGE, 0x22},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if uint16(tt.id) != tt.expect {
|
||||
t.Errorf("%s = 0x%X, want 0x%X", tt.name, uint16(tt.id), tt.expect)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketIDString(t *testing.T) {
|
||||
// Test that String() method works for known packet IDs
|
||||
tests := []struct {
|
||||
id PacketID
|
||||
contains string
|
||||
}{
|
||||
{MSG_HEAD, "MSG_HEAD"},
|
||||
{MSG_SYS_PING, "MSG_SYS_PING"},
|
||||
{MSG_SYS_END, "MSG_SYS_END"},
|
||||
{MSG_SYS_NOP, "MSG_SYS_NOP"},
|
||||
{MSG_SYS_ACK, "MSG_SYS_ACK"},
|
||||
{MSG_SYS_LOGIN, "MSG_SYS_LOGIN"},
|
||||
{MSG_SYS_LOGOUT, "MSG_SYS_LOGOUT"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.contains, func(t *testing.T) {
|
||||
got := tt.id.String()
|
||||
if got != tt.contains {
|
||||
t.Errorf("String() = %q, want %q", got, tt.contains)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketIDUnknown(t *testing.T) {
|
||||
// Unknown packet ID should still have a valid string representation
|
||||
unknown := PacketID(0xFFFF)
|
||||
str := unknown.String()
|
||||
if str == "" {
|
||||
t.Error("String() for unknown PacketID should not be empty")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketIDZero(t *testing.T) {
|
||||
// MSG_HEAD should be 0
|
||||
if MSG_HEAD != 0 {
|
||||
t.Errorf("MSG_HEAD = %d, want 0", MSG_HEAD)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemPacketIDRange(t *testing.T) {
|
||||
// System packets should be in a specific range
|
||||
systemPackets := []PacketID{
|
||||
MSG_SYS_reserve01,
|
||||
MSG_SYS_reserve02,
|
||||
MSG_SYS_reserve03,
|
||||
MSG_SYS_ADD_OBJECT,
|
||||
MSG_SYS_DEL_OBJECT,
|
||||
MSG_SYS_END,
|
||||
MSG_SYS_NOP,
|
||||
MSG_SYS_ACK,
|
||||
MSG_SYS_LOGIN,
|
||||
MSG_SYS_LOGOUT,
|
||||
MSG_SYS_PING,
|
||||
MSG_SYS_TIME,
|
||||
}
|
||||
|
||||
for _, pkt := range systemPackets {
|
||||
// System packets should have IDs > 0 (MSG_HEAD is 0)
|
||||
if pkt < MSG_SYS_reserve01 {
|
||||
t.Errorf("System packet %s has ID %d, should be >= MSG_SYS_reserve01", pkt, pkt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMHFPacketIDRange(t *testing.T) {
|
||||
// MHF packets start at MSG_MHF_SAVEDATA (0x60)
|
||||
mhfPackets := []PacketID{
|
||||
MSG_MHF_SAVEDATA,
|
||||
MSG_MHF_LOADDATA,
|
||||
MSG_MHF_ENUMERATE_QUEST,
|
||||
MSG_MHF_ACQUIRE_TITLE,
|
||||
MSG_MHF_ACQUIRE_DIST_ITEM,
|
||||
MSG_MHF_ACQUIRE_MONTHLY_ITEM,
|
||||
}
|
||||
|
||||
for _, pkt := range mhfPackets {
|
||||
// MHF packets should be >= MSG_MHF_SAVEDATA
|
||||
if pkt < MSG_MHF_SAVEDATA {
|
||||
t.Errorf("MHF packet %s has ID %d, should be >= MSG_MHF_SAVEDATA (%d)", pkt, pkt, MSG_MHF_SAVEDATA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStagePacketIDsSequential(t *testing.T) {
|
||||
// Stage-related packets should be sequential
|
||||
stagePackets := []PacketID{
|
||||
MSG_SYS_CREATE_STAGE,
|
||||
MSG_SYS_STAGE_DESTRUCT,
|
||||
MSG_SYS_ENTER_STAGE,
|
||||
MSG_SYS_BACK_STAGE,
|
||||
MSG_SYS_MOVE_STAGE,
|
||||
MSG_SYS_LEAVE_STAGE,
|
||||
MSG_SYS_LOCK_STAGE,
|
||||
MSG_SYS_UNLOCK_STAGE,
|
||||
}
|
||||
|
||||
for i := 1; i < len(stagePackets); i++ {
|
||||
if stagePackets[i] != stagePackets[i-1]+1 {
|
||||
t.Errorf("Stage packets not sequential: %s (%d) should follow %s (%d)",
|
||||
stagePackets[i], stagePackets[i], stagePackets[i-1], stagePackets[i-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPacketIDUniqueness(t *testing.T) {
|
||||
// Sample of important packet IDs should be unique
|
||||
packets := []PacketID{
|
||||
MSG_HEAD,
|
||||
MSG_SYS_END,
|
||||
MSG_SYS_NOP,
|
||||
MSG_SYS_ACK,
|
||||
MSG_SYS_LOGIN,
|
||||
MSG_SYS_LOGOUT,
|
||||
MSG_SYS_PING,
|
||||
MSG_SYS_TIME,
|
||||
MSG_SYS_CREATE_STAGE,
|
||||
MSG_SYS_ENTER_STAGE,
|
||||
MSG_MHF_SAVEDATA,
|
||||
MSG_MHF_LOADDATA,
|
||||
}
|
||||
|
||||
seen := make(map[PacketID]bool)
|
||||
for _, pkt := range packets {
|
||||
if seen[pkt] {
|
||||
t.Errorf("Duplicate PacketID: %s (%d)", pkt, pkt)
|
||||
}
|
||||
seen[pkt] = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcquirePacketIDs(t *testing.T) {
|
||||
// Verify acquire-related packet IDs exist and are correct type
|
||||
acquirePackets := []PacketID{
|
||||
MSG_MHF_ACQUIRE_DIST_ITEM,
|
||||
MSG_MHF_ACQUIRE_TITLE,
|
||||
MSG_MHF_ACQUIRE_ITEM,
|
||||
MSG_MHF_ACQUIRE_MONTHLY_ITEM,
|
||||
MSG_MHF_ACQUIRE_CAFE_ITEM,
|
||||
MSG_MHF_ACQUIRE_GUILD_TRESURE,
|
||||
}
|
||||
|
||||
for _, pkt := range acquirePackets {
|
||||
str := pkt.String()
|
||||
if str == "" {
|
||||
t.Errorf("PacketID %d should have a string representation", pkt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGuildPacketIDs(t *testing.T) {
|
||||
// Verify guild-related packet IDs
|
||||
guildPackets := []PacketID{
|
||||
MSG_MHF_CREATE_GUILD,
|
||||
MSG_MHF_OPERATE_GUILD,
|
||||
MSG_MHF_OPERATE_GUILD_MEMBER,
|
||||
MSG_MHF_INFO_GUILD,
|
||||
MSG_MHF_ENUMERATE_GUILD,
|
||||
MSG_MHF_UPDATE_GUILD,
|
||||
}
|
||||
|
||||
for _, pkt := range guildPackets {
|
||||
// All guild packets should be MHF packets
|
||||
if pkt < MSG_MHF_SAVEDATA {
|
||||
t.Errorf("Guild packet %s should be an MHF packet (>= 0x60)", pkt)
|
||||
}
|
||||
}
|
||||
}
|
||||
229
server/channelserver/handlers_register_test.go
Normal file
229
server/channelserver/handlers_register_test.go
Normal file
@@ -0,0 +1,229 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
// createMockServerWithRaviente creates a mock server with raviente and semaphore
|
||||
// initialized, which the base createMockServer() does not do.
|
||||
func createMockServerWithRaviente() *Server {
|
||||
s := createMockServer()
|
||||
s.raviente = &Raviente{
|
||||
register: make([]uint32, 30),
|
||||
state: make([]uint32, 30),
|
||||
support: make([]uint32, 30),
|
||||
}
|
||||
s.semaphore = make(map[string]*Semaphore)
|
||||
return s
|
||||
}
|
||||
|
||||
func TestRavienteInitialization(t *testing.T) {
|
||||
r := &Raviente{
|
||||
register: make([]uint32, 30),
|
||||
state: make([]uint32, 30),
|
||||
support: make([]uint32, 30),
|
||||
}
|
||||
if r == nil {
|
||||
t.Fatal("Raviente is nil")
|
||||
}
|
||||
if len(r.register) != 30 {
|
||||
t.Errorf("register length = %d, want 30", len(r.register))
|
||||
}
|
||||
if len(r.state) != 30 {
|
||||
t.Errorf("state length = %d, want 30", len(r.state))
|
||||
}
|
||||
if len(r.support) != 30 {
|
||||
t.Errorf("support length = %d, want 30", len(r.support))
|
||||
}
|
||||
// All values should be zero-initialized
|
||||
for i, v := range r.register {
|
||||
if v != 0 {
|
||||
t.Errorf("register[%d] = %d, want 0", i, v)
|
||||
}
|
||||
}
|
||||
for i, v := range r.state {
|
||||
if v != 0 {
|
||||
t.Errorf("state[%d] = %d, want 0", i, v)
|
||||
}
|
||||
}
|
||||
for i, v := range r.support {
|
||||
if v != 0 {
|
||||
t.Errorf("support[%d] = %d, want 0", i, v)
|
||||
}
|
||||
}
|
||||
if r.id != 0 {
|
||||
t.Errorf("id = %d, want 0", r.id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRavienteMutex(t *testing.T) {
|
||||
r := &Raviente{
|
||||
register: make([]uint32, 30),
|
||||
state: make([]uint32, 30),
|
||||
support: make([]uint32, 30),
|
||||
}
|
||||
|
||||
// Test that we can lock and unlock without deadlock
|
||||
r.Lock()
|
||||
r.register[0] = 42
|
||||
r.Unlock()
|
||||
|
||||
r.Lock()
|
||||
val := r.register[0]
|
||||
r.Unlock()
|
||||
|
||||
if val != 42 {
|
||||
t.Errorf("register[0] = %d, want 42", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRavienteDataAccess(t *testing.T) {
|
||||
r := &Raviente{
|
||||
register: make([]uint32, 30),
|
||||
state: make([]uint32, 30),
|
||||
support: make([]uint32, 30),
|
||||
}
|
||||
|
||||
// Write and verify register data
|
||||
r.register[0] = 100
|
||||
r.register[4] = 200
|
||||
r.register[29] = 300
|
||||
|
||||
if r.register[0] != 100 {
|
||||
t.Errorf("register[0] = %d, want 100", r.register[0])
|
||||
}
|
||||
if r.register[4] != 200 {
|
||||
t.Errorf("register[4] = %d, want 200", r.register[4])
|
||||
}
|
||||
if r.register[29] != 300 {
|
||||
t.Errorf("register[29] = %d, want 300", r.register[29])
|
||||
}
|
||||
|
||||
// Write and verify state data
|
||||
r.state[0] = 500
|
||||
r.state[28] = 600
|
||||
|
||||
if r.state[0] != 500 {
|
||||
t.Errorf("state[0] = %d, want 500", r.state[0])
|
||||
}
|
||||
if r.state[28] != 600 {
|
||||
t.Errorf("state[28] = %d, want 600", r.state[28])
|
||||
}
|
||||
|
||||
// Write and verify support data
|
||||
r.support[0] = 700
|
||||
r.support[24] = 800
|
||||
|
||||
if r.support[0] != 700 {
|
||||
t.Errorf("support[0] = %d, want 700", r.support[0])
|
||||
}
|
||||
if r.support[24] != 800 {
|
||||
t.Errorf("support[24] = %d, want 800", r.support[24])
|
||||
}
|
||||
}
|
||||
|
||||
func TestRavienteID(t *testing.T) {
|
||||
r := &Raviente{
|
||||
register: make([]uint32, 30),
|
||||
state: make([]uint32, 30),
|
||||
support: make([]uint32, 30),
|
||||
}
|
||||
|
||||
r.id = 12345
|
||||
if r.id != 12345 {
|
||||
t.Errorf("id = %d, want 12345", r.id)
|
||||
}
|
||||
|
||||
r.id = 0xFFFF
|
||||
if r.id != 0xFFFF {
|
||||
t.Errorf("id = %d, want %d", r.id, uint16(0xFFFF))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateMockServerWithRaviente(t *testing.T) {
|
||||
s := createMockServerWithRaviente()
|
||||
if s == nil {
|
||||
t.Fatal("createMockServerWithRaviente() returned nil")
|
||||
}
|
||||
if s.raviente == nil {
|
||||
t.Fatal("raviente should not be nil")
|
||||
}
|
||||
if s.semaphore == nil {
|
||||
t.Fatal("semaphore should not be nil")
|
||||
}
|
||||
if len(s.raviente.register) != 30 {
|
||||
t.Errorf("raviente register length = %d, want 30", len(s.raviente.register))
|
||||
}
|
||||
if len(s.raviente.state) != 30 {
|
||||
t.Errorf("raviente state length = %d, want 30", len(s.raviente.state))
|
||||
}
|
||||
if len(s.raviente.support) != 30 {
|
||||
t.Errorf("raviente support length = %d, want 30", len(s.raviente.support))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerTableRegistered(t *testing.T) {
|
||||
s := createMockServer()
|
||||
if s == nil {
|
||||
t.Fatal("createMockServer() returned nil")
|
||||
}
|
||||
|
||||
// Verify handler table is populated
|
||||
if len(handlerTable) == 0 {
|
||||
t.Error("handlers table should not be empty")
|
||||
}
|
||||
|
||||
// Check that key handler types are registered
|
||||
// (these are critical handlers that must always be present)
|
||||
criticalHandlers := []string{
|
||||
"handleMsgSysCreateStage",
|
||||
"handleMsgSysStageDestruct",
|
||||
}
|
||||
_ = criticalHandlers // We just verify the table is non-empty since handler function names aren't directly accessible
|
||||
|
||||
// Verify minimum handler count
|
||||
if len(handlerTable) < 50 {
|
||||
t.Errorf("handlers count = %d, expected at least 50", len(handlerTable))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlerTableNilSession(t *testing.T) {
|
||||
// This test verifies that the handler table exists and has entries
|
||||
// but doesn't call handlers (which would require a real session)
|
||||
_ = createMockServer()
|
||||
|
||||
count := 0
|
||||
for range handlerTable {
|
||||
count++
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
t.Error("No handlers registered")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockServerPacketHandling(t *testing.T) {
|
||||
s := createMockServerWithRaviente()
|
||||
session := createMockSession(1, s)
|
||||
|
||||
// Verify the session and server are properly linked
|
||||
if session.server != s {
|
||||
t.Error("Session server reference mismatch")
|
||||
}
|
||||
|
||||
// Verify byteframe can be created for packet construction
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint32(0) // AckHandle
|
||||
if len(bf.Data()) != 4 {
|
||||
t.Errorf("ByteFrame length = %d, want 4", len(bf.Data()))
|
||||
}
|
||||
|
||||
// Verify packet types can be instantiated
|
||||
pkt := &mhfpacket.MsgSysAck{}
|
||||
if pkt == nil {
|
||||
t.Error("Failed to create MsgSysAck")
|
||||
}
|
||||
}
|
||||
522
server/entranceserver/entrance_server_test.go
Normal file
522
server/entranceserver/entrance_server_test.go
Normal file
@@ -0,0 +1,522 @@
|
||||
package entranceserver
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_config "erupe-ce/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Logger: nil,
|
||||
DB: nil,
|
||||
ErupeConfig: &_config.Config{},
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
if s == nil {
|
||||
t.Fatal("NewServer() returned nil")
|
||||
}
|
||||
if s.isShuttingDown {
|
||||
t.Error("New server should not be shutting down")
|
||||
}
|
||||
if s.erupeConfig == nil {
|
||||
t.Error("erupeConfig should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServerWithNilConfig(t *testing.T) {
|
||||
cfg := &Config{}
|
||||
s := NewServer(cfg)
|
||||
if s == nil {
|
||||
t.Fatal("NewServer() returned nil for empty config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerType(t *testing.T) {
|
||||
s := &Server{}
|
||||
if s.isShuttingDown {
|
||||
t.Error("Zero value server should not be shutting down")
|
||||
}
|
||||
if s.listener != nil {
|
||||
t.Error("Zero value server should have nil listener")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFields(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Logger: nil,
|
||||
DB: nil,
|
||||
ErupeConfig: nil,
|
||||
}
|
||||
|
||||
if cfg.Logger != nil {
|
||||
t.Error("Config Logger should be nil")
|
||||
}
|
||||
if cfg.DB != nil {
|
||||
t.Error("Config DB should be nil")
|
||||
}
|
||||
if cfg.ErupeConfig != nil {
|
||||
t.Error("Config ErupeConfig should be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerShutdownFlag(t *testing.T) {
|
||||
cfg := &Config{
|
||||
ErupeConfig: &_config.Config{},
|
||||
}
|
||||
s := NewServer(cfg)
|
||||
|
||||
if s.isShuttingDown {
|
||||
t.Error("New server should not be shutting down")
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
s.isShuttingDown = true
|
||||
s.Unlock()
|
||||
|
||||
if !s.isShuttingDown {
|
||||
t.Error("Server should be shutting down after flag is set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerConfigStorage(t *testing.T) {
|
||||
erupeConfig := &_config.Config{
|
||||
Host: "192.168.1.100",
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 53310,
|
||||
Entries: []_config.EntranceServerInfo{
|
||||
{
|
||||
Name: "Test Server",
|
||||
IP: "127.0.0.1",
|
||||
Type: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
|
||||
if s.erupeConfig.Host != "192.168.1.100" {
|
||||
t.Errorf("Host = %s, want 192.168.1.100", s.erupeConfig.Host)
|
||||
}
|
||||
if s.erupeConfig.Entrance.Port != 53310 {
|
||||
t.Errorf("Entrance.Port = %d, want 53310", s.erupeConfig.Entrance.Port)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerEntranceEntries(t *testing.T) {
|
||||
entries := []_config.EntranceServerInfo{
|
||||
{
|
||||
Name: "World 1",
|
||||
IP: "10.0.0.1",
|
||||
Type: 1,
|
||||
Recommended: 1,
|
||||
Channels: []_config.EntranceChannelInfo{
|
||||
{Port: 54001, MaxPlayers: 100},
|
||||
{Port: 54002, MaxPlayers: 100},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "World 2",
|
||||
IP: "10.0.0.2",
|
||||
Type: 2,
|
||||
Recommended: 0,
|
||||
Channels: []_config.EntranceChannelInfo{
|
||||
{Port: 54003, MaxPlayers: 50},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 53310,
|
||||
Entries: entries,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{ErupeConfig: erupeConfig}
|
||||
s := NewServer(cfg)
|
||||
|
||||
if len(s.erupeConfig.Entrance.Entries) != 2 {
|
||||
t.Errorf("Entries count = %d, want 2", len(s.erupeConfig.Entrance.Entries))
|
||||
}
|
||||
|
||||
if s.erupeConfig.Entrance.Entries[0].Name != "World 1" {
|
||||
t.Errorf("First entry name = %s, want World 1", s.erupeConfig.Entrance.Entries[0].Name)
|
||||
}
|
||||
|
||||
if len(s.erupeConfig.Entrance.Entries[0].Channels) != 2 {
|
||||
t.Errorf("First entry channels = %d, want 2", len(s.erupeConfig.Entrance.Entries[0].Channels))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptDecryptRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
key byte
|
||||
}{
|
||||
{"empty", []byte{}, 0x00},
|
||||
{"single byte", []byte{0x42}, 0x00},
|
||||
{"multiple bytes", []byte{0x01, 0x02, 0x03, 0x04}, 0x00},
|
||||
{"with key", []byte{0xDE, 0xAD, 0xBE, 0xEF}, 0x55},
|
||||
{"max key", []byte{0x01, 0x02}, 0xFF},
|
||||
{"long data", make([]byte, 100), 0x42},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
encrypted := EncryptBin8(tt.data, tt.key)
|
||||
decrypted := DecryptBin8(encrypted, tt.key)
|
||||
|
||||
if len(decrypted) != len(tt.data) {
|
||||
t.Errorf("decrypted length = %d, want %d", len(decrypted), len(tt.data))
|
||||
return
|
||||
}
|
||||
|
||||
for i := range tt.data {
|
||||
if decrypted[i] != tt.data[i] {
|
||||
t.Errorf("decrypted[%d] = 0x%X, want 0x%X", i, decrypted[i], tt.data[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcSum32Deterministic(t *testing.T) {
|
||||
data := []byte{0x01, 0x02, 0x03, 0x04, 0x05}
|
||||
|
||||
sum1 := CalcSum32(data)
|
||||
sum2 := CalcSum32(data)
|
||||
|
||||
if sum1 != sum2 {
|
||||
t.Errorf("CalcSum32 not deterministic: got 0x%X and 0x%X", sum1, sum2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcSum32DifferentInputs(t *testing.T) {
|
||||
data1 := []byte{0x01, 0x02, 0x03}
|
||||
data2 := []byte{0x01, 0x02, 0x04}
|
||||
|
||||
sum1 := CalcSum32(data1)
|
||||
sum2 := CalcSum32(data2)
|
||||
|
||||
if sum1 == sum2 {
|
||||
t.Error("Different inputs should produce different checksums")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncryptBin8KeyVariation(t *testing.T) {
|
||||
data := []byte{0x01, 0x02, 0x03, 0x04}
|
||||
|
||||
enc1 := EncryptBin8(data, 0x00)
|
||||
enc2 := EncryptBin8(data, 0x01)
|
||||
enc3 := EncryptBin8(data, 0xFF)
|
||||
|
||||
if bytesEqual(enc1, enc2) {
|
||||
t.Error("Different keys should produce different encrypted data (0x00 vs 0x01)")
|
||||
}
|
||||
if bytesEqual(enc2, enc3) {
|
||||
t.Error("Different keys should produce different encrypted data (0x01 vs 0xFF)")
|
||||
}
|
||||
}
|
||||
|
||||
func bytesEqual(a, b []byte) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func TestEncryptBin8LengthPreservation(t *testing.T) {
|
||||
lengths := []int{0, 1, 7, 8, 9, 100, 1000}
|
||||
|
||||
for _, length := range lengths {
|
||||
data := make([]byte, length)
|
||||
for i := range data {
|
||||
data[i] = byte(i % 256)
|
||||
}
|
||||
|
||||
encrypted := EncryptBin8(data, 0x42)
|
||||
if len(encrypted) != length {
|
||||
t.Errorf("EncryptBin8 length %d changed to %d", length, len(encrypted))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalcSum32LargeInput(t *testing.T) {
|
||||
data := make([]byte, 10000)
|
||||
for i := range data {
|
||||
data[i] = byte(i % 256)
|
||||
}
|
||||
|
||||
sum := CalcSum32(data)
|
||||
sum2 := CalcSum32(data)
|
||||
if sum != sum2 {
|
||||
t.Errorf("CalcSum32 inconsistent for large input: 0x%X vs 0x%X", sum, sum2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerMutexLocking(t *testing.T) {
|
||||
cfg := &Config{ErupeConfig: &_config.Config{}}
|
||||
s := NewServer(cfg)
|
||||
|
||||
s.Lock()
|
||||
s.isShuttingDown = true
|
||||
s.Unlock()
|
||||
|
||||
s.Lock()
|
||||
result := s.isShuttingDown
|
||||
s.Unlock()
|
||||
|
||||
if !result {
|
||||
t.Error("Mutex should protect isShuttingDown flag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerStartAndShutdown(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
|
||||
if s.listener == nil {
|
||||
t.Error("Server listener should not be nil after Start()")
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
if s.isShuttingDown {
|
||||
t.Error("Server should not be shutting down after Start()")
|
||||
}
|
||||
s.Unlock()
|
||||
|
||||
s.Shutdown()
|
||||
|
||||
s.Lock()
|
||||
if !s.isShuttingDown {
|
||||
t.Error("Server should be shutting down after Shutdown()")
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func TestServerStartWithInvalidPort(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Port: 1,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err == nil {
|
||||
s.Shutdown()
|
||||
t.Error("Start() should fail with invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerListenerAddress(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr()
|
||||
if addr == nil {
|
||||
t.Error("Listener address should not be nil")
|
||||
}
|
||||
|
||||
tcpAddr, ok := addr.(*net.TCPAddr)
|
||||
if !ok {
|
||||
t.Error("Listener address should be a TCP address")
|
||||
}
|
||||
|
||||
if tcpAddr.Port == 0 {
|
||||
t.Error("Listener port should be assigned")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerAcceptClientsExitsOnShutdown(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
s.Shutdown()
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
s.Lock()
|
||||
if !s.isShuttingDown {
|
||||
t.Error("Server should be marked as shutting down")
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func TestServerHandleConnectionImmediateClose(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() error: %v", err)
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestServerHandleConnectionShortInit(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() error: %v", err)
|
||||
}
|
||||
_, _ = conn.Write([]byte{0, 0, 0, 0})
|
||||
conn.Close()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestServerMultipleConnections(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Entrance: _config.Entrance{
|
||||
Enabled: true,
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
|
||||
conns := make([]net.Conn, 3)
|
||||
for i := range conns {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() %d error: %v", i, err)
|
||||
}
|
||||
conns[i] = conn
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
for _, conn := range conns {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
825
server/signserver/dbutils_test.go
Normal file
825
server/signserver/dbutils_test.go
Normal file
@@ -0,0 +1,825 @@
|
||||
package signserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func TestCharacterStruct(t *testing.T) {
|
||||
c := character{
|
||||
ID: 12345,
|
||||
IsFemale: true,
|
||||
IsNewCharacter: false,
|
||||
Name: "TestHunter",
|
||||
UnkDescString: "Test description",
|
||||
HR: 999,
|
||||
GR: 300,
|
||||
WeaponType: 5,
|
||||
LastLogin: 1700000000,
|
||||
}
|
||||
|
||||
if c.ID != 12345 {
|
||||
t.Errorf("ID = %d, want 12345", c.ID)
|
||||
}
|
||||
if c.IsFemale != true {
|
||||
t.Error("IsFemale should be true")
|
||||
}
|
||||
if c.IsNewCharacter != false {
|
||||
t.Error("IsNewCharacter should be false")
|
||||
}
|
||||
if c.Name != "TestHunter" {
|
||||
t.Errorf("Name = %s, want TestHunter", c.Name)
|
||||
}
|
||||
if c.UnkDescString != "Test description" {
|
||||
t.Errorf("UnkDescString = %s, want Test description", c.UnkDescString)
|
||||
}
|
||||
if c.HR != 999 {
|
||||
t.Errorf("HR = %d, want 999", c.HR)
|
||||
}
|
||||
if c.GR != 300 {
|
||||
t.Errorf("GR = %d, want 300", c.GR)
|
||||
}
|
||||
if c.WeaponType != 5 {
|
||||
t.Errorf("WeaponType = %d, want 5", c.WeaponType)
|
||||
}
|
||||
if c.LastLogin != 1700000000 {
|
||||
t.Errorf("LastLogin = %d, want 1700000000", c.LastLogin)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterStructDefaults(t *testing.T) {
|
||||
c := character{}
|
||||
|
||||
if c.ID != 0 {
|
||||
t.Errorf("default ID = %d, want 0", c.ID)
|
||||
}
|
||||
if c.IsFemale != false {
|
||||
t.Error("default IsFemale should be false")
|
||||
}
|
||||
if c.IsNewCharacter != false {
|
||||
t.Error("default IsNewCharacter should be false")
|
||||
}
|
||||
if c.Name != "" {
|
||||
t.Errorf("default Name = %s, want empty", c.Name)
|
||||
}
|
||||
if c.HR != 0 {
|
||||
t.Errorf("default HR = %d, want 0", c.HR)
|
||||
}
|
||||
if c.GR != 0 {
|
||||
t.Errorf("default GR = %d, want 0", c.GR)
|
||||
}
|
||||
if c.WeaponType != 0 {
|
||||
t.Errorf("default WeaponType = %d, want 0", c.WeaponType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMembersStruct(t *testing.T) {
|
||||
m := members{
|
||||
CID: 100,
|
||||
ID: 200,
|
||||
Name: "FriendName",
|
||||
}
|
||||
|
||||
if m.CID != 100 {
|
||||
t.Errorf("CID = %d, want 100", m.CID)
|
||||
}
|
||||
if m.ID != 200 {
|
||||
t.Errorf("ID = %d, want 200", m.ID)
|
||||
}
|
||||
if m.Name != "FriendName" {
|
||||
t.Errorf("Name = %s, want FriendName", m.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMembersStructDefaults(t *testing.T) {
|
||||
m := members{}
|
||||
|
||||
if m.CID != 0 {
|
||||
t.Errorf("default CID = %d, want 0", m.CID)
|
||||
}
|
||||
if m.ID != 0 {
|
||||
t.Errorf("default ID = %d, want 0", m.ID)
|
||||
}
|
||||
if m.Name != "" {
|
||||
t.Errorf("default Name = %s, want empty", m.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterWeaponTypes(t *testing.T) {
|
||||
weaponTypes := []uint16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}
|
||||
|
||||
for _, wt := range weaponTypes {
|
||||
c := character{WeaponType: wt}
|
||||
if c.WeaponType != wt {
|
||||
t.Errorf("WeaponType = %d, want %d", c.WeaponType, wt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterHRRange(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
hr uint16
|
||||
}{
|
||||
{"min", 0},
|
||||
{"beginner", 1},
|
||||
{"hr30", 30},
|
||||
{"hr50", 50},
|
||||
{"hr99", 99},
|
||||
{"hr299", 299},
|
||||
{"hr998", 998},
|
||||
{"hr999", 999},
|
||||
{"max uint16", 65535},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := character{HR: tt.hr}
|
||||
if c.HR != tt.hr {
|
||||
t.Errorf("HR = %d, want %d", c.HR, tt.hr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterGRRange(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
gr uint16
|
||||
}{
|
||||
{"min", 0},
|
||||
{"gr1", 1},
|
||||
{"gr100", 100},
|
||||
{"gr300", 300},
|
||||
{"gr999", 999},
|
||||
{"max uint16", 65535},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := character{GR: tt.gr}
|
||||
if c.GR != tt.gr {
|
||||
t.Errorf("GR = %d, want %d", c.GR, tt.gr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterIDRange(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id uint32
|
||||
}{
|
||||
{"min", 0},
|
||||
{"small", 1},
|
||||
{"medium", 1000000},
|
||||
{"large", 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := character{ID: tt.id}
|
||||
if c.ID != tt.id {
|
||||
t.Errorf("ID = %d, want %d", c.ID, tt.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterGender(t *testing.T) {
|
||||
male := character{IsFemale: false}
|
||||
if male.IsFemale != false {
|
||||
t.Error("Male character should have IsFemale = false")
|
||||
}
|
||||
|
||||
female := character{IsFemale: true}
|
||||
if female.IsFemale != true {
|
||||
t.Error("Female character should have IsFemale = true")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterNewStatus(t *testing.T) {
|
||||
newChar := character{IsNewCharacter: true}
|
||||
if newChar.IsNewCharacter != true {
|
||||
t.Error("New character should have IsNewCharacter = true")
|
||||
}
|
||||
|
||||
existingChar := character{IsNewCharacter: false}
|
||||
if existingChar.IsNewCharacter != false {
|
||||
t.Error("Existing character should have IsNewCharacter = false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterNameLength(t *testing.T) {
|
||||
names := []string{
|
||||
"",
|
||||
"A",
|
||||
"Hunter",
|
||||
"LongHunterName123",
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
c := character{Name: name}
|
||||
if c.Name != name {
|
||||
t.Errorf("Name = %s, want %s", c.Name, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCharacterLastLogin(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
lastLogin uint32
|
||||
}{
|
||||
{"zero", 0},
|
||||
{"past", 1600000000},
|
||||
{"present", 1700000000},
|
||||
{"future", 1800000000},
|
||||
{"max", 0xFFFFFFFF},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := character{LastLogin: tt.lastLogin}
|
||||
if c.LastLogin != tt.lastLogin {
|
||||
t.Errorf("LastLogin = %d, want %d", c.LastLogin, tt.lastLogin)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMembersCIDAssignment(t *testing.T) {
|
||||
m := members{CID: 12345}
|
||||
if m.CID != 12345 {
|
||||
t.Errorf("CID = %d, want 12345", m.CID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleCharacters(t *testing.T) {
|
||||
chars := []character{
|
||||
{ID: 1, Name: "Char1", HR: 100},
|
||||
{ID: 2, Name: "Char2", HR: 200},
|
||||
{ID: 3, Name: "Char3", HR: 300},
|
||||
}
|
||||
|
||||
for i, c := range chars {
|
||||
expectedID := uint32(i + 1)
|
||||
if c.ID != expectedID {
|
||||
t.Errorf("chars[%d].ID = %d, want %d", i, c.ID, expectedID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleMembers(t *testing.T) {
|
||||
membersList := []members{
|
||||
{CID: 1, ID: 10, Name: "Friend1"},
|
||||
{CID: 1, ID: 20, Name: "Friend2"},
|
||||
{CID: 2, ID: 30, Name: "Friend3"},
|
||||
}
|
||||
|
||||
if membersList[0].CID != membersList[1].CID {
|
||||
t.Error("First two members should share the same CID")
|
||||
}
|
||||
|
||||
if membersList[1].CID == membersList[2].CID {
|
||||
t.Error("Third member should have different CID")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to create a test server with mocked database
|
||||
func newTestServerWithMock(t *testing.T) (*Server, sqlmock.Sqlmock) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create sqlmock: %v", err)
|
||||
}
|
||||
|
||||
sqlxDB := sqlx.NewDb(db, "sqlmock")
|
||||
|
||||
server := &Server{
|
||||
logger: zap.NewNop(),
|
||||
db: sqlxDB,
|
||||
}
|
||||
|
||||
return server, mock
|
||||
}
|
||||
|
||||
func TestGetCharactersForUser(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
rows := sqlmock.NewRows([]string{"id", "is_female", "is_new_character", "name", "unk_desc_string", "hr", "gr", "weapon_type", "last_login"}).
|
||||
AddRow(1, false, false, "Hunter1", "desc1", 100, 50, 3, 1700000000).
|
||||
AddRow(2, true, false, "Hunter2", "desc2", 200, 100, 7, 1700000001)
|
||||
|
||||
mock.ExpectQuery("SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = \\$1 AND deleted = false ORDER BY id").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(rows)
|
||||
|
||||
chars, err := server.getCharactersForUser(1)
|
||||
if err != nil {
|
||||
t.Errorf("getCharactersForUser() error: %v", err)
|
||||
}
|
||||
|
||||
if len(chars) != 2 {
|
||||
t.Errorf("getCharactersForUser() returned %d characters, want 2", len(chars))
|
||||
}
|
||||
|
||||
if chars[0].Name != "Hunter1" {
|
||||
t.Errorf("First character name = %s, want Hunter1", chars[0].Name)
|
||||
}
|
||||
|
||||
if chars[1].IsFemale != true {
|
||||
t.Error("Second character should be female")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCharactersForUserNoCharacters(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
rows := sqlmock.NewRows([]string{"id", "is_female", "is_new_character", "name", "unk_desc_string", "hr", "gr", "weapon_type", "last_login"})
|
||||
|
||||
mock.ExpectQuery("SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = \\$1 AND deleted = false ORDER BY id").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(rows)
|
||||
|
||||
chars, err := server.getCharactersForUser(1)
|
||||
if err != nil {
|
||||
t.Errorf("getCharactersForUser() error: %v", err)
|
||||
}
|
||||
|
||||
if len(chars) != 0 {
|
||||
t.Errorf("getCharactersForUser() returned %d characters, want 0", len(chars))
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCharactersForUserDBError(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = \\$1 AND deleted = false ORDER BY id").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
_, err := server.getCharactersForUser(1)
|
||||
if err == nil {
|
||||
t.Error("getCharactersForUser() should return error")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLastCID(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT last_character FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"last_character"}).AddRow(12345))
|
||||
|
||||
lastCID := server.getLastCID(1)
|
||||
if lastCID != 12345 {
|
||||
t.Errorf("getLastCID() = %d, want 12345", lastCID)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLastCIDNoResult(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT last_character FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnError(sql.ErrNoRows)
|
||||
|
||||
lastCID := server.getLastCID(1)
|
||||
if lastCID != 0 {
|
||||
t.Errorf("getLastCID() with no result = %d, want 0", lastCID)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUserRights(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT rights FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"rights"}).AddRow(30))
|
||||
|
||||
rights := server.getUserRights(1)
|
||||
if rights == 0 {
|
||||
t.Error("getUserRights() should return non-zero value")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetReturnExpiry(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
recentLogin := time.Now().Add(-time.Hour * 24)
|
||||
mock.ExpectQuery("SELECT COALESCE\\(last_login, now\\(\\)\\) FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"last_login"}).AddRow(recentLogin))
|
||||
|
||||
mock.ExpectQuery("SELECT return_expires FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"return_expires"}).AddRow(time.Now().Add(time.Hour * 24 * 30)))
|
||||
|
||||
mock.ExpectExec("UPDATE users SET last_login=\\$1 WHERE id=\\$2").
|
||||
WithArgs(sqlmock.AnyArg(), uint32(1)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
expiry := server.getReturnExpiry(1)
|
||||
|
||||
if expiry.Before(time.Now()) {
|
||||
t.Error("getReturnExpiry() should return future date")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetReturnExpiryInactiveUser(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
oldLogin := time.Now().Add(-time.Hour * 24 * 100)
|
||||
mock.ExpectQuery("SELECT COALESCE\\(last_login, now\\(\\)\\) FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"last_login"}).AddRow(oldLogin))
|
||||
|
||||
mock.ExpectExec("UPDATE users SET return_expires=\\$1 WHERE id=\\$2").
|
||||
WithArgs(sqlmock.AnyArg(), uint32(1)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
mock.ExpectExec("UPDATE users SET last_login=\\$1 WHERE id=\\$2").
|
||||
WithArgs(sqlmock.AnyArg(), uint32(1)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
expiry := server.getReturnExpiry(1)
|
||||
|
||||
if expiry.Before(time.Now()) {
|
||||
t.Error("getReturnExpiry() should return future date for inactive user")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetReturnExpiryDBError(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
recentLogin := time.Now().Add(-time.Hour * 24)
|
||||
mock.ExpectQuery("SELECT COALESCE\\(last_login, now\\(\\)\\) FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"last_login"}).AddRow(recentLogin))
|
||||
|
||||
mock.ExpectQuery("SELECT return_expires FROM users WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnError(sql.ErrNoRows)
|
||||
|
||||
mock.ExpectExec("UPDATE users SET return_expires=\\$1 WHERE id=\\$2").
|
||||
WithArgs(sqlmock.AnyArg(), uint32(1)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
mock.ExpectExec("UPDATE users SET last_login=\\$1 WHERE id=\\$2").
|
||||
WithArgs(sqlmock.AnyArg(), uint32(1)).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
expiry := server.getReturnExpiry(1)
|
||||
|
||||
if expiry.IsZero() {
|
||||
t.Error("getReturnExpiry() should return non-zero time even on error")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewUserChara(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT COUNT\\(\\*\\) FROM characters WHERE user_id = \\$1 AND is_new_character = true").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
|
||||
|
||||
mock.ExpectExec("INSERT INTO characters").
|
||||
WithArgs(uint32(1), sqlmock.AnyArg()).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
|
||||
err := server.newUserChara(1)
|
||||
if err != nil {
|
||||
t.Errorf("newUserChara() error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewUserCharaAlreadyHasNewChar(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT COUNT\\(\\*\\) FROM characters WHERE user_id = \\$1 AND is_new_character = true").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
|
||||
err := server.newUserChara(1)
|
||||
if err != nil {
|
||||
t.Errorf("newUserChara() should return nil when user already has new char: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewUserCharaCountError(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT COUNT\\(\\*\\) FROM characters WHERE user_id = \\$1 AND is_new_character = true").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
err := server.newUserChara(1)
|
||||
if err == nil {
|
||||
t.Error("newUserChara() should return error when count query fails")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewUserCharaInsertError(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT COUNT\\(\\*\\) FROM characters WHERE user_id = \\$1 AND is_new_character = true").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
|
||||
|
||||
mock.ExpectExec("INSERT INTO characters").
|
||||
WithArgs(uint32(1), sqlmock.AnyArg()).
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
err := server.newUserChara(1)
|
||||
if err == nil {
|
||||
t.Error("newUserChara() should return error when insert fails")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterDBAccount(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("INSERT INTO users \\(username, password, return_expires\\) VALUES \\(\\$1, \\$2, \\$3\\) RETURNING id").
|
||||
WithArgs("newuser", sqlmock.AnyArg(), sqlmock.AnyArg()).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1))
|
||||
|
||||
uid, err := server.registerDBAccount("newuser", "password123")
|
||||
if err != nil {
|
||||
t.Errorf("registerDBAccount() error: %v", err)
|
||||
}
|
||||
if uid != 1 {
|
||||
t.Errorf("registerDBAccount() uid = %d, want 1", uid)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRegisterDBAccountDuplicateUser(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("INSERT INTO users \\(username, password, return_expires\\) VALUES \\(\\$1, \\$2, \\$3\\) RETURNING id").
|
||||
WithArgs("existinguser", sqlmock.AnyArg(), sqlmock.AnyArg()).
|
||||
WillReturnError(sql.ErrNoRows)
|
||||
|
||||
_, err := server.registerDBAccount("existinguser", "password123")
|
||||
if err == nil {
|
||||
t.Error("registerDBAccount() should return error for duplicate user")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCharacter(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
// validateToken: SELECT count(*) FROM sign_sessions WHERE token = $1
|
||||
// When tokenID=0, query has no AND clause but both args are still passed to QueryRow
|
||||
mock.ExpectQuery("SELECT count\\(\\*\\) FROM sign_sessions WHERE token = \\$1").
|
||||
WithArgs("validtoken", uint32(0)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
|
||||
mock.ExpectQuery("SELECT is_new_character FROM characters WHERE id = \\$1").
|
||||
WithArgs(123).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"is_new_character"}).AddRow(false))
|
||||
|
||||
mock.ExpectExec("UPDATE characters SET deleted = true WHERE id = \\$1").
|
||||
WithArgs(123).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
err := server.deleteCharacter(123, "validtoken", 0)
|
||||
if err != nil {
|
||||
t.Errorf("deleteCharacter() error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteNewCharacter(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT count\\(\\*\\) FROM sign_sessions WHERE token = \\$1").
|
||||
WithArgs("validtoken", uint32(0)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
|
||||
mock.ExpectQuery("SELECT is_new_character FROM characters WHERE id = \\$1").
|
||||
WithArgs(123).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"is_new_character"}).AddRow(true))
|
||||
|
||||
mock.ExpectExec("DELETE FROM characters WHERE id = \\$1").
|
||||
WithArgs(123).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
err := server.deleteCharacter(123, "validtoken", 0)
|
||||
if err != nil {
|
||||
t.Errorf("deleteCharacter() error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCharacterInvalidToken(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT count\\(\\*\\) FROM sign_sessions WHERE token = \\$1").
|
||||
WithArgs("invalidtoken", uint32(0)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
|
||||
|
||||
err := server.deleteCharacter(123, "invalidtoken", 0)
|
||||
if err == nil {
|
||||
t.Error("deleteCharacter() should return error for invalid token")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeleteCharacterDeleteError(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
mock.ExpectQuery("SELECT count\\(\\*\\) FROM sign_sessions WHERE token = \\$1").
|
||||
WithArgs("validtoken", uint32(0)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
|
||||
mock.ExpectQuery("SELECT is_new_character FROM characters WHERE id = \\$1").
|
||||
WithArgs(123).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"is_new_character"}).AddRow(false))
|
||||
|
||||
mock.ExpectExec("UPDATE characters SET deleted = true WHERE id = \\$1").
|
||||
WithArgs(123).
|
||||
WillReturnError(sql.ErrConnDone)
|
||||
|
||||
err := server.deleteCharacter(123, "validtoken", 0)
|
||||
if err == nil {
|
||||
t.Error("deleteCharacter() should return error when update fails")
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFriendsForCharactersEmpty(t *testing.T) {
|
||||
server, _ := newTestServerWithMock(t)
|
||||
|
||||
chars := []character{}
|
||||
|
||||
friends := server.getFriendsForCharacters(chars)
|
||||
if len(friends) != 0 {
|
||||
t.Errorf("getFriendsForCharacters() for empty chars = %d, want 0", len(friends))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGuildmatesForCharactersEmpty(t *testing.T) {
|
||||
server, _ := newTestServerWithMock(t)
|
||||
|
||||
chars := []character{}
|
||||
|
||||
guildmates := server.getGuildmatesForCharacters(chars)
|
||||
if len(guildmates) != 0 {
|
||||
t.Errorf("getGuildmatesForCharacters() for empty chars = %d, want 0", len(guildmates))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetFriendsForCharacters(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
chars := []character{
|
||||
{ID: 1, Name: "Hunter1"},
|
||||
}
|
||||
|
||||
mock.ExpectQuery("SELECT friends FROM characters WHERE id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"friends"}).AddRow("2,3"))
|
||||
|
||||
mock.ExpectQuery("SELECT id, name FROM characters WHERE id=2 OR id=3").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
|
||||
AddRow(2, "Friend1").
|
||||
AddRow(3, "Friend2"))
|
||||
|
||||
friends := server.getFriendsForCharacters(chars)
|
||||
if len(friends) != 2 {
|
||||
t.Errorf("getFriendsForCharacters() = %d, want 2", len(friends))
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGuildmatesForCharacters(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
chars := []character{
|
||||
{ID: 1, Name: "Hunter1"},
|
||||
}
|
||||
|
||||
mock.ExpectQuery("SELECT count\\(\\*\\) FROM guild_characters WHERE character_id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||
|
||||
mock.ExpectQuery("SELECT guild_id FROM guild_characters WHERE character_id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"guild_id"}).AddRow(100))
|
||||
|
||||
mock.ExpectQuery("SELECT character_id AS id, c.name FROM guild_characters gc JOIN characters c ON c.id = gc.character_id WHERE guild_id=\\$1 AND character_id!=\\$2").
|
||||
WithArgs(100, uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
|
||||
AddRow(2, "Guildmate1").
|
||||
AddRow(3, "Guildmate2"))
|
||||
|
||||
guildmates := server.getGuildmatesForCharacters(chars)
|
||||
if len(guildmates) != 2 {
|
||||
t.Errorf("getGuildmatesForCharacters() = %d, want 2", len(guildmates))
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGuildmatesNotInGuild(t *testing.T) {
|
||||
server, mock := newTestServerWithMock(t)
|
||||
|
||||
chars := []character{
|
||||
{ID: 1, Name: "Hunter1"},
|
||||
}
|
||||
|
||||
mock.ExpectQuery("SELECT count\\(\\*\\) FROM guild_characters WHERE character_id=\\$1").
|
||||
WithArgs(uint32(1)).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0))
|
||||
|
||||
guildmates := server.getGuildmatesForCharacters(chars)
|
||||
if len(guildmates) != 0 {
|
||||
t.Errorf("getGuildmatesForCharacters() for non-guild member = %d, want 0", len(guildmates))
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("unfulfilled expectations: %v", err)
|
||||
}
|
||||
}
|
||||
393
server/signserver/session_test.go
Normal file
393
server/signserver/session_test.go
Normal file
@@ -0,0 +1,393 @@
|
||||
package signserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
_config "erupe-ce/config"
|
||||
"erupe-ce/network"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// mockConn implements net.Conn for testing
|
||||
type mockConn struct {
|
||||
readBuf *bytes.Buffer
|
||||
writeBuf *bytes.Buffer
|
||||
closed bool
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func newMockConn() *mockConn {
|
||||
return &mockConn{
|
||||
readBuf: new(bytes.Buffer),
|
||||
writeBuf: new(bytes.Buffer),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockConn) Read(b []byte) (n int, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.closed {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return m.readBuf.Read(b)
|
||||
}
|
||||
|
||||
func (m *mockConn) Write(b []byte) (n int, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
return m.writeBuf.Write(b)
|
||||
}
|
||||
|
||||
func (m *mockConn) Close() error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *mockConn) LocalAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 53312}
|
||||
}
|
||||
|
||||
func (m *mockConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 12345}
|
||||
}
|
||||
|
||||
func (m *mockConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (m *mockConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
func TestSessionStruct(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
conn := newMockConn()
|
||||
|
||||
s := &Session{
|
||||
logger: logger,
|
||||
server: nil,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
}
|
||||
|
||||
if s.logger != logger {
|
||||
t.Error("Session logger not set correctly")
|
||||
}
|
||||
if s.rawConn != conn {
|
||||
t.Error("Session rawConn not set correctly")
|
||||
}
|
||||
if s.cryptConn == nil {
|
||||
t.Error("Session cryptConn should not be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionStructDefaults(t *testing.T) {
|
||||
s := &Session{}
|
||||
|
||||
if s.logger != nil {
|
||||
t.Error("Default Session logger should be nil")
|
||||
}
|
||||
if s.server != nil {
|
||||
t.Error("Default Session server should be nil")
|
||||
}
|
||||
if s.rawConn != nil {
|
||||
t.Error("Default Session rawConn should be nil")
|
||||
}
|
||||
if s.cryptConn != nil {
|
||||
t.Error("Default Session cryptConn should be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionMutex(t *testing.T) {
|
||||
s := &Session{}
|
||||
|
||||
s.Lock()
|
||||
s.Unlock()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
s.Lock()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
s.Unlock()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
s.Lock()
|
||||
s.Unlock()
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestHandlePacketUnknownRequest(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
logger: logger,
|
||||
erupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
conn := newMockConn()
|
||||
session := &Session{
|
||||
logger: logger,
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteNullTerminatedBytes([]byte("UNKNOWN:100"))
|
||||
bf.WriteNullTerminatedBytes([]byte("data"))
|
||||
|
||||
err := session.handlePacket(bf.Data())
|
||||
if err != nil {
|
||||
t.Errorf("handlePacket() returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePacketWithDevModeLogging(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
DebugOptions: _config.DebugOptions{
|
||||
LogInboundMessages: true,
|
||||
},
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
logger: logger,
|
||||
erupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
conn := newMockConn()
|
||||
session := &Session{
|
||||
logger: logger,
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteNullTerminatedBytes([]byte("TEST:100"))
|
||||
|
||||
err := session.handlePacket(bf.Data())
|
||||
if err != nil {
|
||||
t.Errorf("handlePacket() with dev mode returned error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandlePacketRequestTypes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
reqType string
|
||||
}{
|
||||
{"unknown", "UNKNOWN:100"},
|
||||
{"invalid", "INVALID"},
|
||||
{"empty_version", "TEST:"},
|
||||
{"no_version", "NOVERSION"},
|
||||
{"special_chars", "TEST@#$:100"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{}
|
||||
server := &Server{
|
||||
logger: logger,
|
||||
erupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
conn := newMockConn()
|
||||
session := &Session{
|
||||
logger: logger,
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: network.NewCryptConn(conn),
|
||||
}
|
||||
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteNullTerminatedBytes([]byte(tt.reqType))
|
||||
|
||||
err := session.handlePacket(bf.Data())
|
||||
if err != nil {
|
||||
t.Errorf("handlePacket(%s) returned error: %v", tt.reqType, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockConnImplementsNetConn(t *testing.T) {
|
||||
var _ net.Conn = (*mockConn)(nil)
|
||||
}
|
||||
|
||||
func TestMockConnReadWrite(t *testing.T) {
|
||||
conn := newMockConn()
|
||||
|
||||
testData := []byte("hello")
|
||||
conn.readBuf.Write(testData)
|
||||
|
||||
buf := make([]byte, len(testData))
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
t.Errorf("Read() error: %v", err)
|
||||
}
|
||||
if n != len(testData) {
|
||||
t.Errorf("Read() n = %d, want %d", n, len(testData))
|
||||
}
|
||||
if !bytes.Equal(buf, testData) {
|
||||
t.Errorf("Read() data = %v, want %v", buf, testData)
|
||||
}
|
||||
|
||||
outData := []byte("world")
|
||||
n, err = conn.Write(outData)
|
||||
if err != nil {
|
||||
t.Errorf("Write() error: %v", err)
|
||||
}
|
||||
if n != len(outData) {
|
||||
t.Errorf("Write() n = %d, want %d", n, len(outData))
|
||||
}
|
||||
if !bytes.Equal(conn.writeBuf.Bytes(), outData) {
|
||||
t.Errorf("Write() buffer = %v, want %v", conn.writeBuf.Bytes(), outData)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockConnClose(t *testing.T) {
|
||||
conn := newMockConn()
|
||||
|
||||
err := conn.Close()
|
||||
if err != nil {
|
||||
t.Errorf("Close() error: %v", err)
|
||||
}
|
||||
|
||||
if !conn.closed {
|
||||
t.Error("conn.closed should be true after Close()")
|
||||
}
|
||||
|
||||
buf := make([]byte, 10)
|
||||
_, err = conn.Read(buf)
|
||||
if err != io.EOF {
|
||||
t.Errorf("Read() after close should return EOF, got: %v", err)
|
||||
}
|
||||
|
||||
_, err = conn.Write([]byte("test"))
|
||||
if err != io.ErrClosedPipe {
|
||||
t.Errorf("Write() after close should return ErrClosedPipe, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockConnAddresses(t *testing.T) {
|
||||
conn := newMockConn()
|
||||
|
||||
local := conn.LocalAddr()
|
||||
if local == nil {
|
||||
t.Error("LocalAddr() should not be nil")
|
||||
}
|
||||
if local.String() != "127.0.0.1:53312" {
|
||||
t.Errorf("LocalAddr() = %s, want 127.0.0.1:53312", local.String())
|
||||
}
|
||||
|
||||
remote := conn.RemoteAddr()
|
||||
if remote == nil {
|
||||
t.Error("RemoteAddr() should not be nil")
|
||||
}
|
||||
if remote.String() != "127.0.0.1:12345" {
|
||||
t.Errorf("RemoteAddr() = %s, want 127.0.0.1:12345", remote.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMockConnDeadlines(t *testing.T) {
|
||||
conn := newMockConn()
|
||||
deadline := time.Now().Add(time.Second)
|
||||
|
||||
if err := conn.SetDeadline(deadline); err != nil {
|
||||
t.Errorf("SetDeadline() error: %v", err)
|
||||
}
|
||||
if err := conn.SetReadDeadline(deadline); err != nil {
|
||||
t.Errorf("SetReadDeadline() error: %v", err)
|
||||
}
|
||||
if err := conn.SetWriteDeadline(deadline); err != nil {
|
||||
t.Errorf("SetWriteDeadline() error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionWithCryptConn(t *testing.T) {
|
||||
conn := newMockConn()
|
||||
cryptConn := network.NewCryptConn(conn)
|
||||
|
||||
if cryptConn == nil {
|
||||
t.Fatal("NewCryptConn() returned nil")
|
||||
}
|
||||
|
||||
session := &Session{
|
||||
rawConn: conn,
|
||||
cryptConn: cryptConn,
|
||||
}
|
||||
|
||||
if session.cryptConn != cryptConn {
|
||||
t.Error("Session cryptConn not set correctly")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionWorkWithDevModeLogging(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
DebugOptions: _config.DebugOptions{
|
||||
LogInboundMessages: true,
|
||||
},
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
logger: logger,
|
||||
erupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
clientConn, serverConn := net.Pipe()
|
||||
defer clientConn.Close()
|
||||
defer serverConn.Close()
|
||||
|
||||
session := &Session{
|
||||
logger: logger,
|
||||
server: server,
|
||||
rawConn: serverConn,
|
||||
cryptConn: network.NewCryptConn(serverConn),
|
||||
}
|
||||
|
||||
clientConn.Close()
|
||||
|
||||
session.work()
|
||||
}
|
||||
|
||||
func TestSessionWorkWithEmptyRead(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
logger: logger,
|
||||
erupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
clientConn, serverConn := net.Pipe()
|
||||
defer serverConn.Close()
|
||||
|
||||
session := &Session{
|
||||
logger: logger,
|
||||
server: server,
|
||||
rawConn: serverConn,
|
||||
cryptConn: network.NewCryptConn(serverConn),
|
||||
}
|
||||
|
||||
clientConn.Close()
|
||||
|
||||
session.work()
|
||||
}
|
||||
582
server/signserver/sign_server_test.go
Normal file
582
server/signserver/sign_server_test.go
Normal file
@@ -0,0 +1,582 @@
|
||||
package signserver
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_config "erupe-ce/config"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// makeSignInFailureResp creates a 1-byte failure response for the given RespID.
|
||||
func makeSignInFailureResp(id RespID) []byte {
|
||||
return []byte{uint8(id)}
|
||||
}
|
||||
|
||||
func TestRespIDConstants(t *testing.T) {
|
||||
tests := []struct {
|
||||
respID RespID
|
||||
value uint8
|
||||
}{
|
||||
{SIGN_UNKNOWN, 0},
|
||||
{SIGN_SUCCESS, 1},
|
||||
{SIGN_EFAILED, 2},
|
||||
{SIGN_EILLEGAL, 3},
|
||||
{SIGN_EALERT, 4},
|
||||
{SIGN_EABORT, 5},
|
||||
{SIGN_ERESPONSE, 6},
|
||||
{SIGN_EDATABASE, 7},
|
||||
{SIGN_EABSENCE, 8},
|
||||
{SIGN_ERESIGN, 9},
|
||||
{SIGN_ESUSPEND_D, 10},
|
||||
{SIGN_ELOCK, 11},
|
||||
{SIGN_EPASS, 12},
|
||||
{SIGN_ERIGHT, 13},
|
||||
{SIGN_EAUTH, 14},
|
||||
{SIGN_ESUSPEND, 15},
|
||||
{SIGN_EELIMINATE, 16},
|
||||
{SIGN_ECLOSE, 17},
|
||||
{SIGN_ECLOSE_EX, 18},
|
||||
{SIGN_EINTERVAL, 19},
|
||||
{SIGN_EMOVED, 20},
|
||||
{SIGN_ENOTREADY, 21},
|
||||
{SIGN_EALREADY, 22},
|
||||
{SIGN_EIPADDR, 23},
|
||||
{SIGN_EHANGAME, 24},
|
||||
{SIGN_UPD_ONLY, 25},
|
||||
{SIGN_EMBID, 26},
|
||||
{SIGN_ECOGCODE, 27},
|
||||
{SIGN_ETOKEN, 28},
|
||||
{SIGN_ECOGLINK, 29},
|
||||
{SIGN_EMAINTE, 30},
|
||||
{SIGN_EMAINTE_NOUPDATE, 31},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("RespID_%d", tt.value), func(t *testing.T) {
|
||||
if uint8(tt.respID) != tt.value {
|
||||
t.Errorf("RespID = %d, want %d", uint8(tt.respID), tt.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRespIDType(t *testing.T) {
|
||||
var r RespID = 0xFF
|
||||
if uint8(r) != 0xFF {
|
||||
t.Errorf("RespID max value = %d, want %d", uint8(r), 0xFF)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeSignInFailureResp(t *testing.T) {
|
||||
tests := []RespID{
|
||||
SIGN_UNKNOWN,
|
||||
SIGN_EFAILED,
|
||||
SIGN_EILLEGAL,
|
||||
SIGN_ESUSPEND,
|
||||
SIGN_EELIMINATE,
|
||||
SIGN_EIPADDR,
|
||||
}
|
||||
|
||||
for _, respID := range tests {
|
||||
t.Run(fmt.Sprintf("RespID_%d", respID), func(t *testing.T) {
|
||||
resp := makeSignInFailureResp(respID)
|
||||
|
||||
if len(resp) != 1 {
|
||||
t.Errorf("makeSignInFailureResp() len = %d, want 1", len(resp))
|
||||
}
|
||||
if resp[0] != uint8(respID) {
|
||||
t.Errorf("makeSignInFailureResp() = %d, want %d", resp[0], uint8(respID))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeSignInFailureRespAllCodes(t *testing.T) {
|
||||
for i := uint8(0); i <= 40; i++ {
|
||||
resp := makeSignInFailureResp(RespID(i))
|
||||
if len(resp) != 1 {
|
||||
t.Errorf("makeSignInFailureResp(%d) len = %d, want 1", i, len(resp))
|
||||
}
|
||||
if resp[0] != i {
|
||||
t.Errorf("makeSignInFailureResp(%d) = %d", i, resp[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignSuccessIsOne(t *testing.T) {
|
||||
if SIGN_SUCCESS != 1 {
|
||||
t.Errorf("SIGN_SUCCESS = %d, must be 1", SIGN_SUCCESS)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSignUnknownIsZero(t *testing.T) {
|
||||
if SIGN_UNKNOWN != 0 {
|
||||
t.Errorf("SIGN_UNKNOWN = %d, must be 0", SIGN_UNKNOWN)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRespIDValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
respID RespID
|
||||
value uint8
|
||||
}{
|
||||
{"SIGN_UNKNOWN", SIGN_UNKNOWN, 0},
|
||||
{"SIGN_SUCCESS", SIGN_SUCCESS, 1},
|
||||
{"SIGN_EFAILED", SIGN_EFAILED, 2},
|
||||
{"SIGN_EILLEGAL", SIGN_EILLEGAL, 3},
|
||||
{"SIGN_ESUSPEND", SIGN_ESUSPEND, 15},
|
||||
{"SIGN_EELIMINATE", SIGN_EELIMINATE, 16},
|
||||
{"SIGN_EIPADDR", SIGN_EIPADDR, 23},
|
||||
{"SIGN_EMAINTE", SIGN_EMAINTE, 30},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if uint8(tt.respID) != tt.value {
|
||||
t.Errorf("%s = %d, want %d", tt.name, uint8(tt.respID), tt.value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnknownRespIDRange(t *testing.T) {
|
||||
unknownIDs := []RespID{UNK_32, UNK_33, UNK_34, UNK_35}
|
||||
expectedValues := []uint8{32, 33, 34, 35}
|
||||
|
||||
for i, id := range unknownIDs {
|
||||
if uint8(id) != expectedValues[i] {
|
||||
t.Errorf("Unknown ID %d = %d, want %d", i, uint8(id), expectedValues[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSpecialRespIDs(t *testing.T) {
|
||||
if SIGN_XBRESPONSE != 36 {
|
||||
t.Errorf("SIGN_XBRESPONSE = %d, want 36", SIGN_XBRESPONSE)
|
||||
}
|
||||
if SIGN_EPSI != 37 {
|
||||
t.Errorf("SIGN_EPSI = %d, want 37", SIGN_EPSI)
|
||||
}
|
||||
if SIGN_EMBID_PSI != 38 {
|
||||
t.Errorf("SIGN_EMBID_PSI = %d, want 38", SIGN_EMBID_PSI)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeSignInFailureRespBoundary(t *testing.T) {
|
||||
resp := makeSignInFailureResp(RespID(0))
|
||||
if resp[0] != 0 {
|
||||
t.Errorf("makeSignInFailureResp(0) = %d, want 0", resp[0])
|
||||
}
|
||||
|
||||
resp = makeSignInFailureResp(RespID(255))
|
||||
if resp[0] != 255 {
|
||||
t.Errorf("makeSignInFailureResp(255) = %d, want 255", resp[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorRespIDsAreDifferent(t *testing.T) {
|
||||
seen := make(map[RespID]bool)
|
||||
errorCodes := []RespID{
|
||||
SIGN_UNKNOWN, SIGN_SUCCESS, SIGN_EFAILED, SIGN_EILLEGAL,
|
||||
SIGN_EALERT, SIGN_EABORT, SIGN_ERESPONSE, SIGN_EDATABASE,
|
||||
SIGN_EABSENCE, SIGN_ERESIGN, SIGN_ESUSPEND_D, SIGN_ELOCK,
|
||||
SIGN_EPASS, SIGN_ERIGHT, SIGN_EAUTH, SIGN_ESUSPEND,
|
||||
SIGN_EELIMINATE, SIGN_ECLOSE, SIGN_ECLOSE_EX, SIGN_EINTERVAL,
|
||||
SIGN_EMOVED, SIGN_ENOTREADY, SIGN_EALREADY, SIGN_EIPADDR,
|
||||
SIGN_EHANGAME, SIGN_UPD_ONLY, SIGN_EMBID, SIGN_ECOGCODE,
|
||||
SIGN_ETOKEN, SIGN_ECOGLINK, SIGN_EMAINTE, SIGN_EMAINTE_NOUPDATE,
|
||||
}
|
||||
|
||||
for _, code := range errorCodes {
|
||||
if seen[code] {
|
||||
t.Errorf("Duplicate RespID value: %d", code)
|
||||
}
|
||||
seen[code] = true
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailureRespIsMinimal(t *testing.T) {
|
||||
for i := RespID(0); i <= SIGN_EMBID_PSI; i++ {
|
||||
if i == SIGN_SUCCESS {
|
||||
continue
|
||||
}
|
||||
resp := makeSignInFailureResp(i)
|
||||
if len(resp) != 1 {
|
||||
t.Errorf("makeSignInFailureResp(%d) should be 1 byte, got %d", i, len(resp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServer(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Logger: nil,
|
||||
DB: nil,
|
||||
ErupeConfig: nil,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
if s == nil {
|
||||
t.Fatal("NewServer() returned nil")
|
||||
}
|
||||
if s.isShuttingDown {
|
||||
t.Error("New server should not be shutting down")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServerWithNilConfig(t *testing.T) {
|
||||
cfg := &Config{}
|
||||
s := NewServer(cfg)
|
||||
if s == nil {
|
||||
t.Fatal("NewServer() returned nil for empty config")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerType(t *testing.T) {
|
||||
s := &Server{}
|
||||
if s.isShuttingDown {
|
||||
t.Error("Zero value server should not be shutting down")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigFields(t *testing.T) {
|
||||
cfg := &Config{
|
||||
Logger: nil,
|
||||
DB: nil,
|
||||
ErupeConfig: nil,
|
||||
}
|
||||
|
||||
if cfg.Logger != nil {
|
||||
t.Error("Config Logger should be nil")
|
||||
}
|
||||
if cfg.DB != nil {
|
||||
t.Error("Config DB should be nil")
|
||||
}
|
||||
if cfg.ErupeConfig != nil {
|
||||
t.Error("Config ErupeConfig should be nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerStartAndShutdown(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
if s == nil {
|
||||
t.Fatal("NewServer() returned nil")
|
||||
}
|
||||
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
|
||||
if s.listener == nil {
|
||||
t.Error("Server listener should not be nil after Start()")
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
if s.isShuttingDown {
|
||||
t.Error("Server should not be shutting down after Start()")
|
||||
}
|
||||
s.Unlock()
|
||||
|
||||
s.Shutdown()
|
||||
|
||||
s.Lock()
|
||||
if !s.isShuttingDown {
|
||||
t.Error("Server should be shutting down after Shutdown()")
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func TestServerStartWithInvalidPort(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: -1,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
|
||||
if err == nil {
|
||||
s.Shutdown()
|
||||
t.Error("Start() should fail with invalid port")
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerMutex(t *testing.T) {
|
||||
s := &Server{}
|
||||
|
||||
s.Lock()
|
||||
s.Unlock()
|
||||
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
s.Lock()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
s.Unlock()
|
||||
done <- true
|
||||
}()
|
||||
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
|
||||
s.Lock()
|
||||
s.Unlock()
|
||||
|
||||
<-done
|
||||
}
|
||||
|
||||
func TestServerShutdownIdempotent(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
|
||||
s.Shutdown()
|
||||
|
||||
s.Lock()
|
||||
if !s.isShuttingDown {
|
||||
t.Error("Server should be shutting down")
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func TestServerAcceptClientsExitsOnShutdown(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
s.Shutdown()
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
s.Lock()
|
||||
if !s.isShuttingDown {
|
||||
t.Error("Server should be marked as shutting down")
|
||||
}
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func TestServerHandleConnection(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() error: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
nullInit := make([]byte, 8)
|
||||
_, err = conn.Write(nullInit)
|
||||
if err != nil {
|
||||
t.Fatalf("Write() error: %v", err)
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestServerHandleConnectionWithShortInit(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() error: %v", err)
|
||||
}
|
||||
|
||||
_, _ = conn.Write([]byte{0, 0, 0, 0})
|
||||
conn.Close()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestServerHandleConnectionImmediateClose(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() error: %v", err)
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
|
||||
func TestServerMultipleConnections(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr().String()
|
||||
|
||||
conns := make([]net.Conn, 3)
|
||||
for i := range conns {
|
||||
conn, err := net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
t.Fatalf("Dial() %d error: %v", i, err)
|
||||
}
|
||||
conns[i] = conn
|
||||
|
||||
nullInit := make([]byte, 8)
|
||||
_, _ = conn.Write(nullInit)
|
||||
}
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
for _, conn := range conns {
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerListenerAddress(t *testing.T) {
|
||||
logger := zap.NewNop()
|
||||
erupeConfig := &_config.Config{
|
||||
Sign: _config.Sign{
|
||||
Port: 0,
|
||||
},
|
||||
}
|
||||
|
||||
cfg := &Config{
|
||||
Logger: logger,
|
||||
ErupeConfig: erupeConfig,
|
||||
}
|
||||
|
||||
s := NewServer(cfg)
|
||||
err := s.Start()
|
||||
if err != nil {
|
||||
t.Fatalf("Start() error: %v", err)
|
||||
}
|
||||
defer s.Shutdown()
|
||||
|
||||
addr := s.listener.Addr()
|
||||
if addr == nil {
|
||||
t.Error("Listener address should not be nil")
|
||||
}
|
||||
|
||||
tcpAddr, ok := addr.(*net.TCPAddr)
|
||||
if !ok {
|
||||
t.Error("Listener address should be a TCP address")
|
||||
}
|
||||
|
||||
if tcpAddr.Port == 0 {
|
||||
t.Error("Listener port should be assigned")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user