Files
Erupe/server/channelserver/handlers_guild_test.go
Houmgaor d0837e779c refactor(channelserver): consolidate tests into matching source test files
Move ~300 test functions from 21 catch-all files (handlers_core_test.go,
handlers_coverage*_test.go, *_coverage_test.go) into the *_test.go file
matching each handler's source file. This makes tests discoverable by
convention: tests for handlers_guild.go live in handlers_guild_test.go.

New files: handlers_guild_mission_test.go, sys_time_test.go.
No test logic changed — pure file reorganization.
2026-02-26 23:41:44 +01:00

1336 lines
31 KiB
Go

package channelserver
import (
"encoding/json"
"testing"
"time"
cfg "erupe-ce/config"
"erupe-ce/network/mhfpacket"
)
// TestGuildCreation tests basic guild creation
func TestGuildCreation(t *testing.T) {
tests := []struct {
name string
guildName string
leaderId uint32
motto uint8
valid bool
}{
{
name: "valid_guild_creation",
guildName: "TestGuild",
leaderId: 1,
motto: 1,
valid: true,
},
{
name: "guild_with_long_name",
guildName: "VeryLongGuildNameForTesting",
leaderId: 2,
motto: 2,
valid: true,
},
{
name: "guild_with_special_chars",
guildName: "Guild@#$%",
leaderId: 3,
motto: 1,
valid: true,
},
{
name: "guild_empty_name",
guildName: "",
leaderId: 4,
motto: 1,
valid: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
Name: tt.guildName,
MainMotto: tt.motto,
SubMotto: 1,
CreatedAt: time.Now(),
MemberCount: 1,
RankRP: 0,
EventRP: 0,
RoomRP: 0,
Comment: "Test guild",
Recruiting: true,
FestivalColor: FestivalColorNone,
Souls: 0,
AllianceID: 0,
GuildLeader: GuildLeader{
LeaderCharID: tt.leaderId,
LeaderName: "TestLeader",
},
}
if (len(guild.Name) > 0) != tt.valid {
t.Errorf("guild name validity check failed for '%s'", guild.Name)
}
if guild.LeaderCharID != tt.leaderId {
t.Errorf("guild leader ID mismatch: got %d, want %d", guild.LeaderCharID, tt.leaderId)
}
})
}
}
// TestGuildRankCalculation tests guild rank calculation based on RP
func TestGuildRankCalculation(t *testing.T) {
tests := []struct {
name string
rankRP uint32
wantRank uint16
config cfg.Mode
}{
{
name: "rank_0_minimal_rp",
rankRP: 0,
wantRank: 0,
config: cfg.Z2,
},
{
name: "rank_1_threshold",
rankRP: 3500,
wantRank: 1,
config: cfg.Z2,
},
{
name: "rank_5_middle",
rankRP: 16000,
wantRank: 6,
config: cfg.Z2,
},
{
name: "max_rank",
rankRP: 120001,
wantRank: 17,
config: cfg.Z2,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
RankRP: tt.rankRP,
}
rank := guild.Rank(tt.config)
if rank != tt.wantRank {
t.Errorf("guild rank calculation: got %d, want %d for RP %d", rank, tt.wantRank, tt.rankRP)
}
})
}
}
// TestGuildIconSerialization tests guild icon JSON serialization
func TestGuildIconSerialization(t *testing.T) {
tests := []struct {
name string
parts int
valid bool
}{
{
name: "icon_with_no_parts",
parts: 0,
valid: true,
},
{
name: "icon_with_single_part",
parts: 1,
valid: true,
},
{
name: "icon_with_multiple_parts",
parts: 5,
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
parts := make([]GuildIconPart, tt.parts)
for i := 0; i < tt.parts; i++ {
parts[i] = GuildIconPart{
Index: uint16(i),
ID: uint16(i + 1),
Page: uint8(i % 4),
Size: uint8((i + 1) % 8),
Rotation: uint8(i % 360),
Red: uint8(i * 10 % 256),
Green: uint8(i * 15 % 256),
Blue: uint8(i * 20 % 256),
PosX: uint16(i * 100),
PosY: uint16(i * 50),
}
}
icon := &GuildIcon{Parts: parts}
// Test JSON marshaling
data, err := json.Marshal(icon)
if err != nil && tt.valid {
t.Errorf("failed to marshal icon: %v", err)
}
if data != nil {
// Test JSON unmarshaling
var icon2 GuildIcon
err = json.Unmarshal(data, &icon2)
if err != nil && tt.valid {
t.Errorf("failed to unmarshal icon: %v", err)
}
if len(icon2.Parts) != tt.parts {
t.Errorf("icon parts mismatch: got %d, want %d", len(icon2.Parts), tt.parts)
}
}
})
}
}
// TestGuildIconDatabaseScan tests guild icon database scanning
func TestGuildIconDatabaseScan(t *testing.T) {
tests := []struct {
name string
input interface{}
valid bool
wantErr bool
}{
{
name: "scan_from_bytes",
input: []byte(`{"Parts":[]}`),
valid: true,
wantErr: false,
},
{
name: "scan_from_string",
input: `{"Parts":[{"Index":1,"ID":2}]}`,
valid: true,
wantErr: false,
},
{
name: "scan_invalid_json",
input: []byte(`{invalid json}`),
valid: false,
wantErr: true,
},
{
name: "scan_nil",
input: nil,
valid: false,
wantErr: false, // nil doesn't cause an error in this implementation
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
icon := &GuildIcon{}
err := icon.Scan(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("scan error mismatch: got %v, wantErr %v", err, tt.wantErr)
}
})
}
}
// TestGuildLeaderAssignment tests guild leader assignment and modification
func TestGuildLeaderAssignment(t *testing.T) {
tests := []struct {
name string
leaderId uint32
leaderName string
valid bool
}{
{
name: "valid_leader",
leaderId: 100,
leaderName: "TestLeader",
valid: true,
},
{
name: "leader_with_id_1",
leaderId: 1,
leaderName: "Leader1",
valid: true,
},
{
name: "leader_with_long_name",
leaderId: 999,
leaderName: "VeryLongLeaderName",
valid: true,
},
{
name: "leader_with_empty_name",
leaderId: 500,
leaderName: "",
valid: true, // Name can be empty
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
GuildLeader: GuildLeader{
LeaderCharID: tt.leaderId,
LeaderName: tt.leaderName,
},
}
if guild.LeaderCharID != tt.leaderId {
t.Errorf("leader ID mismatch: got %d, want %d", guild.LeaderCharID, tt.leaderId)
}
if guild.LeaderName != tt.leaderName {
t.Errorf("leader name mismatch: got %s, want %s", guild.LeaderName, tt.leaderName)
}
})
}
}
// TestGuildApplicationTypes tests guild application type handling
func TestGuildApplicationTypes(t *testing.T) {
tests := []struct {
name string
appType GuildApplicationType
valid bool
}{
{
name: "application_applied",
appType: GuildApplicationTypeApplied,
valid: true,
},
{
name: "application_invited",
appType: GuildApplicationTypeInvited,
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &GuildApplication{
ID: 1,
GuildID: 100,
CharID: 200,
ActorID: 300,
ApplicationType: tt.appType,
CreatedAt: time.Now(),
}
if app.ApplicationType != tt.appType {
t.Errorf("application type mismatch: got %s, want %s", app.ApplicationType, tt.appType)
}
if app.GuildID == 0 {
t.Error("guild ID should not be zero")
}
})
}
}
// TestGuildApplicationCreation tests guild application creation
func TestGuildApplicationCreation(t *testing.T) {
tests := []struct {
name string
guildId uint32
charId uint32
valid bool
}{
{
name: "valid_application",
guildId: 100,
charId: 50,
valid: true,
},
{
name: "application_same_guild_char",
guildId: 1,
charId: 1,
valid: true,
},
{
name: "large_ids",
guildId: 999999,
charId: 888888,
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &GuildApplication{
ID: 1,
GuildID: tt.guildId,
CharID: tt.charId,
ActorID: 1,
ApplicationType: GuildApplicationTypeApplied,
CreatedAt: time.Now(),
}
if app.GuildID != tt.guildId {
t.Errorf("guild ID mismatch: got %d, want %d", app.GuildID, tt.guildId)
}
if app.CharID != tt.charId {
t.Errorf("character ID mismatch: got %d, want %d", app.CharID, tt.charId)
}
})
}
}
// TestFestivalColorMapping tests festival color code mapping
func TestFestivalColorMapping(t *testing.T) {
tests := []struct {
name string
color FestivalColor
wantCode int16
shouldMap bool
}{
{
name: "festival_color_none",
color: FestivalColorNone,
wantCode: -1,
shouldMap: true,
},
{
name: "festival_color_blue",
color: FestivalColorBlue,
wantCode: 0,
shouldMap: true,
},
{
name: "festival_color_red",
color: FestivalColorRed,
wantCode: 1,
shouldMap: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
code, exists := FestivalColorCodes[tt.color]
if !exists && tt.shouldMap {
t.Errorf("festival color not in map: %s", tt.color)
}
if exists && code != tt.wantCode {
t.Errorf("festival color code mismatch: got %d, want %d", code, tt.wantCode)
}
})
}
}
// TestGuildMemberCount tests guild member count tracking
func TestGuildMemberCount(t *testing.T) {
tests := []struct {
name string
memberCount uint16
valid bool
}{
{
name: "single_member",
memberCount: 1,
valid: true,
},
{
name: "max_members",
memberCount: 100,
valid: true,
},
{
name: "large_member_count",
memberCount: 65535,
valid: true,
},
{
name: "zero_members",
memberCount: 0,
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
Name: "TestGuild",
MemberCount: tt.memberCount,
}
if guild.MemberCount != tt.memberCount {
t.Errorf("member count mismatch: got %d, want %d", guild.MemberCount, tt.memberCount)
}
})
}
}
// TestGuildRP tests guild RP (rank points and event points)
func TestGuildRP(t *testing.T) {
tests := []struct {
name string
rankRP uint32
eventRP uint32
roomRP uint16
valid bool
}{
{
name: "minimal_rp",
rankRP: 0,
eventRP: 0,
roomRP: 0,
valid: true,
},
{
name: "high_rank_rp",
rankRP: 120000,
eventRP: 50000,
roomRP: 1000,
valid: true,
},
{
name: "max_values",
rankRP: 4294967295,
eventRP: 4294967295,
roomRP: 65535,
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
Name: "TestGuild",
RankRP: tt.rankRP,
EventRP: tt.eventRP,
RoomRP: tt.roomRP,
}
if guild.RankRP != tt.rankRP {
t.Errorf("rank RP mismatch: got %d, want %d", guild.RankRP, tt.rankRP)
}
if guild.EventRP != tt.eventRP {
t.Errorf("event RP mismatch: got %d, want %d", guild.EventRP, tt.eventRP)
}
if guild.RoomRP != tt.roomRP {
t.Errorf("room RP mismatch: got %d, want %d", guild.RoomRP, tt.roomRP)
}
})
}
}
// TestGuildCommentHandling tests guild comment storage and retrieval
func TestGuildCommentHandling(t *testing.T) {
tests := []struct {
name string
comment string
maxLength int
}{
{
name: "empty_comment",
comment: "",
maxLength: 0,
},
{
name: "short_comment",
comment: "Hello",
maxLength: 5,
},
{
name: "long_comment",
comment: "This is a very long guild comment with many characters to test maximum length handling",
maxLength: 86,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
Comment: tt.comment,
}
if guild.Comment != tt.comment {
t.Errorf("comment mismatch: got '%s', want '%s'", guild.Comment, tt.comment)
}
if len(guild.Comment) != tt.maxLength {
t.Errorf("comment length mismatch: got %d, want %d", len(guild.Comment), tt.maxLength)
}
})
}
}
// TestGuildMottoSelection tests guild motto (main and sub mottos)
func TestGuildMottoSelection(t *testing.T) {
tests := []struct {
name string
mainMot uint8
subMot uint8
valid bool
}{
{
name: "motto_pair_0_0",
mainMot: 0,
subMot: 0,
valid: true,
},
{
name: "motto_pair_1_2",
mainMot: 1,
subMot: 2,
valid: true,
},
{
name: "motto_max_values",
mainMot: 255,
subMot: 255,
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
MainMotto: tt.mainMot,
SubMotto: tt.subMot,
}
if guild.MainMotto != tt.mainMot {
t.Errorf("main motto mismatch: got %d, want %d", guild.MainMotto, tt.mainMot)
}
if guild.SubMotto != tt.subMot {
t.Errorf("sub motto mismatch: got %d, want %d", guild.SubMotto, tt.subMot)
}
})
}
}
// TestGuildRecruitingStatus tests guild recruiting flag
func TestGuildRecruitingStatus(t *testing.T) {
tests := []struct {
name string
recruiting bool
}{
{
name: "guild_recruiting",
recruiting: true,
},
{
name: "guild_not_recruiting",
recruiting: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
Recruiting: tt.recruiting,
}
if guild.Recruiting != tt.recruiting {
t.Errorf("recruiting status mismatch: got %v, want %v", guild.Recruiting, tt.recruiting)
}
})
}
}
// TestGuildSoulTracking tests guild soul accumulation
func TestGuildSoulTracking(t *testing.T) {
tests := []struct {
name string
souls uint32
}{
{
name: "no_souls",
souls: 0,
},
{
name: "moderate_souls",
souls: 5000,
},
{
name: "max_souls",
souls: 4294967295,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
Souls: tt.souls,
}
if guild.Souls != tt.souls {
t.Errorf("souls mismatch: got %d, want %d", guild.Souls, tt.souls)
}
})
}
}
// TestGuildPugiData tests guild pug i (treasure chest) names and outfits
func TestGuildPugiData(t *testing.T) {
tests := []struct {
name string
pugiNames [3]string
pugiOutfits [3]uint8
valid bool
}{
{
name: "empty_pugi_data",
pugiNames: [3]string{"", "", ""},
pugiOutfits: [3]uint8{0, 0, 0},
valid: true,
},
{
name: "all_pugi_filled",
pugiNames: [3]string{"Chest1", "Chest2", "Chest3"},
pugiOutfits: [3]uint8{1, 2, 3},
valid: true,
},
{
name: "mixed_pugi_data",
pugiNames: [3]string{"MainChest", "", "AltChest"},
pugiOutfits: [3]uint8{5, 0, 10},
valid: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
PugiName1: tt.pugiNames[0],
PugiName2: tt.pugiNames[1],
PugiName3: tt.pugiNames[2],
PugiOutfit1: tt.pugiOutfits[0],
PugiOutfit2: tt.pugiOutfits[1],
PugiOutfit3: tt.pugiOutfits[2],
}
if guild.PugiName1 != tt.pugiNames[0] || guild.PugiName2 != tt.pugiNames[1] || guild.PugiName3 != tt.pugiNames[2] {
t.Error("pugi names mismatch")
}
if guild.PugiOutfit1 != tt.pugiOutfits[0] || guild.PugiOutfit2 != tt.pugiOutfits[1] || guild.PugiOutfit3 != tt.pugiOutfits[2] {
t.Error("pugi outfits mismatch")
}
})
}
}
// TestGuildRoomExpiry tests guild room rental expiry handling
func TestGuildRoomExpiry(t *testing.T) {
tests := []struct {
name string
expiry time.Time
hasExpiry bool
}{
{
name: "no_room_expiry",
expiry: time.Time{},
hasExpiry: false,
},
{
name: "room_active",
expiry: time.Now().Add(24 * time.Hour),
hasExpiry: true,
},
{
name: "room_expired",
expiry: time.Now().Add(-1 * time.Hour),
hasExpiry: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
RoomExpiry: tt.expiry,
}
if (guild.RoomExpiry.IsZero() == tt.hasExpiry) && tt.hasExpiry {
// If we expect expiry but it's zero, that's an error
if tt.hasExpiry && guild.RoomExpiry.IsZero() {
t.Error("expected room expiry but got zero time")
}
}
// Verify expiry is set correctly
matches := guild.RoomExpiry.Equal(tt.expiry)
_ = matches
// Test passed if Equal matches or if no expiry expected and time is zero
})
}
}
// TestGuildAllianceRelationship tests guild alliance ID tracking
func TestGuildAllianceRelationship(t *testing.T) {
tests := []struct {
name string
allianceId uint32
hasAlliance bool
}{
{
name: "no_alliance",
allianceId: 0,
hasAlliance: false,
},
{
name: "single_alliance",
allianceId: 1,
hasAlliance: true,
},
{
name: "large_alliance_id",
allianceId: 999999,
hasAlliance: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
guild := &Guild{
ID: 1,
AllianceID: tt.allianceId,
}
hasAlliance := guild.AllianceID != 0
if hasAlliance != tt.hasAlliance {
t.Errorf("alliance status mismatch: got %v, want %v", hasAlliance, tt.hasAlliance)
}
if guild.AllianceID != tt.allianceId {
t.Errorf("alliance ID mismatch: got %d, want %d", guild.AllianceID, tt.allianceId)
}
})
}
}
// --- handleMsgMhfGetUdGuildMapInfo tests ---
func TestHandleMsgMhfGetUdGuildMapInfo(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
handleMsgMhfGetUdGuildMapInfo(session, &mhfpacket.MsgMhfGetUdGuildMapInfo{
AckHandle: 1,
})
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("response should have data")
}
default:
t.Error("no response queued")
}
}
// --- handleMsgMhfCheckMonthlyItem tests ---
func TestCheckMonthlyItem_NotClaimed(t *testing.T) {
server := createMockServer()
stampMock := &mockStampRepoForItems{
monthlyClaimedErr: errNotFound,
}
server.stampRepo = stampMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfCheckMonthlyItem{AckHandle: 100, Type: 0}
handleMsgMhfCheckMonthlyItem(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) < 4 {
t.Fatalf("Response too short: %d bytes", len(p.data))
}
default:
t.Error("No response packet queued")
}
}
func TestCheckMonthlyItem_ClaimedThisMonth(t *testing.T) {
server := createMockServer()
stampMock := &mockStampRepoForItems{
monthlyClaimed: TimeAdjusted(), // claimed right now (within this month)
}
server.stampRepo = stampMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfCheckMonthlyItem{AckHandle: 100, Type: 0}
handleMsgMhfCheckMonthlyItem(session, pkt)
select {
case <-session.sendPackets:
// Response received — claimed this month should return 1
default:
t.Error("No response packet queued")
}
}
func TestCheckMonthlyItem_ClaimedLastMonth(t *testing.T) {
server := createMockServer()
stampMock := &mockStampRepoForItems{
monthlyClaimed: TimeMonthStart().Add(-24 * time.Hour), // before this month
}
server.stampRepo = stampMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfCheckMonthlyItem{AckHandle: 100, Type: 1}
handleMsgMhfCheckMonthlyItem(session, pkt)
select {
case <-session.sendPackets:
// Response received — last month claim should return 0 (unclaimed)
default:
t.Error("No response packet queued")
}
}
func TestCheckMonthlyItem_UnknownType(t *testing.T) {
server := createMockServer()
stampMock := &mockStampRepoForItems{}
server.stampRepo = stampMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfCheckMonthlyItem{AckHandle: 100, Type: 99}
handleMsgMhfCheckMonthlyItem(session, pkt)
select {
case <-session.sendPackets:
// Unknown type returns 0 (unclaimed) without DB call
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfEntryRookieGuild(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfEntryRookieGuild{
AckHandle: 12345,
Unk: 42,
}
handleMsgMhfEntryRookieGuild(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("Response packet should have data")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfGenerateUdGuildMap(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfGenerateUdGuildMap{
AckHandle: 12345,
}
handleMsgMhfGenerateUdGuildMap(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("Response packet should have data")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfEnumerateInvGuild(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfEnumerateInvGuild{
AckHandle: 12345,
}
handleMsgMhfEnumerateInvGuild(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("Response packet should have data")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfOperationInvGuild(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperationInvGuild{
AckHandle: 12345,
Operation: 1,
}
handleMsgMhfOperationInvGuild(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("Response packet should have data")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfCheckMonthlyItem_Coverage2(t *testing.T) {
server := createMockServer()
server.stampRepo = &mockStampRepoForItems{monthlyClaimedErr: errNotFound}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfCheckMonthlyItem{
AckHandle: 12345,
Type: 0,
}
handleMsgMhfCheckMonthlyItem(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("Response packet should have data")
}
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfAcquireMonthlyItem_Coverage2(t *testing.T) {
server := createMockServer()
server.stampRepo = &mockStampRepoForItems{}
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfAcquireMonthlyItem{
AckHandle: 12345,
}
handleMsgMhfAcquireMonthlyItem(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("Response packet should have data")
}
default:
t.Error("No response packet queued")
}
}
func TestEmptyGuildHandlers(t *testing.T) {
server := createMockServer()
session := createMockSession(1, server)
tests := []struct {
name string
handler func(s *Session, p mhfpacket.MHFPacket)
}{
{"handleMsgMhfUpdateForceGuildRank", handleMsgMhfUpdateForceGuildRank},
{"handleMsgMhfUpdateGuild", handleMsgMhfUpdateGuild},
{"handleMsgMhfUpdateGuildcard", handleMsgMhfUpdateGuildcard},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
if r := recover(); r != nil {
t.Errorf("%s panicked: %v", tt.name, r)
}
}()
tt.handler(session, nil)
})
}
}
func TestAcquireMonthlyItem_MarksAsClaimed(t *testing.T) {
server := createMockServer()
stampMock := &mockStampRepoForItems{}
server.stampRepo = stampMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfAcquireMonthlyItem{AckHandle: 100, Unk0: 2}
handleMsgMhfAcquireMonthlyItem(session, pkt)
if !stampMock.monthlySetCalled {
t.Error("SetMonthlyClaimed should be called")
}
if stampMock.monthlySetType != "monthly_ex" {
t.Errorf("SetMonthlyClaimed type = %q, want %q", stampMock.monthlySetType, "monthly_ex")
}
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestHandleMsgMhfCreateGuild_Success(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfCreateGuild{AckHandle: 1, Name: "TestGuild"}
handleMsgMhfCreateGuild(session, pkt)
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Error("expected non-empty response")
}
default:
t.Error("expected a response packet")
}
}
func TestHandleMsgMhfCreateGuild_Error(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{saveErr: errNotFound}
// Mock Create to return error - the mockGuildRepo.Create returns (0, nil)
// We need getErr to make it fail. Actually Create is a no-op stub returning nil.
// Let's use a custom approach - we need the Create method to error.
// The mock's Create always returns nil, so let's test the success path worked above
// and test ArrangeGuildMember error paths instead.
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfCreateGuild{AckHandle: 1, Name: "TestGuild"}
handleMsgMhfCreateGuild(session, pkt)
<-session.sendPackets // consume the response
}
func TestHandleMsgMhfArrangeGuildMember_Success(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1, GuildLeader: GuildLeader{LeaderCharID: 100}}
server.guildRepo = &mockGuildRepo{guild: guild}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfArrangeGuildMember{
AckHandle: 1,
GuildID: 1,
CharIDs: []uint32{100, 200, 300},
}
handleMsgMhfArrangeGuildMember(session, pkt)
select {
case <-session.sendPackets:
default:
t.Error("expected response")
}
}
func TestHandleMsgMhfArrangeGuildMember_GetByIDError(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{getErr: errNotFound}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfArrangeGuildMember{AckHandle: 1, GuildID: 999}
handleMsgMhfArrangeGuildMember(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfArrangeGuildMember_NotLeader(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1, GuildLeader: GuildLeader{LeaderCharID: 200, LeaderName: "Other"}}
server.guildRepo = &mockGuildRepo{guild: guild}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfArrangeGuildMember{AckHandle: 1, GuildID: 1}
handleMsgMhfArrangeGuildMember(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfEnumerateGuildMember_GuildIDPositive(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1, MemberCount: 2}
members := []*GuildMember{
{CharID: 100, Name: "Player1", HR: 50, OrderIndex: 0, WeaponType: 3},
{CharID: 200, Name: "Player2", HR: 100, OrderIndex: 1, WeaponType: 1},
}
server.guildRepo = &mockGuildRepo{guild: guild, members: members}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 1}
handleMsgMhfEnumerateGuildMember(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfEnumerateGuildMember_GuildIDZero(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1, MemberCount: 1}
members := []*GuildMember{
{CharID: 100, Name: "Player1", HR: 50, OrderIndex: 0},
}
server.guildRepo = &mockGuildRepo{guild: guild, members: members}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 0}
handleMsgMhfEnumerateGuildMember(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfEnumerateGuildMember_NilGuild(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 0}
handleMsgMhfEnumerateGuildMember(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfEnumerateGuildMember_Applicant(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1}
server.guildRepo = &mockGuildRepo{guild: guild, hasAppResult: true}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfEnumerateGuildMember{AckHandle: 1, GuildID: 1}
handleMsgMhfEnumerateGuildMember(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfGetGuildManageRight(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1, MemberCount: 2}
members := []*GuildMember{
{CharID: 100, Recruiter: true},
{CharID: 200, Recruiter: false},
}
server.guildRepo = &mockGuildRepo{guild: guild, members: members}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfGetGuildManageRight{AckHandle: 1}
handleMsgMhfGetGuildManageRight(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfGetGuildTargetMemberNum_NilGuild(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfGetGuildTargetMemberNum{AckHandle: 1, GuildID: 0}
handleMsgMhfGetGuildTargetMemberNum(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfGetGuildTargetMemberNum_WithGuild(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1, MemberCount: 5}
server.guildRepo = &mockGuildRepo{guild: guild}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfGetGuildTargetMemberNum{AckHandle: 1, GuildID: 1}
handleMsgMhfGetGuildTargetMemberNum(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfEnumerateGuildItem(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfEnumerateGuildItem{AckHandle: 1, GuildID: 1}
handleMsgMhfEnumerateGuildItem(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfUpdateGuildItem(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfUpdateGuildItem{AckHandle: 1, GuildID: 1}
handleMsgMhfUpdateGuildItem(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfUpdateGuildIcon_LeaderSuccess(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1}
membership := &GuildMember{CharID: 100, IsLeader: true}
server.guildRepo = &mockGuildRepo{guild: guild, membership: membership}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfUpdateGuildIcon{
AckHandle: 1,
GuildID: 1,
IconParts: []mhfpacket.GuildIconMsgPart{
{Index: 0, ID: 1, Page: 0, Size: 10, Rotation: 0, Red: 255, Green: 0, Blue: 0, PosX: 50, PosY: 50},
},
}
handleMsgMhfUpdateGuildIcon(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfUpdateGuildIcon_NotLeader(t *testing.T) {
server := createMockServer()
guild := &Guild{ID: 1}
membership := &GuildMember{CharID: 100, IsLeader: false, OrderIndex: 5}
server.guildRepo = &mockGuildRepo{guild: guild, membership: membership}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfUpdateGuildIcon{AckHandle: 1, GuildID: 1}
handleMsgMhfUpdateGuildIcon(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfUpdateGuildIcon_GetByIDError(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{getErr: errNotFound}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfUpdateGuildIcon{AckHandle: 1, GuildID: 999}
handleMsgMhfUpdateGuildIcon(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfReadGuildcard(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfReadGuildcard{AckHandle: 1}
handleMsgMhfReadGuildcard(session, pkt)
<-session.sendPackets
}
func TestHandleMsgMhfSetGuildManageRight(t *testing.T) {
server := createMockServer()
server.guildRepo = &mockGuildRepo{}
session := createMockSession(100, server)
pkt := &mhfpacket.MsgMhfSetGuildManageRight{AckHandle: 1, CharID: 200, Allowed: true}
handleMsgMhfSetGuildManageRight(session, pkt)
<-session.sendPackets
}