test(channelserver): add unit tests for helpers, kouryou, scenario, seibattle, and distitem handlers

Cover previously untested handler files with mock-based unit tests:
- handlers_helpers: load/save character data, ack helpers, updateRights
- handlers_kouryou: get/add/exchange points with success and error paths
- handlers_scenario: scenario counter serialization, 128-entry trim, category exchange flags
- handlers_seibattle: all type codes, Earth response format, data size validation
- handlers_distitem: enumerate/apply/acquire distributions, description retrieval
This commit is contained in:
Houmgaor
2026-02-22 18:55:31 +01:00
parent bcb5086dbb
commit 6c0269d21f
5 changed files with 1098 additions and 0 deletions

View File

@@ -0,0 +1,296 @@
package channelserver
import (
"encoding/binary"
"errors"
"testing"
"time"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
)
// --- mockDistRepo ---
type mockDistRepo struct {
distributions []Distribution
listErr error
items map[uint32][]DistributionItem
itemsErr error
description string
descErr error
recordedDist uint32
recordedChar uint32
recordErr error
}
func (m *mockDistRepo) List(_ uint32, _ uint8) ([]Distribution, error) {
return m.distributions, m.listErr
}
func (m *mockDistRepo) GetItems(distID uint32) ([]DistributionItem, error) {
if m.itemsErr != nil {
return nil, m.itemsErr
}
if m.items != nil {
return m.items[distID], nil
}
return nil, nil
}
func (m *mockDistRepo) RecordAccepted(distID, charID uint32) error {
m.recordedDist = distID
m.recordedChar = charID
return m.recordErr
}
func (m *mockDistRepo) GetDescription(_ uint32) (string, error) {
return m.description, m.descErr
}
func TestHandleMsgMhfEnumerateDistItem_Empty(t *testing.T) {
server := createMockServer()
server.erupeConfig.RealClientMode = cfg.S6
server.distRepo = &mockDistRepo{}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfEnumerateDistItem{AckHandle: 100, DistType: 0}
handleMsgMhfEnumerateDistItem(session, pkt)
select {
case p := <-session.sendPackets:
_, errCode, ackData := parseAckBufData(t, p.data)
if errCode != 0 {
t.Errorf("ErrorCode = %d, want 0", errCode)
}
count := binary.BigEndian.Uint16(ackData[:2])
if count != 0 {
t.Errorf("dist count = %d, want 0", count)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfEnumerateDistItem_WithDistributions(t *testing.T) {
server := createMockServer()
server.erupeConfig.RealClientMode = cfg.S6
server.distRepo = &mockDistRepo{
distributions: []Distribution{
{
ID: 1,
Deadline: time.Unix(1000000, 0),
Rights: 0,
TimesAcceptable: 1,
TimesAccepted: 0,
MinHR: 1,
MaxHR: 999,
EventName: "Test",
},
},
}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfEnumerateDistItem{AckHandle: 100, DistType: 0}
handleMsgMhfEnumerateDistItem(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
count := binary.BigEndian.Uint16(ackData[:2])
if count != 1 {
t.Errorf("dist count = %d, want 1", count)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfApplyDistItem_Empty(t *testing.T) {
server := createMockServer()
server.erupeConfig.RealClientMode = cfg.S6
server.distRepo = &mockDistRepo{}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfApplyDistItem{
AckHandle: 100,
DistributionID: 42,
}
handleMsgMhfApplyDistItem(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
// 4 (distID) + 2 (count=0) = 6
distID := binary.BigEndian.Uint32(ackData[:4])
if distID != 42 {
t.Errorf("distID = %d, want 42", distID)
}
itemCount := binary.BigEndian.Uint16(ackData[4:6])
if itemCount != 0 {
t.Errorf("item count = %d, want 0", itemCount)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfApplyDistItem_WithItems(t *testing.T) {
server := createMockServer()
server.erupeConfig.RealClientMode = cfg.S6
server.distRepo = &mockDistRepo{
items: map[uint32][]DistributionItem{
10: {
{ItemType: 1, ID: 100, ItemID: 200, Quantity: 5},
{ItemType: 2, ID: 101, ItemID: 300, Quantity: 3},
},
},
}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfApplyDistItem{
AckHandle: 100,
DistributionID: 10,
}
handleMsgMhfApplyDistItem(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
itemCount := binary.BigEndian.Uint16(ackData[4:6])
if itemCount != 2 {
t.Errorf("item count = %d, want 2", itemCount)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfAcquireDistItem_ZeroID(t *testing.T) {
server := createMockServer()
server.distRepo = &mockDistRepo{}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfAcquireDistItem{
AckHandle: 100,
DistributionID: 0,
}
handleMsgMhfAcquireDistItem(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Should respond")
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfAcquireDistItem_RecordAccepted(t *testing.T) {
server := createMockServer()
distRepo := &mockDistRepo{
items: map[uint32][]DistributionItem{
5: {},
},
}
server.distRepo = distRepo
session := createMockSession(1, server)
session.charID = 42
pkt := &mhfpacket.MsgMhfAcquireDistItem{
AckHandle: 100,
DistributionID: 5,
}
handleMsgMhfAcquireDistItem(session, pkt)
if distRepo.recordedDist != 5 {
t.Errorf("recorded dist ID = %d, want 5", distRepo.recordedDist)
}
if distRepo.recordedChar != 42 {
t.Errorf("recorded char ID = %d, want 42", distRepo.recordedChar)
}
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Should respond")
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfAcquireDistItem_RecordError(t *testing.T) {
server := createMockServer()
server.distRepo = &mockDistRepo{
recordErr: errors.New("db error"),
}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfAcquireDistItem{
AckHandle: 100,
DistributionID: 5,
}
handleMsgMhfAcquireDistItem(session, pkt)
// Should still send success ack
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Should respond")
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfGetDistDescription_Success(t *testing.T) {
server := createMockServer()
server.distRepo = &mockDistRepo{description: "Test event description"}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetDistDescription{
AckHandle: 100,
DistributionID: 1,
}
handleMsgMhfGetDistDescription(session, pkt)
select {
case p := <-session.sendPackets:
_, errCode, ackData := parseAckBufData(t, p.data)
if errCode != 0 {
t.Errorf("ErrorCode = %d, want 0", errCode)
}
if len(ackData) == 0 {
t.Fatal("AckData should not be empty")
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfGetDistDescription_Error(t *testing.T) {
server := createMockServer()
server.distRepo = &mockDistRepo{descErr: errors.New("not found")}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetDistDescription{
AckHandle: 100,
DistributionID: 999,
}
handleMsgMhfGetDistDescription(session, pkt)
select {
case p := <-session.sendPackets:
_, errCode, ackData := parseAckBufData(t, p.data)
if errCode != 0 {
t.Errorf("ErrorCode = %d, want 0 (still buf succeed)", errCode)
}
if len(ackData) != 4 {
t.Errorf("AckData len = %d, want 4 (fallback)", len(ackData))
}
default:
t.Fatal("No response queued")
}
}

View File

@@ -0,0 +1,211 @@
package channelserver
import (
"errors"
"testing"
)
func TestLoadCharacterData_Success(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.columns["test_col"] = []byte{0xAA, 0xBB, 0xCC}
server.charRepo = charRepo
session := createMockSession(1, server)
loadCharacterData(session, 100, "test_col", nil)
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response packet should have data")
}
default:
t.Fatal("No response packet queued")
}
}
func TestLoadCharacterData_EmptyUsesDefault(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
session := createMockSession(1, server)
defaultData := []byte{0x01, 0x02, 0x03}
loadCharacterData(session, 100, "missing_col", defaultData)
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response packet should have data")
}
default:
t.Fatal("No response packet queued")
}
}
func TestLoadCharacterData_Error(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.loadColumnErr = errors.New("db error")
server.charRepo = charRepo
session := createMockSession(1, server)
defaultData := []byte{0xFF}
loadCharacterData(session, 100, "test_col", defaultData)
// Should still send a response (with default data)
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response packet should have data even on error")
}
default:
t.Fatal("No response packet queued")
}
}
func TestSaveCharacterData_Success(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
session := createMockSession(1, server)
data := []byte{0x01, 0x02, 0x03}
saveCharacterData(session, 100, "test_col", data, 100)
// Should save and ack
if saved := charRepo.columns["test_col"]; saved == nil {
t.Error("Data should be saved to repo")
}
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response packet should have data")
}
default:
t.Fatal("No response packet queued")
}
}
func TestSaveCharacterData_TooLarge(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
session := createMockSession(1, server)
data := make([]byte, 200)
saveCharacterData(session, 100, "test_col", data, 50)
// Should fail with ack
if _, ok := charRepo.columns["test_col"]; ok {
t.Error("Data should NOT be saved when too large")
}
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response packet should have data")
}
default:
t.Fatal("Should queue a fail ack")
}
}
func TestSaveCharacterData_SaveError(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.saveErr = errors.New("save failed")
server.charRepo = charRepo
session := createMockSession(1, server)
data := []byte{0x01}
saveCharacterData(session, 100, "test_col", data, 100)
// Should still queue a fail ack
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response packet should have data")
}
default:
t.Fatal("Should queue a fail ack on save error")
}
}
func TestSaveCharacterData_NoMaxSize(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
server.charRepo = charRepo
session := createMockSession(1, server)
data := make([]byte, 5000)
saveCharacterData(session, 100, "test_col", data, 0)
// maxSize=0 means no limit
if saved := charRepo.columns["test_col"]; saved == nil {
t.Error("Data should be saved when maxSize is 0 (no limit)")
}
select {
case <-session.sendPackets:
default:
t.Fatal("Should queue success ack")
}
}
func TestDoAckEarthSucceed(t *testing.T) {
server := createMockServer()
server.erupeConfig.EarthID = 42
session := createMockSession(1, server)
doAckEarthSucceed(session, 100, nil)
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Response should have data")
}
default:
t.Fatal("Should queue a packet")
}
}
func TestUpdateRights(t *testing.T) {
server := createMockServer()
userRepo := &mockUserRepoGacha{}
userRepo.rights = 30
server.userRepo = userRepo
session := createMockSession(1, server)
updateRights(session)
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Should queue MsgSysUpdateRight")
}
default:
t.Fatal("updateRights should queue a packet")
}
}
func TestUpdateRights_Error(t *testing.T) {
server := createMockServer()
userRepo := &mockUserRepoGacha{rightsErr: errors.New("db error")}
server.userRepo = userRepo
session := createMockSession(1, server)
// Should not panic, falls back to rights=2
updateRights(session)
select {
case pkt := <-session.sendPackets:
if pkt.data == nil {
t.Fatal("Should queue MsgSysUpdateRight even on error")
}
default:
t.Fatal("updateRights should queue a packet even on error")
}
}

View File

@@ -0,0 +1,186 @@
package channelserver
import (
"encoding/binary"
"errors"
"testing"
"erupe-ce/network/mhfpacket"
)
// parseAckBufData extracts AckData from a serialized MsgSysAck buffer response.
// Wire format: opcode(2) + ackHandle(4) + isBuffer(1) + errorCode(1) + dataLen(2) + data(N)
func parseAckBufData(t *testing.T, raw []byte) (ackHandle uint32, errorCode uint8, ackData []byte) {
t.Helper()
if len(raw) < 10 {
t.Fatalf("raw packet too short: %d bytes", len(raw))
}
ackHandle = binary.BigEndian.Uint32(raw[2:6])
isBuffer := raw[6]
errorCode = raw[7]
if isBuffer == 0 {
t.Fatal("Expected buffer response, got simple ack")
}
dataLen := binary.BigEndian.Uint16(raw[8:10])
if int(dataLen) > len(raw)-10 {
t.Fatalf("data len %d exceeds remaining bytes %d", dataLen, len(raw)-10)
}
ackData = raw[10 : 10+dataLen]
return
}
func TestHandleMsgMhfGetKouryouPoint(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.ints["kouryou_point"] = 500
server.charRepo = charRepo
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetKouryouPoint{AckHandle: 100}
handleMsgMhfGetKouryouPoint(session, pkt)
select {
case p := <-session.sendPackets:
_, errCode, ackData := parseAckBufData(t, p.data)
if errCode != 0 {
t.Errorf("ErrorCode = %d, want 0", errCode)
}
if len(ackData) < 4 {
t.Fatal("AckData too short")
}
points := binary.BigEndian.Uint32(ackData[:4])
if points != 500 {
t.Errorf("points = %d, want 500", points)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfGetKouryouPoint_Error(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.readErr = errors.New("db error")
server.charRepo = charRepo
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetKouryouPoint{AckHandle: 100}
handleMsgMhfGetKouryouPoint(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
points := binary.BigEndian.Uint32(ackData[:4])
if points != 0 {
t.Errorf("points = %d, want 0 on error", points)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfAddKouryouPoint(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.ints["kouryou_point"] = 100
server.charRepo = charRepo
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfAddKouryouPoint{
AckHandle: 200,
KouryouPoints: 50,
}
handleMsgMhfAddKouryouPoint(session, pkt)
if charRepo.ints["kouryou_point"] != 150 {
t.Errorf("kouryou_point = %d, want 150", charRepo.ints["kouryou_point"])
}
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
points := binary.BigEndian.Uint32(ackData[:4])
if points != 150 {
t.Errorf("response points = %d, want 150", points)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfAddKouryouPoint_Error(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.adjustErr = errors.New("db error")
server.charRepo = charRepo
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfAddKouryouPoint{
AckHandle: 200,
KouryouPoints: 50,
}
handleMsgMhfAddKouryouPoint(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
points := binary.BigEndian.Uint32(ackData[:4])
if points != 0 {
t.Errorf("response points = %d, want 0 on error", points)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfExchangeKouryouPoint(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.ints["kouryou_point"] = 10000
server.charRepo = charRepo
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfExchangeKouryouPoint{
AckHandle: 300,
KouryouPoints: 10000,
}
handleMsgMhfExchangeKouryouPoint(session, pkt)
if charRepo.ints["kouryou_point"] != 0 {
t.Errorf("kouryou_point = %d, want 0 after exchange", charRepo.ints["kouryou_point"])
}
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
points := binary.BigEndian.Uint32(ackData[:4])
if points != 0 {
t.Errorf("response points = %d, want 0", points)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfExchangeKouryouPoint_Error(t *testing.T) {
server := createMockServer()
charRepo := newMockCharacterRepo()
charRepo.adjustErr = errors.New("db error")
server.charRepo = charRepo
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfExchangeKouryouPoint{
AckHandle: 300,
KouryouPoints: 5000,
}
handleMsgMhfExchangeKouryouPoint(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Should still respond on error")
}
default:
t.Fatal("No response queued")
}
}

View File

@@ -0,0 +1,177 @@
package channelserver
import (
"encoding/binary"
"errors"
"testing"
"erupe-ce/network/mhfpacket"
)
// --- mockScenarioRepo ---
type mockScenarioRepo struct {
scenarios []Scenario
err error
}
func (m *mockScenarioRepo) GetCounters() ([]Scenario, error) {
return m.scenarios, m.err
}
func TestHandleMsgMhfInfoScenarioCounter_Empty(t *testing.T) {
server := createMockServer()
server.scenarioRepo = &mockScenarioRepo{}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfInfoScenarioCounter{AckHandle: 100}
handleMsgMhfInfoScenarioCounter(session, pkt)
select {
case p := <-session.sendPackets:
_, errCode, ackData := parseAckBufData(t, p.data)
if errCode != 0 {
t.Errorf("ErrorCode = %d, want 0", errCode)
}
if len(ackData) < 1 {
t.Fatal("AckData too short")
}
if ackData[0] != 0 {
t.Errorf("scenario count = %d, want 0", ackData[0])
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfInfoScenarioCounter_WithScenarios(t *testing.T) {
server := createMockServer()
server.scenarioRepo = &mockScenarioRepo{
scenarios: []Scenario{
{MainID: 1000, CategoryID: 0},
{MainID: 2000, CategoryID: 3},
{MainID: 3000, CategoryID: 6},
{MainID: 4000, CategoryID: 7},
{MainID: 5000, CategoryID: 1},
},
}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfInfoScenarioCounter{AckHandle: 100}
handleMsgMhfInfoScenarioCounter(session, pkt)
select {
case p := <-session.sendPackets:
_, _, data := parseAckBufData(t, p.data)
if len(data) < 1 {
t.Fatal("AckData too short")
}
count := data[0]
if count != 5 {
t.Errorf("scenario count = %d, want 5", count)
}
// Each scenario: mainID(4) + exchange(1) + categoryID(1) = 6 bytes
expectedLen := 1 + 5*6
if len(data) != expectedLen {
t.Errorf("AckData len = %d, want %d", len(data), expectedLen)
}
// Verify first scenario (categoryID=0, exchange=false)
mainID := binary.BigEndian.Uint32(data[1:5])
if mainID != 1000 {
t.Errorf("first mainID = %d, want 1000", mainID)
}
if data[5] != 0 {
t.Errorf("categoryID=0 should have exchange=false, got %d", data[5])
}
// Verify second scenario (categoryID=3, exchange=true)
if data[5+6] != 1 {
t.Errorf("categoryID=3 should have exchange=true, got %d", data[5+6])
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfInfoScenarioCounter_TrimTo128(t *testing.T) {
server := createMockServer()
scenarios := make([]Scenario, 200)
for i := range scenarios {
scenarios[i] = Scenario{MainID: uint32(i), CategoryID: 0}
}
server.scenarioRepo = &mockScenarioRepo{scenarios: scenarios}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfInfoScenarioCounter{AckHandle: 100}
handleMsgMhfInfoScenarioCounter(session, pkt)
select {
case p := <-session.sendPackets:
_, _, data := parseAckBufData(t, p.data)
if data[0] != 128 {
t.Errorf("scenario count = %d, want 128 (trimmed)", data[0])
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfInfoScenarioCounter_DBError(t *testing.T) {
server := createMockServer()
server.scenarioRepo = &mockScenarioRepo{err: errors.New("db error")}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfInfoScenarioCounter{AckHandle: 100}
handleMsgMhfInfoScenarioCounter(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Should still respond on error")
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfInfoScenarioCounter_CategoryExchangeFlags(t *testing.T) {
tests := []struct {
name string
categoryID uint8
wantExch bool
}{
{"Basic", 0, false},
{"Veteran", 1, false},
{"Other (exchange)", 3, true},
{"Pallone (exchange)", 6, true},
{"Diva (exchange)", 7, true},
{"Unknown category 2", 2, false},
{"Unknown category 4", 4, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := createMockServer()
server.scenarioRepo = &mockScenarioRepo{
scenarios: []Scenario{{MainID: 1, CategoryID: tt.categoryID}},
}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfInfoScenarioCounter{AckHandle: 100}
handleMsgMhfInfoScenarioCounter(session, pkt)
select {
case p := <-session.sendPackets:
_, _, data := parseAckBufData(t, p.data)
isExchange := data[5] != 0
if isExchange != tt.wantExch {
t.Errorf("exchange = %v, want %v for categoryID=%d", isExchange, tt.wantExch, tt.categoryID)
}
default:
t.Fatal("No response queued")
}
})
}
}

View File

@@ -0,0 +1,228 @@
package channelserver
import (
"encoding/binary"
"testing"
"erupe-ce/network/mhfpacket"
)
func TestHandleMsgMhfGetSeibattle_AllTypes(t *testing.T) {
tests := []struct {
name string
pktType uint8
}{
{"Timetable", 1},
{"KeyScore", 3},
{"Career", 4},
{"Opponent", 5},
{"ConventionResult", 6},
{"CharScore", 7},
{"CurResult", 8},
{"UnknownType", 99},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
server := createMockServer()
server.erupeConfig.EarthID = 1
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetSeibattle{
AckHandle: 100,
Type: tt.pktType,
}
handleMsgMhfGetSeibattle(session, pkt)
select {
case p := <-session.sendPackets:
_, errCode, ackData := parseAckBufData(t, p.data)
if errCode != 0 {
t.Errorf("ErrorCode = %d, want 0", errCode)
}
// Earth header: EarthID(4) + 0(4) + 0(4) + count(4) = 16 bytes minimum
if len(ackData) < 16 {
t.Errorf("AckData too short: %d bytes", len(ackData))
}
earthID := binary.BigEndian.Uint32(ackData[:4])
if earthID != 1 {
t.Errorf("EarthID = %d, want 1", earthID)
}
default:
t.Fatal("No response queued")
}
})
}
}
func TestHandleMsgMhfGetSeibattle_TimetableEntryCount(t *testing.T) {
server := createMockServer()
server.erupeConfig.EarthID = 1
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetSeibattle{
AckHandle: 100,
Type: 1, // Timetable
}
handleMsgMhfGetSeibattle(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
count := binary.BigEndian.Uint32(ackData[12:16])
if count != 3 {
t.Errorf("timetable count = %d, want 3", count)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfGetBreakSeibatuLevelReward_DataSize(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetBreakSeibatuLevelReward{AckHandle: 100}
handleMsgMhfGetBreakSeibatuLevelReward(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
// 4 × int32 = 16 bytes
if len(ackData) != 16 {
t.Errorf("AckData len = %d, want 16", len(ackData))
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfGetWeeklySeibatuRankingReward_EarthFormat(t *testing.T) {
server := createMockServer()
server.erupeConfig.EarthID = 42
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetWeeklySeibatuRankingReward{AckHandle: 100}
handleMsgMhfGetWeeklySeibatuRankingReward(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
earthID := binary.BigEndian.Uint32(ackData[:4])
if earthID != 42 {
t.Errorf("EarthID = %d, want 42", earthID)
}
count := binary.BigEndian.Uint32(ackData[12:16])
if count != 1 {
t.Errorf("reward count = %d, want 1", count)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfGetFixedSeibatuRankingTable_DataSize(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGetFixedSeibatuRankingTable{AckHandle: 100}
handleMsgMhfGetFixedSeibatuRankingTable(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
// 4 + 4 + 32 = 40 bytes
if len(ackData) != 40 {
t.Errorf("AckData len = %d, want 40", len(ackData))
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfReadBeatLevel_VerifyIDEcho(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfReadBeatLevel{
AckHandle: 100,
ValidIDCount: 2,
IDs: [16]uint32{0x74, 0x6B},
}
handleMsgMhfReadBeatLevel(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
// 2 entries × (4+4+4+4) = 32 bytes
if len(ackData) != 32 {
t.Errorf("AckData len = %d, want 32", len(ackData))
}
firstID := binary.BigEndian.Uint32(ackData[:4])
if firstID != 0x74 {
t.Errorf("first ID = 0x%x, want 0x74", firstID)
}
secondID := binary.BigEndian.Uint32(ackData[16:20])
if secondID != 0x6B {
t.Errorf("second ID = 0x%x, want 0x6B", secondID)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfReadBeatLevelAllRanking_DataSize(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfReadBeatLevelAllRanking{AckHandle: 100}
handleMsgMhfReadBeatLevelAllRanking(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
// 4+4+4 + 100*(4+4+32) = 4012 bytes
expectedLen := 12 + 100*40
if len(ackData) != expectedLen {
t.Errorf("AckData len = %d, want %d", len(ackData), expectedLen)
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfReadBeatLevelMyRanking_EmptyResponse(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfReadBeatLevelMyRanking{AckHandle: 100}
handleMsgMhfReadBeatLevelMyRanking(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
if len(ackData) != 0 {
t.Errorf("AckData len = %d, want 0", len(ackData))
}
default:
t.Fatal("No response queued")
}
}
func TestHandleMsgMhfReadLastWeekBeatRanking_DataSize(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfReadLastWeekBeatRanking{AckHandle: 100}
handleMsgMhfReadLastWeekBeatRanking(session, pkt)
select {
case p := <-session.sendPackets:
_, _, ackData := parseAckBufData(t, p.data)
if len(ackData) != 16 {
t.Errorf("AckData len = %d, want 16", len(ackData))
}
default:
t.Fatal("No response queued")
}
}