427 lines
11 KiB
Go
427 lines
11 KiB
Go
package guilds
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"zombiezen.com/go/sqlite"
|
|
"zombiezen.com/go/sqlite/sqlitex"
|
|
)
|
|
|
|
// createTestPool creates a temporary test database pool
|
|
func createTestPool(t *testing.T) *sqlitex.Pool {
|
|
// Create temporary directory for test database
|
|
tempDir := t.TempDir()
|
|
dbPath := filepath.Join(tempDir, "test_guilds.db")
|
|
|
|
// Create and initialize database pool
|
|
pool, err := sqlitex.NewPool(dbPath, sqlitex.PoolOptions{
|
|
Flags: sqlite.OpenReadWrite | sqlite.OpenCreate,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Failed to create test database pool: %v", err)
|
|
}
|
|
|
|
// Create guild tables for testing
|
|
conn, err := pool.Take(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("Failed to get connection: %v", err)
|
|
}
|
|
defer pool.Put(conn)
|
|
|
|
err = createGuildTables(conn)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create guild tables: %v", err)
|
|
}
|
|
|
|
return pool
|
|
}
|
|
|
|
// createGuildTables creates the necessary tables for guild testing
|
|
func createGuildTables(conn *sqlite.Conn) error {
|
|
tables := []string{
|
|
`CREATE TABLE IF NOT EXISTS guilds (
|
|
id INTEGER PRIMARY KEY,
|
|
name TEXT NOT NULL,
|
|
motd TEXT,
|
|
level INTEGER DEFAULT 1,
|
|
xp INTEGER DEFAULT 111,
|
|
xp_needed INTEGER DEFAULT 2521,
|
|
formed_on INTEGER DEFAULT 0
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_members (
|
|
char_id INTEGER PRIMARY KEY,
|
|
guild_id INTEGER NOT NULL,
|
|
account_id INTEGER NOT NULL,
|
|
recruiter_id INTEGER DEFAULT 0,
|
|
name TEXT NOT NULL,
|
|
guild_status INTEGER DEFAULT 0,
|
|
points REAL DEFAULT 0.0,
|
|
adventure_class INTEGER DEFAULT 0,
|
|
adventure_level INTEGER DEFAULT 1,
|
|
tradeskill_class INTEGER DEFAULT 0,
|
|
tradeskill_level INTEGER DEFAULT 1,
|
|
rank INTEGER DEFAULT 7,
|
|
member_flags INTEGER DEFAULT 0,
|
|
zone TEXT DEFAULT '',
|
|
join_date INTEGER DEFAULT 0,
|
|
last_login_date INTEGER DEFAULT 0,
|
|
note TEXT DEFAULT '',
|
|
officer_note TEXT DEFAULT '',
|
|
recruiter_description TEXT DEFAULT '',
|
|
recruiter_picture_data BLOB,
|
|
recruiting_show_adventure_class INTEGER DEFAULT 0,
|
|
FOREIGN KEY (guild_id) REFERENCES guilds(id)
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_events (
|
|
event_id INTEGER NOT NULL,
|
|
guild_id INTEGER NOT NULL,
|
|
date INTEGER NOT NULL,
|
|
type INTEGER NOT NULL,
|
|
description TEXT NOT NULL,
|
|
locked INTEGER DEFAULT 0,
|
|
PRIMARY KEY (event_id, guild_id),
|
|
FOREIGN KEY (guild_id) REFERENCES guilds(id)
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_ranks (
|
|
guild_id INTEGER NOT NULL,
|
|
rank INTEGER NOT NULL,
|
|
name TEXT NOT NULL,
|
|
PRIMARY KEY (guild_id, rank),
|
|
FOREIGN KEY (guild_id) REFERENCES guilds(id)
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_permissions (
|
|
guild_id INTEGER NOT NULL,
|
|
rank INTEGER NOT NULL,
|
|
permission INTEGER NOT NULL,
|
|
value INTEGER NOT NULL,
|
|
PRIMARY KEY (guild_id, rank, permission),
|
|
FOREIGN KEY (guild_id) REFERENCES guilds(id)
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_event_filters (
|
|
guild_id INTEGER NOT NULL,
|
|
event_id INTEGER NOT NULL,
|
|
category INTEGER NOT NULL,
|
|
value INTEGER NOT NULL,
|
|
PRIMARY KEY (guild_id, event_id, category),
|
|
FOREIGN KEY (guild_id) REFERENCES guilds(id)
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_recruiting (
|
|
guild_id INTEGER NOT NULL,
|
|
flag INTEGER NOT NULL,
|
|
value INTEGER NOT NULL,
|
|
PRIMARY KEY (guild_id, flag),
|
|
FOREIGN KEY (guild_id) REFERENCES guilds(id)
|
|
)`,
|
|
`CREATE TABLE IF NOT EXISTS guild_point_history (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
char_id INTEGER NOT NULL,
|
|
date INTEGER NOT NULL,
|
|
modified_by TEXT NOT NULL,
|
|
comment TEXT NOT NULL,
|
|
points REAL NOT NULL,
|
|
FOREIGN KEY (char_id) REFERENCES guild_members(char_id)
|
|
)`,
|
|
}
|
|
|
|
for _, sql := range tables {
|
|
err := sqlitex.ExecScript(conn, sql)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// execSQL is a helper to execute SQL with parameters
|
|
func execSQL(t *testing.T, pool *sqlitex.Pool, query string, args ...any) {
|
|
conn, err := pool.Take(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("Failed to get connection: %v", err)
|
|
}
|
|
defer pool.Put(conn)
|
|
|
|
stmt := conn.Prep(query)
|
|
defer stmt.Finalize()
|
|
|
|
for i, arg := range args {
|
|
switch v := arg.(type) {
|
|
case int:
|
|
stmt.BindInt64(i+1, int64(v))
|
|
case int32:
|
|
stmt.BindInt64(i+1, int64(v))
|
|
case int64:
|
|
stmt.BindInt64(i+1, v)
|
|
case float64:
|
|
stmt.BindFloat(i+1, v)
|
|
case string:
|
|
stmt.BindText(i+1, v)
|
|
case []byte:
|
|
stmt.BindBytes(i+1, v)
|
|
case nil:
|
|
stmt.BindNull(i + 1)
|
|
default:
|
|
t.Fatalf("Unsupported argument type: %T", v)
|
|
}
|
|
}
|
|
|
|
_, err = stmt.Step()
|
|
if err != nil {
|
|
t.Fatalf("Failed to execute SQL: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestDatabaseGuildManager_LoadGuilds tests loading guilds from database
|
|
func TestDatabaseGuildManager_LoadGuilds(t *testing.T) {
|
|
pool := createTestPool(t)
|
|
defer pool.Close()
|
|
|
|
dgm := NewDatabaseGuildManager(pool)
|
|
ctx := context.Background()
|
|
|
|
// Insert test data
|
|
formedTime := time.Now().Unix()
|
|
execSQL(t, pool, `INSERT INTO guilds (id, name, motd, level, xp, xp_needed, formed_on)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
1, "Test Guild", "Welcome!", 5, 1000, 5000, formedTime)
|
|
|
|
// Test loading guilds
|
|
guilds, err := dgm.LoadGuilds(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load guilds: %v", err)
|
|
}
|
|
|
|
if len(guilds) != 1 {
|
|
t.Errorf("Expected 1 guild, got %d", len(guilds))
|
|
}
|
|
|
|
if len(guilds) > 0 {
|
|
guild := guilds[0]
|
|
if guild.ID != 1 {
|
|
t.Errorf("Expected guild ID 1, got %d", guild.ID)
|
|
}
|
|
if guild.Name != "Test Guild" {
|
|
t.Errorf("Expected guild name 'Test Guild', got '%s'", guild.Name)
|
|
}
|
|
if guild.MOTD != "Welcome!" {
|
|
t.Errorf("Expected MOTD 'Welcome!', got '%s'", guild.MOTD)
|
|
}
|
|
if guild.Level != 5 {
|
|
t.Errorf("Expected level 5, got %d", guild.Level)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestDatabaseGuildManager_LoadGuildMembers tests loading guild members
|
|
func TestDatabaseGuildManager_LoadGuildMembers(t *testing.T) {
|
|
pool := createTestPool(t)
|
|
defer pool.Close()
|
|
|
|
dgm := NewDatabaseGuildManager(pool)
|
|
ctx := context.Background()
|
|
|
|
// Insert test guild and members
|
|
execSQL(t, pool, `INSERT INTO guilds (id, name) VALUES (?, ?)`, 1, "Test Guild")
|
|
|
|
joinTime := time.Now().Unix()
|
|
loginTime := time.Now().Unix()
|
|
|
|
execSQL(t, pool, `INSERT INTO guild_members
|
|
(char_id, guild_id, account_id, name, guild_status, points,
|
|
adventure_class, adventure_level, tradeskill_class, tradeskill_level,
|
|
rank, member_flags, zone, join_date, last_login_date, note, officer_note,
|
|
recruiter_description, recruiter_picture_data, recruiting_show_adventure_class)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
100, 1, 10, "TestPlayer", 1, 100.5, 5, 50, 2, 20, 3, 0, "Test Zone",
|
|
joinTime, loginTime, "Player note", "Officer note", "Recruiter desc",
|
|
[]byte{0x01, 0x02, 0x03}, 1)
|
|
|
|
// Test loading members
|
|
members, err := dgm.LoadGuildMembers(ctx, 1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load guild members: %v", err)
|
|
}
|
|
|
|
if len(members) != 1 {
|
|
t.Errorf("Expected 1 member, got %d", len(members))
|
|
}
|
|
|
|
if len(members) > 0 {
|
|
member := members[0]
|
|
if member.CharacterID != 100 {
|
|
t.Errorf("Expected character ID 100, got %d", member.CharacterID)
|
|
}
|
|
if member.Name != "TestPlayer" {
|
|
t.Errorf("Expected name 'TestPlayer', got '%s'", member.Name)
|
|
}
|
|
if member.Points != 100.5 {
|
|
t.Errorf("Expected points 100.5, got %f", member.Points)
|
|
}
|
|
if len(member.RecruiterPictureData) != 3 {
|
|
t.Errorf("Expected picture data length 3, got %d", len(member.RecruiterPictureData))
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestDatabaseGuildManager_SaveGuild tests saving guild data
|
|
func TestDatabaseGuildManager_SaveGuild(t *testing.T) {
|
|
pool := createTestPool(t)
|
|
defer pool.Close()
|
|
|
|
dgm := NewDatabaseGuildManager(pool)
|
|
ctx := context.Background()
|
|
|
|
// Create a test guild
|
|
guild := &Guild{
|
|
id: 1,
|
|
name: "New Guild",
|
|
motd: "Test MOTD",
|
|
level: 10,
|
|
expCurrent: 2000,
|
|
expToNextLevel: 8000,
|
|
formedDate: time.Now(),
|
|
}
|
|
|
|
// Save the guild
|
|
err := dgm.SaveGuild(ctx, guild)
|
|
if err != nil {
|
|
t.Fatalf("Failed to save guild: %v", err)
|
|
}
|
|
|
|
// Load and verify
|
|
guilds, err := dgm.LoadGuilds(ctx)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load guilds: %v", err)
|
|
}
|
|
|
|
if len(guilds) != 1 {
|
|
t.Errorf("Expected 1 guild, got %d", len(guilds))
|
|
}
|
|
|
|
if len(guilds) > 0 {
|
|
loaded := guilds[0]
|
|
if loaded.Name != "New Guild" {
|
|
t.Errorf("Expected name 'New Guild', got '%s'", loaded.Name)
|
|
}
|
|
if loaded.MOTD != "Test MOTD" {
|
|
t.Errorf("Expected MOTD 'Test MOTD', got '%s'", loaded.MOTD)
|
|
}
|
|
if loaded.Level != 10 {
|
|
t.Errorf("Expected level 10, got %d", loaded.Level)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestDatabaseGuildManager_CreateAndDeleteGuild tests guild creation and deletion
|
|
func TestDatabaseGuildManager_CreateAndDeleteGuild(t *testing.T) {
|
|
pool := createTestPool(t)
|
|
defer pool.Close()
|
|
|
|
dgm := NewDatabaseGuildManager(pool)
|
|
ctx := context.Background()
|
|
|
|
// Create guild data
|
|
guildData := GuildData{
|
|
Name: "Delete Test Guild",
|
|
MOTD: "To be deleted",
|
|
Level: 1,
|
|
EXPCurrent: 0,
|
|
EXPToNextLevel: 1000,
|
|
FormedDate: time.Now(),
|
|
}
|
|
|
|
// Create the guild
|
|
guildID, err := dgm.CreateGuild(ctx, guildData)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create guild: %v", err)
|
|
}
|
|
|
|
if guildID <= 0 {
|
|
t.Errorf("Expected valid guild ID, got %d", guildID)
|
|
}
|
|
|
|
// Verify it exists
|
|
guild, err := dgm.LoadGuild(ctx, guildID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load created guild: %v", err)
|
|
}
|
|
|
|
if guild.Name != "Delete Test Guild" {
|
|
t.Errorf("Expected name 'Delete Test Guild', got '%s'", guild.Name)
|
|
}
|
|
|
|
// Delete the guild
|
|
err = dgm.DeleteGuild(ctx, guildID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to delete guild: %v", err)
|
|
}
|
|
|
|
// Verify it's gone
|
|
_, err = dgm.LoadGuild(ctx, guildID)
|
|
if err == nil {
|
|
t.Error("Expected error loading deleted guild, got nil")
|
|
}
|
|
}
|
|
|
|
// BenchmarkDatabaseGuildManager_LoadGuilds benchmarks loading guilds
|
|
func BenchmarkDatabaseGuildManager_LoadGuilds(b *testing.B) {
|
|
pool := createTestPool(&testing.T{})
|
|
defer pool.Close()
|
|
|
|
dgm := NewDatabaseGuildManager(pool)
|
|
ctx := context.Background()
|
|
|
|
// Insert test data
|
|
for i := 1; i <= 100; i++ {
|
|
execSQL(&testing.T{}, pool,
|
|
`INSERT INTO guilds (id, name, motd, level, xp, xp_needed, formed_on)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
i, fmt.Sprintf("Guild %d", i), "Welcome!", i%10+1, i*100, i*1000, time.Now().Unix())
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
guilds, err := dgm.LoadGuilds(ctx)
|
|
if err != nil {
|
|
b.Fatalf("Failed to load guilds: %v", err)
|
|
}
|
|
if len(guilds) != 100 {
|
|
b.Errorf("Expected 100 guilds, got %d", len(guilds))
|
|
}
|
|
}
|
|
}
|
|
|
|
// BenchmarkDatabaseGuildManager_SaveGuild benchmarks saving guild data
|
|
func BenchmarkDatabaseGuildManager_SaveGuild(b *testing.B) {
|
|
pool := createTestPool(&testing.T{})
|
|
defer pool.Close()
|
|
|
|
dgm := NewDatabaseGuildManager(pool)
|
|
ctx := context.Background()
|
|
|
|
// Create a test guild
|
|
guild := &Guild{
|
|
id: 1,
|
|
name: "Benchmark Guild",
|
|
motd: "Benchmark MOTD",
|
|
level: 50,
|
|
expCurrent: 50000,
|
|
expToNextLevel: 100000,
|
|
formedDate: time.Now(),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
|
|
for i := 0; i < b.N; i++ {
|
|
err := dgm.SaveGuild(ctx, guild)
|
|
if err != nil {
|
|
b.Fatalf("Failed to save guild: %v", err)
|
|
}
|
|
}
|
|
}
|