mirror of
https://github.com/Mezeporta/Erupe.git
synced 2026-03-21 23:22:34 +01:00
An MMO server without multiplayer defeats the purpose. PostgreSQL is the right choice and Docker Compose already solves the setup pain. This reverts the common/db wrapper, SQLite schema, config Driver field, modernc.org/sqlite dependency, and all repo type changes while keeping the dashboard, wizard, and CI improvements from the previous commit.
1524 lines
43 KiB
Go
1524 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 TestCancelInvitation(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "invite_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "Invited")
|
|
|
|
if err := repo.CreateApplication(guildID, char2, leaderID, GuildApplicationTypeInvited); err != nil {
|
|
t.Fatalf("CreateApplication (invited) failed: %v", err)
|
|
}
|
|
|
|
if err := repo.CancelInvitation(guildID, char2); err != nil {
|
|
t.Fatalf("CancelInvitation failed: %v", err)
|
|
}
|
|
|
|
has, err := repo.HasApplication(guildID, char2)
|
|
if err != nil {
|
|
t.Fatalf("HasApplication failed: %v", err)
|
|
}
|
|
if has {
|
|
t.Error("Expected no application after cancellation")
|
|
}
|
|
}
|
|
|
|
func TestListInvitedCharacters(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "scout_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "Scouted")
|
|
|
|
if err := repo.CreateApplication(guildID, char2, leaderID, GuildApplicationTypeInvited); err != nil {
|
|
t.Fatalf("CreateApplication failed: %v", err)
|
|
}
|
|
|
|
chars, err := repo.ListInvitedCharacters(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListInvitedCharacters failed: %v", err)
|
|
}
|
|
if len(chars) != 1 {
|
|
t.Fatalf("Expected 1 invited character, got %d", len(chars))
|
|
}
|
|
if chars[0].CharID != char2 {
|
|
t.Errorf("Expected char ID %d, got %d", char2, chars[0].CharID)
|
|
}
|
|
if chars[0].Name != "Scouted" {
|
|
t.Errorf("Expected name 'Scouted', got %q", chars[0].Name)
|
|
}
|
|
if chars[0].ActorID != leaderID {
|
|
t.Errorf("Expected actor ID %d, got %d", leaderID, chars[0].ActorID)
|
|
}
|
|
}
|
|
|
|
func TestListInvitedCharactersEmpty(t *testing.T) {
|
|
repo, _, guildID, _ := setupGuildRepo(t)
|
|
|
|
chars, err := repo.ListInvitedCharacters(guildID)
|
|
if err != nil {
|
|
t.Fatalf("ListInvitedCharacters failed: %v", err)
|
|
}
|
|
if len(chars) != 0 {
|
|
t.Errorf("Expected 0 invited characters, got %d", len(chars))
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
|
|
// --- CreateApplicationWithMail ---
|
|
|
|
func TestCreateApplicationWithMail(t *testing.T) {
|
|
repo, db, guildID, leaderID := setupGuildRepo(t)
|
|
|
|
user2 := CreateTestUser(t, db, "scout_mail_user")
|
|
char2 := CreateTestCharacter(t, db, user2, "ScoutTarget")
|
|
|
|
err := repo.CreateApplicationWithMail(
|
|
guildID, char2, leaderID, GuildApplicationTypeInvited,
|
|
leaderID, char2, "Guild Invite", "You have been invited!")
|
|
if err != nil {
|
|
t.Fatalf("CreateApplicationWithMail failed: %v", err)
|
|
}
|
|
|
|
// Verify application was created
|
|
has, err := repo.HasApplication(guildID, char2)
|
|
if err != nil {
|
|
t.Fatalf("HasApplication failed: %v", err)
|
|
}
|
|
if !has {
|
|
t.Error("Expected application to exist after CreateApplicationWithMail")
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
}
|