Files
Erupe/server/channelserver/handlers_guild_ops_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

723 lines
19 KiB
Go

package channelserver
import (
"testing"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
"erupe-ce/common/stringsupport"
)
// --- handleMsgMhfOperateGuild tests ---
func TestOperateGuild_Disband_Success(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildDisband,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.disbandedID != 10 {
t.Errorf("Disband called with guild %d, want 10", guildMock.disbandedID)
}
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("No response data")
}
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_Disband_NotLeader(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 5},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 999 // different from session charID
server.guildRepo = guildMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildDisband,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.disbandedID != 0 {
t.Error("Disband should not be called for non-leader")
}
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_Disband_RepoError(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
disbandErr: errNotFound,
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildDisband,
}
handleMsgMhfOperateGuild(session, pkt)
// response=0 when disband fails
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_Resign_TransferLeadership(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 1
guildMock.members = []*GuildMember{
{CharID: 1, OrderIndex: 1, IsLeader: true},
{CharID: 2, OrderIndex: 2, AvoidLeadership: false},
}
server.guildRepo = guildMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildResign,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.guild.LeaderCharID != 2 {
t.Errorf("Leader should transfer to charID 2, got %d", guildMock.guild.LeaderCharID)
}
if len(guildMock.savedMembers) < 2 {
t.Fatalf("Expected 2 saved members, got %d", len(guildMock.savedMembers))
}
if guildMock.savedGuild == nil {
t.Error("Guild should be saved after resign")
}
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_Resign_SkipsAvoidLeadership(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 1
guildMock.members = []*GuildMember{
{CharID: 1, OrderIndex: 1, IsLeader: true},
{CharID: 2, OrderIndex: 2, AvoidLeadership: true},
{CharID: 3, OrderIndex: 3, AvoidLeadership: false},
}
server.guildRepo = guildMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildResign,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.guild.LeaderCharID != 3 {
t.Errorf("Leader should transfer to charID 3 (skipping 2), got %d", guildMock.guild.LeaderCharID)
}
}
func TestOperateGuild_Apply_Success(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 5},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildApply,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.createdAppArgs == nil {
t.Fatal("CreateApplication should be called")
}
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_Apply_RepoError(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 5},
createAppErr: errNotFound,
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildApply,
}
handleMsgMhfOperateGuild(session, pkt)
// Should still succeed with 0 leader ID
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_Leave_AsApplicant(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsApplicant: true, OrderIndex: 5},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildLeave,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.rejectedCharID != 1 {
t.Errorf("RejectApplication should be called for applicant, got rejectedCharID=%d", guildMock.rejectedCharID)
}
if guildMock.removedCharID != 0 {
t.Error("RemoveCharacter should not be called for applicant")
}
}
func TestOperateGuild_Leave_AsMember(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsApplicant: false, OrderIndex: 5},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildLeave,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.removedCharID != 1 {
t.Errorf("RemoveCharacter should be called with charID 1, got %d", guildMock.removedCharID)
}
if len(mailMock.sentMails) != 1 {
t.Fatalf("Expected 1 withdrawal mail, got %d", len(mailMock.sentMails))
}
if mailMock.sentMails[0].recipientID != 1 {
t.Errorf("Mail recipientID = %d, want 1", mailMock.sentMails[0].recipientID)
}
}
func TestOperateGuild_Leave_MailError(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{sendErr: errNotFound}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsApplicant: false, OrderIndex: 5},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildLeave,
}
// Should not panic; mail error is logged as warning
handleMsgMhfOperateGuild(session, pkt)
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_UpdateComment_Success(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildUpdateComment,
Data2: newNullTermBF([]byte("Test\x00")),
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.savedGuild == nil {
t.Error("Guild should be saved after comment update")
}
}
func TestOperateGuild_UpdateComment_NotLeader(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 10}, // not leader, not sub-leader
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildUpdateComment,
}
handleMsgMhfOperateGuild(session, pkt)
// Should return fail ack
select {
case p := <-session.sendPackets:
if len(p.data) == 0 {
t.Fatal("Expected fail response")
}
default:
t.Error("No response packet queued")
}
}
func TestOperateGuild_UpdateMotto_Success(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildUpdateMotto,
Data1: newMottoBF(5, 3),
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.savedGuild == nil {
t.Error("Guild should be saved after motto update")
}
if guildMock.savedGuild.MainMotto != 3 {
t.Errorf("MainMotto = %d, want 3", guildMock.savedGuild.MainMotto)
}
if guildMock.savedGuild.SubMotto != 5 {
t.Errorf("SubMotto = %d, want 5", guildMock.savedGuild.SubMotto)
}
}
func TestOperateGuild_UpdateMotto_NotLeader(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 10},
}
guildMock.guild = &Guild{ID: 10}
guildMock.guild.LeaderCharID = 999
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildUpdateMotto,
}
handleMsgMhfOperateGuild(session, pkt)
if guildMock.savedGuild != nil {
t.Error("Guild should not be saved when not leader")
}
}
func TestOperateGuild_GuildNotFound(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{}
guildMock.getErr = errNotFound
server.guildRepo = guildMock
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuild{
AckHandle: 100,
GuildID: 10,
Action: mhfpacket.OperateGuildDisband,
}
handleMsgMhfOperateGuild(session, pkt)
// Should return fail ack
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
// --- handleMsgMhfOperateGuildMember tests ---
func TestOperateGuildMember_Accept(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuildMember{
AckHandle: 100,
GuildID: 10,
CharID: 42,
Action: mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT,
}
handleMsgMhfOperateGuildMember(session, pkt)
if guildMock.acceptedCharID != 42 {
t.Errorf("AcceptApplication charID = %d, want 42", guildMock.acceptedCharID)
}
if len(mailMock.sentMails) != 1 {
t.Fatalf("Expected 1 mail, got %d", len(mailMock.sentMails))
}
if mailMock.sentMails[0].recipientID != 42 {
t.Errorf("Mail recipientID = %d, want 42", mailMock.sentMails[0].recipientID)
}
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuildMember_Reject(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuildMember{
AckHandle: 100,
GuildID: 10,
CharID: 42,
Action: mhfpacket.OPERATE_GUILD_MEMBER_ACTION_REJECT,
}
handleMsgMhfOperateGuildMember(session, pkt)
if guildMock.rejectedCharID != 42 {
t.Errorf("RejectApplication charID = %d, want 42", guildMock.rejectedCharID)
}
if len(mailMock.sentMails) != 1 {
t.Fatalf("Expected 1 mail, got %d", len(mailMock.sentMails))
}
}
func TestOperateGuildMember_Kick(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuildMember{
AckHandle: 100,
GuildID: 10,
CharID: 42,
Action: mhfpacket.OPERATE_GUILD_MEMBER_ACTION_KICK,
}
handleMsgMhfOperateGuildMember(session, pkt)
if guildMock.removedCharID != 42 {
t.Errorf("RemoveCharacter charID = %d, want 42", guildMock.removedCharID)
}
if len(mailMock.sentMails) != 1 {
t.Fatalf("Expected 1 mail, got %d", len(mailMock.sentMails))
}
}
func TestOperateGuildMember_MailError(t *testing.T) {
server := createMockServer()
mailMock := &mockMailRepo{sendErr: errNotFound}
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 1
server.guildRepo = guildMock
server.mailRepo = mailMock
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuildMember{
AckHandle: 100,
GuildID: 10,
CharID: 42,
Action: mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT,
}
// Should not panic; mail error logged as warning
handleMsgMhfOperateGuildMember(session, pkt)
select {
case <-session.sendPackets:
default:
t.Error("No response packet queued")
}
}
func TestOperateGuildMember_NotLeaderOrSub(t *testing.T) {
server := createMockServer()
guildMock := &mockGuildRepo{
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 10}, // not sub-leader
}
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
guildMock.guild.LeaderCharID = 999 // not the session char
server.guildRepo = guildMock
server.mailRepo = &mockMailRepo{}
ensureGuildService(server)
session := createMockSession(1, server)
pkt := &mhfpacket.MsgMhfOperateGuildMember{
AckHandle: 100,
GuildID: 10,
CharID: 42,
Action: mhfpacket.OPERATE_GUILD_MEMBER_ACTION_ACCEPT,
}
handleMsgMhfOperateGuildMember(session, pkt)
if guildMock.acceptedCharID != 0 {
t.Error("Should not accept when actor lacks permission")
}
}
// --- byteframe helpers for packet Data fields ---
func newNullTermBF(data []byte) *byteframe.ByteFrame {
bf := byteframe.NewByteFrame()
bf.WriteBytes(data)
_, _ = bf.Seek(0, 0)
return bf
}
func newMottoBF(sub, main uint8) *byteframe.ByteFrame {
bf := byteframe.NewByteFrame()
bf.WriteUint16(0) // skipped
bf.WriteUint8(sub) // SubMotto
bf.WriteUint8(main) // MainMotto
_, _ = bf.Seek(0, 0)
return bf
}
func TestHandleRenamePugi_Pugi1(t *testing.T) {
srv := createMockServer()
guild := &Guild{ID: 1}
srv.guildRepo = &mockGuildRepo{guild: guild}
s := createMockSession(100, srv)
bf := byteframe.NewByteFrame()
nameBytes := stringsupport.UTF8ToSJIS("TestPugi")
bf.WriteBytes(nameBytes)
bf.WriteUint8(0) // null terminator
bf.Seek(0, 0)
handleRenamePugi(s, bf, guild, 1)
if guild.PugiName1 != "TestPugi" {
t.Errorf("PugiName1 = %q, want TestPugi", guild.PugiName1)
}
}
func TestHandleRenamePugi_Pugi2(t *testing.T) {
srv := createMockServer()
guild := &Guild{ID: 1}
srv.guildRepo = &mockGuildRepo{guild: guild}
s := createMockSession(100, srv)
bf := byteframe.NewByteFrame()
nameBytes := stringsupport.UTF8ToSJIS("Pugi2")
bf.WriteBytes(nameBytes)
bf.WriteUint8(0)
bf.Seek(0, 0)
handleRenamePugi(s, bf, guild, 2)
if guild.PugiName2 != "Pugi2" {
t.Errorf("PugiName2 = %q, want Pugi2", guild.PugiName2)
}
}
func TestHandleRenamePugi_Pugi3Default(t *testing.T) {
srv := createMockServer()
guild := &Guild{ID: 1}
srv.guildRepo = &mockGuildRepo{guild: guild}
s := createMockSession(100, srv)
bf := byteframe.NewByteFrame()
nameBytes := stringsupport.UTF8ToSJIS("Pugi3")
bf.WriteBytes(nameBytes)
bf.WriteUint8(0)
bf.Seek(0, 0)
handleRenamePugi(s, bf, guild, 3)
if guild.PugiName3 != "Pugi3" {
t.Errorf("PugiName3 = %q, want Pugi3", guild.PugiName3)
}
}
func TestHandleChangePugi_AllNums(t *testing.T) {
srv := createMockServer()
guild := &Guild{ID: 1}
srv.guildRepo = &mockGuildRepo{guild: guild}
s := createMockSession(100, srv)
handleChangePugi(s, 5, guild, 1)
if guild.PugiOutfit1 != 5 {
t.Errorf("PugiOutfit1 = %d, want 5", guild.PugiOutfit1)
}
handleChangePugi(s, 10, guild, 2)
if guild.PugiOutfit2 != 10 {
t.Errorf("PugiOutfit2 = %d, want 10", guild.PugiOutfit2)
}
handleChangePugi(s, 15, guild, 3)
if guild.PugiOutfit3 != 15 {
t.Errorf("PugiOutfit3 = %d, want 15", guild.PugiOutfit3)
}
}
func TestHandleAvoidLeadershipUpdate_Success(t *testing.T) {
srv := createMockServer()
membership := &GuildMember{CharID: 100, AvoidLeadership: false}
srv.guildRepo = &mockGuildRepo{membership: membership}
s := createMockSession(100, srv)
pkt := &mhfpacket.MsgMhfOperateGuild{AckHandle: 1}
handleAvoidLeadershipUpdate(s, pkt, true)
<-s.sendPackets
if !membership.AvoidLeadership {
t.Error("AvoidLeadership should be true")
}
}
func TestHandleAvoidLeadershipUpdate_GetMembershipError(t *testing.T) {
srv := createMockServer()
srv.guildRepo = &mockGuildRepo{getMemberErr: errNotFound}
s := createMockSession(100, srv)
pkt := &mhfpacket.MsgMhfOperateGuild{AckHandle: 1}
handleAvoidLeadershipUpdate(s, pkt, true)
<-s.sendPackets
}