package loot import ( "context" "fmt" "math/rand" "testing" "zombiezen.com/go/sqlite/sqlitex" ) // setupTestDB creates a test database with minimal schema func setupTestDB(t testing.TB) *sqlitex.Pool { // Create unique database name to avoid test contamination dbName := fmt.Sprintf("file:loot_test_%s_%d.db?mode=memory&cache=shared", t.Name(), rand.Int63()) pool, err := sqlitex.NewPool(dbName, sqlitex.PoolOptions{ PoolSize: 10, }) if err != nil { t.Fatalf("Failed to create test database pool: %v", err) } // Create complete test schema matching the real database structure schema := ` CREATE TABLE loottable ( id INTEGER PRIMARY KEY, name TEXT DEFAULT '', mincoin INTEGER DEFAULT 0, maxcoin INTEGER DEFAULT 0, maxlootitems INTEGER DEFAULT 5, lootdrop_probability REAL DEFAULT 100.0, coin_probability REAL DEFAULT 75.0 ); CREATE TABLE lootdrop ( loot_table_id INTEGER, item_id INTEGER, item_charges INTEGER DEFAULT 1, equip_item INTEGER DEFAULT 0, probability REAL DEFAULT 25.0, no_drop_quest_completed_id INTEGER DEFAULT 0 ); CREATE TABLE spawn_loot ( spawn_id INTEGER, loottable_id INTEGER ); CREATE TABLE loot_global ( type TEXT, loot_table INTEGER, value1 INTEGER DEFAULT 0, value2 INTEGER DEFAULT 0, value3 INTEGER DEFAULT 0, value4 INTEGER DEFAULT 0 ); ` // Execute schema on connection ctx := context.Background() conn, err := pool.Take(ctx) if err != nil { t.Fatalf("Failed to get connection: %v", err) } defer pool.Put(conn) if err := sqlitex.ExecuteScript(conn, schema, nil); err != nil { t.Fatalf("Failed to create test schema: %v", err) } return pool } func TestNewLootDatabase(t *testing.T) { pool := setupTestDB(t) defer pool.Close() lootDB := NewLootDatabase(pool) if lootDB == nil { t.Fatal("Expected non-nil LootDatabase") } if lootDB.pool == nil { t.Fatal("Expected non-nil database pool") } } func TestLootDatabaseBasicOperation(t *testing.T) { pool := setupTestDB(t) defer pool.Close() lootDB := NewLootDatabase(pool) // Test that LoadAllLootData doesn't crash (even with empty database) err := lootDB.LoadAllLootData() if err != nil { t.Fatalf("LoadAllLootData should not fail with empty database: %v", err) } // Test that GetLootTable returns nil for non-existent table table := lootDB.GetLootTable(999) if table != nil { t.Error("Expected nil for non-existent loot table") } } func TestLootDatabaseWithData(t *testing.T) { pool := setupTestDB(t) defer pool.Close() // Insert test data ctx := context.Background() conn, err := pool.Take(ctx) if err != nil { t.Fatalf("Failed to get connection: %v", err) } defer pool.Put(conn) // Insert a test loot table err = sqlitex.Execute(conn, `INSERT INTO loottable (id, name, mincoin, maxcoin, maxlootitems, lootdrop_probability, coin_probability) VALUES (?, ?, ?, ?, ?, ?, ?)`, &sqlitex.ExecOptions{ Args: []any{1, "Test Loot Table", 10, 50, 3, 75.0, 50.0}, }) if err != nil { t.Fatalf("Failed to insert test loot table: %v", err) } // Insert test loot drops err = sqlitex.Execute(conn, `INSERT INTO lootdrop (loot_table_id, item_id, item_charges, equip_item, probability, no_drop_quest_completed_id) VALUES (?, ?, ?, ?, ?, ?)`, &sqlitex.ExecOptions{ Args: []any{1, 101, 1, 0, 25.0, 0}, }) if err != nil { t.Fatalf("Failed to insert test loot drop: %v", err) } // Insert spawn loot assignment err = sqlitex.Execute(conn, `INSERT INTO spawn_loot (spawn_id, loottable_id) VALUES (?, ?)`, &sqlitex.ExecOptions{ Args: []any{1001, 1}, }) if err != nil { t.Fatalf("Failed to insert spawn loot assignment: %v", err) } // Load all loot data lootDB := NewLootDatabase(pool) err = lootDB.LoadAllLootData() if err != nil { t.Fatalf("Failed to load loot data: %v", err) } // Verify loot table was loaded table := lootDB.GetLootTable(1) if table == nil { t.Fatal("Expected to find loot table 1") } if table.Name != "Test Loot Table" { t.Errorf("Expected table name 'Test Loot Table', got '%s'", table.Name) } if table.MinCoin != 10 { t.Errorf("Expected min coin 10, got %d", table.MinCoin) } if table.MaxCoin != 50 { t.Errorf("Expected max coin 50, got %d", table.MaxCoin) } if len(table.Drops) != 1 { t.Errorf("Expected 1 loot drop, got %d", len(table.Drops)) } else { drop := table.Drops[0] if drop.ItemID != 101 { t.Errorf("Expected item ID 101, got %d", drop.ItemID) } if drop.Probability != 25.0 { t.Errorf("Expected probability 25.0, got %f", drop.Probability) } } // Verify spawn loot assignment tables := lootDB.GetSpawnLootTables(1001) if len(tables) != 1 { t.Errorf("Expected 1 loot table for spawn 1001, got %d", len(tables)) } else if tables[0] != 1 { t.Errorf("Expected loot table ID 1 for spawn 1001, got %d", tables[0]) } } // Benchmark tests func BenchmarkLootDatabaseCreation(b *testing.B) { for i := 0; i < b.N; i++ { pool := setupTestDB(b) lootDB := NewLootDatabase(pool) if lootDB == nil { b.Fatal("Expected non-nil LootDatabase") } pool.Close() } }