test: add unit tests for cherry-pick impacted handlers

Add comprehensive tests documenting current behavior before applying
fixes from main branch. Tests cover:

- Cafe item PointCost parsing (uint32 vs uint16 for different client modes)
- Guild poogie outfit unlock calculation bug (math.Pow issue)
- Guild manage right nil pointer condition (&& vs || logic)
- InfoGuild applicant GR field size for <G10 clients
- Stage binary wait infinite loop risk (no timeout)
- Entrance server hardcoded clan member limit
This commit is contained in:
Houmgaor
2026-01-30 01:01:21 +01:00
parent 65afcb5f41
commit 982393daf4
4 changed files with 794 additions and 0 deletions

View File

@@ -0,0 +1,172 @@
package mhfpacket
import (
"io"
"testing"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
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())
}
}