mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
test(channelserver): add handler tests for session, gacha, shop, and plate
Cover critical paths that previously had no test coverage: - Session: login success/error paths, ping, logkey, record log, global sema lock/unlock, rights reload, announce - Gacha: point queries, coin deduction, item receive with overflow and freeze, normal/stepup/box gacha play, stepup status lifecycle, weighted random selection - Shop: enumeration across all shop types, exchange purchases, fpoint-to-item and item-to-fpoint exchange, fpoint exchange list with Z2 vs ZZ encoding - Plate: load/save for platedata, platebox, platemyset with oversized payload rejection, diff path, and cache invalidation Add mockSessionRepo, mockGachaRepo, mockShopRepo, and mockUserRepoGacha to support the new test scenarios. Add loadColumnErr field to mockCharacterRepo for diff-path error testing.
This commit is contained in:
660
server/channelserver/handlers_gacha_test.go
Normal file
660
server/channelserver/handlers_gacha_test.go
Normal file
@@ -0,0 +1,660 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
func TestHandleMsgMhfGetGachaPlayHistory_StubResponse(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetGachaPlayHistory{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetGachaPlayHistory(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetGachaPoint(t *testing.T) {
|
||||
server := createMockServer()
|
||||
userRepo := &mockUserRepoGacha{
|
||||
gachaFP: 100,
|
||||
gachaGP: 200,
|
||||
gachaGT: 300,
|
||||
}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetGachaPoint{AckHandle: 100}
|
||||
handleMsgMhfGetGachaPoint(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfUseGachaPoint_TrialCoins(t *testing.T) {
|
||||
server := createMockServer()
|
||||
userRepo := &mockUserRepoGacha{}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfUseGachaPoint{
|
||||
AckHandle: 100,
|
||||
TrialCoins: 10,
|
||||
PremiumCoins: 0,
|
||||
}
|
||||
handleMsgMhfUseGachaPoint(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfUseGachaPoint_PremiumCoins(t *testing.T) {
|
||||
server := createMockServer()
|
||||
userRepo := &mockUserRepoGacha{}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfUseGachaPoint{
|
||||
AckHandle: 100,
|
||||
TrialCoins: 0,
|
||||
PremiumCoins: 5,
|
||||
}
|
||||
handleMsgMhfUseGachaPoint(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfReceiveGachaItem_Normal(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
// Store 2 items: count byte + 2 * 5 bytes each
|
||||
data := []byte{2, 1, 0, 100, 0, 5, 2, 0, 200, 0, 10}
|
||||
charRepo.columns["gacha_items"] = data
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfReceiveGachaItem{AckHandle: 100, Freeze: false}
|
||||
handleMsgMhfReceiveGachaItem(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
// After non-freeze receive, gacha_items should be cleared
|
||||
if charRepo.columns["gacha_items"] != nil {
|
||||
t.Error("Expected gacha_items to be cleared after receive")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfReceiveGachaItem_Overflow(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
// Build data with >36 items (overflow scenario): count=37, 37*5=185 bytes + 1 count byte = 186
|
||||
data := make([]byte, 186)
|
||||
data[0] = 37
|
||||
for i := 1; i < 186; i++ {
|
||||
data[i] = byte(i % 256)
|
||||
}
|
||||
charRepo.columns["gacha_items"] = data
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfReceiveGachaItem{AckHandle: 100, Freeze: false}
|
||||
handleMsgMhfReceiveGachaItem(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
// After overflow, remaining items should be saved
|
||||
saved := charRepo.columns["gacha_items"]
|
||||
if saved == nil {
|
||||
t.Error("Expected overflow items to be saved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfReceiveGachaItem_Freeze(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
data := []byte{1, 1, 0, 100, 0, 5}
|
||||
charRepo.columns["gacha_items"] = data
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfReceiveGachaItem{AckHandle: 100, Freeze: true}
|
||||
handleMsgMhfReceiveGachaItem(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
// Freeze should NOT clear the items
|
||||
if charRepo.columns["gacha_items"] == nil {
|
||||
t.Error("Expected gacha_items to be preserved on freeze")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayNormalGacha_TransactError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{txErr: errors.New("transact failed")}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayNormalGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayNormalGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayNormalGacha_RewardPoolError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
txRolls: 1,
|
||||
rewardPoolErr: errors.New("pool error"),
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayNormalGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayNormalGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success - returns empty result
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayNormalGacha_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
txRolls: 1,
|
||||
rewardPool: []GachaEntry{
|
||||
{ID: 10, Weight: 100, Rarity: 3},
|
||||
},
|
||||
entryItems: map[uint32][]GachaItem{
|
||||
10: {{ItemType: 1, ItemID: 500, Quantity: 1}},
|
||||
},
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayNormalGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayNormalGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
// Verify gacha items were stored
|
||||
if charRepo.columns["gacha_items"] == nil {
|
||||
t.Error("Expected gacha items to be saved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayStepupGacha_TransactError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{txErr: errors.New("transact failed")}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayStepupGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayStepupGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success - returns empty result
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayStepupGacha_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
txRolls: 1,
|
||||
rewardPool: []GachaEntry{
|
||||
{ID: 10, Weight: 100, Rarity: 2},
|
||||
},
|
||||
entryItems: map[uint32][]GachaItem{
|
||||
10: {{ItemType: 1, ItemID: 600, Quantity: 2}},
|
||||
},
|
||||
guaranteedItems: []GachaItem{
|
||||
{ItemType: 1, ItemID: 700, Quantity: 1},
|
||||
},
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayStepupGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayStepupGacha(session, pkt)
|
||||
|
||||
if !gachaRepo.deletedStepup {
|
||||
t.Error("Expected stepup to be deleted")
|
||||
}
|
||||
if gachaRepo.insertedStep != 1 {
|
||||
t.Errorf("Expected insertedStep=1, got %d", gachaRepo.insertedStep)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetStepupStatus_FreshStep(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
stepupStep: 2,
|
||||
stepupTime: time.Now(), // recent, not stale
|
||||
hasEntryType: true,
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetStepupStatus{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetStepupStatus(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetStepupStatus_StaleStep(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
stepupStep: 3,
|
||||
stepupTime: time.Now().Add(-48 * time.Hour), // stale
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetStepupStatus{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetStepupStatus(session, pkt)
|
||||
|
||||
if !gachaRepo.deletedStepup {
|
||||
t.Error("Expected stale stepup to be deleted")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetStepupStatus_NoRows(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
stepupErr: sql.ErrNoRows,
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetStepupStatus{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetStepupStatus(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetStepupStatus_NoEntryType(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
stepupStep: 2,
|
||||
stepupTime: time.Now(),
|
||||
hasEntryType: false, // no matching entry type -> reset
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetStepupStatus{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetStepupStatus(session, pkt)
|
||||
|
||||
if !gachaRepo.deletedStepup {
|
||||
t.Error("Expected stepup to be reset when no entry type")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetBoxGachaInfo_Error(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
boxEntryIDsErr: errors.New("db error"),
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetBoxGachaInfo{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetBoxGachaInfo(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns empty
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetBoxGachaInfo_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
boxEntryIDs: []uint32{10, 20, 30},
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetBoxGachaInfo{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfGetBoxGachaInfo(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayBoxGacha_TransactError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{txErr: errors.New("transact failed")}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayBoxGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayBoxGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayBoxGacha_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
txRolls: 1,
|
||||
rewardPool: []GachaEntry{
|
||||
{ID: 10, Weight: 100, Rarity: 1},
|
||||
},
|
||||
entryItems: map[uint32][]GachaItem{
|
||||
10: {{ItemType: 1, ItemID: 800, Quantity: 1}},
|
||||
},
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayBoxGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayBoxGacha(session, pkt)
|
||||
|
||||
if len(gachaRepo.insertedBoxIDs) == 0 {
|
||||
t.Error("Expected box entry to be inserted")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfResetBoxGachaInfo(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfResetBoxGachaInfo{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfResetBoxGachaInfo(session, pkt)
|
||||
|
||||
if !gachaRepo.deletedBox {
|
||||
t.Error("Expected box entries to be deleted")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayFreeGacha_StubACK(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayFreeGacha{AckHandle: 100, GachaID: 1}
|
||||
handleMsgMhfPlayFreeGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRandomEntries_NonBox(t *testing.T) {
|
||||
entries := []GachaEntry{
|
||||
{ID: 1, Weight: 50},
|
||||
{ID: 2, Weight: 50},
|
||||
}
|
||||
result, err := getRandomEntries(entries, 3, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(result) != 3 {
|
||||
t.Errorf("Expected 3 entries, got %d", len(result))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRandomEntries_Box(t *testing.T) {
|
||||
entries := []GachaEntry{
|
||||
{ID: 1, Weight: 50},
|
||||
{ID: 2, Weight: 50},
|
||||
{ID: 3, Weight: 50},
|
||||
}
|
||||
result, err := getRandomEntries(entries, 2, true)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(result) != 2 {
|
||||
t.Errorf("Expected 2 entries, got %d", len(result))
|
||||
}
|
||||
// Box mode removes entries without replacement — all IDs should be unique
|
||||
if result[0].ID == result[1].ID {
|
||||
t.Error("Box mode should return unique entries")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfPlayStepupGacha_RewardPoolError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
txRolls: 1,
|
||||
rewardPoolErr: errors.New("pool error"),
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfPlayStepupGacha{AckHandle: 100, GachaID: 1, RollType: 0}
|
||||
handleMsgMhfPlayStepupGacha(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
// Verify minimal response (1 byte)
|
||||
_ = p
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the response payload of GetGachaPoint contains the expected values
|
||||
func TestHandleMsgMhfGetGachaPoint_ResponsePayload(t *testing.T) {
|
||||
server := createMockServer()
|
||||
userRepo := &mockUserRepoGacha{
|
||||
gachaFP: 111,
|
||||
gachaGP: 222,
|
||||
gachaGT: 333,
|
||||
}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetGachaPoint{AckHandle: 100}
|
||||
handleMsgMhfGetGachaPoint(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
// The ack wraps the payload. The handler writes gp, gt, fp (12 bytes).
|
||||
// Just verify we got a reasonable-sized response.
|
||||
if len(p.data) < 12 {
|
||||
t.Errorf("Expected at least 12 bytes of gacha point data in response, got %d", len(p.data))
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the response when no gacha items exist (default column)
|
||||
func TestHandleMsgMhfReceiveGachaItem_Empty(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
// No gacha_items set — will return default {0x00}
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfReceiveGachaItem{AckHandle: 100, Freeze: false}
|
||||
handleMsgMhfReceiveGachaItem(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
// The response should contain the default byte
|
||||
bf := byteframe.NewByteFrameFromBytes(p.data)
|
||||
_ = bf
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
381
server/channelserver/handlers_plate_test.go
Normal file
381
server/channelserver/handlers_plate_test.go
Normal file
@@ -0,0 +1,381 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/server/channelserver/compression/nullcomp"
|
||||
)
|
||||
|
||||
func TestHandleMsgMhfLoadPlateData(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
charRepo.columns["platedata"] = []byte{0x01, 0x02, 0x03}
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfLoadPlateData{AckHandle: 100}
|
||||
handleMsgMhfLoadPlateData(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfLoadPlateData_Empty(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
// No platedata column set — loadCharacterData uses nil default
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfLoadPlateData{AckHandle: 100}
|
||||
handleMsgMhfLoadPlateData(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateData_OversizedPayload(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfSavePlateData{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: make([]byte, plateDataMaxPayload+1),
|
||||
IsDataDiff: false,
|
||||
}
|
||||
handleMsgMhfSavePlateData(session, pkt)
|
||||
|
||||
// Should still get ACK
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
// Data should NOT have been saved
|
||||
if charRepo.columns["platedata"] != nil {
|
||||
t.Error("Expected platedata to NOT be saved when oversized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateData_FullSave(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
payload := []byte{0x10, 0x20, 0x30, 0x40}
|
||||
pkt := &mhfpacket.MsgMhfSavePlateData{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: payload,
|
||||
IsDataDiff: false,
|
||||
}
|
||||
handleMsgMhfSavePlateData(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
saved := charRepo.columns["platedata"]
|
||||
if saved == nil {
|
||||
t.Fatal("Expected platedata to be saved")
|
||||
}
|
||||
if len(saved) != len(payload) {
|
||||
t.Errorf("Expected saved data length %d, got %d", len(payload), len(saved))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateData_DiffPath_LoadError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
charRepo.loadColumnErr = errors.New("load failed")
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfSavePlateData{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: []byte{0x01},
|
||||
IsDataDiff: true,
|
||||
}
|
||||
handleMsgMhfSavePlateData(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns ACK even on error
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateData_DiffPath_SaveError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
// Provide compressed data so decompress works
|
||||
original := make([]byte, 100)
|
||||
compressed, _ := nullcomp.Compress(original)
|
||||
charRepo.columns["platedata"] = compressed
|
||||
charRepo.saveErr = errors.New("save failed")
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
// Build a valid diff payload: matchCount=2 (offset becomes 1), diffCount=2 (means 1 byte), then 1 data byte
|
||||
diffPayload := []byte{2, 2, 0xAA}
|
||||
pkt := &mhfpacket.MsgMhfSavePlateData{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: diffPayload,
|
||||
IsDataDiff: true,
|
||||
}
|
||||
handleMsgMhfSavePlateData(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns ACK even on save error
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfLoadPlateBox(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
charRepo.columns["platebox"] = []byte{0xAA, 0xBB}
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfLoadPlateBox{AckHandle: 100}
|
||||
handleMsgMhfLoadPlateBox(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateBox_OversizedPayload(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfSavePlateBox{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: make([]byte, plateBoxMaxPayload+1),
|
||||
IsDataDiff: false,
|
||||
}
|
||||
handleMsgMhfSavePlateBox(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
if charRepo.columns["platebox"] != nil {
|
||||
t.Error("Expected platebox to NOT be saved when oversized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateBox_FullSave(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
payload := []byte{0xCC, 0xDD}
|
||||
pkt := &mhfpacket.MsgMhfSavePlateBox{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: payload,
|
||||
IsDataDiff: false,
|
||||
}
|
||||
handleMsgMhfSavePlateBox(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
if charRepo.columns["platebox"] == nil {
|
||||
t.Fatal("Expected platebox to be saved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateBox_DiffPath(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
// Provide compressed data
|
||||
original := make([]byte, 100)
|
||||
compressed, _ := nullcomp.Compress(original)
|
||||
charRepo.columns["platebox"] = compressed
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
// Valid diff: matchCount=2 (offset becomes 1), diffCount=2 (1 byte), data byte
|
||||
diffPayload := []byte{2, 2, 0xBB}
|
||||
pkt := &mhfpacket.MsgMhfSavePlateBox{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: diffPayload,
|
||||
IsDataDiff: true,
|
||||
}
|
||||
handleMsgMhfSavePlateBox(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfLoadPlateMyset(t *testing.T) {
|
||||
server := createMockServer()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfLoadPlateMyset{AckHandle: 100}
|
||||
handleMsgMhfLoadPlateMyset(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateMyset_OversizedPayload(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfSavePlateMyset{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: make([]byte, plateMysetMaxPayload+1),
|
||||
}
|
||||
handleMsgMhfSavePlateMyset(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
if charRepo.columns["platemyset"] != nil {
|
||||
t.Error("Expected platemyset to NOT be saved when oversized")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateMyset_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
payload := make([]byte, plateMysetDefaultLen)
|
||||
payload[0] = 0xFF
|
||||
pkt := &mhfpacket.MsgMhfSavePlateMyset{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: payload,
|
||||
}
|
||||
handleMsgMhfSavePlateMyset(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
|
||||
if charRepo.columns["platemyset"] == nil {
|
||||
t.Fatal("Expected platemyset to be saved")
|
||||
}
|
||||
if charRepo.columns["platemyset"][0] != 0xFF {
|
||||
t.Error("Expected first byte to be 0xFF")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfSavePlateData_CacheInvalidation(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
session := createMockSession(42, server)
|
||||
|
||||
// Pre-populate the cache
|
||||
server.userBinary.Set(42, 2, []byte{0x01})
|
||||
server.userBinary.Set(42, 3, []byte{0x02})
|
||||
|
||||
pkt := &mhfpacket.MsgMhfSavePlateData{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: []byte{0x10},
|
||||
IsDataDiff: false,
|
||||
}
|
||||
handleMsgMhfSavePlateData(session, pkt)
|
||||
|
||||
// Verify cache was invalidated
|
||||
if data := server.userBinary.GetCopy(42, 2); len(data) > 0 {
|
||||
t.Error("Expected user binary type 2 to be invalidated")
|
||||
}
|
||||
if data := server.userBinary.GetCopy(42, 3); len(data) > 0 {
|
||||
t.Error("Expected user binary type 3 to be invalidated")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
372
server/channelserver/handlers_session_test.go
Normal file
372
server/channelserver/handlers_session_test.go
Normal file
@@ -0,0 +1,372 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
func TestHandleMsgSysTerminalLog_ReturnsLogIDPlusOne(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysTerminalLog{
|
||||
AckHandle: 100,
|
||||
LogID: 5,
|
||||
Entries: []mhfpacket.TerminalLogEntry{
|
||||
{Type1: 1, Type2: 2, Unk0: 3, Unk1: 4, Unk2: 5, Unk3: 6},
|
||||
},
|
||||
}
|
||||
handleMsgSysTerminalLog(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) < 4 {
|
||||
t.Fatal("Response too short")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysLogin_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.DebugOptions.DisableTokenCheck = true
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
sessionRepo := &mockSessionRepo{}
|
||||
server.sessionRepo = sessionRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(0, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysLogin{
|
||||
AckHandle: 100,
|
||||
CharID0: 42,
|
||||
LoginTokenString: "test-token",
|
||||
}
|
||||
handleMsgSysLogin(session, pkt)
|
||||
|
||||
if session.charID != 42 {
|
||||
t.Errorf("Expected charID 42, got %d", session.charID)
|
||||
}
|
||||
if session.token != "test-token" {
|
||||
t.Errorf("Expected token 'test-token', got %q", session.token)
|
||||
}
|
||||
if sessionRepo.boundToken != "test-token" {
|
||||
t.Errorf("Expected BindSession called with 'test-token', got %q", sessionRepo.boundToken)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysLogin_GetUserIDError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.DebugOptions.DisableTokenCheck = true
|
||||
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = &mockCharRepoGetUserIDErr{
|
||||
mockCharacterRepo: charRepo,
|
||||
getUserIDErr: errors.New("user not found"),
|
||||
}
|
||||
|
||||
sessionRepo := &mockSessionRepo{}
|
||||
server.sessionRepo = sessionRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(0, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysLogin{
|
||||
AckHandle: 100,
|
||||
CharID0: 42,
|
||||
LoginTokenString: "test-token",
|
||||
}
|
||||
handleMsgSysLogin(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// got a response (fail ACK)
|
||||
default:
|
||||
t.Error("No response packet queued on GetUserID error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysLogin_BindSessionError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.DebugOptions.DisableTokenCheck = true
|
||||
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
sessionRepo := &mockSessionRepo{bindErr: errors.New("bind failed")}
|
||||
server.sessionRepo = sessionRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(0, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysLogin{
|
||||
AckHandle: 100,
|
||||
CharID0: 42,
|
||||
LoginTokenString: "test-token",
|
||||
}
|
||||
handleMsgSysLogin(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// got a response (fail ACK)
|
||||
default:
|
||||
t.Error("No response packet queued on BindSession error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysLogin_SetLastCharacterError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.DebugOptions.DisableTokenCheck = true
|
||||
|
||||
charRepo := newMockCharacterRepo()
|
||||
server.charRepo = charRepo
|
||||
|
||||
sessionRepo := &mockSessionRepo{}
|
||||
server.sessionRepo = sessionRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{setLastCharErr: errors.New("set failed")}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(0, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysLogin{
|
||||
AckHandle: 100,
|
||||
CharID0: 42,
|
||||
LoginTokenString: "test-token",
|
||||
}
|
||||
handleMsgSysLogin(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// got a response (fail ACK)
|
||||
default:
|
||||
t.Error("No response packet queued on SetLastCharacter error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysPing_Session(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysPing{AckHandle: 100}
|
||||
handleMsgSysPing(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysIssueLogkey_GeneratesKey(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysIssueLogkey{AckHandle: 100}
|
||||
handleMsgSysIssueLogkey(session, pkt)
|
||||
|
||||
if len(session.logKey) != 16 {
|
||||
t.Errorf("Expected 16-byte log key, got %d bytes", len(session.logKey))
|
||||
}
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysRecordLog_ZZMode(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
server.userBinary = NewUserBinaryStore()
|
||||
|
||||
guildRepo := &mockGuildRepoForMail{}
|
||||
server.guildRepo = guildRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
// Create a stage for the session (handler accesses s.stage.reservedClientSlots)
|
||||
stage := &Stage{
|
||||
id: "testStage",
|
||||
clients: make(map[*Session]uint32),
|
||||
reservedClientSlots: make(map[uint32]bool),
|
||||
}
|
||||
stage.reservedClientSlots[1] = true
|
||||
session.stage = stage
|
||||
|
||||
// Build kill log data: 32 header bytes + 176 monster bytes
|
||||
data := make([]byte, 32+176)
|
||||
// Set monster index 5 to have 2 kills (a large monster per mhfmon)
|
||||
data[32+5] = 2
|
||||
|
||||
pkt := &mhfpacket.MsgSysRecordLog{
|
||||
AckHandle: 100,
|
||||
Data: data,
|
||||
}
|
||||
handleMsgSysRecordLog(session, pkt)
|
||||
|
||||
// Check that reserved slot was cleaned up
|
||||
if _, exists := stage.reservedClientSlots[1]; exists {
|
||||
t.Error("Expected reserved client slot to be removed")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysLockGlobalSema_LocalChannel(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.GlobalID = "ch1"
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysLockGlobalSema{
|
||||
AckHandle: 100,
|
||||
UserIDString: "someStage",
|
||||
ServerChannelIDString: "ch1",
|
||||
}
|
||||
handleMsgSysLockGlobalSema(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysLockGlobalSema_RemoteMatch(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.GlobalID = "ch1"
|
||||
|
||||
otherChannel := createMockServer()
|
||||
otherChannel.GlobalID = "ch2"
|
||||
otherChannel.stages.Store("prefix_testStage", &Stage{
|
||||
id: "prefix_testStage",
|
||||
clients: make(map[*Session]uint32),
|
||||
reservedClientSlots: make(map[uint32]bool),
|
||||
})
|
||||
server.Channels = []*Server{server, otherChannel}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysLockGlobalSema{
|
||||
AckHandle: 100,
|
||||
UserIDString: "testStage",
|
||||
ServerChannelIDString: "ch1",
|
||||
}
|
||||
handleMsgSysLockGlobalSema(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
_ = byteframe.NewByteFrameFromBytes(p.data)
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysUnlockGlobalSema_Session(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgSysUnlockGlobalSema{AckHandle: 100}
|
||||
handleMsgSysUnlockGlobalSema(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgSysRightsReload_Session(t *testing.T) {
|
||||
server := createMockServer()
|
||||
userRepo := &mockUserRepoGacha{rights: 0x02}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgSysRightsReload{AckHandle: 100}
|
||||
handleMsgSysRightsReload(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfAnnounce_Session(t *testing.T) {
|
||||
server := createMockServer()
|
||||
session := createMockSession(1, server)
|
||||
|
||||
dataBf := byteframe.NewByteFrame()
|
||||
dataBf.WriteUint8(2) // type = berserk
|
||||
|
||||
pkt := &mhfpacket.MsgMhfAnnounce{
|
||||
AckHandle: 100,
|
||||
IPAddress: binary.LittleEndian.Uint32([]byte{127, 0, 0, 1}),
|
||||
Port: 54001,
|
||||
StageID: make([]byte, 32),
|
||||
Data: byteframe.NewByteFrameFromBytes(dataBf.Data()),
|
||||
}
|
||||
handleMsgMhfAnnounce(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
// mockCharRepoGetUserIDErr wraps mockCharacterRepo to return an error from GetUserID
|
||||
type mockCharRepoGetUserIDErr struct {
|
||||
*mockCharacterRepo
|
||||
getUserIDErr error
|
||||
}
|
||||
|
||||
func (m *mockCharRepoGetUserIDErr) GetUserID(_ uint32) (uint32, error) {
|
||||
return 0, m.getUserIDErr
|
||||
}
|
||||
476
server/channelserver/handlers_shop_test.go
Normal file
476
server/channelserver/handlers_shop_test.go
Normal file
@@ -0,0 +1,476 @@
|
||||
package channelserver
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"erupe-ce/common/byteframe"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
)
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Case1_G7EarlyReturn(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.G7
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: 1,
|
||||
ShopID: 0,
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Case1_GachaList(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
gachas: []Gacha{
|
||||
{ID: 1, Name: "TestGacha", MinGR: 0, MinHR: 0, GachaType: 1},
|
||||
},
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: 1,
|
||||
ShopID: 0,
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Case1_ListShopError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
listShopErr: errors.New("db error"),
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: 1,
|
||||
ShopID: 0,
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns empty on error
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Case2_GachaDetail(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
shopType: 1, // non-box
|
||||
allEntries: []GachaEntry{
|
||||
{ID: 10, EntryType: 1, ItemType: 1, ItemNumber: 100, ItemQuantity: 5,
|
||||
Weight: 50, Rarity: 2, Rolls: 1, FrontierPoints: 10, DailyLimit: 3, Name: "Item1"},
|
||||
},
|
||||
entryItems: map[uint32][]GachaItem{
|
||||
10: {{ItemType: 1, ItemID: 500, Quantity: 1}},
|
||||
},
|
||||
weightDivisor: 1.0,
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: 2,
|
||||
ShopID: 1,
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Case2_AllEntriesError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
gachaRepo := &mockGachaRepo{
|
||||
allEntriesErr: errors.New("db error"),
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: 2,
|
||||
ShopID: 1,
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns empty on error
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Case10_ShopItems(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
shopRepo := &mockShopRepo{
|
||||
shopItems: []ShopItem{
|
||||
{ID: 1, ItemID: 100, Cost: 500, Quantity: 10, MinHR: 1},
|
||||
{ID: 2, ItemID: 200, Cost: 1000, Quantity: 5, MinHR: 3},
|
||||
{ID: 3, ItemID: 300, Cost: 2000, Quantity: 1, MinHR: 5},
|
||||
},
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: 10,
|
||||
ShopID: 0,
|
||||
Limit: 2, // Limit to 2 items
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfEnumerateShop_Cases3to9(t *testing.T) {
|
||||
for _, shopType := range []uint8{3, 4, 5, 6, 7, 8, 9} {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
shopRepo := &mockShopRepo{
|
||||
shopItems: []ShopItem{
|
||||
{ID: 1, ItemID: 100, Cost: 500, Quantity: 10},
|
||||
},
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfEnumerateShop{
|
||||
AckHandle: 100,
|
||||
ShopType: shopType,
|
||||
ShopID: 0,
|
||||
Limit: 100,
|
||||
}
|
||||
handleMsgMhfEnumerateShop(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Errorf("No response for shop type %d", shopType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfAcquireExchangeShop_RecordsPurchases(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
// Build payload: 2 exchanges, one with non-zero hash, one with zero hash
|
||||
payload := byteframe.NewByteFrame()
|
||||
payload.WriteUint16(2) // count
|
||||
payload.WriteUint32(12345) // itemHash 1
|
||||
payload.WriteUint32(3) // buyCount 1
|
||||
payload.WriteUint32(0) // itemHash 2 (zero, should be skipped)
|
||||
payload.WriteUint32(1) // buyCount 2
|
||||
|
||||
pkt := &mhfpacket.MsgMhfAcquireExchangeShop{
|
||||
AckHandle: 100,
|
||||
RawDataPayload: payload.Data(),
|
||||
}
|
||||
handleMsgMhfAcquireExchangeShop(session, pkt)
|
||||
|
||||
if len(shopRepo.purchases) != 1 {
|
||||
t.Errorf("Expected 1 purchase recorded (skipping zero hash), got %d", len(shopRepo.purchases))
|
||||
}
|
||||
if len(shopRepo.purchases) > 0 && shopRepo.purchases[0].itemHash != 12345 {
|
||||
t.Errorf("Expected itemHash=12345, got %d", shopRepo.purchases[0].itemHash)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// success
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfExchangeFpoint2Item_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointQuantity: 1,
|
||||
fpointValue: 100,
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{fpDeductBalance: 900}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfExchangeFpoint2Item{
|
||||
AckHandle: 100,
|
||||
TradeID: 1,
|
||||
Quantity: 1,
|
||||
}
|
||||
handleMsgMhfExchangeFpoint2Item(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfExchangeFpoint2Item_GetFpointItemError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointItemErr: errors.New("not found"),
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfExchangeFpoint2Item{
|
||||
AckHandle: 100,
|
||||
TradeID: 999,
|
||||
Quantity: 1,
|
||||
}
|
||||
handleMsgMhfExchangeFpoint2Item(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns fail
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfExchangeFpoint2Item_DeductError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointQuantity: 1,
|
||||
fpointValue: 100,
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{fpDeductErr: errors.New("insufficient")}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfExchangeFpoint2Item{
|
||||
AckHandle: 100,
|
||||
TradeID: 1,
|
||||
Quantity: 1,
|
||||
}
|
||||
handleMsgMhfExchangeFpoint2Item(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns fail
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfExchangeItem2Fpoint_Success(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointQuantity: 1,
|
||||
fpointValue: 50,
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{fpCreditBalance: 1050}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfExchangeItem2Fpoint{
|
||||
AckHandle: 100,
|
||||
TradeID: 1,
|
||||
Quantity: 1,
|
||||
}
|
||||
handleMsgMhfExchangeItem2Fpoint(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfExchangeItem2Fpoint_GetFpointItemError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointItemErr: errors.New("not found"),
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
server.userRepo = &mockUserRepoGacha{}
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfExchangeItem2Fpoint{
|
||||
AckHandle: 100,
|
||||
TradeID: 999,
|
||||
Quantity: 1,
|
||||
}
|
||||
handleMsgMhfExchangeItem2Fpoint(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns fail
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfExchangeItem2Fpoint_CreditError(t *testing.T) {
|
||||
server := createMockServer()
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointQuantity: 1,
|
||||
fpointValue: 50,
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
userRepo := &mockUserRepoGacha{fpCreditErr: errors.New("credit error")}
|
||||
server.userRepo = userRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
session.userID = 1
|
||||
|
||||
pkt := &mhfpacket.MsgMhfExchangeItem2Fpoint{
|
||||
AckHandle: 100,
|
||||
TradeID: 1,
|
||||
Quantity: 1,
|
||||
}
|
||||
handleMsgMhfExchangeItem2Fpoint(session, pkt)
|
||||
|
||||
select {
|
||||
case <-session.sendPackets:
|
||||
// returns fail
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetFpointExchangeList_Z2Mode(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.Z2
|
||||
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointExchanges: []FPointExchange{
|
||||
{ID: 1, ItemType: 1, ItemID: 100, Quantity: 5, FPoints: 10, Buyable: true},
|
||||
{ID: 2, ItemType: 2, ItemID: 200, Quantity: 1, FPoints: 50, Buyable: false},
|
||||
},
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetFpointExchangeList{AckHandle: 100}
|
||||
handleMsgMhfGetFpointExchangeList(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleMsgMhfGetFpointExchangeList_ZZMode(t *testing.T) {
|
||||
server := createMockServer()
|
||||
server.erupeConfig.RealClientMode = cfg.ZZ
|
||||
|
||||
shopRepo := &mockShopRepo{
|
||||
fpointExchanges: []FPointExchange{
|
||||
{ID: 1, ItemType: 1, ItemID: 100, Quantity: 5, FPoints: 10, Buyable: true},
|
||||
},
|
||||
}
|
||||
server.shopRepo = shopRepo
|
||||
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfGetFpointExchangeList{AckHandle: 100}
|
||||
handleMsgMhfGetFpointExchangeList(session, pkt)
|
||||
|
||||
select {
|
||||
case p := <-session.sendPackets:
|
||||
if len(p.data) == 0 {
|
||||
t.Fatal("Empty response")
|
||||
}
|
||||
default:
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
@@ -110,9 +110,10 @@ type mockCharacterRepo struct {
|
||||
strings map[string]string
|
||||
bools map[string]bool
|
||||
|
||||
adjustErr error
|
||||
readErr error
|
||||
saveErr error
|
||||
adjustErr error
|
||||
readErr error
|
||||
saveErr error
|
||||
loadColumnErr error
|
||||
|
||||
// LoadSaveData mock fields
|
||||
loadSaveDataID uint32
|
||||
@@ -167,7 +168,12 @@ func (m *mockCharacterRepo) SaveTime(_ uint32, column string, value time.Time) e
|
||||
return m.saveErr
|
||||
}
|
||||
|
||||
func (m *mockCharacterRepo) LoadColumn(_ uint32, column string) ([]byte, error) { return m.columns[column], nil }
|
||||
func (m *mockCharacterRepo) LoadColumn(_ uint32, column string) ([]byte, error) {
|
||||
if m.loadColumnErr != nil {
|
||||
return nil, m.loadColumnErr
|
||||
}
|
||||
return m.columns[column], nil
|
||||
}
|
||||
func (m *mockCharacterRepo) SaveColumn(_ uint32, column string, data []byte) error { m.columns[column] = data; return m.saveErr }
|
||||
func (m *mockCharacterRepo) GetName(_ uint32) (string, error) { return "TestChar", nil }
|
||||
func (m *mockCharacterRepo) GetUserID(_ uint32) (uint32, error) { return 1, nil }
|
||||
@@ -735,3 +741,199 @@ func (m *mockHouseRepoForItems) GetWarehouseEquipData(_ uint32, _ uint8) ([]byte
|
||||
func (m *mockHouseRepoForItems) SetWarehouseEquipData(_ uint32, _ uint8, _ []byte) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetTitles(_ uint32) ([]Title, error) { return nil, nil }
|
||||
func (m *mockHouseRepoForItems) AcquireTitle(_ uint16, _ uint32) error { return nil }
|
||||
|
||||
// --- mockSessionRepo ---
|
||||
|
||||
type mockSessionRepo struct {
|
||||
validateErr error
|
||||
bindErr error
|
||||
clearErr error
|
||||
updateErr error
|
||||
|
||||
boundToken string
|
||||
clearedToken string
|
||||
}
|
||||
|
||||
func (m *mockSessionRepo) ValidateLoginToken(_ string, _ uint32, _ uint32) error { return m.validateErr }
|
||||
func (m *mockSessionRepo) BindSession(token string, _ uint16, _ uint32) error {
|
||||
m.boundToken = token
|
||||
return m.bindErr
|
||||
}
|
||||
func (m *mockSessionRepo) ClearSession(token string) error {
|
||||
m.clearedToken = token
|
||||
return m.clearErr
|
||||
}
|
||||
func (m *mockSessionRepo) UpdatePlayerCount(_ uint16, _ int) error { return m.updateErr }
|
||||
|
||||
// --- mockGachaRepo ---
|
||||
|
||||
type mockGachaRepo struct {
|
||||
// GetEntryForTransaction
|
||||
txItemType uint8
|
||||
txItemNumber uint16
|
||||
txRolls int
|
||||
txErr error
|
||||
|
||||
// GetRewardPool
|
||||
rewardPool []GachaEntry
|
||||
rewardPoolErr error
|
||||
|
||||
// GetItemsForEntry
|
||||
entryItems map[uint32][]GachaItem
|
||||
entryItemsErr error
|
||||
|
||||
// GetGuaranteedItems
|
||||
guaranteedItems []GachaItem
|
||||
|
||||
// Stepup
|
||||
stepupStep uint8
|
||||
stepupTime time.Time
|
||||
stepupErr error
|
||||
hasEntryType bool
|
||||
deletedStepup bool
|
||||
insertedStep uint8
|
||||
|
||||
// Box
|
||||
boxEntryIDs []uint32
|
||||
boxEntryIDsErr error
|
||||
insertedBoxIDs []uint32
|
||||
deletedBox bool
|
||||
|
||||
// Shop
|
||||
gachas []Gacha
|
||||
listShopErr error
|
||||
shopType int
|
||||
allEntries []GachaEntry
|
||||
allEntriesErr error
|
||||
weightDivisor float64
|
||||
|
||||
// FrontierPoints from gacha
|
||||
addFPErr error
|
||||
}
|
||||
|
||||
func (m *mockGachaRepo) GetEntryForTransaction(_ uint32, _ uint8) (uint8, uint16, int, error) {
|
||||
return m.txItemType, m.txItemNumber, m.txRolls, m.txErr
|
||||
}
|
||||
func (m *mockGachaRepo) GetRewardPool(_ uint32) ([]GachaEntry, error) {
|
||||
return m.rewardPool, m.rewardPoolErr
|
||||
}
|
||||
func (m *mockGachaRepo) GetItemsForEntry(entryID uint32) ([]GachaItem, error) {
|
||||
if m.entryItemsErr != nil {
|
||||
return nil, m.entryItemsErr
|
||||
}
|
||||
if m.entryItems != nil {
|
||||
return m.entryItems[entryID], nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockGachaRepo) GetGuaranteedItems(_ uint8, _ uint32) ([]GachaItem, error) {
|
||||
return m.guaranteedItems, nil
|
||||
}
|
||||
func (m *mockGachaRepo) GetStepupStep(_ uint32, _ uint32) (uint8, error) {
|
||||
return m.stepupStep, m.stepupErr
|
||||
}
|
||||
func (m *mockGachaRepo) GetStepupWithTime(_ uint32, _ uint32) (uint8, time.Time, error) {
|
||||
return m.stepupStep, m.stepupTime, m.stepupErr
|
||||
}
|
||||
func (m *mockGachaRepo) HasEntryType(_ uint32, _ uint8) (bool, error) {
|
||||
return m.hasEntryType, nil
|
||||
}
|
||||
func (m *mockGachaRepo) DeleteStepup(_ uint32, _ uint32) error {
|
||||
m.deletedStepup = true
|
||||
return nil
|
||||
}
|
||||
func (m *mockGachaRepo) InsertStepup(_ uint32, step uint8, _ uint32) error {
|
||||
m.insertedStep = step
|
||||
return nil
|
||||
}
|
||||
func (m *mockGachaRepo) GetBoxEntryIDs(_ uint32, _ uint32) ([]uint32, error) {
|
||||
return m.boxEntryIDs, m.boxEntryIDsErr
|
||||
}
|
||||
func (m *mockGachaRepo) InsertBoxEntry(_ uint32, entryID uint32, _ uint32) error {
|
||||
m.insertedBoxIDs = append(m.insertedBoxIDs, entryID)
|
||||
return nil
|
||||
}
|
||||
func (m *mockGachaRepo) DeleteBoxEntries(_ uint32, _ uint32) error {
|
||||
m.deletedBox = true
|
||||
return nil
|
||||
}
|
||||
func (m *mockGachaRepo) ListShop() ([]Gacha, error) { return m.gachas, m.listShopErr }
|
||||
func (m *mockGachaRepo) GetShopType(_ uint32) (int, error) { return m.shopType, nil }
|
||||
func (m *mockGachaRepo) GetAllEntries(_ uint32) ([]GachaEntry, error) {
|
||||
return m.allEntries, m.allEntriesErr
|
||||
}
|
||||
func (m *mockGachaRepo) GetWeightDivisor(_ uint32) (float64, error) { return m.weightDivisor, nil }
|
||||
|
||||
// --- mockShopRepo ---
|
||||
|
||||
type mockShopRepo struct {
|
||||
shopItems []ShopItem
|
||||
shopItemsErr error
|
||||
purchases []shopPurchaseRecord
|
||||
recordErr error
|
||||
fpointQuantity int
|
||||
fpointValue int
|
||||
fpointItemErr error
|
||||
fpointExchanges []FPointExchange
|
||||
}
|
||||
|
||||
type shopPurchaseRecord struct {
|
||||
charID, itemHash, quantity uint32
|
||||
}
|
||||
|
||||
func (m *mockShopRepo) GetShopItems(_ uint8, _ uint32, _ uint32) ([]ShopItem, error) {
|
||||
return m.shopItems, m.shopItemsErr
|
||||
}
|
||||
func (m *mockShopRepo) RecordPurchase(charID, itemHash, quantity uint32) error {
|
||||
m.purchases = append(m.purchases, shopPurchaseRecord{charID, itemHash, quantity})
|
||||
return m.recordErr
|
||||
}
|
||||
func (m *mockShopRepo) GetFpointItem(_ uint32) (int, int, error) {
|
||||
return m.fpointQuantity, m.fpointValue, m.fpointItemErr
|
||||
}
|
||||
func (m *mockShopRepo) GetFpointExchangeList() ([]FPointExchange, error) {
|
||||
return m.fpointExchanges, nil
|
||||
}
|
||||
|
||||
// --- mockUserRepoGacha (UserRepo with configurable gacha fields) ---
|
||||
|
||||
type mockUserRepoGacha struct {
|
||||
mockUserRepoForItems
|
||||
|
||||
gachaFP, gachaGP, gachaGT uint32
|
||||
trialCoins uint16
|
||||
deductTrialErr error
|
||||
deductPremiumErr error
|
||||
deductFPErr error
|
||||
addFPFromGachaErr error
|
||||
|
||||
fpDeductBalance uint32
|
||||
fpDeductErr error
|
||||
fpCreditBalance uint32
|
||||
fpCreditErr error
|
||||
|
||||
setLastCharErr error
|
||||
rights uint32
|
||||
rightsErr error
|
||||
}
|
||||
|
||||
func (m *mockUserRepoGacha) GetGachaPoints(_ uint32) (uint32, uint32, uint32, error) {
|
||||
return m.gachaFP, m.gachaGP, m.gachaGT, nil
|
||||
}
|
||||
func (m *mockUserRepoGacha) GetTrialCoins(_ uint32) (uint16, error) { return m.trialCoins, nil }
|
||||
func (m *mockUserRepoGacha) DeductTrialCoins(_ uint32, _ uint32) error { return m.deductTrialErr }
|
||||
func (m *mockUserRepoGacha) DeductPremiumCoins(_ uint32, _ uint32) error {
|
||||
return m.deductPremiumErr
|
||||
}
|
||||
func (m *mockUserRepoGacha) DeductFrontierPoints(_ uint32, _ uint32) error { return m.deductFPErr }
|
||||
func (m *mockUserRepoGacha) AddFrontierPointsFromGacha(_ uint32, _ uint32, _ uint8) error {
|
||||
return m.addFPFromGachaErr
|
||||
}
|
||||
func (m *mockUserRepoGacha) AdjustFrontierPointsDeduct(_ uint32, _ int) (uint32, error) {
|
||||
return m.fpDeductBalance, m.fpDeductErr
|
||||
}
|
||||
func (m *mockUserRepoGacha) AdjustFrontierPointsCredit(_ uint32, _ int) (uint32, error) {
|
||||
return m.fpCreditBalance, m.fpCreditErr
|
||||
}
|
||||
func (m *mockUserRepoGacha) SetLastCharacter(_ uint32, _ uint32) error { return m.setLastCharErr }
|
||||
func (m *mockUserRepoGacha) GetRights(_ uint32) (uint32, error) { return m.rights, m.rightsErr }
|
||||
|
||||
Reference in New Issue
Block a user