mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-22 07:32:32 +01:00
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:
296
server/channelserver/handlers_distitem_test.go
Normal file
296
server/channelserver/handlers_distitem_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
211
server/channelserver/handlers_helpers_test.go
Normal file
211
server/channelserver/handlers_helpers_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
|
||||
186
server/channelserver/handlers_kouryou_test.go
Normal file
186
server/channelserver/handlers_kouryou_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
177
server/channelserver/handlers_scenario_test.go
Normal file
177
server/channelserver/handlers_scenario_test.go
Normal 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")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
228
server/channelserver/handlers_seibattle_test.go
Normal file
228
server/channelserver/handlers_seibattle_test.go
Normal 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")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user