mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-26 09:33:02 +01:00
test(channelserver): add handler tests for guild ops, scout, board, and items
Cover the 4 handler files that had no tests: handlers_guild_ops.go,
handlers_guild_scout.go, handlers_guild_board.go, and handlers_items.go.
44 new tests exercise the error-logging paths added in 8fe6f60 and the
core handler logic (disband, resign, apply, leave, accept/reject/kick,
scout answer, message board CRUD, weekly stamps, item box parsing).
New mock types: mockGuildRepoOps (enhanced guild repo with configurable
errors and state tracking), mockUserRepoForItems, mockStampRepoForItems,
mockHouseRepoForItems. Coverage rises from 41.1% to 43.7%.
This commit is contained in:
241
server/channelserver/handlers_guild_board_test.go
Normal file
241
server/channelserver/handlers_guild_board_test.go
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- handleMsgMhfUpdateGuildMessageBoard tests ---
|
||||||
|
|
||||||
|
func TestUpdateGuildMessageBoard_CreatePost(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 1},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
guildMock.guild.LeaderCharID = 1
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
MessageOp: 0, // Create
|
||||||
|
PostType: 0,
|
||||||
|
StampID: 5,
|
||||||
|
Title: "Test Title",
|
||||||
|
Body: "Test Body",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfUpdateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
if guildMock.createdPost == nil {
|
||||||
|
t.Fatal("CreatePost should be called")
|
||||||
|
}
|
||||||
|
if guildMock.createdPost[0].(uint32) != 10 {
|
||||||
|
t.Errorf("CreatePost guildID = %d, want 10", guildMock.createdPost[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateGuildMessageBoard_DeletePost(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 1},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
guildMock.guild.LeaderCharID = 1
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
MessageOp: 1, // Delete
|
||||||
|
PostID: 42,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfUpdateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
if guildMock.deletedPostID != 42 {
|
||||||
|
t.Errorf("DeletePost postID = %d, want 42", guildMock.deletedPostID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateGuildMessageBoard_NoGuild(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{}
|
||||||
|
guildMock.getErr = errNotFound
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
MessageOp: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfUpdateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
// Returns early with empty success
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateGuildMessageBoard_Applicant(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
hasAppResult: true, // is an applicant
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
guildMock.guild.LeaderCharID = 999
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
MessageOp: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfUpdateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
if guildMock.createdPost != nil {
|
||||||
|
t.Error("Applicant should not be able to create posts")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateGuildMessageBoard_HasAppError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
hasAppErr: errNotFound, // error checking app status
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
guildMock.guild.LeaderCharID = 1
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfUpdateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
MessageOp: 0,
|
||||||
|
Title: "Test",
|
||||||
|
Body: "Body",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should log warning and treat as non-applicant (applicant=false on error)
|
||||||
|
handleMsgMhfUpdateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- handleMsgMhfEnumerateGuildMessageBoard tests ---
|
||||||
|
|
||||||
|
func TestEnumerateGuildMessageBoard_NoPosts(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
posts: []*MessageBoardPost{},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
BoardType: 0,
|
||||||
|
MaxPosts: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfEnumerateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnumerateGuildMessageBoard_WithPosts(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
posts: []*MessageBoardPost{
|
||||||
|
{ID: 1, AuthorID: 100, StampID: 5, Title: "Hello", Body: "World", Timestamp: time.Now()},
|
||||||
|
{ID: 2, AuthorID: 200, StampID: 0, Title: "Test", Body: "Post", Timestamp: time.Now()},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
BoardType: 0,
|
||||||
|
MaxPosts: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfEnumerateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) < 8 {
|
||||||
|
t.Errorf("Response too short for 2 posts: %d bytes", len(p.data))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEnumerateGuildMessageBoard_DBError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
listPostsErr: errNotFound,
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfEnumerateGuildMessageBoard{
|
||||||
|
AckHandle: 100,
|
||||||
|
BoardType: 0,
|
||||||
|
MaxPosts: 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfEnumerateGuildMessageBoard(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
607
server/channelserver/handlers_guild_ops_test.go
Normal file
607
server/channelserver/handlers_guild_ops_test.go
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- handleMsgMhfOperateGuild tests ---
|
||||||
|
|
||||||
|
func TestOperateGuild_Disband_Success(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
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.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 := &mockGuildRepoOps{
|
||||||
|
membership: &GuildMember{GuildID: 10, CharID: 1, OrderIndex: 5},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10}
|
||||||
|
guildMock.guild.LeaderCharID = 999 // different from session charID
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
membership: &GuildMember{GuildID: 10, CharID: 1, IsLeader: true, OrderIndex: 1},
|
||||||
|
disbandErr: errNotFound,
|
||||||
|
}
|
||||||
|
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.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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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 := &mockGuildRepoOps{}
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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
|
||||||
|
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 := &mockGuildRepoOps{
|
||||||
|
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{}
|
||||||
|
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
|
||||||
|
}
|
||||||
262
server/channelserver/handlers_guild_scout_test.go
Normal file
262
server/channelserver/handlers_guild_scout_test.go
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- handleMsgMhfAnswerGuildScout tests ---
|
||||||
|
|
||||||
|
func TestAnswerGuildScout_Accept(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
mailMock := &mockMailRepo{}
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
application: &GuildApplication{GuildID: 10, CharID: 1},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
|
||||||
|
guildMock.guild.LeaderCharID = 50
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.mailRepo = mailMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfAnswerGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
LeaderID: 50,
|
||||||
|
Answer: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfAnswerGuildScout(session, pkt)
|
||||||
|
|
||||||
|
if guildMock.acceptedCharID != 1 {
|
||||||
|
t.Errorf("AcceptApplication charID = %d, want 1", guildMock.acceptedCharID)
|
||||||
|
}
|
||||||
|
if len(mailMock.sentMails) != 2 {
|
||||||
|
t.Fatalf("Expected 2 mails (self + leader), got %d", len(mailMock.sentMails))
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnswerGuildScout_Decline(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
mailMock := &mockMailRepo{}
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
application: &GuildApplication{GuildID: 10, CharID: 1},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
|
||||||
|
guildMock.guild.LeaderCharID = 50
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.mailRepo = mailMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfAnswerGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
LeaderID: 50,
|
||||||
|
Answer: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfAnswerGuildScout(session, pkt)
|
||||||
|
|
||||||
|
if guildMock.rejectedCharID != 1 {
|
||||||
|
t.Errorf("RejectApplication charID = %d, want 1", guildMock.rejectedCharID)
|
||||||
|
}
|
||||||
|
if len(mailMock.sentMails) != 2 {
|
||||||
|
t.Fatalf("Expected 2 mails (self + leader), got %d", len(mailMock.sentMails))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnswerGuildScout_GuildNotFound(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
guildMock := &mockGuildRepoOps{}
|
||||||
|
guildMock.getErr = errNotFound
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.mailRepo = &mockMailRepo{}
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfAnswerGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
LeaderID: 50,
|
||||||
|
Answer: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfAnswerGuildScout(session, pkt)
|
||||||
|
|
||||||
|
// Should return fail ack
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnswerGuildScout_ApplicationMissing(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
mailMock := &mockMailRepo{}
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
application: nil, // no application found
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
|
||||||
|
guildMock.guild.LeaderCharID = 50
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.mailRepo = mailMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfAnswerGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
LeaderID: 50,
|
||||||
|
Answer: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfAnswerGuildScout(session, pkt)
|
||||||
|
|
||||||
|
// No mails should be sent when application is missing
|
||||||
|
if len(mailMock.sentMails) != 0 {
|
||||||
|
t.Errorf("Expected 0 mails for missing application, got %d", len(mailMock.sentMails))
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnswerGuildScout_MailError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
mailMock := &mockMailRepo{sendErr: errNotFound}
|
||||||
|
guildMock := &mockGuildRepoOps{
|
||||||
|
application: &GuildApplication{GuildID: 10, CharID: 1},
|
||||||
|
}
|
||||||
|
guildMock.guild = &Guild{ID: 10, Name: "TestGuild"}
|
||||||
|
guildMock.guild.LeaderCharID = 50
|
||||||
|
server.guildRepo = guildMock
|
||||||
|
server.mailRepo = mailMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfAnswerGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
LeaderID: 50,
|
||||||
|
Answer: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not panic; mail errors logged as warnings
|
||||||
|
handleMsgMhfAnswerGuildScout(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- handleMsgMhfGetRejectGuildScout tests ---
|
||||||
|
|
||||||
|
func TestGetRejectGuildScout_Restricted(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
charMock.bools["restrict_guild_scout"] = true
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetRejectGuildScout{AckHandle: 100}
|
||||||
|
|
||||||
|
handleMsgMhfGetRejectGuildScout(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) < 4 {
|
||||||
|
t.Fatal("Response too short")
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRejectGuildScout_Open(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
charMock.bools["restrict_guild_scout"] = false
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetRejectGuildScout{AckHandle: 100}
|
||||||
|
|
||||||
|
handleMsgMhfGetRejectGuildScout(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetRejectGuildScout_DBError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
charMock.readErr = errNotFound
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfGetRejectGuildScout{AckHandle: 100}
|
||||||
|
|
||||||
|
handleMsgMhfGetRejectGuildScout(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- handleMsgMhfSetRejectGuildScout tests ---
|
||||||
|
|
||||||
|
func TestSetRejectGuildScout_Success(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfSetRejectGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
Reject: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfSetRejectGuildScout(session, pkt)
|
||||||
|
|
||||||
|
if !charMock.bools["restrict_guild_scout"] {
|
||||||
|
t.Error("restrict_guild_scout should be true")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetRejectGuildScout_DBError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
charMock := newMockCharacterRepo()
|
||||||
|
charMock.saveErr = errNotFound
|
||||||
|
server.charRepo = charMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfSetRejectGuildScout{
|
||||||
|
AckHandle: 100,
|
||||||
|
Reject: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfSetRejectGuildScout(session, pkt)
|
||||||
|
|
||||||
|
// Should return fail ack
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
364
server/channelserver/handlers_items_test.go
Normal file
364
server/channelserver/handlers_items_test.go
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
package channelserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"erupe-ce/common/byteframe"
|
||||||
|
"erupe-ce/common/mhfitem"
|
||||||
|
"erupe-ce/network/mhfpacket"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- userGetItems tests ---
|
||||||
|
|
||||||
|
func TestUserGetItems_NilData(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
userMock := &mockUserRepoForItems{itemBoxData: nil}
|
||||||
|
server.userRepo = userMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
session.userID = 1
|
||||||
|
|
||||||
|
items := userGetItems(session)
|
||||||
|
|
||||||
|
if len(items) != 0 {
|
||||||
|
t.Errorf("Expected empty items, got %d", len(items))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserGetItems_DBError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
userMock := &mockUserRepoForItems{itemBoxErr: errNotFound}
|
||||||
|
server.userRepo = userMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
session.userID = 1
|
||||||
|
|
||||||
|
items := userGetItems(session)
|
||||||
|
|
||||||
|
if len(items) != 0 {
|
||||||
|
t.Errorf("Expected empty items on error, got %d", len(items))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserGetItems_ParsesData(t *testing.T) {
|
||||||
|
// Build serialized item box with 1 item
|
||||||
|
bf := byteframe.NewByteFrame()
|
||||||
|
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
|
||||||
|
|
||||||
|
server := createMockServer()
|
||||||
|
userMock := &mockUserRepoForItems{itemBoxData: bf.Data()}
|
||||||
|
server.userRepo = userMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
session.userID = 1
|
||||||
|
|
||||||
|
items := userGetItems(session)
|
||||||
|
|
||||||
|
if len(items) != 1 {
|
||||||
|
t.Fatalf("Expected 1 item, got %d", len(items))
|
||||||
|
}
|
||||||
|
if items[0].Item.ItemID != 500 {
|
||||||
|
t.Errorf("ItemID = %d, want 500", items[0].Item.ItemID)
|
||||||
|
}
|
||||||
|
if items[0].Quantity != 3 {
|
||||||
|
t.Errorf("Quantity = %d, want 3", items[0].Quantity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- handleMsgMhfCheckWeeklyStamp tests ---
|
||||||
|
|
||||||
|
func TestCheckWeeklyStamp_InvalidType(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfCheckWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "invalid",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfCheckWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckWeeklyStamp_FirstCheck(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
checkedErr: errNotFound, // no existing record
|
||||||
|
totals: [2]uint16{0, 0},
|
||||||
|
}
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfCheckWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "hl",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfCheckWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
if !stampMock.initCalled {
|
||||||
|
t.Error("Init should be called on first check")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case p := <-session.sendPackets:
|
||||||
|
if len(p.data) < 14 {
|
||||||
|
t.Errorf("Response too short: %d bytes", len(p.data))
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckWeeklyStamp_WithinWeek(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
checkedTime: TimeAdjusted(), // checked right now (within this week)
|
||||||
|
totals: [2]uint16{3, 1},
|
||||||
|
}
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfCheckWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "hl",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfCheckWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
if stampMock.incrementCalled {
|
||||||
|
t.Error("IncrementTotal should not be called within same week")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckWeeklyStamp_WeekRollover(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
checkedTime: TimeWeekStart().Add(-24 * time.Hour), // before this week
|
||||||
|
totals: [2]uint16{5, 2},
|
||||||
|
}
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfCheckWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "ex",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfCheckWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
if !stampMock.incrementCalled {
|
||||||
|
t.Error("IncrementTotal should be called after week rollover")
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckWeeklyStamp_GetTotalsError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
checkedTime: TimeAdjusted(),
|
||||||
|
totalsErr: errNotFound,
|
||||||
|
}
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfCheckWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "hl",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not panic; logs warning, returns zeros
|
||||||
|
handleMsgMhfCheckWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- handleMsgMhfExchangeWeeklyStamp tests ---
|
||||||
|
|
||||||
|
func TestExchangeWeeklyStamp_InvalidType(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfExchangeWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "invalid",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfExchangeWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExchangeWeeklyStamp_HL(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
exchangeResult: [2]uint16{10, 5},
|
||||||
|
}
|
||||||
|
houseMock := newMockHouseRepoForItems()
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
server.houseRepo = houseMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfExchangeWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "hl",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfExchangeWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
// Verify warehouse gift box was updated (index 10)
|
||||||
|
if houseMock.setData[10] == nil {
|
||||||
|
t.Error("Gift box should be updated with ticket item")
|
||||||
|
}
|
||||||
|
// Parse the gift box to verify the item
|
||||||
|
if len(houseMock.setData[10]) > 0 {
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(houseMock.setData[10])
|
||||||
|
count := bf.ReadUint16()
|
||||||
|
if count != 1 {
|
||||||
|
t.Errorf("Expected 1 item in gift box, got %d", count)
|
||||||
|
}
|
||||||
|
bf.ReadUint16() // unused
|
||||||
|
item := mhfitem.ReadWarehouseItem(bf)
|
||||||
|
if item.Item.ItemID != 1630 {
|
||||||
|
t.Errorf("ItemID = %d, want 1630 (HL ticket)", item.Item.ItemID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExchangeWeeklyStamp_EX(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
exchangeResult: [2]uint16{10, 5},
|
||||||
|
}
|
||||||
|
houseMock := newMockHouseRepoForItems()
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
server.houseRepo = houseMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfExchangeWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "ex",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfExchangeWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
if houseMock.setData[10] == nil {
|
||||||
|
t.Error("Gift box should be updated with ticket item")
|
||||||
|
}
|
||||||
|
if len(houseMock.setData[10]) > 0 {
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(houseMock.setData[10])
|
||||||
|
count := bf.ReadUint16()
|
||||||
|
if count != 1 {
|
||||||
|
t.Errorf("Expected 1 item in gift box, got %d", count)
|
||||||
|
}
|
||||||
|
bf.ReadUint16() // unused
|
||||||
|
item := mhfitem.ReadWarehouseItem(bf)
|
||||||
|
if item.Item.ItemID != 1631 {
|
||||||
|
t.Errorf("ItemID = %d, want 1631 (EX ticket)", item.Item.ItemID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExchangeWeeklyStamp_ExchangeError(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
exchangeErr: errNotFound,
|
||||||
|
}
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfExchangeWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "hl",
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfExchangeWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
// Should return fail ack
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExchangeWeeklyStamp_Yearly(t *testing.T) {
|
||||||
|
server := createMockServer()
|
||||||
|
stampMock := &mockStampRepoForItems{
|
||||||
|
yearlyResult: [2]uint16{20, 10},
|
||||||
|
}
|
||||||
|
houseMock := newMockHouseRepoForItems()
|
||||||
|
server.stampRepo = stampMock
|
||||||
|
server.houseRepo = houseMock
|
||||||
|
session := createMockSession(1, server)
|
||||||
|
|
||||||
|
pkt := &mhfpacket.MsgMhfExchangeWeeklyStamp{
|
||||||
|
AckHandle: 100,
|
||||||
|
StampType: "ex",
|
||||||
|
ExchangeType: 10, // Yearly
|
||||||
|
}
|
||||||
|
|
||||||
|
handleMsgMhfExchangeWeeklyStamp(session, pkt)
|
||||||
|
|
||||||
|
if houseMock.setData[10] == nil {
|
||||||
|
t.Error("Gift box should be updated with yearly ticket")
|
||||||
|
}
|
||||||
|
if len(houseMock.setData[10]) > 0 {
|
||||||
|
bf := byteframe.NewByteFrameFromBytes(houseMock.setData[10])
|
||||||
|
count := bf.ReadUint16()
|
||||||
|
if count != 1 {
|
||||||
|
t.Errorf("Expected 1 item in gift box, got %d", count)
|
||||||
|
}
|
||||||
|
bf.ReadUint16() // unused
|
||||||
|
item := mhfitem.ReadWarehouseItem(bf)
|
||||||
|
if item.Item.ItemID != 2210 {
|
||||||
|
t.Errorf("ItemID = %d, want 2210 (yearly ticket)", item.Item.ItemID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-session.sendPackets:
|
||||||
|
default:
|
||||||
|
t.Error("No response packet queued")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -330,3 +330,269 @@ func (m *mockGuildRepoForMail) InsertKillLog(_ uint32, _ int, _ uint8, _ time.Ti
|
|||||||
func (m *mockGuildRepoForMail) ListInvitedCharacters(_ uint32) ([]*ScoutedCharacter, error) { return nil, nil }
|
func (m *mockGuildRepoForMail) ListInvitedCharacters(_ uint32) ([]*ScoutedCharacter, error) { return nil, nil }
|
||||||
func (m *mockGuildRepoForMail) RolloverDailyRP(_ uint32, _ time.Time) error { return nil }
|
func (m *mockGuildRepoForMail) RolloverDailyRP(_ uint32, _ time.Time) error { return nil }
|
||||||
func (m *mockGuildRepoForMail) AddWeeklyBonusUsers(_ uint32, _ uint8) error { return nil }
|
func (m *mockGuildRepoForMail) AddWeeklyBonusUsers(_ uint32, _ uint8) error { return nil }
|
||||||
|
|
||||||
|
// --- mockGuildRepoOps (enhanced guild repo for ops/scout/board tests) ---
|
||||||
|
|
||||||
|
type mockGuildRepoOps struct {
|
||||||
|
mockGuildRepoForMail
|
||||||
|
|
||||||
|
// Configurable errors
|
||||||
|
saveErr error
|
||||||
|
saveMemberErr error
|
||||||
|
disbandErr error
|
||||||
|
getMembersErr error
|
||||||
|
acceptErr error
|
||||||
|
rejectErr error
|
||||||
|
removeErr error
|
||||||
|
createAppErr error
|
||||||
|
getMemberErr error
|
||||||
|
hasAppResult bool
|
||||||
|
hasAppErr error
|
||||||
|
listPostsErr error
|
||||||
|
createPostErr error
|
||||||
|
deletePostErr error
|
||||||
|
|
||||||
|
// State tracking
|
||||||
|
disbandedID uint32
|
||||||
|
removedCharID uint32
|
||||||
|
acceptedCharID uint32
|
||||||
|
rejectedCharID uint32
|
||||||
|
savedGuild *Guild
|
||||||
|
savedMembers []*GuildMember
|
||||||
|
createdAppArgs []interface{}
|
||||||
|
createdPost []interface{}
|
||||||
|
deletedPostID uint32
|
||||||
|
|
||||||
|
// Data
|
||||||
|
membership *GuildMember
|
||||||
|
application *GuildApplication
|
||||||
|
posts []*MessageBoardPost
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) GetByID(guildID uint32) (*Guild, error) {
|
||||||
|
if m.getErr != nil {
|
||||||
|
return nil, m.getErr
|
||||||
|
}
|
||||||
|
if m.guild != nil && m.guild.ID == guildID {
|
||||||
|
return m.guild, nil
|
||||||
|
}
|
||||||
|
return nil, errNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) GetByCharID(charID uint32) (*Guild, error) {
|
||||||
|
if m.getErr != nil {
|
||||||
|
return nil, m.getErr
|
||||||
|
}
|
||||||
|
return m.guild, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) GetMembers(guildID uint32, applicants bool) ([]*GuildMember, error) {
|
||||||
|
if m.getMembersErr != nil {
|
||||||
|
return nil, m.getMembersErr
|
||||||
|
}
|
||||||
|
return m.members, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) GetCharacterMembership(_ uint32) (*GuildMember, error) {
|
||||||
|
if m.getMemberErr != nil {
|
||||||
|
return nil, m.getMemberErr
|
||||||
|
}
|
||||||
|
return m.membership, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) Save(guild *Guild) error {
|
||||||
|
m.savedGuild = guild
|
||||||
|
return m.saveErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) SaveMember(member *GuildMember) error {
|
||||||
|
m.savedMembers = append(m.savedMembers, member)
|
||||||
|
return m.saveMemberErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) Disband(guildID uint32) error {
|
||||||
|
m.disbandedID = guildID
|
||||||
|
return m.disbandErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) RemoveCharacter(charID uint32) error {
|
||||||
|
m.removedCharID = charID
|
||||||
|
return m.removeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) AcceptApplication(guildID, charID uint32) error {
|
||||||
|
m.acceptedCharID = charID
|
||||||
|
return m.acceptErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) RejectApplication(guildID, charID uint32) error {
|
||||||
|
m.rejectedCharID = charID
|
||||||
|
return m.rejectErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) CreateApplication(guildID, charID, actorID uint32, appType GuildApplicationType) error {
|
||||||
|
m.createdAppArgs = []interface{}{guildID, charID, actorID, appType}
|
||||||
|
return m.createAppErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) HasApplication(guildID, charID uint32) (bool, error) {
|
||||||
|
return m.hasAppResult, m.hasAppErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) GetApplication(guildID, charID uint32, appType GuildApplicationType) (*GuildApplication, error) {
|
||||||
|
return m.application, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) ListPosts(guildID uint32, postType int) ([]*MessageBoardPost, error) {
|
||||||
|
if m.listPostsErr != nil {
|
||||||
|
return nil, m.listPostsErr
|
||||||
|
}
|
||||||
|
return m.posts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) CreatePost(guildID, authorID, stampID uint32, postType int, title, body string, maxPosts int) error {
|
||||||
|
m.createdPost = []interface{}{guildID, authorID, stampID, postType, title, body, maxPosts}
|
||||||
|
return m.createPostErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockGuildRepoOps) DeletePost(postID uint32) error {
|
||||||
|
m.deletedPostID = postID
|
||||||
|
return m.deletePostErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- mockUserRepoForItems ---
|
||||||
|
|
||||||
|
type mockUserRepoForItems struct {
|
||||||
|
itemBoxData []byte
|
||||||
|
itemBoxErr error
|
||||||
|
setData []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockUserRepoForItems) GetItemBox(_ uint32) ([]byte, error) {
|
||||||
|
return m.itemBoxData, m.itemBoxErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockUserRepoForItems) SetItemBox(_ uint32, data []byte) error {
|
||||||
|
m.setData = data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 }
|
||||||
|
|
||||||
|
// --- mockStampRepoForItems ---
|
||||||
|
|
||||||
|
type mockStampRepoForItems struct {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) GetChecked(_ uint32, _ string) (time.Time, error) {
|
||||||
|
return m.checkedTime, m.checkedErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) Init(_ uint32, _ time.Time) error {
|
||||||
|
m.initCalled = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) SetChecked(_ uint32, _ string, _ time.Time) error {
|
||||||
|
m.setCalled = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) IncrementTotal(_ uint32, _ string) error {
|
||||||
|
m.incrementCalled = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) GetTotals(_ uint32, _ string) (uint16, uint16, error) {
|
||||||
|
return m.totals[0], m.totals[1], m.totalsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) ExchangeYearly(_ uint32) (uint16, uint16, error) {
|
||||||
|
return m.yearlyResult[0], m.yearlyResult[1], m.yearlyErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockStampRepoForItems) Exchange(_ uint32, _ string) (uint16, uint16, error) {
|
||||||
|
return m.exchangeResult[0], m.exchangeResult[1], m.exchangeErr
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- mockHouseRepoForItems ---
|
||||||
|
|
||||||
|
type mockHouseRepoForItems struct {
|
||||||
|
warehouseItems map[uint8][]byte
|
||||||
|
setData map[uint8][]byte
|
||||||
|
setErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockHouseRepoForItems() *mockHouseRepoForItems {
|
||||||
|
return &mockHouseRepoForItems{
|
||||||
|
warehouseItems: make(map[uint8][]byte),
|
||||||
|
setData: make(map[uint8][]byte),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockHouseRepoForItems) GetWarehouseItemData(_ uint32, index uint8) ([]byte, error) {
|
||||||
|
return m.warehouseItems[index], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockHouseRepoForItems) SetWarehouseItemData(_ uint32, index uint8, data []byte) error {
|
||||||
|
m.setData[index] = data
|
||||||
|
return m.setErr
|
||||||
|
}
|
||||||
|
|
||||||
|
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) 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) 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 }
|
||||||
|
|||||||
Reference in New Issue
Block a user