mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-25 00:54:05 +01:00
test: add unit tests for core packages
Add comprehensive test coverage for: - common/token: token generation and RNG tests - common/stringsupport: string encoding, CSV operations - common/byteframe: binary read/write operations - common/mhfcourse: course/subscription logic - network/crypt_packet: packet header parsing - network/binpacket: binary packet round-trips - network/mhfpacket: packet interface and opcode mapping - config: configuration struct and loading - server/entranceserver: response building - server/signserver: response ID constants - server/signv2server: HTTP endpoint validation - server/channelserver: session, semaphore, and handler tests All tests pass with race detector enabled.
This commit is contained in:
463
network/mhfpacket/mhfpacket_test.go
Normal file
463
network/mhfpacket/mhfpacket_test.go
Normal file
@@ -0,0 +1,463 @@
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user