mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-27 18:12:50 +01:00
style: run gofmt across entire codebase
330 non-vendor files had minor formatting inconsistencies (comment alignment, whitespace). No logic changes.
This commit is contained in:
@@ -3,8 +3,8 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"erupe-ce/common/token"
|
||||
"errors"
|
||||
"erupe-ce/common/token"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
|
||||
@@ -67,11 +67,11 @@ func TestUserIDFromTokenScenarios(t *testing.T) {
|
||||
// TestGetReturnExpiryCalculation tests the return expiry calculation logic
|
||||
func TestGetReturnExpiryCalculation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
lastLogin time.Time
|
||||
currentTime time.Time
|
||||
shouldUpdate bool
|
||||
description string
|
||||
name string
|
||||
lastLogin time.Time
|
||||
currentTime time.Time
|
||||
shouldUpdate bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "RecentLogin",
|
||||
@@ -126,10 +126,10 @@ func TestGetReturnExpiryCalculation(t *testing.T) {
|
||||
// TestCharacterCreationConstraints tests character creation constraints
|
||||
func TestCharacterCreationConstraints(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
currentCount int
|
||||
allowCreation bool
|
||||
description string
|
||||
name string
|
||||
currentCount int
|
||||
allowCreation bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "NoCharacters",
|
||||
@@ -170,22 +170,22 @@ func TestCharacterCreationConstraints(t *testing.T) {
|
||||
// TestCharacterDeletionLogic tests the character deletion behavior
|
||||
func TestCharacterDeletionLogic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
name string
|
||||
isNewCharacter bool
|
||||
expectedAction string
|
||||
description string
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "NewCharacterDeletion",
|
||||
isNewCharacter: true,
|
||||
expectedAction: "DELETE",
|
||||
description: "New characters should be hard deleted",
|
||||
description: "New characters should be hard deleted",
|
||||
},
|
||||
{
|
||||
name: "FinalizedCharacterDeletion",
|
||||
isNewCharacter: false,
|
||||
expectedAction: "SOFT_DELETE",
|
||||
description: "Finalized characters should be soft deleted (marked as deleted)",
|
||||
description: "Finalized characters should be soft deleted (marked as deleted)",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -367,10 +367,10 @@ func TestCreateLoginTokenContext(t *testing.T) {
|
||||
// TestPasswordValidation tests password validation logic
|
||||
func TestPasswordValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
password string
|
||||
isValid bool
|
||||
reason string
|
||||
name string
|
||||
password string
|
||||
isValid bool
|
||||
reason string
|
||||
}{
|
||||
{
|
||||
name: "NormalPassword",
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/common/gametime"
|
||||
cfg "erupe-ce/config"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -150,9 +150,9 @@ func TestLoginEndpointEmptyCredentials(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
username string
|
||||
password string
|
||||
name string
|
||||
username string
|
||||
password string
|
||||
wantPanic bool // Note: will panic without real DB
|
||||
}{
|
||||
{"EmptyUsername", "", "password", true},
|
||||
|
||||
@@ -36,23 +36,23 @@ type ChannelRegistry interface {
|
||||
|
||||
// SessionSnapshot is an immutable copy of session data taken under lock.
|
||||
type SessionSnapshot struct {
|
||||
CharID uint32
|
||||
Name string
|
||||
StageID string
|
||||
ServerIP net.IP
|
||||
ServerPort uint16
|
||||
UserBinary3 []byte // Copy of userBinaryParts index 3
|
||||
CharID uint32
|
||||
Name string
|
||||
StageID string
|
||||
ServerIP net.IP
|
||||
ServerPort uint16
|
||||
UserBinary3 []byte // Copy of userBinaryParts index 3
|
||||
}
|
||||
|
||||
// StageSnapshot is an immutable copy of stage data taken under lock.
|
||||
type StageSnapshot struct {
|
||||
ServerIP net.IP
|
||||
ServerPort uint16
|
||||
StageID string
|
||||
ClientCount int
|
||||
Reserved int
|
||||
MaxPlayers uint16
|
||||
RawBinData0 []byte
|
||||
RawBinData1 []byte
|
||||
RawBinData3 []byte
|
||||
ServerIP net.IP
|
||||
ServerPort uint16
|
||||
StageID string
|
||||
ClientCount int
|
||||
Reserved int
|
||||
MaxPlayers uint16
|
||||
RawBinData0 []byte
|
||||
RawBinData1 []byte
|
||||
RawBinData3 []byte
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ func NewMockNetConn() *MockNetConn {
|
||||
func (m *MockNetConn) Read(b []byte) (n int, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
|
||||
if m.closed {
|
||||
return 0, io.EOF
|
||||
}
|
||||
@@ -58,7 +58,7 @@ func (m *MockNetConn) Read(b []byte) (n int, err error) {
|
||||
func (m *MockNetConn) Write(b []byte) (n int, err error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
|
||||
if m.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
@@ -130,7 +130,7 @@ func TestClientConnection_GracefulLoginLogout(t *testing.T) {
|
||||
// Simulate client connecting
|
||||
mockConn := NewMockNetConn()
|
||||
session := createTestSessionForServerWithChar(server, charID, "ClientChar")
|
||||
|
||||
|
||||
// In real scenario, this would be set up by the connection handler
|
||||
// For testing, we test handlers directly without starting packet loops
|
||||
|
||||
@@ -394,11 +394,11 @@ func TestClientConnection_SaveDuringCombat(t *testing.T) {
|
||||
t.Log("Simulating save/logout while in quest/stage")
|
||||
|
||||
session := createTestSessionForServerWithChar(server, charID, "CombatChar")
|
||||
|
||||
|
||||
// Simulate being in a stage (quest)
|
||||
// In real scenario, session.stage would be set when entering quest
|
||||
// For now, we'll just test the basic save/logout flow
|
||||
|
||||
|
||||
// Note: Not calling Start() - testing handlers directly
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
|
||||
@@ -596,4 +596,3 @@ func TestClientConnection_PacketDuringLogout(t *testing.T) {
|
||||
t.Log("Race outcome: logout handler wrote last - marker byte overwritten (valid)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,9 +69,9 @@ func TestDecompress_WithValidHeader(t *testing.T) {
|
||||
|
||||
func TestDecompress_WithoutHeader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []byte
|
||||
expectError bool
|
||||
name string
|
||||
input []byte
|
||||
expectError bool
|
||||
expectOriginal bool // Expect original data returned
|
||||
}{
|
||||
{
|
||||
|
||||
@@ -9,6 +9,6 @@ const (
|
||||
|
||||
// Raviente semaphore constants
|
||||
const (
|
||||
raviSemaphoreStride = 0x10000 // ID spacing between hs_l0* semaphores
|
||||
raviSemaphoreStride = 0x10000 // ID spacing between hs_l0* semaphores
|
||||
raviSemaphoreMax = uint16(127) // max players per Raviente semaphore
|
||||
)
|
||||
|
||||
@@ -297,9 +297,9 @@ func TestGetAchData_Level7SilverTrophy(t *testing.T) {
|
||||
// produces the correct gold trophy and the last threshold as Required/Progress.
|
||||
func TestGetAchData_MaxedOut_AllCurves(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id uint8
|
||||
score int32
|
||||
name string
|
||||
id uint8
|
||||
score int32
|
||||
lastThresh int32
|
||||
}{
|
||||
// Curve 0: {5,15,30,50,100,150,200,300} sum=850, last=300
|
||||
@@ -383,11 +383,11 @@ func TestGetAchData_Curve2_FestaWins(t *testing.T) {
|
||||
wantReq uint32
|
||||
}{
|
||||
{0, 0, 0, 1},
|
||||
{1, 1, 0, 2}, // Exactly at first threshold
|
||||
{2, 1, 1, 2}, // One into second threshold
|
||||
{3, 2, 0, 3}, // Exactly at second cumulative
|
||||
{36, 8, 8, 8}, // Max level (sum of all thresholds)
|
||||
{100, 8, 8, 8}, // Well above max
|
||||
{1, 1, 0, 2}, // Exactly at first threshold
|
||||
{2, 1, 1, 2}, // One into second threshold
|
||||
{3, 2, 0, 3}, // Exactly at second cumulative
|
||||
{36, 8, 8, 8}, // Max level (sum of all thresholds)
|
||||
{100, 8, 8, 8}, // Well above max
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
@@ -35,9 +35,9 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
|
||||
tmp := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
|
||||
|
||||
const (
|
||||
timerPayloadSize = 0x10 // expected payload length for timer packets
|
||||
timerPayloadSize = 0x10 // expected payload length for timer packets
|
||||
timerSubtype = uint16(0x0002) // timer data subtype identifier
|
||||
timerFlag = uint8(0x18) // timer flag byte
|
||||
timerFlag = uint8(0x18) // timer flag byte
|
||||
)
|
||||
if pkt.BroadcastType == BroadcastTypeStage && pkt.MessageType == BinaryMessageTypeData && len(pkt.RawDataPayload) == timerPayloadSize {
|
||||
if tmp.ReadUint16() == timerSubtype && tmp.ReadUint8() == timerFlag {
|
||||
|
||||
@@ -154,7 +154,7 @@ func TestBroadcastTypes(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.SetBE() // Targeted uses BE
|
||||
msg := &binpacket.MsgBinTargeted{
|
||||
TargetCharIDs: []uint32{1, 2, 3},
|
||||
TargetCharIDs: []uint32{1, 2, 3},
|
||||
RawDataPayload: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
||||
}
|
||||
_ = msg.Build(bf)
|
||||
@@ -218,8 +218,8 @@ func TestBroadcastTypes(t *testing.T) {
|
||||
// TestBinaryMessageTypes verifies different message types are handled
|
||||
func TestBinaryMessageTypes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
messageType uint8
|
||||
name string
|
||||
messageType uint8
|
||||
buildPayload func() []byte
|
||||
}{
|
||||
{
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/common/byteframe"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
// TestHandleMsgSysEnumerateClient tests client enumeration in stages
|
||||
func TestHandleMsgSysEnumerateClient(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
stageID string
|
||||
getType uint8
|
||||
setupStage func(*Server, string)
|
||||
wantClientCount int
|
||||
wantFailure bool
|
||||
name string
|
||||
stageID string
|
||||
getType uint8
|
||||
setupStage func(*Server, string)
|
||||
wantClientCount int
|
||||
wantFailure bool
|
||||
}{
|
||||
{
|
||||
name: "enumerate_all_clients",
|
||||
|
||||
@@ -1256,4 +1256,3 @@ func TestParseChatCommand_UnknownCommand(t *testing.T) {
|
||||
t.Errorf("chat responses = %d, want 0 (unknown command is silent)", n)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -141,8 +141,8 @@ func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs1(t *testing.T) {
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfMercenaryHuntdata{
|
||||
AckHandle: 12345,
|
||||
RequestType: 1,
|
||||
AckHandle: 12345,
|
||||
RequestType: 1,
|
||||
}
|
||||
|
||||
handleMsgMhfMercenaryHuntdata(session, pkt)
|
||||
@@ -162,8 +162,8 @@ func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs0(t *testing.T) {
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfMercenaryHuntdata{
|
||||
AckHandle: 12345,
|
||||
RequestType: 0,
|
||||
AckHandle: 12345,
|
||||
RequestType: 0,
|
||||
}
|
||||
|
||||
handleMsgMhfMercenaryHuntdata(session, pkt)
|
||||
@@ -183,8 +183,8 @@ func TestHandleMsgMhfMercenaryHuntdata_RequestTypeIs2(t *testing.T) {
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfMercenaryHuntdata{
|
||||
AckHandle: 12345,
|
||||
RequestType: 2,
|
||||
AckHandle: 12345,
|
||||
RequestType: 2,
|
||||
}
|
||||
|
||||
handleMsgMhfMercenaryHuntdata(session, pkt)
|
||||
|
||||
@@ -17,7 +17,7 @@ func TestHandleMsgMhfGetPaperData_Case0(t *testing.T) {
|
||||
|
||||
handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{
|
||||
AckHandle: 1,
|
||||
DataType: 0,
|
||||
DataType: 0,
|
||||
})
|
||||
|
||||
select {
|
||||
@@ -36,7 +36,7 @@ func TestHandleMsgMhfGetPaperData_Case5(t *testing.T) {
|
||||
|
||||
handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{
|
||||
AckHandle: 1,
|
||||
DataType: 5,
|
||||
DataType: 5,
|
||||
})
|
||||
|
||||
select {
|
||||
@@ -55,7 +55,7 @@ func TestHandleMsgMhfGetPaperData_Case6(t *testing.T) {
|
||||
|
||||
handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{
|
||||
AckHandle: 1,
|
||||
DataType: 6,
|
||||
DataType: 6,
|
||||
})
|
||||
|
||||
select {
|
||||
@@ -75,7 +75,7 @@ func TestHandleMsgMhfGetPaperData_GreaterThan1000_KnownKey(t *testing.T) {
|
||||
// 6001 is a known key in paperGiftData
|
||||
handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{
|
||||
AckHandle: 1,
|
||||
DataType: 6001,
|
||||
DataType: 6001,
|
||||
})
|
||||
|
||||
select {
|
||||
@@ -95,7 +95,7 @@ func TestHandleMsgMhfGetPaperData_GreaterThan1000_UnknownKey(t *testing.T) {
|
||||
// 9999 is not a known key in paperGiftData
|
||||
handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{
|
||||
AckHandle: 1,
|
||||
DataType: 9999,
|
||||
DataType: 9999,
|
||||
})
|
||||
|
||||
select {
|
||||
@@ -114,7 +114,7 @@ func TestHandleMsgMhfGetPaperData_DefaultUnknownLessThan1000(t *testing.T) {
|
||||
// Unknown type < 1000, hits default case then falls to else branch
|
||||
handleMsgMhfGetPaperData(session, &mhfpacket.MsgMhfGetPaperData{
|
||||
AckHandle: 1,
|
||||
DataType: 99,
|
||||
DataType: 99,
|
||||
})
|
||||
|
||||
select {
|
||||
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
// TestCharacterSaveDataPersistenceEdgeCases tests edge cases in character savedata persistence
|
||||
func TestCharacterSaveDataPersistenceEdgeCases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
charID uint32
|
||||
charName string
|
||||
isNew bool
|
||||
playtime uint32
|
||||
wantValid bool
|
||||
name string
|
||||
charID uint32
|
||||
charName string
|
||||
isNew bool
|
||||
playtime uint32
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid_new_character",
|
||||
@@ -85,34 +85,34 @@ func TestCharacterSaveDataPersistenceEdgeCases(t *testing.T) {
|
||||
// TestSaveDataCompressionRoundTrip tests compression/decompression edge cases
|
||||
func TestSaveDataCompressionRoundTrip(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
dataSize int
|
||||
name string
|
||||
dataSize int
|
||||
dataPattern byte
|
||||
compresses bool
|
||||
compresses bool
|
||||
}{
|
||||
{
|
||||
name: "empty_data",
|
||||
dataSize: 0,
|
||||
name: "empty_data",
|
||||
dataSize: 0,
|
||||
dataPattern: 0x00,
|
||||
compresses: true,
|
||||
compresses: true,
|
||||
},
|
||||
{
|
||||
name: "small_data",
|
||||
dataSize: 10,
|
||||
name: "small_data",
|
||||
dataSize: 10,
|
||||
dataPattern: 0xFF,
|
||||
compresses: false, // Small data may not compress well
|
||||
compresses: false, // Small data may not compress well
|
||||
},
|
||||
{
|
||||
name: "highly_repetitive_data",
|
||||
dataSize: 1000,
|
||||
name: "highly_repetitive_data",
|
||||
dataSize: 1000,
|
||||
dataPattern: 0xAA,
|
||||
compresses: true, // Highly repetitive should compress
|
||||
compresses: true, // Highly repetitive should compress
|
||||
},
|
||||
{
|
||||
name: "random_data",
|
||||
dataSize: 500,
|
||||
name: "random_data",
|
||||
dataSize: 500,
|
||||
dataPattern: 0x00, // Will be varied by position
|
||||
compresses: false,
|
||||
compresses: false,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -149,34 +149,34 @@ func TestSaveDataCompressionRoundTrip(t *testing.T) {
|
||||
// TestSaveDataPointerHandling tests edge cases in save data pointer management
|
||||
func TestSaveDataPointerHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pointerCount int
|
||||
name string
|
||||
pointerCount int
|
||||
maxPointerValue int
|
||||
valid bool
|
||||
valid bool
|
||||
}{
|
||||
{
|
||||
name: "no_pointers",
|
||||
pointerCount: 0,
|
||||
name: "no_pointers",
|
||||
pointerCount: 0,
|
||||
maxPointerValue: 0,
|
||||
valid: true,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "single_pointer",
|
||||
pointerCount: 1,
|
||||
name: "single_pointer",
|
||||
pointerCount: 1,
|
||||
maxPointerValue: 100,
|
||||
valid: true,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "multiple_pointers",
|
||||
pointerCount: 10,
|
||||
name: "multiple_pointers",
|
||||
pointerCount: 10,
|
||||
maxPointerValue: 5000,
|
||||
valid: true,
|
||||
valid: true,
|
||||
},
|
||||
{
|
||||
name: "max_pointers",
|
||||
pointerCount: 100,
|
||||
name: "max_pointers",
|
||||
pointerCount: 100,
|
||||
maxPointerValue: 1000000,
|
||||
valid: true,
|
||||
valid: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -321,12 +321,12 @@ func TestSaveDataRPHandling(t *testing.T) {
|
||||
// TestSaveDataHousingDataHandling tests various housing/decorative data fields
|
||||
func TestSaveDataHousingDataHandling(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
houseTier []byte
|
||||
houseData []byte
|
||||
name string
|
||||
houseTier []byte
|
||||
houseData []byte
|
||||
bookshelfData []byte
|
||||
galleryData []byte
|
||||
validEmpty bool
|
||||
galleryData []byte
|
||||
validEmpty bool
|
||||
}{
|
||||
{
|
||||
name: "all_empty_housing",
|
||||
@@ -588,8 +588,8 @@ func TestSaveDataBoundaryValues(t *testing.T) {
|
||||
// TestSaveDataSerialization tests savedata can be serialized to binary format
|
||||
func TestSaveDataSerialization(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
charID uint32
|
||||
name string
|
||||
charID uint32
|
||||
playtime uint32
|
||||
}{
|
||||
{
|
||||
@@ -643,18 +643,18 @@ func TestSaveDataTimestampHandling(t *testing.T) {
|
||||
expectFresh bool
|
||||
}{
|
||||
{
|
||||
name: "just_saved",
|
||||
ageSeconds: 0,
|
||||
name: "just_saved",
|
||||
ageSeconds: 0,
|
||||
expectFresh: true,
|
||||
},
|
||||
{
|
||||
name: "recent_save",
|
||||
ageSeconds: 60,
|
||||
name: "recent_save",
|
||||
ageSeconds: 60,
|
||||
expectFresh: true,
|
||||
},
|
||||
{
|
||||
name: "old_save",
|
||||
ageSeconds: 86400, // 1 day old
|
||||
name: "old_save",
|
||||
ageSeconds: 86400, // 1 day old
|
||||
expectFresh: false,
|
||||
},
|
||||
}
|
||||
@@ -745,9 +745,9 @@ func TestDataCorruptionRecovery(t *testing.T) {
|
||||
// TestChecksumValidation tests savedata checksum validation
|
||||
func TestChecksumValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
checksumValid bool
|
||||
name string
|
||||
data []byte
|
||||
checksumValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid_checksum",
|
||||
@@ -794,11 +794,11 @@ func TestChecksumValidation(t *testing.T) {
|
||||
// TestSaveDataBackupRestoration tests backup and restoration functionality
|
||||
func TestSaveDataBackupRestoration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
originalCharID uint32
|
||||
originalPlaytime uint32
|
||||
hasBackup bool
|
||||
canRestore bool
|
||||
name string
|
||||
originalCharID uint32
|
||||
originalPlaytime uint32
|
||||
hasBackup bool
|
||||
canRestore bool
|
||||
}{
|
||||
{
|
||||
name: "backup_with_restore",
|
||||
@@ -862,11 +862,11 @@ func TestSaveDataBackupRestoration(t *testing.T) {
|
||||
// TestSaveDataVersionMigration tests savedata version migration and compatibility
|
||||
func TestSaveDataVersionMigration(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sourceVersion int
|
||||
targetVersion int
|
||||
canMigrate bool
|
||||
dataLoss bool
|
||||
name string
|
||||
sourceVersion int
|
||||
targetVersion int
|
||||
canMigrate bool
|
||||
dataLoss bool
|
||||
}{
|
||||
{
|
||||
name: "same_version",
|
||||
@@ -932,10 +932,10 @@ func TestSaveDataVersionMigration(t *testing.T) {
|
||||
// TestSaveDataRollback tests rollback to previous savedata state
|
||||
func TestSaveDataRollback(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
snapshots int
|
||||
canRollback bool
|
||||
rollbackSteps int
|
||||
name string
|
||||
snapshots int
|
||||
canRollback bool
|
||||
rollbackSteps int
|
||||
}{
|
||||
{
|
||||
name: "single_snapshot",
|
||||
@@ -1048,18 +1048,18 @@ func TestSaveDataConcurrentAccess(t *testing.T) {
|
||||
concurrentWrites int
|
||||
}{
|
||||
{
|
||||
name: "multiple_readers",
|
||||
concurrentReads: 5,
|
||||
name: "multiple_readers",
|
||||
concurrentReads: 5,
|
||||
concurrentWrites: 0,
|
||||
},
|
||||
{
|
||||
name: "multiple_writers",
|
||||
concurrentReads: 0,
|
||||
name: "multiple_writers",
|
||||
concurrentReads: 0,
|
||||
concurrentWrites: 3,
|
||||
},
|
||||
{
|
||||
name: "mixed_access",
|
||||
concurrentReads: 3,
|
||||
name: "mixed_access",
|
||||
concurrentReads: 3,
|
||||
concurrentWrites: 2,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -125,19 +125,19 @@ func TestScenarioSaveErrorHandling(t *testing.T) {
|
||||
// 3. The function should return early after sending fail ACK
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
name string
|
||||
scenarioData []byte
|
||||
wantError bool
|
||||
wantError bool
|
||||
}{
|
||||
{
|
||||
name: "valid_scenario_data",
|
||||
name: "valid_scenario_data",
|
||||
scenarioData: []byte{0x01, 0x02, 0x03},
|
||||
wantError: false,
|
||||
wantError: false,
|
||||
},
|
||||
{
|
||||
name: "empty_scenario_data",
|
||||
name: "empty_scenario_data",
|
||||
scenarioData: []byte{},
|
||||
wantError: false, // Empty data is valid
|
||||
wantError: false, // Empty data is valid
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -13,10 +13,10 @@ import (
|
||||
|
||||
// Diva Defense event duration constants (all values in seconds)
|
||||
const (
|
||||
divaPhaseDuration = 601200 // 6d 23h = first song phase
|
||||
divaInterlude = 3900 // 65 min = gap between phases
|
||||
divaPhaseDuration = 601200 // 6d 23h = first song phase
|
||||
divaInterlude = 3900 // 65 min = gap between phases
|
||||
divaWeekDuration = secsPerWeek // 7 days = subsequent phase length
|
||||
divaTotalLifespan = 2977200 // ~34.5 days = full event window
|
||||
divaTotalLifespan = 2977200 // ~34.5 days = full event window
|
||||
)
|
||||
|
||||
func cleanupDiva(s *Session) {
|
||||
|
||||
@@ -106,4 +106,3 @@ func TestHandleMsgMhfEnumerateRanking_State3(t *testing.T) {
|
||||
t.Error("No response packet queued")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -326,8 +326,8 @@ func TestHandleMsgMhfPlayStepupGacha_Success(t *testing.T) {
|
||||
func TestHandleMsgMhfGetStepupStatus_FreshStep(t *testing.T) {
|
||||
server := createMockServer()
|
||||
gachaRepo := &mockGachaRepo{
|
||||
stepupStep: 2,
|
||||
stepupTime: time.Now(), // recent, not stale
|
||||
stepupStep: 2,
|
||||
stepupTime: time.Now(), // recent, not stale
|
||||
hasEntryType: true,
|
||||
}
|
||||
server.gachaRepo = gachaRepo
|
||||
|
||||
@@ -33,8 +33,8 @@ func TestLoadGuildCooking_WithActiveMeals(t *testing.T) {
|
||||
server := createMockServer()
|
||||
guildMock := &mockGuildRepoOps{
|
||||
meals: []*GuildMeal{
|
||||
{ID: 1, MealID: 100, Level: 3, CreatedAt: TimeAdjusted()}, // active (within 60 min)
|
||||
{ID: 2, MealID: 200, Level: 1, CreatedAt: TimeAdjusted().Add(-2 * time.Hour)}, // expired
|
||||
{ID: 1, MealID: 100, Level: 3, CreatedAt: TimeAdjusted()}, // active (within 60 min)
|
||||
{ID: 2, MealID: 200, Level: 1, CreatedAt: TimeAdjusted().Add(-2 * time.Hour)}, // expired
|
||||
},
|
||||
}
|
||||
guildMock.guild = &Guild{ID: 10}
|
||||
|
||||
@@ -599,9 +599,9 @@ func newNullTermBF(data []byte) *byteframe.ByteFrame {
|
||||
|
||||
func newMottoBF(sub, main uint8) *byteframe.ByteFrame {
|
||||
bf := byteframe.NewByteFrame()
|
||||
bf.WriteUint16(0) // skipped
|
||||
bf.WriteUint8(sub) // SubMotto
|
||||
bf.WriteUint8(main) // MainMotto
|
||||
bf.WriteUint16(0) // skipped
|
||||
bf.WriteUint8(sub) // SubMotto
|
||||
bf.WriteUint8(main) // MainMotto
|
||||
_, _ = bf.Seek(0, 0)
|
||||
return bf
|
||||
}
|
||||
|
||||
@@ -208,4 +208,3 @@ func TestUpdateRights_Error(t *testing.T) {
|
||||
t.Fatal("updateRights should queue a packet even on error")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ package channelserver
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/common/mhfitem"
|
||||
"erupe-ce/common/token"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"testing"
|
||||
|
||||
@@ -699,7 +699,7 @@ func TestWarehouseItemSerialization(t *testing.T) {
|
||||
items []mhfitem.MHFItemStack
|
||||
}{
|
||||
{
|
||||
name: "empty_warehouse",
|
||||
name: "empty_warehouse",
|
||||
items: []mhfitem.MHFItemStack{},
|
||||
},
|
||||
{
|
||||
@@ -747,11 +747,11 @@ func TestWarehouseEquipmentSerialization(t *testing.T) {
|
||||
equipment: []mhfitem.MHFEquipment{},
|
||||
},
|
||||
{
|
||||
name: "single_equipment",
|
||||
name: "single_equipment",
|
||||
equipment: createTestEquipment([]uint16{100}, []uint32{1}),
|
||||
},
|
||||
{
|
||||
name: "multiple_equipment",
|
||||
name: "multiple_equipment",
|
||||
equipment: createTestEquipment([]uint16{100, 101, 102}, []uint32{1, 2, 3}),
|
||||
},
|
||||
}
|
||||
@@ -837,16 +837,16 @@ func TestWarehouseItemDiff(t *testing.T) {
|
||||
// TestWarehouseEquipmentMerge verifies equipment merging logic
|
||||
func TestWarehouseEquipmentMerge(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
oldEquip []mhfitem.MHFEquipment
|
||||
newEquip []mhfitem.MHFEquipment
|
||||
wantMerged int
|
||||
name string
|
||||
oldEquip []mhfitem.MHFEquipment
|
||||
newEquip []mhfitem.MHFEquipment
|
||||
wantMerged int
|
||||
}{
|
||||
{
|
||||
name: "merge_empty",
|
||||
oldEquip: []mhfitem.MHFEquipment{},
|
||||
newEquip: []mhfitem.MHFEquipment{},
|
||||
wantMerged: 0,
|
||||
name: "merge_empty",
|
||||
oldEquip: []mhfitem.MHFEquipment{},
|
||||
newEquip: []mhfitem.MHFEquipment{},
|
||||
wantMerged: 0,
|
||||
},
|
||||
{
|
||||
name: "add_new_equipment",
|
||||
|
||||
@@ -45,10 +45,10 @@ func TestUserGetItems_ParsesData(t *testing.T) {
|
||||
bf.WriteUint16(1) // numStacks
|
||||
bf.WriteUint16(0) // unused
|
||||
// Item stack: warehouseID(4) + itemID(2) + quantity(2) + unk0(4) = 12 bytes
|
||||
bf.WriteUint32(100) // warehouseID
|
||||
bf.WriteUint16(500) // itemID
|
||||
bf.WriteUint16(3) // quantity
|
||||
bf.WriteUint32(0) // unk0
|
||||
bf.WriteUint32(100) // warehouseID
|
||||
bf.WriteUint16(500) // itemID
|
||||
bf.WriteUint16(3) // quantity
|
||||
bf.WriteUint32(0) // unk0
|
||||
|
||||
server := createMockServer()
|
||||
userMock := &mockUserRepoForItems{itemBoxData: bf.Data()}
|
||||
|
||||
@@ -86,9 +86,9 @@ func TestGetAirouDetails_Empty(t *testing.T) {
|
||||
|
||||
func TestGetAirouDetails_SingleCat(t *testing.T) {
|
||||
input := Airou{
|
||||
ID: 42,
|
||||
Name: []byte("TestCat"),
|
||||
Task: 4,
|
||||
ID: 42,
|
||||
Name: []byte("TestCat"),
|
||||
Task: 4,
|
||||
Personality: 3,
|
||||
Class: 2,
|
||||
Experience: 1500,
|
||||
@@ -175,16 +175,16 @@ func TestGetAirouDetails_ExtraTrailingBytes(t *testing.T) {
|
||||
|
||||
catBuf := new(bytes.Buffer)
|
||||
_ = binary.Write(catBuf, binary.BigEndian, uint32(99)) // catID
|
||||
catBuf.WriteByte(0) // skip
|
||||
catBuf.Write(make([]byte, 18)) // name
|
||||
catBuf.WriteByte(3) // currentTask
|
||||
catBuf.Write(make([]byte, 16)) // appearance skip
|
||||
catBuf.WriteByte(1) // personality
|
||||
catBuf.WriteByte(2) // class
|
||||
catBuf.Write(make([]byte, 5)) // affection skip
|
||||
catBuf.WriteByte(0) // skip
|
||||
catBuf.Write(make([]byte, 18)) // name
|
||||
catBuf.WriteByte(3) // currentTask
|
||||
catBuf.Write(make([]byte, 16)) // appearance skip
|
||||
catBuf.WriteByte(1) // personality
|
||||
catBuf.WriteByte(2) // class
|
||||
catBuf.Write(make([]byte, 5)) // affection skip
|
||||
_ = binary.Write(catBuf, binary.BigEndian, uint32(500)) // experience
|
||||
catBuf.WriteByte(0) // weapon equipped bool
|
||||
catBuf.WriteByte(6) // weaponType
|
||||
catBuf.WriteByte(0) // weapon equipped bool
|
||||
catBuf.WriteByte(6) // weaponType
|
||||
_ = binary.Write(catBuf, binary.BigEndian, uint16(50)) // weaponID
|
||||
|
||||
catData := catBuf.Bytes()
|
||||
@@ -238,8 +238,8 @@ func TestHandleMsgMhfMercenaryHuntdata_Unk0_1(t *testing.T) {
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfMercenaryHuntdata{
|
||||
AckHandle: 12345,
|
||||
RequestType: 1,
|
||||
AckHandle: 12345,
|
||||
RequestType: 1,
|
||||
}
|
||||
|
||||
handleMsgMhfMercenaryHuntdata(session, pkt)
|
||||
@@ -260,8 +260,8 @@ func TestHandleMsgMhfMercenaryHuntdata_Unk0_0(t *testing.T) {
|
||||
session := createMockSession(1, server)
|
||||
|
||||
pkt := &mhfpacket.MsgMhfMercenaryHuntdata{
|
||||
AckHandle: 12345,
|
||||
RequestType: 0,
|
||||
AckHandle: 12345,
|
||||
RequestType: 0,
|
||||
}
|
||||
|
||||
handleMsgMhfMercenaryHuntdata(session, pkt)
|
||||
|
||||
@@ -73,10 +73,10 @@ func TestBackportQuestBasic(t *testing.T) {
|
||||
// TestFindSubSliceIndices tests byte slice pattern finding
|
||||
func TestFindSubSliceIndices(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data []byte
|
||||
pattern []byte
|
||||
expected int
|
||||
name string
|
||||
data []byte
|
||||
pattern []byte
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "single_match",
|
||||
@@ -213,9 +213,9 @@ func TestEnumerateQuestBasicStructure(t *testing.T) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
// Build a minimal response structure
|
||||
bf.WriteUint16(0) // Returned count
|
||||
bf.WriteUint16(0) // Returned count
|
||||
bf.WriteUint16(uint16(time.Now().Unix() & 0xFFFF)) // Unix timestamp offset
|
||||
bf.WriteUint16(0) // Tune values count
|
||||
bf.WriteUint16(0) // Tune values count
|
||||
|
||||
data := bf.Data()
|
||||
|
||||
@@ -301,12 +301,12 @@ func TestEnumerateQuestTuneValuesEncoding(t *testing.T) {
|
||||
// TestEventQuestCycleCalculation tests event quest cycle calculations
|
||||
func TestEventQuestCycleCalculation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
startTime time.Time
|
||||
activeDays int
|
||||
inactiveDays int
|
||||
currentTime time.Time
|
||||
shouldBeActive bool
|
||||
name string
|
||||
startTime time.Time
|
||||
activeDays int
|
||||
inactiveDays int
|
||||
currentTime time.Time
|
||||
shouldBeActive bool
|
||||
}{
|
||||
{
|
||||
name: "active_period",
|
||||
@@ -409,8 +409,8 @@ func TestMakeEventQuestPacketStructure(t *testing.T) {
|
||||
questType := uint8(16)
|
||||
|
||||
bf.WriteUint32(questID)
|
||||
bf.WriteUint32(0) // Unk
|
||||
bf.WriteUint8(0) // Unk
|
||||
bf.WriteUint32(0) // Unk
|
||||
bf.WriteUint8(0) // Unk
|
||||
bf.WriteUint8(maxPlayers)
|
||||
bf.WriteUint8(questType)
|
||||
bf.WriteBool(true) // Multi-player
|
||||
@@ -626,13 +626,13 @@ func TestGetUdBonusQuestInfoStructure(t *testing.T) {
|
||||
bf.SetLE()
|
||||
|
||||
// Example UD bonus quest info entry
|
||||
bf.WriteUint8(0) // Unk0
|
||||
bf.WriteUint8(0) // Unk1
|
||||
bf.WriteUint32(uint32(time.Now().Unix())) // StartTime
|
||||
bf.WriteUint32(uint32(time.Now().Add(30*24*time.Hour).Unix())) // EndTime
|
||||
bf.WriteUint32(0) // Unk4
|
||||
bf.WriteUint8(0) // Unk5
|
||||
bf.WriteUint8(0) // Unk6
|
||||
bf.WriteUint8(0) // Unk0
|
||||
bf.WriteUint8(0) // Unk1
|
||||
bf.WriteUint32(uint32(time.Now().Unix())) // StartTime
|
||||
bf.WriteUint32(uint32(time.Now().Add(30 * 24 * time.Hour).Unix())) // EndTime
|
||||
bf.WriteUint32(0) // Unk4
|
||||
bf.WriteUint8(0) // Unk5
|
||||
bf.WriteUint8(0) // Unk6
|
||||
|
||||
data := bf.Data()
|
||||
|
||||
@@ -646,8 +646,8 @@ func TestGetUdBonusQuestInfoStructure(t *testing.T) {
|
||||
bf2 := byteframe.NewByteFrameFromBytes(data)
|
||||
bf2.SetLE()
|
||||
|
||||
bf2.ReadUint8() // Unk0
|
||||
bf2.ReadUint8() // Unk1
|
||||
bf2.ReadUint8() // Unk0
|
||||
bf2.ReadUint8() // Unk1
|
||||
startTime := bf2.ReadUint32()
|
||||
endTime := bf2.ReadUint32()
|
||||
bf2.ReadUint32() // Unk4
|
||||
@@ -665,9 +665,9 @@ func BenchmarkQuestEnumeration(b *testing.B) {
|
||||
bf := byteframe.NewByteFrame()
|
||||
|
||||
// Build a response with tune values
|
||||
bf.WriteUint16(0) // Returned count
|
||||
bf.WriteUint16(0) // Returned count
|
||||
bf.WriteUint16(uint16(time.Now().Unix() & 0xFFFF))
|
||||
bf.WriteUint16(100) // 100 tune values
|
||||
bf.WriteUint16(100) // 100 tune values
|
||||
|
||||
for j := 0; j < 100; j++ {
|
||||
bf.WriteUint16(uint16(j))
|
||||
|
||||
@@ -155,11 +155,11 @@ func TestRengokuData_SaveLoadRoundTrip(t *testing.T) {
|
||||
|
||||
// Build a realistic payload with non-zero skill data
|
||||
payload := buildRengokuTestPayload(
|
||||
15, 18519, // MP: 15 stages, 18519 points
|
||||
4, 381, // SP: 4 stages, 381 points
|
||||
[3]uint16{0x0012, 0x0034, 0x0056}, // skill slot IDs
|
||||
15, 18519, // MP: 15 stages, 18519 points
|
||||
4, 381, // SP: 4 stages, 381 points
|
||||
[3]uint16{0x0012, 0x0034, 0x0056}, // skill slot IDs
|
||||
[3]uint32{0x00110001, 0x00220002, 0x00330003}, // equipped skills
|
||||
[3]uint32{100, 200, 300}, // skill points invested
|
||||
[3]uint32{100, 200, 300}, // skill points invested
|
||||
)
|
||||
|
||||
// === SAVE ===
|
||||
@@ -212,8 +212,8 @@ func TestRengokuData_SaveLoadRoundTrip_AcrossSessions(t *testing.T) {
|
||||
session1 := createTestSessionForServerWithChar(server, charID, "RengokuChar2")
|
||||
|
||||
payload := buildRengokuTestPayload(
|
||||
80, 342295, // MP: deep run
|
||||
38, 54634, // SP: deep run
|
||||
80, 342295, // MP: deep run
|
||||
38, 54634, // SP: deep run
|
||||
[3]uint16{0x00AA, 0x00BB, 0x00CC},
|
||||
[3]uint32{0xDEAD0001, 0xBEEF0002, 0xCAFE0003},
|
||||
[3]uint32{500, 750, 1000},
|
||||
@@ -1052,9 +1052,9 @@ func TestRengokuData_LargePayload(t *testing.T) {
|
||||
}
|
||||
// Ensure valid score region at offsets 71-90
|
||||
binary.BigEndian.PutUint32(payload[71:75], 20) // maxStageMp
|
||||
binary.BigEndian.PutUint32(payload[75:79], 30000) // maxScoreMp
|
||||
binary.BigEndian.PutUint32(payload[83:87], 10) // maxStageSp
|
||||
binary.BigEndian.PutUint32(payload[87:91], 15000) // maxScoreSp
|
||||
binary.BigEndian.PutUint32(payload[75:79], 30000) // maxScoreMp
|
||||
binary.BigEndian.PutUint32(payload[83:87], 10) // maxStageSp
|
||||
binary.BigEndian.PutUint32(payload[87:91], 15000) // maxScoreSp
|
||||
|
||||
savePkt := &mhfpacket.MsgMhfSaveRengokuData{
|
||||
AckHandle: 10001,
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/common/mhfitem"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/server/channelserver/compression/nullcomp"
|
||||
)
|
||||
@@ -263,7 +263,7 @@ func TestSaveLoad_CurrentEquipment(t *testing.T) {
|
||||
copy(saveData[88:], []byte("TestChar\x00"))
|
||||
|
||||
// Set weapon type at known offset (simplified)
|
||||
weaponTypeOffset := 500 // Example offset
|
||||
weaponTypeOffset := 500 // Example offset
|
||||
saveData[weaponTypeOffset] = 0x03 // Great Sword
|
||||
|
||||
compressed, err := nullcomp.Compress(saveData)
|
||||
@@ -377,10 +377,10 @@ func TestSaveLoad_Transmog(t *testing.T) {
|
||||
setSize := 76 // G10+
|
||||
numSets := 1
|
||||
transmogData := make([]byte, 2+numSets*(2+setSize))
|
||||
transmogData[0] = 1 // version
|
||||
transmogData[0] = 1 // version
|
||||
transmogData[1] = byte(numSets) // count
|
||||
transmogData[2] = 0 // index high byte
|
||||
transmogData[3] = 1 // index low byte (set #1)
|
||||
transmogData[2] = 0 // index high byte
|
||||
transmogData[3] = 1 // index low byte (set #1)
|
||||
|
||||
// Save transmog data
|
||||
pkt := &mhfpacket.MsgMhfSaveDecoMyset{
|
||||
|
||||
@@ -223,11 +223,11 @@ func TestHandleMsgMhfAcquireExchangeShop_RecordsPurchases(t *testing.T) {
|
||||
|
||||
// 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
|
||||
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,
|
||||
|
||||
@@ -18,7 +18,6 @@ func TestCreateStageSuccess(t *testing.T) {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
s := createTestSession(mock)
|
||||
|
||||
|
||||
// Create a new stage
|
||||
pkt := &mhfpacket.MsgSysCreateStage{
|
||||
StageID: "test_stage_1",
|
||||
@@ -46,7 +45,6 @@ func TestCreateStageDuplicate(t *testing.T) {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
s := createTestSession(mock)
|
||||
|
||||
|
||||
// Create first stage
|
||||
pkt1 := &mhfpacket.MsgSysCreateStage{
|
||||
StageID: "test_stage",
|
||||
@@ -76,7 +74,6 @@ func TestStageLocking(t *testing.T) {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
s := createTestSession(mock)
|
||||
|
||||
|
||||
// Create a stage
|
||||
stage := NewStage("locked_stage")
|
||||
stage.host = s
|
||||
@@ -105,7 +102,6 @@ func TestStageReservation(t *testing.T) {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
s := createTestSession(mock)
|
||||
|
||||
|
||||
// Create a stage
|
||||
stage := NewStage("reserved_stage")
|
||||
stage.host = s
|
||||
@@ -164,7 +160,7 @@ func TestStageBinaryData(t *testing.T) {
|
||||
stage := NewStage("binary_stage")
|
||||
stage.rawBinaryData = make(map[stageBinaryKey][]byte)
|
||||
s.stage = stage
|
||||
|
||||
|
||||
s.server.stages.Store("binary_stage", stage)
|
||||
|
||||
// Store binary data directly
|
||||
@@ -231,7 +227,6 @@ func TestIsStageFull(t *testing.T) {
|
||||
stage.clients[client] = uint32(i)
|
||||
}
|
||||
|
||||
|
||||
s.server.stages.Store("full_test_stage", stage)
|
||||
|
||||
result := isStageFull(s, "full_test_stage")
|
||||
@@ -303,7 +298,6 @@ func TestDestructEmptyStages(t *testing.T) {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
s := createTestSession(mock)
|
||||
|
||||
|
||||
// Create stages with different client counts
|
||||
emptyStage := NewStage("empty_stage")
|
||||
emptyStage.clients = make(map[*Session]uint32)
|
||||
@@ -423,7 +417,6 @@ func TestConcurrentStageOperations(t *testing.T) {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
baseSession := createTestSession(mock)
|
||||
|
||||
|
||||
// Create a stage
|
||||
stage := NewStage("concurrent_stage")
|
||||
stage.clients = make(map[*Session]uint32)
|
||||
|
||||
@@ -12,7 +12,7 @@ func TestHandleMsgMhfInfoTournament_Type0(t *testing.T) {
|
||||
|
||||
pkt := &mhfpacket.MsgMhfInfoTournament{
|
||||
AckHandle: 12345,
|
||||
QueryType: 0,
|
||||
QueryType: 0,
|
||||
}
|
||||
|
||||
handleMsgMhfInfoTournament(session, pkt)
|
||||
@@ -34,7 +34,7 @@ func TestHandleMsgMhfInfoTournament_Type1(t *testing.T) {
|
||||
|
||||
pkt := &mhfpacket.MsgMhfInfoTournament{
|
||||
AckHandle: 12345,
|
||||
QueryType: 1,
|
||||
QueryType: 1,
|
||||
}
|
||||
|
||||
handleMsgMhfInfoTournament(session, pkt)
|
||||
|
||||
@@ -35,7 +35,7 @@ func TestHandleMsgMhfGetTenrouirai_Default(t *testing.T) {
|
||||
pkt := &mhfpacket.MsgMhfGetTenrouirai{
|
||||
AckHandle: 12345,
|
||||
Unk0: 0,
|
||||
DataType: 0,
|
||||
DataType: 0,
|
||||
}
|
||||
|
||||
handleMsgMhfGetTenrouirai(session, pkt)
|
||||
|
||||
@@ -19,22 +19,22 @@ func IntegrationTest_PacketQueueFlow(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
packetCount int
|
||||
queueDelay time.Duration
|
||||
wantPackets int
|
||||
name string
|
||||
packetCount int
|
||||
queueDelay time.Duration
|
||||
wantPackets int
|
||||
}{
|
||||
{
|
||||
name: "sequential_packets",
|
||||
packetCount: 10,
|
||||
queueDelay: 10 * time.Millisecond,
|
||||
wantPackets: 10,
|
||||
name: "sequential_packets",
|
||||
packetCount: 10,
|
||||
queueDelay: 10 * time.Millisecond,
|
||||
wantPackets: 10,
|
||||
},
|
||||
{
|
||||
name: "rapid_fire_packets",
|
||||
packetCount: 50,
|
||||
queueDelay: 1 * time.Millisecond,
|
||||
wantPackets: 50,
|
||||
name: "rapid_fire_packets",
|
||||
packetCount: 50,
|
||||
queueDelay: 1 * time.Millisecond,
|
||||
wantPackets: 50,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func IntegrationTest_PacketQueueFlow(t *testing.T) {
|
||||
|
||||
s := &Session{
|
||||
sendPackets: make(chan packet, 100),
|
||||
server: &Server{
|
||||
server: &Server{
|
||||
erupeConfig: &cfg.Config{
|
||||
DebugOptions: cfg.DebugOptions{
|
||||
LogOutboundMessages: false,
|
||||
@@ -126,7 +126,7 @@ func IntegrationTest_ConcurrentQueueing(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
s.cryptConn = mock
|
||||
s.cryptConn = mock
|
||||
|
||||
go s.sendLoop()
|
||||
|
||||
@@ -224,7 +224,7 @@ func IntegrationTest_AckPacketFlow(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
s.cryptConn = mock
|
||||
s.cryptConn = mock
|
||||
|
||||
go s.sendLoop()
|
||||
|
||||
@@ -289,7 +289,7 @@ func IntegrationTest_MixedPacketTypes(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
s.cryptConn = mock
|
||||
s.cryptConn = mock
|
||||
|
||||
go s.sendLoop()
|
||||
|
||||
@@ -345,7 +345,7 @@ func IntegrationTest_PacketOrderPreservation(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
s.cryptConn = mock
|
||||
s.cryptConn = mock
|
||||
|
||||
go s.sendLoop()
|
||||
|
||||
@@ -403,7 +403,7 @@ func IntegrationTest_QueueBackpressure(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
s.cryptConn = mock
|
||||
s.cryptConn = mock
|
||||
|
||||
go s.sendLoop()
|
||||
|
||||
@@ -443,10 +443,10 @@ func IntegrationTest_GuildEnumerationFlow(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
guildCount int
|
||||
name string
|
||||
guildCount int
|
||||
membersPerGuild int
|
||||
wantValid bool
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "single_guild",
|
||||
@@ -530,22 +530,22 @@ func IntegrationTest_ConcurrentClientAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
name string
|
||||
concurrentClients int
|
||||
packetsPerClient int
|
||||
wantTotalPackets int
|
||||
}{
|
||||
{
|
||||
name: "two_concurrent_clients",
|
||||
name: "two_concurrent_clients",
|
||||
concurrentClients: 2,
|
||||
packetsPerClient: 5,
|
||||
wantTotalPackets: 10,
|
||||
wantTotalPackets: 10,
|
||||
},
|
||||
{
|
||||
name: "five_concurrent_clients",
|
||||
name: "five_concurrent_clients",
|
||||
concurrentClients: 5,
|
||||
packetsPerClient: 10,
|
||||
wantTotalPackets: 50,
|
||||
wantTotalPackets: 50,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -174,34 +174,51 @@ func (m *mockCharacterRepo) LoadColumn(_ uint32, column string) ([]byte, error)
|
||||
}
|
||||
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 }
|
||||
func (m *mockCharacterRepo) UpdateLastLogin(_ uint32, _ int64) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateTimePlayed(_ uint32, _ int) error { return nil }
|
||||
func (m *mockCharacterRepo) GetCharIDsByUserID(_ uint32) ([]uint32, error) { return nil, nil }
|
||||
func (m *mockCharacterRepo) SaveBool(_ uint32, col string, v bool) error { m.bools[col] = v; return nil }
|
||||
func (m *mockCharacterRepo) SaveString(_ uint32, col string, v string) error { m.strings[col] = v; return nil }
|
||||
func (m *mockCharacterRepo) ReadBool(_ uint32, col string) (bool, error) { return m.bools[col], nil }
|
||||
func (m *mockCharacterRepo) ReadString(_ uint32, col string) (string, error) { return m.strings[col], 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 }
|
||||
func (m *mockCharacterRepo) UpdateLastLogin(_ uint32, _ int64) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateTimePlayed(_ uint32, _ int) error { return nil }
|
||||
func (m *mockCharacterRepo) GetCharIDsByUserID(_ uint32) ([]uint32, error) { return nil, nil }
|
||||
func (m *mockCharacterRepo) SaveBool(_ uint32, col string, v bool) error {
|
||||
m.bools[col] = v
|
||||
return nil
|
||||
}
|
||||
func (m *mockCharacterRepo) SaveString(_ uint32, col string, v string) error {
|
||||
m.strings[col] = v
|
||||
return nil
|
||||
}
|
||||
func (m *mockCharacterRepo) ReadBool(_ uint32, col string) (bool, error) { return m.bools[col], nil }
|
||||
func (m *mockCharacterRepo) ReadString(_ uint32, col string) (string, error) {
|
||||
return m.strings[col], nil
|
||||
}
|
||||
func (m *mockCharacterRepo) LoadColumnWithDefault(_ uint32, col string, def []byte) ([]byte, error) {
|
||||
if d, ok := m.columns[col]; ok {
|
||||
return d, nil
|
||||
}
|
||||
return def, nil
|
||||
}
|
||||
func (m *mockCharacterRepo) SetDeleted(_ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateDailyCafe(_ uint32, _ time.Time, _, _ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) ResetDailyQuests(_ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) ReadEtcPoints(_ uint32) (uint32, uint32, uint32, error) { return 0, 0, 0, nil }
|
||||
func (m *mockCharacterRepo) ResetCafeTime(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateGuildPostChecked(_ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) ReadGuildPostChecked(_ uint32) (time.Time, error) { return time.Time{}, nil }
|
||||
func (m *mockCharacterRepo) SaveMercenary(_ uint32, _ []byte, _ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateGCPAndPact(_ uint32, _ uint32, _ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) FindByRastaID(_ int) (uint32, string, error) { return 0, "", nil }
|
||||
func (m *mockCharacterRepo) SaveCharacterData(_ uint32, _ []byte, _, _ uint16, _ bool, _ uint8, _ uint16) error { return nil }
|
||||
func (m *mockCharacterRepo) SaveHouseData(_ uint32, _ []byte, _, _, _, _, _ []byte) error { return nil }
|
||||
func (m *mockCharacterRepo) SetDeleted(_ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateDailyCafe(_ uint32, _ time.Time, _, _ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) ResetDailyQuests(_ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) ReadEtcPoints(_ uint32) (uint32, uint32, uint32, error) {
|
||||
return 0, 0, 0, nil
|
||||
}
|
||||
func (m *mockCharacterRepo) ResetCafeTime(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateGuildPostChecked(_ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) ReadGuildPostChecked(_ uint32) (time.Time, error) {
|
||||
return time.Time{}, nil
|
||||
}
|
||||
func (m *mockCharacterRepo) SaveMercenary(_ uint32, _ []byte, _ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) UpdateGCPAndPact(_ uint32, _ uint32, _ uint32) error { return nil }
|
||||
func (m *mockCharacterRepo) FindByRastaID(_ int) (uint32, string, error) { return 0, "", nil }
|
||||
func (m *mockCharacterRepo) SaveCharacterData(_ uint32, _ []byte, _, _ uint16, _ bool, _ uint8, _ uint16) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockCharacterRepo) SaveHouseData(_ uint32, _ []byte, _, _, _, _, _ []byte) error { return nil }
|
||||
func (m *mockCharacterRepo) LoadSaveData(_ uint32) (uint32, []byte, bool, string, error) {
|
||||
return m.loadSaveDataID, m.loadSaveDataData, m.loadSaveDataNew, m.loadSaveDataName, m.loadSaveDataErr
|
||||
}
|
||||
@@ -209,10 +226,10 @@ func (m *mockCharacterRepo) LoadSaveData(_ uint32) (uint32, []byte, bool, string
|
||||
// --- mockGoocooRepo ---
|
||||
|
||||
type mockGoocooRepo struct {
|
||||
slots map[uint32][]byte
|
||||
ensureCalled bool
|
||||
clearCalled []uint32
|
||||
savedSlots map[uint32][]byte
|
||||
slots map[uint32][]byte
|
||||
ensureCalled bool
|
||||
clearCalled []uint32
|
||||
savedSlots map[uint32][]byte
|
||||
}
|
||||
|
||||
func newMockGoocooRepo() *mockGoocooRepo {
|
||||
@@ -265,77 +282,87 @@ func (m *mockGuildRepoForMail) GetMembers(_ uint32, _ bool) ([]*GuildMember, err
|
||||
}
|
||||
|
||||
// Stub out all other GuildRepo methods.
|
||||
func (m *mockGuildRepoForMail) GetByID(_ uint32) (*Guild, error) { return nil, errNotFound }
|
||||
func (m *mockGuildRepoForMail) ListAll() ([]*Guild, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) GetByID(_ uint32) (*Guild, error) { return nil, errNotFound }
|
||||
func (m *mockGuildRepoForMail) ListAll() ([]*Guild, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) Create(_ uint32, _ string) (int32, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) Save(_ *Guild) error { return nil }
|
||||
func (m *mockGuildRepoForMail) Disband(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RemoveCharacter(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AcceptApplication(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) Save(_ *Guild) error { return nil }
|
||||
func (m *mockGuildRepoForMail) Disband(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RemoveCharacter(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AcceptApplication(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CreateApplication(_, _, _ uint32, _ GuildApplicationType) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) CreateApplicationWithMail(_, _, _ uint32, _ GuildApplicationType, _, _ uint32, _, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) CancelInvitation(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RejectApplication(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ArrangeCharacters(_ []uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CancelInvitation(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RejectApplication(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ArrangeCharacters(_ []uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetApplication(_, _ uint32, _ GuildApplicationType) (*GuildApplication, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) HasApplication(_, _ uint32) (bool, error) { return false, nil }
|
||||
func (m *mockGuildRepoForMail) GetItemBox(_ uint32) ([]byte, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) SaveItemBox(_ uint32, _ []byte) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetCharacterMembership(_ uint32) (*GuildMember, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) SaveMember(_ *GuildMember) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetRecruiting(_ uint32, _ bool) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetPugiOutfits(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetRecruiter(_ uint32, _ bool) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddMemberDailyRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) HasApplication(_, _ uint32) (bool, error) { return false, nil }
|
||||
func (m *mockGuildRepoForMail) GetItemBox(_ uint32) ([]byte, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) SaveItemBox(_ uint32, _ []byte) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetCharacterMembership(_ uint32) (*GuildMember, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) SaveMember(_ *GuildMember) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetRecruiting(_ uint32, _ bool) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetPugiOutfits(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetRecruiter(_ uint32, _ bool) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddMemberDailyRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ExchangeEventRP(_ uint32, _ uint16) (uint32, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) AddRankRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddEventRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetRoomRP(_ uint32) (uint16, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) SetRoomRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddRoomRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetRoomExpiry(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListPosts(_ uint32, _ int) ([]*MessageBoardPost, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreatePost(_, _, _ uint32, _ int, _, _ string, _ int) error { return nil }
|
||||
func (m *mockGuildRepoForMail) DeletePost(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) UpdatePost(_ uint32, _, _ string) error { return nil }
|
||||
func (m *mockGuildRepoForMail) UpdatePostStamp(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetPostLikedBy(_ uint32) (string, error) { return "", nil }
|
||||
func (m *mockGuildRepoForMail) SetPostLikedBy(_ uint32, _ string) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CountNewPosts(_ uint32, _ time.Time) (int, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) GetAllianceByID(_ uint32) (*GuildAlliance, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) ListAlliances() ([]*GuildAlliance, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateAlliance(_ string, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) DeleteAlliance(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RemoveGuildFromAlliance(_, _, _, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddRankRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddEventRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetRoomRP(_ uint32) (uint16, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) SetRoomRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddRoomRP(_ uint32, _ uint16) error { return nil }
|
||||
func (m *mockGuildRepoForMail) SetRoomExpiry(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListPosts(_ uint32, _ int) ([]*MessageBoardPost, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) CreatePost(_, _, _ uint32, _ int, _, _ string, _ int) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) DeletePost(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) UpdatePost(_ uint32, _, _ string) error { return nil }
|
||||
func (m *mockGuildRepoForMail) UpdatePostStamp(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetPostLikedBy(_ uint32) (string, error) { return "", nil }
|
||||
func (m *mockGuildRepoForMail) SetPostLikedBy(_ uint32, _ string) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CountNewPosts(_ uint32, _ time.Time) (int, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) GetAllianceByID(_ uint32) (*GuildAlliance, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) ListAlliances() ([]*GuildAlliance, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateAlliance(_ string, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) DeleteAlliance(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RemoveGuildFromAlliance(_, _, _, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListAdventures(_ uint32) ([]*GuildAdventure, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateAdventure(_, _ uint32, _, _ int64) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CreateAdventureWithCharge(_, _, _ uint32, _, _ int64) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CollectAdventure(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ChargeAdventure(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetPendingHunt(_ uint32) (*TreasureHunt, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) ListGuildHunts(_, _ uint32) ([]*TreasureHunt, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateHunt(_, _, _, _ uint32, _ []byte, _ string) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AcquireHunt(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RegisterHuntReport(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CollectHunt(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ClaimHuntReward(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListMeals(_ uint32) ([]*GuildMeal, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateMeal(_, _, _ uint32, _ time.Time) (uint32, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) UpdateMeal(_, _, _ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ClaimHuntBox(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListGuildKills(_, _ uint32) ([]*GuildKill, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CountGuildKills(_, _ uint32) (int, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) ClearTreasureHunt(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CreateAdventure(_, _ uint32, _, _ int64) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CreateAdventureWithCharge(_, _, _ uint32, _, _ int64) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) CollectAdventure(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ChargeAdventure(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) GetPendingHunt(_ uint32) (*TreasureHunt, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) ListGuildHunts(_, _ uint32) ([]*TreasureHunt, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateHunt(_, _, _, _ uint32, _ []byte, _ string) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AcquireHunt(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) RegisterHuntReport(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) CollectHunt(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ClaimHuntReward(_, _ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListMeals(_ uint32) ([]*GuildMeal, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CreateMeal(_, _, _ uint32, _ time.Time) (uint32, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) UpdateMeal(_, _, _ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ClaimHuntBox(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListGuildKills(_, _ uint32) ([]*GuildKill, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) CountGuildKills(_, _ uint32) (int, error) { return 0, nil }
|
||||
func (m *mockGuildRepoForMail) ClearTreasureHunt(_ uint32) error { return nil }
|
||||
func (m *mockGuildRepoForMail) InsertKillLog(_ uint32, _ int, _ uint8, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListInvitedCharacters(_ uint32) ([]*ScoutedCharacter, error) { return nil, nil }
|
||||
func (m *mockGuildRepoForMail) RolloverDailyRP(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddWeeklyBonusUsers(_ uint32, _ uint8) error { return nil }
|
||||
func (m *mockGuildRepoForMail) ListInvitedCharacters(_ uint32) ([]*ScoutedCharacter, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (m *mockGuildRepoForMail) RolloverDailyRP(_ uint32, _ time.Time) error { return nil }
|
||||
func (m *mockGuildRepoForMail) AddWeeklyBonusUsers(_ uint32, _ uint8) error { return nil }
|
||||
|
||||
// --- mockGuildRepoOps (enhanced guild repo for ops/scout/board tests) ---
|
||||
|
||||
@@ -371,7 +398,7 @@ type mockGuildRepoOps struct {
|
||||
|
||||
// Alliance
|
||||
alliance *GuildAlliance
|
||||
getAllianceErr error
|
||||
getAllianceErr error
|
||||
createAllianceErr error
|
||||
deleteAllianceErr error
|
||||
removeAllyErr error
|
||||
@@ -386,29 +413,29 @@ type mockGuildRepoOps struct {
|
||||
updateMealErr error
|
||||
|
||||
// Adventure
|
||||
adventures []*GuildAdventure
|
||||
listAdvErr error
|
||||
createAdvErr error
|
||||
collectAdvID uint32
|
||||
chargeAdvID uint32
|
||||
chargeAdvAmount uint32
|
||||
adventures []*GuildAdventure
|
||||
listAdvErr error
|
||||
createAdvErr error
|
||||
collectAdvID uint32
|
||||
chargeAdvID uint32
|
||||
chargeAdvAmount uint32
|
||||
|
||||
// Treasure hunt
|
||||
pendingHunt *TreasureHunt
|
||||
guildHunts []*TreasureHunt
|
||||
listHuntsErr error
|
||||
acquireHuntID uint32
|
||||
reportHuntID uint32
|
||||
collectHuntID uint32
|
||||
claimHuntID uint32
|
||||
createHuntErr error
|
||||
pendingHunt *TreasureHunt
|
||||
guildHunts []*TreasureHunt
|
||||
listHuntsErr error
|
||||
acquireHuntID uint32
|
||||
reportHuntID uint32
|
||||
collectHuntID uint32
|
||||
claimHuntID uint32
|
||||
createHuntErr error
|
||||
|
||||
// Hunt data
|
||||
guildKills []*GuildKill
|
||||
listKillsErr error
|
||||
countKills int
|
||||
countKillsErr error
|
||||
claimBoxCalled bool
|
||||
guildKills []*GuildKill
|
||||
listKillsErr error
|
||||
countKills int
|
||||
countKillsErr error
|
||||
claimBoxCalled bool
|
||||
|
||||
// Data
|
||||
membership *GuildMember
|
||||
@@ -623,46 +650,56 @@ func (m *mockUserRepoForItems) SetItemBox(_ uint32, data []byte) error {
|
||||
}
|
||||
|
||||
// Stub all other UserRepo methods.
|
||||
func (m *mockUserRepoForItems) GetGachaPoints(_ uint32) (uint32, uint32, uint32, error) { return 0, 0, 0, nil }
|
||||
func (m *mockUserRepoForItems) GetTrialCoins(_ uint32) (uint16, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) DeductTrialCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) DeductPremiumCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AddPremiumCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AddTrialCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) DeductFrontierPoints(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AddFrontierPoints(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AdjustFrontierPointsDeduct(_ uint32, _ int) (uint32, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) AdjustFrontierPointsCredit(_ uint32, _ int) (uint32, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) AddFrontierPointsFromGacha(_ uint32, _ uint32, _ uint8) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetRights(_ uint32) (uint32, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) SetRights(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) IsOp(_ uint32) (bool, error) { return false, nil }
|
||||
func (m *mockUserRepoForItems) SetLastCharacter(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetTimer(_ uint32) (bool, error) { return false, nil }
|
||||
func (m *mockUserRepoForItems) SetTimer(_ uint32, _ bool) error { return nil }
|
||||
func (m *mockUserRepoForItems) CountByPSNID(_ string) (int, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) SetPSNID(_ uint32, _ string) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetDiscordToken(_ uint32) (string, error) { return "", nil }
|
||||
func (m *mockUserRepoForItems) SetDiscordToken(_ uint32, _ string) error { return nil }
|
||||
func (m *mockUserRepoForItems) LinkDiscord(_ string, _ string) (string, error) { return "", nil }
|
||||
func (m *mockUserRepoForItems) SetPasswordByDiscordID(_ string, _ []byte) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetByIDAndUsername(_ uint32) (uint32, string, error) { return 0, "", nil }
|
||||
func (m *mockUserRepoForItems) BanUser(_ uint32, _ *time.Time) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetGachaPoints(_ uint32) (uint32, uint32, uint32, error) {
|
||||
return 0, 0, 0, nil
|
||||
}
|
||||
func (m *mockUserRepoForItems) GetTrialCoins(_ uint32) (uint16, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) DeductTrialCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) DeductPremiumCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AddPremiumCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AddTrialCoins(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) DeductFrontierPoints(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AddFrontierPoints(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) AdjustFrontierPointsDeduct(_ uint32, _ int) (uint32, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (m *mockUserRepoForItems) AdjustFrontierPointsCredit(_ uint32, _ int) (uint32, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (m *mockUserRepoForItems) AddFrontierPointsFromGacha(_ uint32, _ uint32, _ uint8) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockUserRepoForItems) GetRights(_ uint32) (uint32, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) SetRights(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) IsOp(_ uint32) (bool, error) { return false, nil }
|
||||
func (m *mockUserRepoForItems) SetLastCharacter(_ uint32, _ uint32) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetTimer(_ uint32) (bool, error) { return false, nil }
|
||||
func (m *mockUserRepoForItems) SetTimer(_ uint32, _ bool) error { return nil }
|
||||
func (m *mockUserRepoForItems) CountByPSNID(_ string) (int, error) { return 0, nil }
|
||||
func (m *mockUserRepoForItems) SetPSNID(_ uint32, _ string) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetDiscordToken(_ uint32) (string, error) { return "", nil }
|
||||
func (m *mockUserRepoForItems) SetDiscordToken(_ uint32, _ string) error { return nil }
|
||||
func (m *mockUserRepoForItems) LinkDiscord(_ string, _ string) (string, error) { return "", nil }
|
||||
func (m *mockUserRepoForItems) SetPasswordByDiscordID(_ string, _ []byte) error { return nil }
|
||||
func (m *mockUserRepoForItems) GetByIDAndUsername(_ uint32) (uint32, string, error) {
|
||||
return 0, "", nil
|
||||
}
|
||||
func (m *mockUserRepoForItems) BanUser(_ uint32, _ *time.Time) error { return nil }
|
||||
|
||||
// --- mockStampRepoForItems ---
|
||||
|
||||
type mockStampRepoForItems struct {
|
||||
checkedTime time.Time
|
||||
checkedErr error
|
||||
totals [2]uint16 // total, redeemed
|
||||
totalsErr error
|
||||
initCalled bool
|
||||
checkedTime time.Time
|
||||
checkedErr error
|
||||
totals [2]uint16 // total, redeemed
|
||||
totalsErr error
|
||||
initCalled bool
|
||||
incrementCalled bool
|
||||
setCalled bool
|
||||
exchangeResult [2]uint16
|
||||
exchangeErr error
|
||||
yearlyResult [2]uint16
|
||||
yearlyErr error
|
||||
setCalled bool
|
||||
exchangeResult [2]uint16
|
||||
exchangeErr error
|
||||
yearlyResult [2]uint16
|
||||
yearlyErr error
|
||||
|
||||
// Monthly item fields
|
||||
monthlyClaimed time.Time
|
||||
@@ -736,27 +773,33 @@ func (m *mockHouseRepoForItems) SetWarehouseItemData(_ uint32, index uint8, data
|
||||
return m.setErr
|
||||
}
|
||||
|
||||
func (m *mockHouseRepoForItems) InitializeWarehouse(_ uint32) error { return nil }
|
||||
func (m *mockHouseRepoForItems) InitializeWarehouse(_ uint32) error { return nil }
|
||||
|
||||
// Stub all other HouseRepo methods.
|
||||
func (m *mockHouseRepoForItems) UpdateInterior(_ uint32, _ []byte) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetHouseByCharID(_ uint32) (HouseData, error) { return HouseData{}, nil }
|
||||
func (m *mockHouseRepoForItems) SearchHousesByName(_ string) ([]HouseData, error) { return nil, nil }
|
||||
func (m *mockHouseRepoForItems) UpdateHouseState(_ uint32, _ uint8, _ string) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetHouseAccess(_ uint32) (uint8, string, error) { return 0, "", nil }
|
||||
func (m *mockHouseRepoForItems) UpdateInterior(_ uint32, _ []byte) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetHouseByCharID(_ uint32) (HouseData, error) {
|
||||
return HouseData{}, nil
|
||||
}
|
||||
func (m *mockHouseRepoForItems) SearchHousesByName(_ string) ([]HouseData, error) { return nil, nil }
|
||||
func (m *mockHouseRepoForItems) UpdateHouseState(_ uint32, _ uint8, _ string) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetHouseAccess(_ uint32) (uint8, string, error) { return 0, "", nil }
|
||||
func (m *mockHouseRepoForItems) GetHouseContents(_ uint32) ([]byte, []byte, []byte, []byte, []byte, []byte, []byte, error) {
|
||||
return nil, nil, nil, nil, nil, nil, nil, nil
|
||||
}
|
||||
func (m *mockHouseRepoForItems) GetMission(_ uint32) ([]byte, error) { return nil, nil }
|
||||
func (m *mockHouseRepoForItems) UpdateMission(_ uint32, _ []byte) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetMission(_ uint32) ([]byte, error) { return nil, nil }
|
||||
func (m *mockHouseRepoForItems) UpdateMission(_ uint32, _ []byte) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetWarehouseNames(_ uint32) ([10]string, [10]string, error) {
|
||||
return [10]string{}, [10]string{}, nil
|
||||
}
|
||||
func (m *mockHouseRepoForItems) RenameWarehouseBox(_ uint32, _ uint8, _ uint8, _ string) error { return nil }
|
||||
func (m *mockHouseRepoForItems) GetWarehouseEquipData(_ uint32, _ uint8) ([]byte, error) { return nil, nil }
|
||||
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 }
|
||||
func (m *mockHouseRepoForItems) RenameWarehouseBox(_ uint32, _ uint8, _ uint8, _ string) error {
|
||||
return nil
|
||||
}
|
||||
func (m *mockHouseRepoForItems) GetWarehouseEquipData(_ uint32, _ uint8) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
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 ---
|
||||
|
||||
@@ -766,11 +809,13 @@ type mockSessionRepo struct {
|
||||
clearErr error
|
||||
updateErr error
|
||||
|
||||
boundToken string
|
||||
boundToken string
|
||||
clearedToken string
|
||||
}
|
||||
|
||||
func (m *mockSessionRepo) ValidateLoginToken(_ string, _ uint32, _ uint32) error { return m.validateErr }
|
||||
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
|
||||
@@ -816,10 +861,10 @@ type mockGachaRepo struct {
|
||||
deletedBox bool
|
||||
|
||||
// Shop
|
||||
gachas []Gacha
|
||||
listShopErr error
|
||||
shopType int
|
||||
allEntries []GachaEntry
|
||||
gachas []Gacha
|
||||
listShopErr error
|
||||
shopType int
|
||||
allEntries []GachaEntry
|
||||
allEntriesErr error
|
||||
weightDivisor float64
|
||||
|
||||
@@ -873,8 +918,8 @@ 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) 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
|
||||
}
|
||||
@@ -917,11 +962,11 @@ type mockUserRepoGacha struct {
|
||||
mockUserRepoForItems
|
||||
|
||||
gachaFP, gachaGP, gachaGT uint32
|
||||
trialCoins uint16
|
||||
deductTrialErr error
|
||||
deductPremiumErr error
|
||||
deductFPErr error
|
||||
addFPFromGachaErr error
|
||||
trialCoins uint16
|
||||
deductTrialErr error
|
||||
deductPremiumErr error
|
||||
deductFPErr error
|
||||
addFPFromGachaErr error
|
||||
|
||||
fpDeductBalance uint32
|
||||
fpDeductErr error
|
||||
@@ -952,4 +997,4 @@ func (m *mockUserRepoGacha) AdjustFrontierPointsCredit(_ uint32, _ int) (uint32,
|
||||
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 }
|
||||
func (m *mockUserRepoGacha) GetRights(_ uint32) (uint32, error) { return m.rights, m.rightsErr }
|
||||
|
||||
@@ -25,19 +25,19 @@ import (
|
||||
|
||||
// SaveHandlerMonitor tracks calls to save handlers
|
||||
type SaveHandlerMonitor struct {
|
||||
mu sync.Mutex
|
||||
savedataCallCount int
|
||||
hunterNaviCallCount int
|
||||
kouryouPointCallCount int
|
||||
warehouseCallCount int
|
||||
decomysetCallCount int
|
||||
savedataAtLogout bool
|
||||
lastSavedataTime time.Time
|
||||
lastHunterNaviTime time.Time
|
||||
lastKouryouPointTime time.Time
|
||||
lastWarehouseTime time.Time
|
||||
lastDecomysetTime time.Time
|
||||
logoutTime time.Time
|
||||
mu sync.Mutex
|
||||
savedataCallCount int
|
||||
hunterNaviCallCount int
|
||||
kouryouPointCallCount int
|
||||
warehouseCallCount int
|
||||
decomysetCallCount int
|
||||
savedataAtLogout bool
|
||||
lastSavedataTime time.Time
|
||||
lastHunterNaviTime time.Time
|
||||
lastKouryouPointTime time.Time
|
||||
lastWarehouseTime time.Time
|
||||
lastDecomysetTime time.Time
|
||||
logoutTime time.Time
|
||||
}
|
||||
|
||||
func (m *SaveHandlerMonitor) RecordSavedata() {
|
||||
@@ -79,7 +79,7 @@ func (m *SaveHandlerMonitor) RecordLogout() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.logoutTime = time.Now()
|
||||
|
||||
|
||||
// Check if savedata was called within 5 seconds before logout
|
||||
if !m.lastSavedataTime.IsZero() && m.logoutTime.Sub(m.lastSavedataTime) < 5*time.Second {
|
||||
m.savedataAtLogout = true
|
||||
@@ -89,7 +89,7 @@ func (m *SaveHandlerMonitor) RecordLogout() {
|
||||
func (m *SaveHandlerMonitor) GetStats() string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
|
||||
return fmt.Sprintf(`Save Handler Statistics:
|
||||
- Savedata calls: %d (last: %v)
|
||||
- HunterNavi calls: %d (last: %v)
|
||||
@@ -385,7 +385,7 @@ func TestSequential_RepeatedLogoutLoginCycles(t *testing.T) {
|
||||
// Verify data after each cycle
|
||||
var savedCompressed []byte
|
||||
_ = db.QueryRow("SELECT savedata FROM characters WHERE id = $1", charID).Scan(&savedCompressed)
|
||||
|
||||
|
||||
if len(savedCompressed) > 0 {
|
||||
decompressed, err := nullcomp.Decompress(savedCompressed)
|
||||
if err != nil {
|
||||
@@ -393,7 +393,7 @@ func TestSequential_RepeatedLogoutLoginCycles(t *testing.T) {
|
||||
} else if len(decompressed) > 7001 {
|
||||
savedCycle := (int(decompressed[7000]) << 8) | int(decompressed[7001])
|
||||
if savedCycle != cycle {
|
||||
t.Errorf("Cycle %d: ❌ Data corruption - expected cycle %d, got %d",
|
||||
t.Errorf("Cycle %d: ❌ Data corruption - expected cycle %d, got %d",
|
||||
cycle, cycle, savedCycle)
|
||||
} else {
|
||||
t.Logf("Cycle %d: ✓ Data correct", cycle)
|
||||
@@ -431,7 +431,7 @@ func TestRealtime_SaveDataTimestamps(t *testing.T) {
|
||||
saveData := make([]byte, 150000)
|
||||
copy(saveData[88:], []byte("TimestampChar\x00"))
|
||||
compressed, _ := nullcomp.Compress(saveData)
|
||||
|
||||
|
||||
savePkt := &mhfpacket.MsgMhfSavedata{
|
||||
SaveType: 0,
|
||||
AckHandle: 11001,
|
||||
@@ -477,7 +477,7 @@ func TestRealtime_SaveDataTimestamps(t *testing.T) {
|
||||
if !lastSaveTime.IsZero() && !logoutTime.IsZero() {
|
||||
gap := logoutTime.Sub(lastSaveTime)
|
||||
t.Logf("Time between last save and logout: %v", gap.Round(time.Millisecond))
|
||||
|
||||
|
||||
if gap > 50*time.Millisecond {
|
||||
t.Log("⚠️ Significant gap between last save and logout")
|
||||
t.Log("Player changes after last save would be LOST")
|
||||
@@ -498,4 +498,3 @@ func containsAny(s string, substrs []string) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/common/mhfitem"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network/clientctx"
|
||||
"erupe-ce/network/mhfpacket"
|
||||
"erupe-ce/server/channelserver/compression/nullcomp"
|
||||
@@ -18,9 +18,9 @@ import (
|
||||
// ============================================================================
|
||||
// SESSION LIFECYCLE INTEGRATION TESTS
|
||||
// Full end-to-end tests that simulate the complete player session lifecycle
|
||||
//
|
||||
//
|
||||
// These tests address the core issue: handler-level tests don't catch problems
|
||||
// with the logout flow. Players report data loss because logout doesn't
|
||||
// with the logout flow. Players report data loss because logout doesn't
|
||||
// trigger save handlers.
|
||||
//
|
||||
// Test Strategy:
|
||||
@@ -580,16 +580,16 @@ func createTestServerWithDB(t *testing.T, db *sqlx.DB) *Server {
|
||||
// Create minimal server for testing
|
||||
// Note: This may need adjustment based on actual Server initialization
|
||||
server := &Server{
|
||||
db: db,
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
db: db,
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
userBinary: NewUserBinaryStore(),
|
||||
minidata: NewMinidataStore(),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
erupeConfig: &cfg.Config{
|
||||
RealClientMode: cfg.ZZ,
|
||||
},
|
||||
isShuttingDown: false,
|
||||
done: make(chan struct{}),
|
||||
isShuttingDown: false,
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Create logger
|
||||
@@ -617,7 +617,7 @@ func createTestServerWithDB(t *testing.T, db *sqlx.DB) *Server {
|
||||
func createTestSessionForServerWithChar(server *Server, charID uint32, name string) *Session {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
mockNetConn := NewMockNetConn() // Create a mock net.Conn for the session map key
|
||||
|
||||
|
||||
session := &Session{
|
||||
logger: server.logger,
|
||||
server: server,
|
||||
@@ -638,4 +638,3 @@ func createTestSessionForServerWithChar(server *Server, charID uint32, name stri
|
||||
|
||||
return session
|
||||
}
|
||||
|
||||
|
||||
@@ -43,13 +43,13 @@ type Config struct {
|
||||
// own locks internally and may be acquired at any point.
|
||||
type Server struct {
|
||||
sync.Mutex
|
||||
Registry ChannelRegistry
|
||||
ID uint16
|
||||
GlobalID string
|
||||
IP string
|
||||
Port uint16
|
||||
logger *zap.Logger
|
||||
db *sqlx.DB
|
||||
Registry ChannelRegistry
|
||||
ID uint16
|
||||
GlobalID string
|
||||
IP string
|
||||
Port uint16
|
||||
logger *zap.Logger
|
||||
db *sqlx.DB
|
||||
charRepo CharacterRepo
|
||||
guildRepo GuildRepo
|
||||
userRepo UserRepo
|
||||
@@ -71,13 +71,13 @@ type Server struct {
|
||||
miscRepo MiscRepo
|
||||
scenarioRepo ScenarioRepo
|
||||
mercenaryRepo MercenaryRepo
|
||||
erupeConfig *cfg.Config
|
||||
acceptConns chan net.Conn
|
||||
deleteConns chan net.Conn
|
||||
sessions map[net.Conn]*Session
|
||||
listener net.Listener // Listener that is created when Server.Start is called.
|
||||
isShuttingDown bool
|
||||
done chan struct{} // Closed on Shutdown to wake background goroutines.
|
||||
erupeConfig *cfg.Config
|
||||
acceptConns chan net.Conn
|
||||
deleteConns chan net.Conn
|
||||
sessions map[net.Conn]*Session
|
||||
listener net.Listener // Listener that is created when Server.Start is called.
|
||||
isShuttingDown bool
|
||||
done chan struct{} // Closed on Shutdown to wake background goroutines.
|
||||
|
||||
stages StageMap
|
||||
|
||||
@@ -107,28 +107,28 @@ type Server struct {
|
||||
// NewServer creates a new Server type.
|
||||
func NewServer(config *Config) *Server {
|
||||
s := &Server{
|
||||
ID: config.ID,
|
||||
logger: config.Logger,
|
||||
db: config.DB,
|
||||
erupeConfig: config.ErupeConfig,
|
||||
acceptConns: make(chan net.Conn),
|
||||
deleteConns: make(chan net.Conn),
|
||||
done: make(chan struct{}),
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
userBinary: NewUserBinaryStore(),
|
||||
minidata: NewMinidataStore(),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
semaphoreIndex: 7,
|
||||
discordBot: config.DiscordBot,
|
||||
name: config.Name,
|
||||
ID: config.ID,
|
||||
logger: config.Logger,
|
||||
db: config.DB,
|
||||
erupeConfig: config.ErupeConfig,
|
||||
acceptConns: make(chan net.Conn),
|
||||
deleteConns: make(chan net.Conn),
|
||||
done: make(chan struct{}),
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
userBinary: NewUserBinaryStore(),
|
||||
minidata: NewMinidataStore(),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
semaphoreIndex: 7,
|
||||
discordBot: config.DiscordBot,
|
||||
name: config.Name,
|
||||
raviente: &Raviente{
|
||||
id: 1,
|
||||
register: make([]uint32, 30),
|
||||
state: make([]uint32, 30),
|
||||
support: make([]uint32, 30),
|
||||
},
|
||||
questCache: NewQuestCache(config.ErupeConfig.QuestCacheExpiry),
|
||||
handlerTable: buildHandlerTable(),
|
||||
questCache: NewQuestCache(config.ErupeConfig.QuestCacheExpiry),
|
||||
handlerTable: buildHandlerTable(),
|
||||
}
|
||||
|
||||
s.charRepo = NewCharacterRepository(config.DB)
|
||||
|
||||
@@ -36,9 +36,11 @@ func (m *mockConn) RemoteAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 12345}
|
||||
}
|
||||
|
||||
func (m *mockConn) Read(b []byte) (n int, err error) { return 0, nil }
|
||||
func (m *mockConn) Write(b []byte) (n int, err error) { return len(b), nil }
|
||||
func (m *mockConn) LocalAddr() net.Addr { return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 54321} }
|
||||
func (m *mockConn) Read(b []byte) (n int, err error) { return 0, nil }
|
||||
func (m *mockConn) Write(b []byte) (n int, err error) { return len(b), nil }
|
||||
func (m *mockConn) LocalAddr() net.Addr {
|
||||
return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 54321}
|
||||
}
|
||||
func (m *mockConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (m *mockConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (m *mockConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
@@ -53,10 +55,10 @@ func (m *mockConn) WasClosed() bool {
|
||||
func createTestServer() *Server {
|
||||
logger, _ := zap.NewDevelopment()
|
||||
s := &Server{
|
||||
ID: 1,
|
||||
logger: logger,
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
ID: 1,
|
||||
logger: logger,
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
semaphore: make(map[string]*Semaphore),
|
||||
questCache: NewQuestCache(0),
|
||||
erupeConfig: &cfg.Config{
|
||||
DebugOptions: cfg.DebugOptions{
|
||||
@@ -79,15 +81,15 @@ func createTestServer() *Server {
|
||||
func createTestSessionForServer(server *Server, conn net.Conn, charID uint32, name string) *Session {
|
||||
mock := &MockCryptConn{sentPackets: make([][]byte, 0)}
|
||||
s := &Session{
|
||||
logger: server.logger,
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: mock,
|
||||
sendPackets: make(chan packet, 20),
|
||||
logger: server.logger,
|
||||
server: server,
|
||||
rawConn: conn,
|
||||
cryptConn: mock,
|
||||
sendPackets: make(chan packet, 20),
|
||||
clientContext: &clientctx.ClientContext{},
|
||||
lastPacket: time.Now(),
|
||||
charID: charID,
|
||||
Name: name,
|
||||
lastPacket: time.Now(),
|
||||
charID: charID,
|
||||
Name: name,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -319,4 +319,3 @@ func TestStageBroadcastMHF_EmptyStage(t *testing.T) {
|
||||
// Should not panic with empty stage
|
||||
stage.BroadcastMHF(pkt, nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/network"
|
||||
"sync"
|
||||
@@ -76,27 +76,27 @@ func createTestSession(mock network.Conn) *Session {
|
||||
// with their own terminators instead of being concatenated
|
||||
func TestPacketQueueIndividualSending(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
packetCount int
|
||||
wantPackets int
|
||||
name string
|
||||
packetCount int
|
||||
wantPackets int
|
||||
wantTerminators int
|
||||
}{
|
||||
{
|
||||
name: "single_packet",
|
||||
packetCount: 1,
|
||||
wantPackets: 1,
|
||||
name: "single_packet",
|
||||
packetCount: 1,
|
||||
wantPackets: 1,
|
||||
wantTerminators: 1,
|
||||
},
|
||||
{
|
||||
name: "multiple_packets",
|
||||
packetCount: 5,
|
||||
wantPackets: 5,
|
||||
name: "multiple_packets",
|
||||
packetCount: 5,
|
||||
wantPackets: 5,
|
||||
wantTerminators: 5,
|
||||
},
|
||||
{
|
||||
name: "many_packets",
|
||||
packetCount: 20,
|
||||
wantPackets: 20,
|
||||
name: "many_packets",
|
||||
packetCount: 20,
|
||||
wantPackets: 20,
|
||||
wantTerminators: 20,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -287,4 +287,3 @@ func TestStageNewMaxPlayers(t *testing.T) {
|
||||
t.Errorf("initial maxPlayers = %d, want 127", stage.maxPlayers)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
// convenient access to adjusted server time, daily/weekly boundaries, and the
|
||||
// absolute game timestamp used by the MHF client.
|
||||
|
||||
func TimeAdjusted() time.Time { return gametime.Adjusted() }
|
||||
func TimeMidnight() time.Time { return gametime.Midnight() }
|
||||
func TimeWeekStart() time.Time { return gametime.WeekStart() }
|
||||
func TimeWeekNext() time.Time { return gametime.WeekNext() }
|
||||
func TimeMonthStart() time.Time { return gametime.MonthStart() }
|
||||
func TimeGameAbsolute() uint32 { return gametime.GameAbsolute() }
|
||||
func TimeAdjusted() time.Time { return gametime.Adjusted() }
|
||||
func TimeMidnight() time.Time { return gametime.Midnight() }
|
||||
func TimeWeekStart() time.Time { return gametime.WeekStart() }
|
||||
func TimeWeekNext() time.Time { return gametime.WeekNext() }
|
||||
func TimeMonthStart() time.Time { return gametime.MonthStart() }
|
||||
func TimeGameAbsolute() uint32 { return gametime.GameAbsolute() }
|
||||
|
||||
@@ -38,8 +38,8 @@ func (m *mockPacket) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext
|
||||
func createMockServer() *Server {
|
||||
logger, _ := zap.NewDevelopment()
|
||||
s := &Server{
|
||||
logger: logger,
|
||||
erupeConfig: &cfg.Config{},
|
||||
logger: logger,
|
||||
erupeConfig: &cfg.Config{},
|
||||
// stages is a StageMap (zero value is ready to use)
|
||||
sessions: make(map[net.Conn]*Session),
|
||||
handlerTable: buildHandlerTable(),
|
||||
|
||||
@@ -124,6 +124,7 @@ func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReplaceTextAll replaces every match of regex in text by calling handler with
|
||||
// the first capture group of each match and substituting the result.
|
||||
func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string {
|
||||
|
||||
@@ -229,8 +229,8 @@ func TestNormalizeDiscordMessage_Integration(t *testing.T) {
|
||||
contains: []string{"Hello", ":smile:"},
|
||||
},
|
||||
{
|
||||
name: "mixed content",
|
||||
input: "<@123456789012345678> sent :wave:",
|
||||
name: "mixed content",
|
||||
input: "<@123456789012345678> sent :wave:",
|
||||
contains: []string{"sent"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ func TestEncodeServerInfo_EmptyClanMemberLimits(t *testing.T) {
|
||||
Port: 53310,
|
||||
Entries: []cfg.EntranceServerInfo{
|
||||
{
|
||||
Name: "TestServer",
|
||||
Description: "Test",
|
||||
IP: "127.0.0.1",
|
||||
Type: 0,
|
||||
Recommended: 0,
|
||||
Name: "TestServer",
|
||||
Description: "Test",
|
||||
IP: "127.0.0.1",
|
||||
Type: 0,
|
||||
Recommended: 0,
|
||||
AllowedClientFlags: 0xFFFFFFFF,
|
||||
Channels: []cfg.EntranceChannelInfo{
|
||||
{
|
||||
@@ -74,10 +74,10 @@ func TestEncodeServerInfo_EmptyClanMemberLimits(t *testing.T) {
|
||||
func TestClanMemberLimitsBoundsChecking(t *testing.T) {
|
||||
// Test the bounds checking logic directly
|
||||
testCases := []struct {
|
||||
name string
|
||||
clanMemberLimits [][]uint8
|
||||
expectedValue uint8
|
||||
expectDefault bool
|
||||
name string
|
||||
clanMemberLimits [][]uint8
|
||||
expectedValue uint8
|
||||
expectDefault bool
|
||||
}{
|
||||
{"empty array", [][]uint8{}, 60, true},
|
||||
{"single row with 2 columns", [][]uint8{{1, 50}}, 50, false},
|
||||
@@ -112,7 +112,6 @@ func TestClanMemberLimitsBoundsChecking(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TestEncodeServerInfo_WithMockRepo tests encodeServerInfo with a mock server repo
|
||||
func TestEncodeServerInfo_WithMockRepo(t *testing.T) {
|
||||
config := &cfg.Config{
|
||||
@@ -123,11 +122,11 @@ func TestEncodeServerInfo_WithMockRepo(t *testing.T) {
|
||||
Port: 53310,
|
||||
Entries: []cfg.EntranceServerInfo{
|
||||
{
|
||||
Name: "TestServer",
|
||||
Description: "Test",
|
||||
IP: "127.0.0.1",
|
||||
Type: 0,
|
||||
Recommended: 0,
|
||||
Name: "TestServer",
|
||||
Description: "Test",
|
||||
IP: "127.0.0.1",
|
||||
Type: 0,
|
||||
Recommended: 0,
|
||||
AllowedClientFlags: 0xFFFFFFFF,
|
||||
Channels: []cfg.EntranceChannelInfo{
|
||||
{
|
||||
@@ -218,11 +217,11 @@ func TestEncodeServerInfo_MissingSecondColumnClanMemberLimits(t *testing.T) {
|
||||
Port: 53310,
|
||||
Entries: []cfg.EntranceServerInfo{
|
||||
{
|
||||
Name: "TestServer",
|
||||
Description: "Test",
|
||||
IP: "127.0.0.1",
|
||||
Type: 0,
|
||||
Recommended: 0,
|
||||
Name: "TestServer",
|
||||
Description: "Test",
|
||||
IP: "127.0.0.1",
|
||||
Type: 0,
|
||||
Recommended: 0,
|
||||
AllowedClientFlags: 0xFFFFFFFF,
|
||||
Channels: []cfg.EntranceChannelInfo{
|
||||
{
|
||||
|
||||
@@ -2,10 +2,10 @@ package signserver
|
||||
|
||||
import (
|
||||
"erupe-ce/common/byteframe"
|
||||
"erupe-ce/common/gametime"
|
||||
ps "erupe-ce/common/pascalstring"
|
||||
"erupe-ce/common/stringsupport"
|
||||
cfg "erupe-ce/config"
|
||||
"erupe-ce/common/gametime"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
Reference in New Issue
Block a user