mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
Scout invitations were stored in guild_applications with type 'invited', forcing the scout list response to use charID as the invitation ID — a known hack that made CancelGuildScout semantically incorrect. Introduce a dedicated guild_invites table (migration 0012) with a serial PK. The scout list now returns real invite IDs and actual InvitedAt timestamps. CancelGuildScout cancels by PK. AcceptInvite and DeclineInvite operate on guild_invites while player-applied applications remain in guild_applications unchanged.
1533 lines
43 KiB
Go
1533 lines
43 KiB
Go
package channelserver
|
|
|
|
import (
|
|
"fmt"
|
|
"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)
|
|
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)
|
|
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)
|
|
}
|
|
}
|
|
|
|
// --- Invitation / Scout tests ---
|
|
|
|
func TestCancelInvite(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "invite_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "Invited")
|
|
|
|
if err := repo.CreateInviteWithMail(guildID, char2, leaderID, leaderID, char2, "Invite", "body"); err != nil {
|
|
t.Fatalf("CreateInviteWithMail failed: %v", err)
|
|
}
|
|
|
|
invites, err := repo.ListInvites(guildID)
|
|
if err != nil || len(invites) != 1 {
|
|
t.Fatalf("Expected 1 invite, got %d (err: %v)", len(invites), err)
|
|
}
|
|
|
|
if err := repo.CancelInvite(invites[0].ID); err != nil {
|
|
t.Fatalf("CancelInvite failed: %v", err)
|
|
}
|
|
|
|
has, err := repo.HasInvite(guildID, char2)
|
|
if err != nil {
|
|
t.Fatalf("HasInvite failed: %v", err)
|
|
}
|
|
if has {
|
|
t.Error("Expected no invite after cancellation")
|
|
}
|
|
}
|
|
|
|
func TestListInvites(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "scout_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "Scouted")
|
|
|
|
if err := repo.CreateInviteWithMail(guildID, char2, leaderID, leaderID, char2, "Invite", "body"); err != nil {
|
|
t.Fatalf("CreateInviteWithMail failed: %v", err)
|
|
}
|
|
|
|
invites, err := repo.ListInvites(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListInvites failed: %v", err)
|
|
}
|
|
if len(invites) != 1 {
|
|
t.Fatalf("Expected 1 invite, got %d", len(invites))
|
|
}
|
|
if invites[0].CharID != char2 {
|
|
t.Errorf("Expected char ID %d, got %d", char2, invites[0].CharID)
|
|
}
|
|
if invites[0].Name != "Scouted" {
|
|
t.Errorf("Expected name 'Scouted', got %q", invites[0].Name)
|
|
}
|
|
if invites[0].ActorID != leaderID {
|
|
t.Errorf("Expected actor ID %d, got %d", leaderID, invites[0].ActorID)
|
|
}
|
|
if invites[0].ID == 0 {
|
|
t.Error("Expected non-zero invite ID")
|
|
}
|
|
if invites[0].InvitedAt.IsZero() {
|
|
t.Error("Expected non-zero InvitedAt timestamp")
|
|
}
|
|
}
|
|
|
|
func TestListInvitesEmpty(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
invites, err := repo.ListInvites(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListInvites failed: %v", err)
|
|
}
|
|
if len(invites) != 0 {
|
|
t.Errorf("Expected 0 invites, got %d", len(invites))
|
|
}
|
|
}
|
|
|
|
func TestGetByCharIDWithApplication(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "app_char_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "Applicant2")
|
|
|
|
if err := repo.CreateApplication(guildID, char2, char2, GuildApplicationTypeApplied); err != nil {
|
|
t.Fatalf("CreateApplication failed: %v", err)
|
|
}
|
|
|
|
guild, err := repo.GetByCharID(char2)
|
|
if err != nil {
|
|
t.Fatalf("GetByCharID failed: %v", err)
|
|
}
|
|
if guild == nil {
|
|
t.Fatal("Expected guild via application, got nil")
|
|
}
|
|
if guild.ID != guildID {
|
|
t.Errorf("Expected guild ID %d, got %d", guildID, guild.ID)
|
|
}
|
|
}
|
|
|
|
func TestGetMembersApplicants(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "applicant_member_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "AppMember")
|
|
|
|
if err := repo.CreateApplication(guildID, char2, char2, GuildApplicationTypeApplied); err != nil {
|
|
t.Fatalf("CreateApplication failed: %v", err)
|
|
}
|
|
|
|
applicants, err := repo.GetMembers(guildID, true)
|
|
if err != nil {
|
|
t.Fatalf("GetMembers(applicants=true) failed: %v", err)
|
|
}
|
|
if len(applicants) != 1 {
|
|
t.Fatalf("Expected 1 applicant, got %d", len(applicants))
|
|
}
|
|
if applicants[0].CharID != char2 {
|
|
t.Errorf("Expected applicant char ID %d, got %d", char2, applicants[0].CharID)
|
|
}
|
|
if !applicants[0].IsApplicant {
|
|
t.Error("Expected IsApplicant=true")
|
|
}
|
|
}
|
|
|
|
// --- SetPugiOutfits ---
|
|
|
|
func TestSetPugiOutfits(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.SetPugiOutfits(guildID, 0xFF); err != nil {
|
|
t.Fatalf("SetPugiOutfits failed: %v", err)
|
|
}
|
|
|
|
var outfits uint32
|
|
if err := db.QueryRow("SELECT pugi_outfits FROM guilds WHERE id=$1", guildID).Scan(&outfits); err != nil {
|
|
t.Fatalf("Verification failed: %v", err)
|
|
}
|
|
if outfits != 0xFF {
|
|
t.Errorf("Expected pugi_outfits=0xFF, got %d", outfits)
|
|
}
|
|
}
|
|
|
|
// --- Guild Posts ---
|
|
|
|
func TestCreateAndListPosts(t *testing.T) {
|
|
repo, db, guildID, charID := setupGuildRepo(t)
|
|
_ = db
|
|
|
|
if err := repo.CreatePost(guildID, charID, 1, 0, "Hello", "World", 10); err != nil {
|
|
t.Fatalf("CreatePost failed: %v", err)
|
|
}
|
|
if err := repo.CreatePost(guildID, charID, 2, 0, "Second", "Post", 10); err != nil {
|
|
t.Fatalf("CreatePost 2 failed: %v", err)
|
|
}
|
|
|
|
posts, err := repo.ListPosts(guildID, 0)
|
|
if err != nil {
|
|
t.Fatalf("ListPosts failed: %v", err)
|
|
}
|
|
if len(posts) != 2 {
|
|
t.Fatalf("Expected 2 posts, got %d", len(posts))
|
|
}
|
|
// Newest first
|
|
if posts[0].Title != "Second" {
|
|
t.Errorf("Expected newest first, got %q", posts[0].Title)
|
|
}
|
|
}
|
|
|
|
func TestCreatePostMaxPosts(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
// Create 3 posts with maxPosts=2 — the oldest should be soft-deleted
|
|
for i := 0; i < 3; i++ {
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, fmt.Sprintf("Post%d", i), "body", 2); err != nil {
|
|
t.Fatalf("CreatePost %d failed: %v", i, err)
|
|
}
|
|
}
|
|
|
|
posts, err := repo.ListPosts(guildID, 0)
|
|
if err != nil {
|
|
t.Fatalf("ListPosts failed: %v", err)
|
|
}
|
|
if len(posts) != 2 {
|
|
t.Errorf("Expected 2 posts after max enforcement, got %d", len(posts))
|
|
}
|
|
}
|
|
|
|
func TestDeletePost(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, "ToDelete", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost failed: %v", err)
|
|
}
|
|
posts, _ := repo.ListPosts(guildID, 0)
|
|
if len(posts) == 0 {
|
|
t.Fatal("Expected post to exist")
|
|
}
|
|
|
|
if err := repo.DeletePost(posts[0].ID); err != nil {
|
|
t.Fatalf("DeletePost failed: %v", err)
|
|
}
|
|
|
|
posts, _ = repo.ListPosts(guildID, 0)
|
|
if len(posts) != 0 {
|
|
t.Errorf("Expected 0 posts after delete, got %d", len(posts))
|
|
}
|
|
}
|
|
|
|
func TestUpdatePost(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, "Original", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost failed: %v", err)
|
|
}
|
|
posts, _ := repo.ListPosts(guildID, 0)
|
|
|
|
if err := repo.UpdatePost(posts[0].ID, "Updated", "new body"); err != nil {
|
|
t.Fatalf("UpdatePost failed: %v", err)
|
|
}
|
|
|
|
posts, _ = repo.ListPosts(guildID, 0)
|
|
if posts[0].Title != "Updated" || posts[0].Body != "new body" {
|
|
t.Errorf("Expected 'Updated'/'new body', got %q/%q", posts[0].Title, posts[0].Body)
|
|
}
|
|
}
|
|
|
|
func TestUpdatePostStamp(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, "Stamp", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost failed: %v", err)
|
|
}
|
|
posts, _ := repo.ListPosts(guildID, 0)
|
|
|
|
if err := repo.UpdatePostStamp(posts[0].ID, 42); err != nil {
|
|
t.Fatalf("UpdatePostStamp failed: %v", err)
|
|
}
|
|
|
|
posts, _ = repo.ListPosts(guildID, 0)
|
|
if posts[0].StampID != 42 {
|
|
t.Errorf("Expected stamp_id=42, got %d", posts[0].StampID)
|
|
}
|
|
}
|
|
|
|
func TestPostLikedBy(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, "Like", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost failed: %v", err)
|
|
}
|
|
posts, _ := repo.ListPosts(guildID, 0)
|
|
|
|
if err := repo.SetPostLikedBy(posts[0].ID, "100,200"); err != nil {
|
|
t.Fatalf("SetPostLikedBy failed: %v", err)
|
|
}
|
|
|
|
liked, err := repo.GetPostLikedBy(posts[0].ID)
|
|
if err != nil {
|
|
t.Fatalf("GetPostLikedBy failed: %v", err)
|
|
}
|
|
if liked != "100,200" {
|
|
t.Errorf("Expected '100,200', got %q", liked)
|
|
}
|
|
}
|
|
|
|
func TestCountNewPosts(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
since := time.Now().Add(-1 * time.Hour)
|
|
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, "New", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost failed: %v", err)
|
|
}
|
|
|
|
count, err := repo.CountNewPosts(guildID, since)
|
|
if err != nil {
|
|
t.Fatalf("CountNewPosts failed: %v", err)
|
|
}
|
|
if count != 1 {
|
|
t.Errorf("Expected 1 new post, got %d", count)
|
|
}
|
|
|
|
// Future time should yield 0
|
|
count, err = repo.CountNewPosts(guildID, time.Now().Add(1*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("CountNewPosts (future) failed: %v", err)
|
|
}
|
|
if count != 0 {
|
|
t.Errorf("Expected 0 new posts with future time, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestListPostsByType(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreatePost(guildID, charID, 0, 0, "TypeA", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost type 0 failed: %v", err)
|
|
}
|
|
if err := repo.CreatePost(guildID, charID, 0, 1, "TypeB", "body", 10); err != nil {
|
|
t.Fatalf("CreatePost type 1 failed: %v", err)
|
|
}
|
|
|
|
posts0, _ := repo.ListPosts(guildID, 0)
|
|
posts1, _ := repo.ListPosts(guildID, 1)
|
|
if len(posts0) != 1 {
|
|
t.Errorf("Expected 1 type-0 post, got %d", len(posts0))
|
|
}
|
|
if len(posts1) != 1 {
|
|
t.Errorf("Expected 1 type-1 post, got %d", len(posts1))
|
|
}
|
|
}
|
|
|
|
// --- Guild Alliances ---
|
|
|
|
func TestCreateAndGetAlliance(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAlliance("TestAlliance", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
|
|
var allianceID uint32
|
|
if err := db.QueryRow("SELECT id FROM guild_alliances WHERE parent_id=$1", guildID).Scan(&allianceID); err != nil {
|
|
t.Fatalf("Alliance not found in DB: %v", err)
|
|
}
|
|
|
|
alliance, err := repo.GetAllianceByID(allianceID)
|
|
if err != nil {
|
|
t.Fatalf("GetAllianceByID failed: %v", err)
|
|
}
|
|
if alliance == nil {
|
|
t.Fatal("Expected alliance, got nil")
|
|
}
|
|
if alliance.Name != "TestAlliance" {
|
|
t.Errorf("Expected name 'TestAlliance', got %q", alliance.Name)
|
|
}
|
|
if alliance.ParentGuildID != guildID {
|
|
t.Errorf("Expected parent guild %d, got %d", guildID, alliance.ParentGuildID)
|
|
}
|
|
if alliance.ParentGuild.ID != guildID {
|
|
t.Errorf("Expected populated ParentGuild.ID=%d, got %d", guildID, alliance.ParentGuild.ID)
|
|
}
|
|
}
|
|
|
|
func TestGetAllianceByIDNotFound(t *testing.T) {
|
|
repo, _, _, _ := setupGuildRepo(t)
|
|
|
|
alliance, err := repo.GetAllianceByID(999999)
|
|
if err != nil {
|
|
t.Fatalf("GetAllianceByID failed: %v", err)
|
|
}
|
|
if alliance != nil {
|
|
t.Errorf("Expected nil for non-existent alliance, got: %+v", alliance)
|
|
}
|
|
}
|
|
|
|
func TestListAlliances(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAlliance("Alliance1", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
|
|
// Create a second guild and alliance
|
|
user2 := CreateTestUser(t, db, "alliance_user2")
|
|
char2 := CreateTestCharacter(t, db, user2, "AlliLeader2")
|
|
guild2 := CreateTestGuild(t, db, char2, "AlliGuild2")
|
|
if err := repo.CreateAlliance("Alliance2", guild2); err != nil {
|
|
t.Fatalf("CreateAlliance 2 failed: %v", err)
|
|
}
|
|
|
|
alliances, err := repo.ListAlliances()
|
|
if err != nil {
|
|
t.Fatalf("ListAlliances failed: %v", err)
|
|
}
|
|
if len(alliances) < 2 {
|
|
t.Errorf("Expected at least 2 alliances, got %d", len(alliances))
|
|
}
|
|
}
|
|
|
|
func TestDeleteAlliance(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAlliance("ToDelete", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
|
|
var allianceID uint32
|
|
if err := db.QueryRow("SELECT id FROM guild_alliances WHERE parent_id=$1", guildID).Scan(&allianceID); err != nil {
|
|
t.Fatalf("Alliance not found: %v", err)
|
|
}
|
|
|
|
if err := repo.DeleteAlliance(allianceID); err != nil {
|
|
t.Fatalf("DeleteAlliance failed: %v", err)
|
|
}
|
|
|
|
alliance, err := repo.GetAllianceByID(allianceID)
|
|
if err != nil {
|
|
t.Fatalf("GetAllianceByID after delete failed: %v", err)
|
|
}
|
|
if alliance != nil {
|
|
t.Errorf("Expected nil after delete, got: %+v", alliance)
|
|
}
|
|
}
|
|
|
|
func TestRemoveGuildFromAllianceSub1(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "alli_sub1_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "Sub1Leader")
|
|
guild2 := CreateTestGuild(t, db, char2, "SubGuild1")
|
|
|
|
if err := repo.CreateAlliance("AlliSub", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
var allianceID uint32
|
|
if err := db.QueryRow("SELECT id FROM guild_alliances WHERE parent_id=$1", guildID).Scan(&allianceID); err != nil {
|
|
t.Fatalf("Failed to get alliance ID: %v", err)
|
|
}
|
|
|
|
// Add sub1
|
|
if _, err := db.Exec("UPDATE guild_alliances SET sub1_id=$1 WHERE id=$2", guild2, allianceID); err != nil {
|
|
t.Fatalf("Failed to set sub1: %v", err)
|
|
}
|
|
|
|
// Remove sub1
|
|
if err := repo.RemoveGuildFromAlliance(allianceID, guild2, guild2, 0); err != nil {
|
|
t.Fatalf("RemoveGuildFromAlliance failed: %v", err)
|
|
}
|
|
|
|
alliance, err := repo.GetAllianceByID(allianceID)
|
|
if err != nil {
|
|
t.Fatalf("GetAllianceByID failed: %v", err)
|
|
}
|
|
if alliance == nil {
|
|
t.Fatal("Expected alliance to still exist")
|
|
}
|
|
if alliance.SubGuild1ID != 0 {
|
|
t.Errorf("Expected sub1_id=0, got %d", alliance.SubGuild1ID)
|
|
}
|
|
}
|
|
|
|
func TestRemoveGuildFromAllianceSub1ShiftsSub2(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "alli_shift_user2")
|
|
char2 := CreateTestCharacter(t, db, user2, "Shift2Leader")
|
|
guild2 := CreateTestGuild(t, db, char2, "ShiftGuild2")
|
|
|
|
user3 := CreateTestUser(t, db, "alli_shift_user3")
|
|
char3 := CreateTestCharacter(t, db, user3, "Shift3Leader")
|
|
guild3 := CreateTestGuild(t, db, char3, "ShiftGuild3")
|
|
|
|
if err := repo.CreateAlliance("AlliShift", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
var allianceID uint32
|
|
if err := db.QueryRow("SELECT id FROM guild_alliances WHERE parent_id=$1", guildID).Scan(&allianceID); err != nil {
|
|
t.Fatalf("Failed to get alliance ID: %v", err)
|
|
}
|
|
if _, err := db.Exec("UPDATE guild_alliances SET sub1_id=$1, sub2_id=$2 WHERE id=$3", guild2, guild3, allianceID); err != nil {
|
|
t.Fatalf("Failed to set sub guilds: %v", err)
|
|
}
|
|
|
|
// Remove sub1 — sub2 should shift into sub1's slot
|
|
if err := repo.RemoveGuildFromAlliance(allianceID, guild2, guild2, guild3); err != nil {
|
|
t.Fatalf("RemoveGuildFromAlliance failed: %v", err)
|
|
}
|
|
|
|
alliance, err := repo.GetAllianceByID(allianceID)
|
|
if err != nil {
|
|
t.Fatalf("GetAllianceByID failed: %v", err)
|
|
}
|
|
if alliance == nil {
|
|
t.Fatal("Expected alliance to still exist")
|
|
}
|
|
if alliance.SubGuild1ID != guild3 {
|
|
t.Errorf("Expected sub1_id=%d (shifted from sub2), got %d", guild3, alliance.SubGuild1ID)
|
|
}
|
|
if alliance.SubGuild2ID != 0 {
|
|
t.Errorf("Expected sub2_id=0, got %d", alliance.SubGuild2ID)
|
|
}
|
|
}
|
|
|
|
func TestRemoveGuildFromAllianceSub2(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "alli_s2_user2")
|
|
char2 := CreateTestCharacter(t, db, user2, "S2Leader2")
|
|
guild2 := CreateTestGuild(t, db, char2, "S2Guild2")
|
|
|
|
user3 := CreateTestUser(t, db, "alli_s2_user3")
|
|
char3 := CreateTestCharacter(t, db, user3, "S2Leader3")
|
|
guild3 := CreateTestGuild(t, db, char3, "S2Guild3")
|
|
|
|
if err := repo.CreateAlliance("AlliS2", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
var allianceID uint32
|
|
if err := db.QueryRow("SELECT id FROM guild_alliances WHERE parent_id=$1", guildID).Scan(&allianceID); err != nil {
|
|
t.Fatalf("Failed to get alliance ID: %v", err)
|
|
}
|
|
if _, err := db.Exec("UPDATE guild_alliances SET sub1_id=$1, sub2_id=$2 WHERE id=$3", guild2, guild3, allianceID); err != nil {
|
|
t.Fatalf("Failed to set sub guilds: %v", err)
|
|
}
|
|
|
|
// Remove sub2 directly
|
|
if err := repo.RemoveGuildFromAlliance(allianceID, guild3, guild2, guild3); err != nil {
|
|
t.Fatalf("RemoveGuildFromAlliance failed: %v", err)
|
|
}
|
|
|
|
alliance, err := repo.GetAllianceByID(allianceID)
|
|
if err != nil {
|
|
t.Fatalf("GetAllianceByID failed: %v", err)
|
|
}
|
|
if alliance == nil {
|
|
t.Fatal("Expected alliance to still exist")
|
|
}
|
|
if alliance.SubGuild1ID != guild2 {
|
|
t.Errorf("Expected sub1_id=%d unchanged, got %d", guild2, alliance.SubGuild1ID)
|
|
}
|
|
if alliance.SubGuild2ID != 0 {
|
|
t.Errorf("Expected sub2_id=0, got %d", alliance.SubGuild2ID)
|
|
}
|
|
}
|
|
|
|
// --- Guild Adventures ---
|
|
|
|
func TestCreateAndListAdventures(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAdventure(guildID, 5, 1000, 2000); err != nil {
|
|
t.Fatalf("CreateAdventure failed: %v", err)
|
|
}
|
|
|
|
adventures, err := repo.ListAdventures(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListAdventures failed: %v", err)
|
|
}
|
|
if len(adventures) != 1 {
|
|
t.Fatalf("Expected 1 adventure, got %d", len(adventures))
|
|
}
|
|
if adventures[0].Destination != 5 {
|
|
t.Errorf("Expected destination=5, got %d", adventures[0].Destination)
|
|
}
|
|
if adventures[0].Depart != 1000 {
|
|
t.Errorf("Expected depart=1000, got %d", adventures[0].Depart)
|
|
}
|
|
if adventures[0].Return != 2000 {
|
|
t.Errorf("Expected return=2000, got %d", adventures[0].Return)
|
|
}
|
|
}
|
|
|
|
func TestCreateAdventureWithCharge(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAdventureWithCharge(guildID, 3, 50, 1000, 2000); err != nil {
|
|
t.Fatalf("CreateAdventureWithCharge failed: %v", err)
|
|
}
|
|
|
|
adventures, err := repo.ListAdventures(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListAdventures failed: %v", err)
|
|
}
|
|
if len(adventures) != 1 {
|
|
t.Fatalf("Expected 1 adventure, got %d", len(adventures))
|
|
}
|
|
if adventures[0].Charge != 50 {
|
|
t.Errorf("Expected charge=50, got %d", adventures[0].Charge)
|
|
}
|
|
}
|
|
|
|
func TestChargeAdventure(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAdventure(guildID, 1, 1000, 2000); err != nil {
|
|
t.Fatalf("CreateAdventure failed: %v", err)
|
|
}
|
|
adventures, _ := repo.ListAdventures(guildID)
|
|
advID := adventures[0].ID
|
|
|
|
if err := repo.ChargeAdventure(advID, 25); err != nil {
|
|
t.Fatalf("ChargeAdventure failed: %v", err)
|
|
}
|
|
|
|
var charge uint32
|
|
if err := db.QueryRow("SELECT charge FROM guild_adventures WHERE id=$1", advID).Scan(&charge); err != nil {
|
|
t.Fatalf("Failed to get charge: %v", err)
|
|
}
|
|
if charge != 25 {
|
|
t.Errorf("Expected charge=25, got %d", charge)
|
|
}
|
|
}
|
|
|
|
func TestCollectAdventure(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateAdventure(guildID, 1, 1000, 2000); err != nil {
|
|
t.Fatalf("CreateAdventure failed: %v", err)
|
|
}
|
|
adventures, _ := repo.ListAdventures(guildID)
|
|
advID := adventures[0].ID
|
|
|
|
if err := repo.CollectAdventure(advID, charID); err != nil {
|
|
t.Fatalf("CollectAdventure failed: %v", err)
|
|
}
|
|
|
|
// Verify collected_by updated
|
|
adventures, _ = repo.ListAdventures(guildID)
|
|
if adventures[0].CollectedBy == "" {
|
|
t.Error("Expected collected_by to be non-empty")
|
|
}
|
|
}
|
|
|
|
func TestListAdventuresEmpty(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
adventures, err := repo.ListAdventures(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListAdventures failed: %v", err)
|
|
}
|
|
if len(adventures) != 0 {
|
|
t.Errorf("Expected 0 adventures, got %d", len(adventures))
|
|
}
|
|
}
|
|
|
|
// --- Guild Treasure Hunts ---
|
|
|
|
func TestCreateAndGetPendingHunt(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
huntData := []byte{0xAA, 0xBB, 0xCC}
|
|
if err := repo.CreateHunt(guildID, charID, 10, 1, huntData, ""); err != nil {
|
|
t.Fatalf("CreateHunt failed: %v", err)
|
|
}
|
|
|
|
hunt, err := repo.GetPendingHunt(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetPendingHunt failed: %v", err)
|
|
}
|
|
if hunt == nil {
|
|
t.Fatal("Expected pending hunt, got nil")
|
|
}
|
|
if hunt.HostID != charID {
|
|
t.Errorf("Expected host_id=%d, got %d", charID, hunt.HostID)
|
|
}
|
|
if hunt.Destination != 10 {
|
|
t.Errorf("Expected destination=10, got %d", hunt.Destination)
|
|
}
|
|
if hunt.Level != 1 {
|
|
t.Errorf("Expected level=1, got %d", hunt.Level)
|
|
}
|
|
if len(hunt.HuntData) != 3 || hunt.HuntData[0] != 0xAA {
|
|
t.Errorf("Expected hunt_data [AA BB CC], got %x", hunt.HuntData)
|
|
}
|
|
}
|
|
|
|
func TestGetPendingHuntNone(t *testing.T) {
|
|
repo, _, _, charID := setupGuildRepo(t)
|
|
|
|
hunt, err := repo.GetPendingHunt(charID)
|
|
if err != nil {
|
|
t.Fatalf("GetPendingHunt failed: %v", err)
|
|
}
|
|
if hunt != nil {
|
|
t.Errorf("Expected nil when no pending hunt, got: %+v", hunt)
|
|
}
|
|
}
|
|
|
|
func TestAcquireHunt(t *testing.T) {
|
|
repo, db, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateHunt(guildID, charID, 10, 2, nil, ""); err != nil {
|
|
t.Fatalf("CreateHunt failed: %v", err)
|
|
}
|
|
hunt, _ := repo.GetPendingHunt(charID)
|
|
|
|
if err := repo.AcquireHunt(hunt.HuntID); err != nil {
|
|
t.Fatalf("AcquireHunt failed: %v", err)
|
|
}
|
|
|
|
// After acquiring, it should no longer appear as pending
|
|
pending, _ := repo.GetPendingHunt(charID)
|
|
if pending != nil {
|
|
t.Error("Expected no pending hunt after acquire")
|
|
}
|
|
|
|
// Verify in DB
|
|
var acquired bool
|
|
if err := db.QueryRow("SELECT acquired FROM guild_hunts WHERE id=$1", hunt.HuntID).Scan(&acquired); err != nil {
|
|
t.Fatalf("Failed to get acquired: %v", err)
|
|
}
|
|
if !acquired {
|
|
t.Error("Expected acquired=true in DB")
|
|
}
|
|
}
|
|
|
|
func TestListGuildHunts(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
// Create a level-2 hunt and acquire it
|
|
if err := repo.CreateHunt(guildID, charID, 10, 2, []byte{0x01}, ""); err != nil {
|
|
t.Fatalf("CreateHunt failed: %v", err)
|
|
}
|
|
hunt, _ := repo.GetPendingHunt(charID)
|
|
if err := repo.AcquireHunt(hunt.HuntID); err != nil {
|
|
t.Fatalf("AcquireHunt failed: %v", err)
|
|
}
|
|
|
|
// Create a level-1 hunt (should not appear)
|
|
if err := repo.CreateHunt(guildID, charID, 20, 1, nil, ""); err != nil {
|
|
t.Fatalf("CreateHunt level-1 failed: %v", err)
|
|
}
|
|
|
|
hunts, err := repo.ListGuildHunts(guildID, charID)
|
|
if err != nil {
|
|
t.Fatalf("ListGuildHunts failed: %v", err)
|
|
}
|
|
if len(hunts) != 1 {
|
|
t.Fatalf("Expected 1 acquired level-2 hunt, got %d", len(hunts))
|
|
}
|
|
if hunts[0].Destination != 10 {
|
|
t.Errorf("Expected destination=10, got %d", hunts[0].Destination)
|
|
}
|
|
}
|
|
|
|
func TestRegisterHuntReport(t *testing.T) {
|
|
repo, db, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateHunt(guildID, charID, 10, 2, nil, ""); err != nil {
|
|
t.Fatalf("CreateHunt failed: %v", err)
|
|
}
|
|
hunt, _ := repo.GetPendingHunt(charID)
|
|
|
|
if err := repo.RegisterHuntReport(hunt.HuntID, charID); err != nil {
|
|
t.Fatalf("RegisterHuntReport failed: %v", err)
|
|
}
|
|
|
|
var treasureHunt *uint32
|
|
if err := db.QueryRow("SELECT treasure_hunt FROM guild_characters WHERE character_id=$1", charID).Scan(&treasureHunt); err != nil {
|
|
t.Fatalf("Failed to get treasure_hunt: %v", err)
|
|
}
|
|
if treasureHunt == nil || *treasureHunt != hunt.HuntID {
|
|
t.Errorf("Expected treasure_hunt=%d, got %v", hunt.HuntID, treasureHunt)
|
|
}
|
|
}
|
|
|
|
func TestCollectHunt(t *testing.T) {
|
|
repo, db, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateHunt(guildID, charID, 10, 2, nil, ""); err != nil {
|
|
t.Fatalf("CreateHunt failed: %v", err)
|
|
}
|
|
hunt, _ := repo.GetPendingHunt(charID)
|
|
if err := repo.RegisterHuntReport(hunt.HuntID, charID); err != nil {
|
|
t.Fatalf("RegisterHuntReport failed: %v", err)
|
|
}
|
|
|
|
if err := repo.CollectHunt(hunt.HuntID); err != nil {
|
|
t.Fatalf("CollectHunt failed: %v", err)
|
|
}
|
|
|
|
// Hunt should be marked collected
|
|
var collected bool
|
|
if err := db.QueryRow("SELECT collected FROM guild_hunts WHERE id=$1", hunt.HuntID).Scan(&collected); err != nil {
|
|
t.Fatalf("Failed to scan collected: %v", err)
|
|
}
|
|
if !collected {
|
|
t.Error("Expected collected=true")
|
|
}
|
|
|
|
// Character's treasure_hunt should be cleared
|
|
var treasureHunt *uint32
|
|
if err := db.QueryRow("SELECT treasure_hunt FROM guild_characters WHERE character_id=$1", charID).Scan(&treasureHunt); err != nil {
|
|
t.Fatalf("Failed to get treasure_hunt: %v", err)
|
|
}
|
|
if treasureHunt != nil {
|
|
t.Errorf("Expected treasure_hunt=NULL, got %v", *treasureHunt)
|
|
}
|
|
}
|
|
|
|
func TestClaimHuntReward(t *testing.T) {
|
|
repo, db, guildID, charID := setupGuildRepo(t)
|
|
|
|
if err := repo.CreateHunt(guildID, charID, 10, 2, nil, ""); err != nil {
|
|
t.Fatalf("CreateHunt failed: %v", err)
|
|
}
|
|
hunt, _ := repo.GetPendingHunt(charID)
|
|
|
|
if err := repo.ClaimHuntReward(hunt.HuntID, charID); err != nil {
|
|
t.Fatalf("ClaimHuntReward failed: %v", err)
|
|
}
|
|
|
|
var count int
|
|
if err := db.QueryRow("SELECT COUNT(*) FROM guild_hunts_claimed WHERE hunt_id=$1 AND character_id=$2", hunt.HuntID, charID).Scan(&count); err != nil {
|
|
t.Fatalf("Failed to scan claimed count: %v", err)
|
|
}
|
|
if count != 1 {
|
|
t.Errorf("Expected 1 claimed entry, got %d", count)
|
|
}
|
|
}
|
|
|
|
// --- Guild Meals ---
|
|
|
|
func TestCreateAndListMeals(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
now := time.Now().UTC().Truncate(time.Second)
|
|
id, err := repo.CreateMeal(guildID, 5, 3, now)
|
|
if err != nil {
|
|
t.Fatalf("CreateMeal failed: %v", err)
|
|
}
|
|
if id == 0 {
|
|
t.Error("Expected non-zero meal ID")
|
|
}
|
|
|
|
meals, err := repo.ListMeals(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListMeals failed: %v", err)
|
|
}
|
|
if len(meals) != 1 {
|
|
t.Fatalf("Expected 1 meal, got %d", len(meals))
|
|
}
|
|
if meals[0].MealID != 5 {
|
|
t.Errorf("Expected meal_id=5, got %d", meals[0].MealID)
|
|
}
|
|
if meals[0].Level != 3 {
|
|
t.Errorf("Expected level=3, got %d", meals[0].Level)
|
|
}
|
|
}
|
|
|
|
func TestUpdateMeal(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
now := time.Now().UTC().Truncate(time.Second)
|
|
id, _ := repo.CreateMeal(guildID, 5, 3, now)
|
|
|
|
later := now.Add(30 * time.Minute)
|
|
if err := repo.UpdateMeal(id, 10, 5, later); err != nil {
|
|
t.Fatalf("UpdateMeal failed: %v", err)
|
|
}
|
|
|
|
meals, _ := repo.ListMeals(guildID)
|
|
if meals[0].MealID != 10 {
|
|
t.Errorf("Expected meal_id=10, got %d", meals[0].MealID)
|
|
}
|
|
if meals[0].Level != 5 {
|
|
t.Errorf("Expected level=5, got %d", meals[0].Level)
|
|
}
|
|
}
|
|
|
|
func TestListMealsEmpty(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
meals, err := repo.ListMeals(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListMeals failed: %v", err)
|
|
}
|
|
if len(meals) != 0 {
|
|
t.Errorf("Expected 0 meals, got %d", len(meals))
|
|
}
|
|
}
|
|
|
|
// --- Kill tracking ---
|
|
|
|
func TestClaimHuntBox(t *testing.T) {
|
|
repo, db, _, charID := setupGuildRepo(t)
|
|
|
|
claimedAt := time.Now().UTC().Truncate(time.Second)
|
|
if err := repo.ClaimHuntBox(charID, claimedAt); err != nil {
|
|
t.Fatalf("ClaimHuntBox failed: %v", err)
|
|
}
|
|
|
|
var got time.Time
|
|
if err := db.QueryRow("SELECT box_claimed FROM guild_characters WHERE character_id=$1", charID).Scan(&got); err != nil {
|
|
t.Fatalf("Failed to scan box_claimed: %v", err)
|
|
}
|
|
if !got.Equal(claimedAt) {
|
|
t.Errorf("Expected box_claimed=%v, got %v", claimedAt, got)
|
|
}
|
|
}
|
|
|
|
func TestListAndCountGuildKills(t *testing.T) {
|
|
repo, db, guildID, charID := setupGuildRepo(t)
|
|
|
|
// Set box_claimed to the past so kills after it are visible
|
|
past := time.Now().Add(-1 * time.Hour).UTC().Truncate(time.Second)
|
|
if err := repo.ClaimHuntBox(charID, past); err != nil {
|
|
t.Fatalf("ClaimHuntBox failed: %v", err)
|
|
}
|
|
|
|
// Insert kill logs for this character
|
|
if _, err := db.Exec("INSERT INTO kill_logs (character_id, monster, quantity, timestamp) VALUES ($1, 100, 1, NOW())", charID); err != nil {
|
|
t.Fatalf("Failed to insert kill log: %v", err)
|
|
}
|
|
if _, err := db.Exec("INSERT INTO kill_logs (character_id, monster, quantity, timestamp) VALUES ($1, 200, 1, NOW())", charID); err != nil {
|
|
t.Fatalf("Failed to insert kill log: %v", err)
|
|
}
|
|
|
|
kills, err := repo.ListGuildKills(guildID, charID)
|
|
if err != nil {
|
|
t.Fatalf("ListGuildKills failed: %v", err)
|
|
}
|
|
if len(kills) != 2 {
|
|
t.Fatalf("Expected 2 kills, got %d", len(kills))
|
|
}
|
|
|
|
count, err := repo.CountGuildKills(guildID, charID)
|
|
if err != nil {
|
|
t.Fatalf("CountGuildKills failed: %v", err)
|
|
}
|
|
if count != 2 {
|
|
t.Errorf("Expected count=2, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestListGuildKillsEmpty(t *testing.T) {
|
|
repo, _, guildID, charID := setupGuildRepo(t)
|
|
|
|
// Set box_claimed to now — no kills after it
|
|
if err := repo.ClaimHuntBox(charID, time.Now().UTC()); err != nil {
|
|
t.Fatalf("ClaimHuntBox failed: %v", err)
|
|
}
|
|
|
|
kills, err := repo.ListGuildKills(guildID, charID)
|
|
if err != nil {
|
|
t.Fatalf("ListGuildKills failed: %v", err)
|
|
}
|
|
if len(kills) != 0 {
|
|
t.Errorf("Expected 0 kills, got %d", len(kills))
|
|
}
|
|
|
|
count, err := repo.CountGuildKills(guildID, charID)
|
|
if err != nil {
|
|
t.Fatalf("CountGuildKills failed: %v", err)
|
|
}
|
|
if count != 0 {
|
|
t.Errorf("Expected count=0, got %d", count)
|
|
}
|
|
}
|
|
|
|
// --- Disband with alliance cleanup ---
|
|
|
|
func TestDisbandCleansUpAlliance(t *testing.T) {
|
|
repo, db, guildID, _ := setupGuildRepo(t)
|
|
|
|
// Create alliance with this guild as parent
|
|
if err := repo.CreateAlliance("DisbandAlliance", guildID); err != nil {
|
|
t.Fatalf("CreateAlliance failed: %v", err)
|
|
}
|
|
|
|
var allianceID uint32
|
|
if err := db.QueryRow("SELECT id FROM guild_alliances WHERE parent_id=$1", guildID).Scan(&allianceID); err != nil {
|
|
t.Fatalf("Failed to scan alliance ID: %v", err)
|
|
}
|
|
|
|
if err := repo.Disband(guildID); err != nil {
|
|
t.Fatalf("Disband failed: %v", err)
|
|
}
|
|
|
|
// Alliance should be deleted too (parent_id match in Disband)
|
|
alliance, _ := repo.GetAllianceByID(allianceID)
|
|
if alliance != nil {
|
|
t.Errorf("Expected alliance to be deleted after parent guild disband, got: %+v", alliance)
|
|
}
|
|
}
|
|
|
|
// --- CreateInviteWithMail ---
|
|
|
|
func TestCreateInviteWithMail(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "scout_mail_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "ScoutTarget")
|
|
|
|
err := repo.CreateInviteWithMail(guildID, char2, leaderID, leaderID, char2, "Guild Invite", "You have been invited!")
|
|
if err != nil {
|
|
t.Fatalf("CreateInviteWithMail failed: %v", err)
|
|
}
|
|
|
|
// Verify invite was created
|
|
has, err := repo.HasInvite(guildID, char2)
|
|
if err != nil {
|
|
t.Fatalf("HasInvite failed: %v", err)
|
|
}
|
|
if !has {
|
|
t.Error("Expected invite to exist after CreateInviteWithMail")
|
|
}
|
|
|
|
// Verify mail was sent
|
|
var mailCount int
|
|
if err := db.QueryRow(
|
|
"SELECT COUNT(*) FROM mail WHERE sender_id=$1 AND recipient_id=$2 AND subject=$3",
|
|
leaderID, char2, "Guild Invite").Scan(&mailCount); err != nil {
|
|
t.Fatalf("Mail verification query failed: %v", err)
|
|
}
|
|
if mailCount != 1 {
|
|
t.Errorf("Expected 1 mail row, got %d", mailCount)
|
|
}
|
|
}
|