mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
Per anti-patterns.md item #9, guild-related SQL was scattered across ~15 handler files with no repository abstraction. Following the same pattern established by CharacterRepository, this centralizes all guilds, guild_characters, and guild_applications table access into a single GuildRepository (~30 methods). guild_model.go and handlers_guild_member.go are trimmed to types and pure business logic only. All handler files (guild_*, festa, mail, house, mercenary, rengoku) now call s.server.guildRepo methods instead of direct DB queries or methods on domain objects.
532 lines
14 KiB
Go
532 lines
14 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/jmoiron/sqlx"
|
|
)
|
|
|
|
func setupGuildRepo(t *testing.T) (*GuildRepository, *sqlx.DB, uint32, uint32) {
|
|
t.Helper()
|
|
db := SetupTestDB(t)
|
|
userID := CreateTestUser(t, db, "guild_test_user")
|
|
charID := CreateTestCharacter(t, db, userID, "GuildLeader")
|
|
repo := NewGuildRepository(db)
|
|
guildID := CreateTestGuild(t, db, charID, "TestGuild")
|
|
t.Cleanup(func() { TeardownTestDB(t, db) })
|
|
return repo, db, guildID, charID
|
|
}
|
|
|
|
func TestGetByID(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
guild, err := repo.GetByID(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID failed: %v", err)
|
|
}
|
|
if guild == nil {
|
|
t.Fatal("Expected guild, got nil")
|
|
}
|
|
if guild.ID != guildID {
|
|
t.Errorf("Expected guild ID %d, got %d", guildID, guild.ID)
|
|
}
|
|
if guild.Name != "TestGuild" {
|
|
t.Errorf("Expected name 'TestGuild', got %q", guild.Name)
|
|
}
|
|
if guild.LeaderCharID != charID {
|
|
t.Errorf("Expected leader %d, got %d", charID, guild.LeaderCharID)
|
|
}
|
|
}
|
|
|
|
func TestGetByIDNotFound(t *testing.T) {
|
|
repo, _, _, _ := setupGuildRepo(t)
|
|
|
|
guild, err := repo.GetByID(999999)
|
|
if err != nil {
|
|
t.Fatalf("GetByID failed: %v", err)
|
|
}
|
|
if guild != nil {
|
|
t.Errorf("Expected nil for non-existent guild, got: %+v", guild)
|
|
}
|
|
}
|
|
|
|
func TestGetByCharID(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
guild, err := repo.GetByCharID(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetByCharID failed: %v", err)
|
|
}
|
|
if guild == nil {
|
|
t.Fatal("Expected guild, got nil")
|
|
}
|
|
if guild.ID != guildID {
|
|
t.Errorf("Expected guild ID %d, got %d", guildID, guild.ID)
|
|
}
|
|
}
|
|
|
|
func TestGetByCharIDNotFound(t *testing.T) {
|
|
repo, _, _, _ := setupGuildRepo(t)
|
|
|
|
guild, err := repo.GetByCharID(999999)
|
|
if err != nil {
|
|
t.Fatalf("GetByCharID failed: %v", err)
|
|
}
|
|
if guild != nil {
|
|
t.Errorf("Expected nil for non-member, got: %+v", guild)
|
|
}
|
|
}
|
|
|
|
func TestCreate(t *testing.T) {
|
|
db := SetupTestDB(t)
|
|
defer TeardownTestDB(t, db)
|
|
repo := NewGuildRepository(db)
|
|
userID := CreateTestUser(t, db, "create_guild_user")
|
|
charID := CreateTestCharacter(t, db, userID, "CreateLeader")
|
|
|
|
guildID, err := repo.Create(charID, "NewGuild")
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
if guildID <= 0 {
|
|
t.Errorf("Expected positive guild ID, got %d", guildID)
|
|
}
|
|
|
|
// Verify guild exists
|
|
guild, err := repo.GetByID(uint32(guildID))
|
|
if err != nil {
|
|
t.Fatalf("GetByID after Create failed: %v", err)
|
|
}
|
|
if guild == nil {
|
|
t.Fatal("Created guild not found")
|
|
}
|
|
if guild.Name != "NewGuild" {
|
|
t.Errorf("Expected name 'NewGuild', got %q", guild.Name)
|
|
}
|
|
|
|
// Verify leader is a member
|
|
member, err := repo.GetCharacterMembership(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership failed: %v", err)
|
|
}
|
|
if member == nil {
|
|
t.Fatal("Leader not found as guild member")
|
|
}
|
|
}
|
|
|
|
func TestSaveGuild(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
guild, err := repo.GetByID(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID failed: %v", err)
|
|
}
|
|
|
|
guild.Comment = "Updated comment"
|
|
guild.MainMotto = 5
|
|
guild.SubMotto = 3
|
|
|
|
if err := repo.Save(guild); err != nil {
|
|
t.Fatalf("Save failed: %v", err)
|
|
}
|
|
|
|
updated, err := repo.GetByID(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID after Save failed: %v", err)
|
|
}
|
|
if updated.Comment != "Updated comment" {
|
|
t.Errorf("Expected comment 'Updated comment', got %q", updated.Comment)
|
|
}
|
|
if updated.MainMotto != 5 || updated.SubMotto != 3 {
|
|
t.Errorf("Expected mottos 5/3, got %d/%d", updated.MainMotto, updated.SubMotto)
|
|
}
|
|
}
|
|
|
|
func TestDisband(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.Disband(guildID); err != nil {
|
|
t.Fatalf("Disband failed: %v", err)
|
|
}
|
|
|
|
guild, err := repo.GetByID(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetByID after Disband failed: %v", err)
|
|
}
|
|
if guild != nil {
|
|
t.Errorf("Expected nil after disband, got: %+v", guild)
|
|
}
|
|
|
|
member, err := repo.GetCharacterMembership(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership after Disband failed: %v", err)
|
|
}
|
|
if member != nil {
|
|
t.Errorf("Expected nil membership after disband, got: %+v", member)
|
|
}
|
|
}
|
|
|
|
func TestGetMembers(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
// Add a second member
|
|
user2 := CreateTestUser(t, db, "member_user")
|
|
member2 := CreateTestCharacter(t, db, user2, "Member2")
|
|
if _, err := db.Exec("INSERT INTO guild_characters (guild_id, character_id, order_index) VALUES ($1, $2, 2)", guildID, member2); err != nil {
|
|
t.Fatalf("Failed to add member: %v", err)
|
|
}
|
|
|
|
members, err := repo.GetMembers(guildID, false)
|
|
if err != nil {
|
|
t.Fatalf("GetMembers failed: %v", err)
|
|
}
|
|
if len(members) != 2 {
|
|
t.Fatalf("Expected 2 members, got %d", len(members))
|
|
}
|
|
|
|
ids := map[uint32]bool{leaderID: false, member2: false}
|
|
for _, m := range members {
|
|
ids[m.CharID] = true
|
|
}
|
|
if !ids[leaderID] || !ids[member2] {
|
|
t.Errorf("Expected members %d and %d, got: %v", leaderID, member2, members)
|
|
}
|
|
}
|
|
|
|
func TestGetCharacterMembership(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
member, err := repo.GetCharacterMembership(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership failed: %v", err)
|
|
}
|
|
if member == nil {
|
|
t.Fatal("Expected membership, got nil")
|
|
}
|
|
if member.GuildID != guildID {
|
|
t.Errorf("Expected guild ID %d, got %d", guildID, member.GuildID)
|
|
}
|
|
if !member.IsLeader {
|
|
t.Error("Expected leader flag to be true")
|
|
}
|
|
}
|
|
|
|
func TestSaveMember(t *testing.T) {
|
|
repo, _, _, charID := setupGuildRepo(t)
|
|
|
|
member, err := repo.GetCharacterMembership(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership failed: %v", err)
|
|
}
|
|
|
|
member.AvoidLeadership = true
|
|
member.OrderIndex = 5
|
|
|
|
if err := repo.SaveMember(member); err != nil {
|
|
t.Fatalf("SaveMember failed: %v", err)
|
|
}
|
|
|
|
updated, err := repo.GetCharacterMembership(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership after Save failed: %v", err)
|
|
}
|
|
if !updated.AvoidLeadership {
|
|
t.Error("Expected avoid_leadership=true")
|
|
}
|
|
if updated.OrderIndex != 5 {
|
|
t.Errorf("Expected order_index=5, got %d", updated.OrderIndex)
|
|
}
|
|
}
|
|
|
|
func TestRemoveCharacter(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
// Add and remove a member
|
|
user2 := CreateTestUser(t, db, "remove_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "RemoveMe")
|
|
if _, err := db.Exec("INSERT INTO guild_characters (guild_id, character_id, order_index) VALUES ($1, $2, 2)", guildID, char2); err != nil {
|
|
t.Fatalf("Failed to add member: %v", err)
|
|
}
|
|
|
|
if err := repo.RemoveCharacter(char2); err != nil {
|
|
t.Fatalf("RemoveCharacter failed: %v", err)
|
|
}
|
|
|
|
member, err := repo.GetCharacterMembership(char2)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership after remove failed: %v", err)
|
|
}
|
|
if member != nil {
|
|
t.Errorf("Expected nil membership after remove, got: %+v", member)
|
|
}
|
|
}
|
|
|
|
func TestApplicationWorkflow(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "applicant_user")
|
|
applicantID := CreateTestCharacter(t, db, user2, "Applicant")
|
|
|
|
// Create application
|
|
err := repo.CreateApplication(guildID, applicantID, applicantID, GuildApplicationTypeApplied, nil)
|
|
if err != nil {
|
|
t.Fatalf("CreateApplication failed: %v", err)
|
|
}
|
|
|
|
// Check HasApplication
|
|
has, err := repo.HasApplication(guildID, applicantID)
|
|
if err != nil {
|
|
t.Fatalf("HasApplication failed: %v", err)
|
|
}
|
|
if !has {
|
|
t.Error("Expected application to exist")
|
|
}
|
|
|
|
// Get application
|
|
app, err := repo.GetApplication(guildID, applicantID, GuildApplicationTypeApplied)
|
|
if err != nil {
|
|
t.Fatalf("GetApplication failed: %v", err)
|
|
}
|
|
if app == nil {
|
|
t.Fatal("Expected application, got nil")
|
|
}
|
|
|
|
// Accept
|
|
err = repo.AcceptApplication(guildID, applicantID)
|
|
if err != nil {
|
|
t.Fatalf("AcceptApplication failed: %v", err)
|
|
}
|
|
|
|
// Verify membership
|
|
member, err := repo.GetCharacterMembership(applicantID)
|
|
if err != nil {
|
|
t.Fatalf("GetCharacterMembership after accept failed: %v", err)
|
|
}
|
|
if member == nil {
|
|
t.Fatal("Expected membership after accept")
|
|
}
|
|
|
|
// Verify application removed
|
|
has, err = repo.HasApplication(guildID, applicantID)
|
|
if err != nil {
|
|
t.Fatalf("HasApplication after accept failed: %v", err)
|
|
}
|
|
if has {
|
|
t.Error("Expected no application after accept")
|
|
}
|
|
}
|
|
|
|
func TestRejectApplication(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "reject_user")
|
|
applicantID := CreateTestCharacter(t, db, user2, "Rejected")
|
|
|
|
err := repo.CreateApplication(guildID, applicantID, applicantID, GuildApplicationTypeApplied, nil)
|
|
if err != nil {
|
|
t.Fatalf("CreateApplication failed: %v", err)
|
|
}
|
|
|
|
err = repo.RejectApplication(guildID, applicantID)
|
|
if err != nil {
|
|
t.Fatalf("RejectApplication failed: %v", err)
|
|
}
|
|
|
|
has, err := repo.HasApplication(guildID, applicantID)
|
|
if err != nil {
|
|
t.Fatalf("HasApplication after reject failed: %v", err)
|
|
}
|
|
if has {
|
|
t.Error("Expected no application after reject")
|
|
}
|
|
}
|
|
|
|
func TestSetRecruiting(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.SetRecruiting(guildID, false); err != nil {
|
|
t.Fatalf("SetRecruiting failed: %v", err)
|
|
}
|
|
|
|
var recruiting bool
|
|
if err := db.QueryRow("SELECT recruiting FROM guilds WHERE id=$1", guildID).Scan(&recruiting); err != nil {
|
|
t.Fatalf("Verification query failed: %v", err)
|
|
}
|
|
if recruiting {
|
|
t.Error("Expected recruiting=false")
|
|
}
|
|
}
|
|
|
|
func TestRPOperations(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
// AddRankRP
|
|
if err := repo.AddRankRP(guildID, 100); err != nil {
|
|
t.Fatalf("AddRankRP failed: %v", err)
|
|
}
|
|
var rankRP uint16
|
|
if err := db.QueryRow("SELECT rank_rp FROM guilds WHERE id=$1", guildID).Scan(&rankRP); err != nil {
|
|
t.Fatalf("Verification failed: %v", err)
|
|
}
|
|
if rankRP != 100 {
|
|
t.Errorf("Expected rank_rp=100, got %d", rankRP)
|
|
}
|
|
|
|
// AddEventRP
|
|
if err := repo.AddEventRP(guildID, 50); err != nil {
|
|
t.Fatalf("AddEventRP failed: %v", err)
|
|
}
|
|
|
|
// ExchangeEventRP
|
|
balance, err := repo.ExchangeEventRP(guildID, 20)
|
|
if err != nil {
|
|
t.Fatalf("ExchangeEventRP failed: %v", err)
|
|
}
|
|
if balance != 30 {
|
|
t.Errorf("Expected event_rp balance=30, got %d", balance)
|
|
}
|
|
|
|
// Room RP operations
|
|
if err := repo.AddRoomRP(guildID, 10); err != nil {
|
|
t.Fatalf("AddRoomRP failed: %v", err)
|
|
}
|
|
roomRP, err := repo.GetRoomRP(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetRoomRP failed: %v", err)
|
|
}
|
|
if roomRP != 10 {
|
|
t.Errorf("Expected room_rp=10, got %d", roomRP)
|
|
}
|
|
|
|
if err := repo.SetRoomRP(guildID, 0); err != nil {
|
|
t.Fatalf("SetRoomRP failed: %v", err)
|
|
}
|
|
roomRP, err = repo.GetRoomRP(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetRoomRP after reset failed: %v", err)
|
|
}
|
|
if roomRP != 0 {
|
|
t.Errorf("Expected room_rp=0, got %d", roomRP)
|
|
}
|
|
|
|
// SetRoomExpiry
|
|
expiry := time.Date(2025, 7, 1, 0, 0, 0, 0, time.UTC)
|
|
if err := repo.SetRoomExpiry(guildID, expiry); err != nil {
|
|
t.Fatalf("SetRoomExpiry failed: %v", err)
|
|
}
|
|
var gotExpiry time.Time
|
|
if err := db.QueryRow("SELECT room_expiry FROM guilds WHERE id=$1", guildID).Scan(&gotExpiry); err != nil {
|
|
t.Fatalf("Verification failed: %v", err)
|
|
}
|
|
if !gotExpiry.Equal(expiry) {
|
|
t.Errorf("Expected expiry %v, got %v", expiry, gotExpiry)
|
|
}
|
|
}
|
|
|
|
func TestItemBox(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
// Initially nil
|
|
data, err := repo.GetItemBox(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetItemBox failed: %v", err)
|
|
}
|
|
if data != nil {
|
|
t.Errorf("Expected nil item box initially, got %x", data)
|
|
}
|
|
|
|
// Save and retrieve
|
|
blob := []byte{0x01, 0x02, 0x03}
|
|
if err := repo.SaveItemBox(guildID, blob); err != nil {
|
|
t.Fatalf("SaveItemBox failed: %v", err)
|
|
}
|
|
|
|
data, err = repo.GetItemBox(guildID)
|
|
if err != nil {
|
|
t.Fatalf("GetItemBox after save failed: %v", err)
|
|
}
|
|
if len(data) != 3 || data[0] != 0x01 || data[2] != 0x03 {
|
|
t.Errorf("Expected %x, got %x", blob, data)
|
|
}
|
|
}
|
|
|
|
func TestListAll(t *testing.T) {
|
|
repo, db, _, _ := setupGuildRepo(t)
|
|
|
|
// Create a second guild
|
|
user2 := CreateTestUser(t, db, "list_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "ListLeader")
|
|
CreateTestGuild(t, db, char2, "SecondGuild")
|
|
|
|
guilds, err := repo.ListAll()
|
|
if err != nil {
|
|
t.Fatalf("ListAll failed: %v", err)
|
|
}
|
|
if len(guilds) < 2 {
|
|
t.Errorf("Expected at least 2 guilds, got %d", len(guilds))
|
|
}
|
|
}
|
|
|
|
func TestArrangeCharacters(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
// Add two more members
|
|
user2 := CreateTestUser(t, db, "arrange_user2")
|
|
char2 := CreateTestCharacter(t, db, user2, "Char2")
|
|
user3 := CreateTestUser(t, db, "arrange_user3")
|
|
char3 := CreateTestCharacter(t, db, user3, "Char3")
|
|
if _, err := db.Exec("INSERT INTO guild_characters (guild_id, character_id, order_index) VALUES ($1, $2, 2)", guildID, char2); err != nil {
|
|
t.Fatalf("Failed to add member: %v", err)
|
|
}
|
|
if _, err := db.Exec("INSERT INTO guild_characters (guild_id, character_id, order_index) VALUES ($1, $2, 3)", guildID, char3); err != nil {
|
|
t.Fatalf("Failed to add member: %v", err)
|
|
}
|
|
|
|
// Rearrange (excludes leader, sets order_index starting at 2)
|
|
if err := repo.ArrangeCharacters([]uint32{char3, char2}); err != nil {
|
|
t.Fatalf("ArrangeCharacters failed: %v", err)
|
|
}
|
|
|
|
// Verify order changed
|
|
var order2, order3 uint16
|
|
_ = db.QueryRow("SELECT order_index FROM guild_characters WHERE character_id=$1", char2).Scan(&order2)
|
|
_ = db.QueryRow("SELECT order_index FROM guild_characters WHERE character_id=$1", char3).Scan(&order3)
|
|
if order3 != 2 || order2 != 3 {
|
|
t.Errorf("Expected char3=2, char2=3 but got char3=%d, char2=%d", order3, order2)
|
|
}
|
|
_ = leaderID
|
|
}
|
|
|
|
func TestSetRecruiter(t *testing.T) {
|
|
repo, db, _, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.SetRecruiter(charID, true); err != nil {
|
|
t.Fatalf("SetRecruiter failed: %v", err)
|
|
}
|
|
|
|
var recruiter bool
|
|
if err := db.QueryRow("SELECT recruiter FROM guild_characters WHERE character_id=$1", charID).Scan(&recruiter); err != nil {
|
|
t.Fatalf("Verification failed: %v", err)
|
|
}
|
|
if !recruiter {
|
|
t.Error("Expected recruiter=true")
|
|
}
|
|
}
|
|
|
|
func TestAddMemberDailyRP(t *testing.T) {
|
|
repo, db, _, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.AddMemberDailyRP(charID, 25); err != nil {
|
|
t.Fatalf("AddMemberDailyRP failed: %v", err)
|
|
}
|
|
|
|
var rp uint16
|
|
if err := db.QueryRow("SELECT rp_today FROM guild_characters WHERE character_id=$1", charID).Scan(&rp); err != nil {
|
|
t.Fatalf("Verification failed: %v", err)
|
|
}
|
|
if rp != 25 {
|
|
t.Errorf("Expected rp_today=25, got %d", rp)
|
|
}
|
|
}
|