501 lines
13 KiB
Go
501 lines
13 KiB
Go
package items
|
|
|
|
import (
|
|
"database/sql"
|
|
"testing"
|
|
|
|
_ "zombiezen.com/go/sqlite"
|
|
)
|
|
|
|
// setupTestDB creates a test database with minimal schema
|
|
func setupTestDB(t *testing.T) *sql.DB {
|
|
db, err := sql.Open("sqlite", ":memory:")
|
|
if err != nil {
|
|
t.Fatalf("Failed to open test database: %v", err)
|
|
}
|
|
|
|
// Create minimal test schema
|
|
schema := `
|
|
CREATE TABLE items (
|
|
id INTEGER PRIMARY KEY,
|
|
soe_id INTEGER DEFAULT 0,
|
|
name TEXT NOT NULL,
|
|
description TEXT DEFAULT '',
|
|
icon INTEGER DEFAULT 0,
|
|
icon2 INTEGER DEFAULT 0,
|
|
icon_heroic_op INTEGER DEFAULT 0,
|
|
icon_heroic_op2 INTEGER DEFAULT 0,
|
|
icon_id INTEGER DEFAULT 0,
|
|
icon_backdrop INTEGER DEFAULT 0,
|
|
icon_border INTEGER DEFAULT 0,
|
|
icon_tint_red INTEGER DEFAULT 0,
|
|
icon_tint_green INTEGER DEFAULT 0,
|
|
icon_tint_blue INTEGER DEFAULT 0,
|
|
tier INTEGER DEFAULT 1,
|
|
level INTEGER DEFAULT 1,
|
|
success_sellback INTEGER DEFAULT 0,
|
|
stack_size INTEGER DEFAULT 1,
|
|
generic_info_show_name INTEGER DEFAULT 1,
|
|
generic_info_item_flags INTEGER DEFAULT 0,
|
|
generic_info_item_flags2 INTEGER DEFAULT 0,
|
|
generic_info_creator_flag INTEGER DEFAULT 0,
|
|
generic_info_condition INTEGER DEFAULT 100,
|
|
generic_info_weight INTEGER DEFAULT 10,
|
|
generic_info_skill_req1 INTEGER DEFAULT 0,
|
|
generic_info_skill_req2 INTEGER DEFAULT 0,
|
|
generic_info_skill_min_level INTEGER DEFAULT 0,
|
|
generic_info_item_type INTEGER DEFAULT 0,
|
|
generic_info_appearance_id INTEGER DEFAULT 0,
|
|
generic_info_appearance_red INTEGER DEFAULT 0,
|
|
generic_info_appearance_green INTEGER DEFAULT 0,
|
|
generic_info_appearance_blue INTEGER DEFAULT 0,
|
|
generic_info_appearance_highlight_red INTEGER DEFAULT 0,
|
|
generic_info_appearance_highlight_green INTEGER DEFAULT 0,
|
|
generic_info_appearance_highlight_blue INTEGER DEFAULT 0,
|
|
generic_info_collectable INTEGER DEFAULT 0,
|
|
generic_info_offers_quest_id INTEGER DEFAULT 0,
|
|
generic_info_part_of_quest_id INTEGER DEFAULT 0,
|
|
generic_info_max_charges INTEGER DEFAULT 0,
|
|
generic_info_adventure_classes INTEGER DEFAULT 0,
|
|
generic_info_tradeskill_classes INTEGER DEFAULT 0,
|
|
generic_info_adventure_default_level INTEGER DEFAULT 1,
|
|
generic_info_tradeskill_default_level INTEGER DEFAULT 1,
|
|
generic_info_usable INTEGER DEFAULT 0,
|
|
generic_info_harvest INTEGER DEFAULT 0,
|
|
generic_info_body_drop INTEGER DEFAULT 0,
|
|
generic_info_pvp_description INTEGER DEFAULT 0,
|
|
generic_info_merc_only INTEGER DEFAULT 0,
|
|
generic_info_mount_only INTEGER DEFAULT 0,
|
|
generic_info_set_id INTEGER DEFAULT 0,
|
|
generic_info_collectable_unk INTEGER DEFAULT 0,
|
|
generic_info_transmuted_material INTEGER DEFAULT 0,
|
|
broker_price INTEGER DEFAULT 0,
|
|
sell_price INTEGER DEFAULT 0,
|
|
max_sell_value INTEGER DEFAULT 0,
|
|
created TEXT DEFAULT CURRENT_TIMESTAMP,
|
|
script_name TEXT DEFAULT '',
|
|
lua_script TEXT DEFAULT ''
|
|
);
|
|
|
|
CREATE TABLE item_mod_stats (
|
|
item_id INTEGER,
|
|
stat_type INTEGER,
|
|
stat_subtype INTEGER DEFAULT 0,
|
|
value REAL,
|
|
stat_name TEXT DEFAULT '',
|
|
level INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE item_effects (
|
|
item_id INTEGER,
|
|
effect TEXT,
|
|
percentage INTEGER DEFAULT 0,
|
|
subbulletflag INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE item_appearances (
|
|
item_id INTEGER,
|
|
type INTEGER,
|
|
red INTEGER DEFAULT 0,
|
|
green INTEGER DEFAULT 0,
|
|
blue INTEGER DEFAULT 0,
|
|
highlight_red INTEGER DEFAULT 0,
|
|
highlight_green INTEGER DEFAULT 0,
|
|
highlight_blue INTEGER DEFAULT 0
|
|
);
|
|
|
|
CREATE TABLE item_levels_override (
|
|
item_id INTEGER,
|
|
adventure_class INTEGER,
|
|
tradeskill_class INTEGER,
|
|
level INTEGER
|
|
);
|
|
|
|
CREATE TABLE item_mod_strings (
|
|
item_id INTEGER,
|
|
stat_string TEXT
|
|
);
|
|
|
|
CREATE TABLE character_items (
|
|
char_id INTEGER,
|
|
item_id INTEGER,
|
|
unique_id INTEGER PRIMARY KEY,
|
|
inv_slot_id INTEGER,
|
|
slot_id INTEGER,
|
|
appearance_type INTEGER DEFAULT 0,
|
|
icon INTEGER DEFAULT 0,
|
|
icon2 INTEGER DEFAULT 0,
|
|
count INTEGER DEFAULT 1,
|
|
tier INTEGER DEFAULT 1,
|
|
bag_id INTEGER DEFAULT 0,
|
|
details_count INTEGER DEFAULT 1,
|
|
creator TEXT DEFAULT '',
|
|
adornment_slot0 INTEGER DEFAULT 0,
|
|
adornment_slot1 INTEGER DEFAULT 0,
|
|
adornment_slot2 INTEGER DEFAULT 0,
|
|
group_id INTEGER DEFAULT 0,
|
|
creator_app TEXT DEFAULT '',
|
|
random_seed INTEGER DEFAULT 0,
|
|
created TEXT DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE item_details_weapon (
|
|
item_id INTEGER PRIMARY KEY,
|
|
wield_type INTEGER DEFAULT 2,
|
|
damage_low1 INTEGER DEFAULT 1,
|
|
damage_high1 INTEGER DEFAULT 2,
|
|
damage_low2 INTEGER DEFAULT 0,
|
|
damage_high2 INTEGER DEFAULT 0,
|
|
damage_low3 INTEGER DEFAULT 0,
|
|
damage_high3 INTEGER DEFAULT 0,
|
|
delay_hundredths INTEGER DEFAULT 300,
|
|
rating REAL DEFAULT 1.0
|
|
);
|
|
|
|
CREATE TABLE item_details_armor (
|
|
item_id INTEGER PRIMARY KEY,
|
|
mitigation_low INTEGER DEFAULT 1,
|
|
mitigation_high INTEGER DEFAULT 2
|
|
);
|
|
|
|
CREATE TABLE item_details_bag (
|
|
item_id INTEGER PRIMARY KEY,
|
|
num_slots INTEGER DEFAULT 6,
|
|
weight_reduction INTEGER DEFAULT 0
|
|
);
|
|
`
|
|
|
|
if _, err := db.Exec(schema); err != nil {
|
|
t.Fatalf("Failed to create test schema: %v", err)
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
// insertTestItem inserts a test item into the database
|
|
func insertTestItem(t *testing.T, db *sql.DB, itemID int32, name string, itemType int8) {
|
|
query := `
|
|
INSERT INTO items (id, name, generic_info_item_type)
|
|
VALUES (?, ?, ?)
|
|
`
|
|
_, err := db.Exec(query, itemID, name, itemType)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert test item: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestNewItemDatabase(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
idb := NewItemDatabase(db)
|
|
if idb == nil {
|
|
t.Fatal("Expected non-nil ItemDatabase")
|
|
}
|
|
|
|
if idb.db != db {
|
|
t.Error("Expected database connection to be set")
|
|
}
|
|
|
|
if len(idb.queries) == 0 {
|
|
t.Error("Expected queries to be prepared")
|
|
}
|
|
|
|
if len(idb.loadedItems) != 0 {
|
|
t.Error("Expected loadedItems to be empty initially")
|
|
}
|
|
}
|
|
|
|
func TestLoadItems(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Insert test items
|
|
insertTestItem(t, db, 1, "Test Sword", ItemTypeWeapon)
|
|
insertTestItem(t, db, 2, "Test Armor", ItemTypeArmor)
|
|
insertTestItem(t, db, 3, "Test Bag", ItemTypeBag)
|
|
|
|
// Add weapon details for sword
|
|
_, err := db.Exec(`
|
|
INSERT INTO item_details_weapon (item_id, damage_low1, damage_high1, delay_hundredths)
|
|
VALUES (1, 10, 15, 250)
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert weapon details: %v", err)
|
|
}
|
|
|
|
// Add armor details
|
|
_, err = db.Exec(`
|
|
INSERT INTO item_details_armor (item_id, mitigation_low, mitigation_high)
|
|
VALUES (2, 5, 8)
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert armor details: %v", err)
|
|
}
|
|
|
|
// Add bag details
|
|
_, err = db.Exec(`
|
|
INSERT INTO item_details_bag (item_id, num_slots, weight_reduction)
|
|
VALUES (3, 6, 10)
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert bag details: %v", err)
|
|
}
|
|
|
|
idb := NewItemDatabase(db)
|
|
masterList := NewMasterItemList()
|
|
|
|
err = idb.LoadItems(masterList)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load items: %v", err)
|
|
}
|
|
|
|
if masterList.GetItemCount() != 3 {
|
|
t.Errorf("Expected 3 items, got %d", masterList.GetItemCount())
|
|
}
|
|
|
|
// Test specific items
|
|
sword := masterList.GetItem(1)
|
|
if sword == nil {
|
|
t.Fatal("Expected to find sword item")
|
|
}
|
|
if sword.Name != "Test Sword" {
|
|
t.Errorf("Expected sword name 'Test Sword', got '%s'", sword.Name)
|
|
}
|
|
if sword.WeaponInfo == nil {
|
|
t.Error("Expected weapon info to be loaded")
|
|
} else {
|
|
if sword.WeaponInfo.DamageLow1 != 10 {
|
|
t.Errorf("Expected damage low 10, got %d", sword.WeaponInfo.DamageLow1)
|
|
}
|
|
}
|
|
|
|
armor := masterList.GetItem(2)
|
|
if armor == nil {
|
|
t.Fatal("Expected to find armor item")
|
|
}
|
|
if armor.ArmorInfo == nil {
|
|
t.Error("Expected armor info to be loaded")
|
|
}
|
|
|
|
bag := masterList.GetItem(3)
|
|
if bag == nil {
|
|
t.Fatal("Expected to find bag item")
|
|
}
|
|
if bag.BagInfo == nil {
|
|
t.Error("Expected bag info to be loaded")
|
|
}
|
|
}
|
|
|
|
func TestLoadItemStats(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Insert test item
|
|
insertTestItem(t, db, 1, "Test Item", ItemTypeNormal)
|
|
|
|
// Add item stats
|
|
_, err := db.Exec(`
|
|
INSERT INTO item_mod_stats (item_id, stat_type, stat_subtype, value, stat_name)
|
|
VALUES (1, 0, 0, 10.0, 'Strength')
|
|
`)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert item stats: %v", err)
|
|
}
|
|
|
|
idb := NewItemDatabase(db)
|
|
masterList := NewMasterItemList()
|
|
|
|
err = idb.LoadItems(masterList)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load items: %v", err)
|
|
}
|
|
|
|
item := masterList.GetItem(1)
|
|
if item == nil {
|
|
t.Fatal("Expected to find item")
|
|
}
|
|
|
|
if len(item.ItemStats) != 1 {
|
|
t.Errorf("Expected 1 item stat, got %d", len(item.ItemStats))
|
|
}
|
|
|
|
if item.ItemStats[0].StatName != "Strength" {
|
|
t.Errorf("Expected stat name 'Strength', got '%s'", item.ItemStats[0].StatName)
|
|
}
|
|
|
|
if item.ItemStats[0].Value != 10.0 {
|
|
t.Errorf("Expected stat value 10.0, got %f", item.ItemStats[0].Value)
|
|
}
|
|
}
|
|
|
|
func TestSaveAndLoadCharacterItems(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Insert test item template
|
|
insertTestItem(t, db, 1, "Test Item", ItemTypeNormal)
|
|
|
|
idb := NewItemDatabase(db)
|
|
masterList := NewMasterItemList()
|
|
|
|
// Load item templates
|
|
err := idb.LoadItems(masterList)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load items: %v", err)
|
|
}
|
|
|
|
// Create test character items
|
|
inventory := NewPlayerItemList()
|
|
equipment := NewEquipmentItemList()
|
|
|
|
// Create an item instance
|
|
template := masterList.GetItem(1)
|
|
if template == nil {
|
|
t.Fatal("Expected to find item template")
|
|
}
|
|
|
|
item := NewItemFromTemplate(template)
|
|
item.Details.InvSlotID = 1000 // Inventory slot
|
|
item.Details.Count = 5
|
|
|
|
inventory.AddItem(item)
|
|
|
|
// Save character items
|
|
charID := uint32(123)
|
|
err = idb.SaveCharacterItems(charID, inventory, equipment)
|
|
if err != nil {
|
|
t.Fatalf("Failed to save character items: %v", err)
|
|
}
|
|
|
|
// Load character items
|
|
loadedInventory, loadedEquipment, err := idb.LoadCharacterItems(charID, masterList)
|
|
if err != nil {
|
|
t.Fatalf("Failed to load character items: %v", err)
|
|
}
|
|
|
|
if loadedInventory.GetNumberOfItems() != 1 {
|
|
t.Errorf("Expected 1 inventory item, got %d", loadedInventory.GetNumberOfItems())
|
|
}
|
|
|
|
if loadedEquipment.GetNumberOfItems() != 0 {
|
|
t.Errorf("Expected 0 equipped items, got %d", loadedEquipment.GetNumberOfItems())
|
|
}
|
|
|
|
// Verify item properties
|
|
allItems := loadedInventory.GetAllItems()
|
|
if len(allItems) != 1 {
|
|
t.Fatalf("Expected 1 item in all items, got %d", len(allItems))
|
|
}
|
|
|
|
loadedItem := allItems[int32(item.Details.UniqueID)]
|
|
if loadedItem == nil {
|
|
t.Fatal("Expected to find loaded item")
|
|
}
|
|
|
|
if loadedItem.Details.Count != 5 {
|
|
t.Errorf("Expected item count 5, got %d", loadedItem.Details.Count)
|
|
}
|
|
|
|
if loadedItem.Name != "Test Item" {
|
|
t.Errorf("Expected item name 'Test Item', got '%s'", loadedItem.Name)
|
|
}
|
|
}
|
|
|
|
func TestDeleteCharacterItem(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
// Insert test character item directly
|
|
charID := uint32(123)
|
|
uniqueID := int64(456)
|
|
_, err := db.Exec(`
|
|
INSERT INTO character_items (char_id, item_id, unique_id, inv_slot_id, slot_id, count)
|
|
VALUES (?, 1, ?, 1000, 0, 1)
|
|
`, charID, uniqueID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert test character item: %v", err)
|
|
}
|
|
|
|
idb := NewItemDatabase(db)
|
|
|
|
// Delete the item
|
|
err = idb.DeleteCharacterItem(charID, uniqueID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to delete character item: %v", err)
|
|
}
|
|
|
|
// Verify item was deleted
|
|
var count int
|
|
err = db.QueryRow("SELECT COUNT(*) FROM character_items WHERE char_id = ? AND unique_id = ?", charID, uniqueID).Scan(&count)
|
|
if err != nil {
|
|
t.Fatalf("Failed to query character items: %v", err)
|
|
}
|
|
|
|
if count != 0 {
|
|
t.Errorf("Expected 0 items after deletion, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestGetCharacterItemCount(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
charID := uint32(123)
|
|
idb := NewItemDatabase(db)
|
|
|
|
// Initially should be 0
|
|
count, err := idb.GetCharacterItemCount(charID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get character item count: %v", err)
|
|
}
|
|
if count != 0 {
|
|
t.Errorf("Expected 0 items initially, got %d", count)
|
|
}
|
|
|
|
// Insert test items
|
|
for i := 0; i < 3; i++ {
|
|
_, err := db.Exec(`
|
|
INSERT INTO character_items (char_id, item_id, unique_id, inv_slot_id, slot_id, count)
|
|
VALUES (?, 1, ?, 1000, 0, 1)
|
|
`, charID, i+1)
|
|
if err != nil {
|
|
t.Fatalf("Failed to insert test character item: %v", err)
|
|
}
|
|
}
|
|
|
|
// Should now be 3
|
|
count, err = idb.GetCharacterItemCount(charID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to get character item count: %v", err)
|
|
}
|
|
if count != 3 {
|
|
t.Errorf("Expected 3 items, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestNextUniqueItemID(t *testing.T) {
|
|
id1 := NextUniqueItemID()
|
|
id2 := NextUniqueItemID()
|
|
|
|
if id1 >= id2 {
|
|
t.Errorf("Expected unique IDs to be increasing, got %d and %d", id1, id2)
|
|
}
|
|
|
|
if id1 == id2 {
|
|
t.Error("Expected unique IDs to be different")
|
|
}
|
|
}
|
|
|
|
func TestItemDatabaseClose(t *testing.T) {
|
|
db := setupTestDB(t)
|
|
defer db.Close()
|
|
|
|
idb := NewItemDatabase(db)
|
|
|
|
// Should not error when closing
|
|
err := idb.Close()
|
|
if err != nil {
|
|
t.Errorf("Expected no error when closing, got: %v", err)
|
|
}
|
|
} |