fixes for heroic ops
This commit is contained in:
parent
966501670f
commit
1c26bb600d
@ -6,8 +6,8 @@ const (
|
|||||||
MaxAbilities = 6
|
MaxAbilities = 6
|
||||||
|
|
||||||
// Special ability icon values
|
// Special ability icon values
|
||||||
AbilityIconAny = 0xFFFF // Wildcard - any ability can be used
|
AbilityIconAny = -1 // Wildcard - any ability can be used (using -1 to fit in int16)
|
||||||
AbilityIconNone = 0 // No ability required
|
AbilityIconNone = 0 // No ability required
|
||||||
|
|
||||||
// Default wheel timer (in seconds)
|
// Default wheel timer (in seconds)
|
||||||
DefaultWheelTimerSeconds = 10
|
DefaultWheelTimerSeconds = 10
|
||||||
|
File diff suppressed because it is too large
Load Diff
721
internal/heroic_ops/database_test.go
Normal file
721
internal/heroic_ops/database_test.go
Normal file
@ -0,0 +1,721 @@
|
|||||||
|
package heroic_ops
|
||||||
|
|
||||||
|
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_heroic_ops.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 heroic ops tables for testing
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
err = dhom.EnsureHOTables(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create heroic ops tables: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool
|
||||||
|
}
|
||||||
|
|
||||||
|
// execSQL is a helper to execute SQL with parameters
|
||||||
|
func execSQL(t *testing.T, pool *sqlitex.Pool, query string, args ...interface{}) {
|
||||||
|
conn, err := pool.Take(context.Background())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get connection: %v", err)
|
||||||
|
}
|
||||||
|
defer pool.Put(conn)
|
||||||
|
|
||||||
|
err = sqlitex.Execute(conn, query, &sqlitex.ExecOptions{
|
||||||
|
Args: args,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to execute SQL: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_LoadStarters tests loading starters from database
|
||||||
|
func TestDatabaseHeroicOPManager_LoadStarters(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Insert test starter data
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, ?, ?, 0, 0, 0, 0, 0.0, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
1, HOTypeStarter, 5, 100, 10, 20, 30, 40, 50, 60, "Test Starter", "A test starter")
|
||||||
|
|
||||||
|
// Test loading starters
|
||||||
|
starters, err := dhom.LoadStarters(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load starters: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(starters) != 1 {
|
||||||
|
t.Errorf("Expected 1 starter, got %d", len(starters))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(starters) > 0 {
|
||||||
|
starter := starters[0]
|
||||||
|
if starter.ID != 1 {
|
||||||
|
t.Errorf("Expected starter ID 1, got %d", starter.ID)
|
||||||
|
}
|
||||||
|
if starter.StarterClass != 5 {
|
||||||
|
t.Errorf("Expected starter class 5, got %d", starter.StarterClass)
|
||||||
|
}
|
||||||
|
if starter.Name != "Test Starter" {
|
||||||
|
t.Errorf("Expected name 'Test Starter', got '%s'", starter.Name)
|
||||||
|
}
|
||||||
|
if starter.Ability1 != 10 {
|
||||||
|
t.Errorf("Expected ability1 10, got %d", starter.Ability1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_LoadStarter tests loading a specific starter
|
||||||
|
func TestDatabaseHeroicOPManager_LoadStarter(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Insert test starter data
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, ?, ?, 0, 0, 0, 0, 0.0, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
2, HOTypeStarter, 10, 200, 11, 21, 31, 41, 51, 61, "Specific Starter", "A specific test starter")
|
||||||
|
|
||||||
|
// Test loading specific starter
|
||||||
|
starter, err := dhom.LoadStarter(ctx, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load starter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if starter.ID != 2 {
|
||||||
|
t.Errorf("Expected starter ID 2, got %d", starter.ID)
|
||||||
|
}
|
||||||
|
if starter.StarterClass != 10 {
|
||||||
|
t.Errorf("Expected starter class 10, got %d", starter.StarterClass)
|
||||||
|
}
|
||||||
|
if starter.Name != "Specific Starter" {
|
||||||
|
t.Errorf("Expected name 'Specific Starter', got '%s'", starter.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test loading non-existent starter
|
||||||
|
_, err = dhom.LoadStarter(ctx, 999)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error loading non-existent starter, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_LoadWheels tests loading wheels from database
|
||||||
|
func TestDatabaseHeroicOPManager_LoadWheels(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Insert test wheel data
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
10, HOTypeWheel, 1, 1, 300, 1001, 0.75, 15, 25, 35, 45, 55, 65, "Test Wheel", "A test wheel")
|
||||||
|
|
||||||
|
// Test loading wheels
|
||||||
|
wheels, err := dhom.LoadWheels(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load wheels: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wheels) != 1 {
|
||||||
|
t.Errorf("Expected 1 wheel, got %d", len(wheels))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wheels) > 0 {
|
||||||
|
wheel := wheels[0]
|
||||||
|
if wheel.ID != 10 {
|
||||||
|
t.Errorf("Expected wheel ID 10, got %d", wheel.ID)
|
||||||
|
}
|
||||||
|
if wheel.StarterLinkID != 1 {
|
||||||
|
t.Errorf("Expected starter link ID 1, got %d", wheel.StarterLinkID)
|
||||||
|
}
|
||||||
|
if wheel.SpellID != 1001 {
|
||||||
|
t.Errorf("Expected spell ID 1001, got %d", wheel.SpellID)
|
||||||
|
}
|
||||||
|
if wheel.Chance != 0.75 {
|
||||||
|
t.Errorf("Expected chance 0.75, got %f", wheel.Chance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_LoadWheelsForStarter tests loading wheels for specific starter
|
||||||
|
func TestDatabaseHeroicOPManager_LoadWheelsForStarter(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
starterID := int32(5)
|
||||||
|
|
||||||
|
// Insert multiple wheels for the same starter
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
20, HOTypeWheel, starterID, 1, 400, 1002, 0.5, 16, 26, 36, 46, 56, 66, "Wheel 1", "First wheel")
|
||||||
|
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
21, HOTypeWheel, starterID, 2, 500, 1003, 0.8, 17, 27, 37, 47, 57, 67, "Wheel 2", "Second wheel")
|
||||||
|
|
||||||
|
// Test loading wheels for specific starter
|
||||||
|
wheels, err := dhom.LoadWheelsForStarter(ctx, starterID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load wheels for starter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wheels) != 2 {
|
||||||
|
t.Errorf("Expected 2 wheels, got %d", len(wheels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test loading wheels for non-existent starter
|
||||||
|
wheels, err = dhom.LoadWheelsForStarter(ctx, 999)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load wheels for non-existent starter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wheels) != 0 {
|
||||||
|
t.Errorf("Expected 0 wheels for non-existent starter, got %d", len(wheels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_SaveStarter tests saving a heroic op starter
|
||||||
|
func TestDatabaseHeroicOPManager_SaveStarter(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Create a test starter
|
||||||
|
starter := &HeroicOPStarter{
|
||||||
|
ID: 100,
|
||||||
|
StartClass: 15,
|
||||||
|
StarterIcon: 500,
|
||||||
|
Abilities: [6]int16{70, 80, 90, 100, 110, 120},
|
||||||
|
Name: "Saved Starter",
|
||||||
|
Description: "A saved test starter",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the starter
|
||||||
|
err := dhom.SaveStarter(ctx, starter)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save starter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and verify
|
||||||
|
loaded, err := dhom.LoadStarter(ctx, 100)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load saved starter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loaded.StarterClass != 15 {
|
||||||
|
t.Errorf("Expected starter class 15, got %d", loaded.StarterClass)
|
||||||
|
}
|
||||||
|
if loaded.Name != "Saved Starter" {
|
||||||
|
t.Errorf("Expected name 'Saved Starter', got '%s'", loaded.Name)
|
||||||
|
}
|
||||||
|
if loaded.Ability1 != 70 {
|
||||||
|
t.Errorf("Expected ability1 70, got %d", loaded.Ability1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_SaveWheel tests saving a heroic op wheel
|
||||||
|
func TestDatabaseHeroicOPManager_SaveWheel(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Create a test wheel
|
||||||
|
wheel := &HeroicOPWheel{
|
||||||
|
ID: 200,
|
||||||
|
StarterLinkID: 100,
|
||||||
|
Order: 2,
|
||||||
|
ShiftIcon: 600,
|
||||||
|
SpellID: 2001,
|
||||||
|
Chance: 0.9,
|
||||||
|
Abilities: [6]int16{71, 81, 91, 101, 111, 121},
|
||||||
|
Name: "Saved Wheel",
|
||||||
|
Description: "A saved test wheel",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the wheel
|
||||||
|
err := dhom.SaveWheel(ctx, wheel)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save wheel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and verify
|
||||||
|
loaded, err := dhom.LoadWheel(ctx, 200)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load saved wheel: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loaded.StarterLinkID != 100 {
|
||||||
|
t.Errorf("Expected starter link ID 100, got %d", loaded.StarterLinkID)
|
||||||
|
}
|
||||||
|
if loaded.SpellID != 2001 {
|
||||||
|
t.Errorf("Expected spell ID 2001, got %d", loaded.SpellID)
|
||||||
|
}
|
||||||
|
if loaded.Chance != 0.9 {
|
||||||
|
t.Errorf("Expected chance 0.9, got %f", loaded.Chance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_DeleteStarter tests deleting a starter and its wheels
|
||||||
|
func TestDatabaseHeroicOPManager_DeleteStarter(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
starterID := int32(300)
|
||||||
|
|
||||||
|
// Insert starter
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, ?, ?, 0, 0, 0, 0, 0.0, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
starterID, HOTypeStarter, 20, 700, 72, 82, 92, 102, 112, 122, "Delete Test Starter", "To be deleted")
|
||||||
|
|
||||||
|
// Insert associated wheels
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
400, HOTypeWheel, starterID, 1, 800, 3001, 0.6, 73, 83, 93, 103, 113, 123, "Wheel to Delete", "Will be deleted")
|
||||||
|
|
||||||
|
// Verify they exist
|
||||||
|
_, err := dhom.LoadStarter(ctx, starterID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Starter should exist before deletion: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wheels, err := dhom.LoadWheelsForStarter(ctx, starterID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load wheels before deletion: %v", err)
|
||||||
|
}
|
||||||
|
if len(wheels) != 1 {
|
||||||
|
t.Errorf("Expected 1 wheel before deletion, got %d", len(wheels))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the starter
|
||||||
|
err = dhom.DeleteStarter(ctx, starterID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete starter: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify they're gone
|
||||||
|
_, err = dhom.LoadStarter(ctx, starterID)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error loading deleted starter, got nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
wheels, err = dhom.LoadWheelsForStarter(ctx, starterID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load wheels after deletion: %v", err)
|
||||||
|
}
|
||||||
|
if len(wheels) != 0 {
|
||||||
|
t.Errorf("Expected 0 wheels after deletion, got %d", len(wheels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_HeroicOPInstance tests HO instance operations
|
||||||
|
func TestDatabaseHeroicOPManager_HeroicOPInstance(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Create a test HO instance
|
||||||
|
ho := &HeroicOP{
|
||||||
|
ID: 1000,
|
||||||
|
EncounterID: 500,
|
||||||
|
StarterID: 10,
|
||||||
|
WheelID: 20,
|
||||||
|
State: int8(HOStateWheelPhase),
|
||||||
|
StartTime: time.Now(),
|
||||||
|
WheelStartTime: time.Now().Add(5 * time.Second),
|
||||||
|
TimeRemaining: 8000,
|
||||||
|
TotalTime: 10000,
|
||||||
|
Complete: 0,
|
||||||
|
Countered: [6]int8{1, 1, 0, 0, 0, 0},
|
||||||
|
ShiftUsed: 0,
|
||||||
|
StarterProgress: 3,
|
||||||
|
CompletedBy: 0,
|
||||||
|
SpellName: "Test Spell",
|
||||||
|
SpellDescription: "A test completion spell",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the HO instance
|
||||||
|
err := dhom.SaveHOInstance(ctx, ho)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save HO instance: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and verify
|
||||||
|
loaded, err := dhom.LoadHOInstance(ctx, 1000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load HO instance: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if loaded.EncounterID != 500 {
|
||||||
|
t.Errorf("Expected encounter ID 500, got %d", loaded.EncounterID)
|
||||||
|
}
|
||||||
|
if loaded.State != int8(HOStateWheelPhase) {
|
||||||
|
t.Errorf("Expected state %d, got %d", HOStateWheelPhase, loaded.State)
|
||||||
|
}
|
||||||
|
if loaded.TimeRemaining != 8000 {
|
||||||
|
t.Errorf("Expected time remaining 8000, got %d", loaded.TimeRemaining)
|
||||||
|
}
|
||||||
|
if loaded.SpellName != "Test Spell" {
|
||||||
|
t.Errorf("Expected spell name 'Test Spell', got '%s'", loaded.SpellName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the instance
|
||||||
|
err = dhom.DeleteHOInstance(ctx, 1000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to delete HO instance: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify it's gone
|
||||||
|
_, err = dhom.LoadHOInstance(ctx, 1000)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Expected error loading deleted HO instance, got nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_HeroicOPEvents tests HO event operations
|
||||||
|
func TestDatabaseHeroicOPManager_HeroicOPEvents(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
instanceID := int64(2000)
|
||||||
|
|
||||||
|
// Create test events
|
||||||
|
events := []*HeroicOPEvent{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
InstanceID: instanceID,
|
||||||
|
EventType: EventHOStarted,
|
||||||
|
CharacterID: 100,
|
||||||
|
AbilityIcon: 50,
|
||||||
|
Timestamp: time.Now(),
|
||||||
|
Data: "started",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: 2,
|
||||||
|
InstanceID: instanceID,
|
||||||
|
EventType: EventHOAbilityUsed,
|
||||||
|
CharacterID: 101,
|
||||||
|
AbilityIcon: 51,
|
||||||
|
Timestamp: time.Now().Add(1 * time.Second),
|
||||||
|
Data: "ability used",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save events
|
||||||
|
for _, event := range events {
|
||||||
|
err := dhom.SaveHOEvent(ctx, event)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to save HO event: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and verify
|
||||||
|
loadedEvents, err := dhom.LoadHOEvents(ctx, instanceID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to load HO events: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(loadedEvents) != 2 {
|
||||||
|
t.Errorf("Expected 2 events, got %d", len(loadedEvents))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Events should be ordered by timestamp
|
||||||
|
if len(loadedEvents) >= 2 {
|
||||||
|
if loadedEvents[0].EventType != EventHOStarted {
|
||||||
|
t.Errorf("Expected first event type %d, got %d", EventHOStarted, loadedEvents[0].EventType)
|
||||||
|
}
|
||||||
|
if loadedEvents[1].EventType != EventHOAbilityUsed {
|
||||||
|
t.Errorf("Expected second event type %d, got %d", EventHOAbilityUsed, loadedEvents[1].EventType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_Statistics tests HO statistics retrieval
|
||||||
|
func TestDatabaseHeroicOPManager_Statistics(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
characterID := int32(1001)
|
||||||
|
|
||||||
|
// Insert test events for statistics
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_op_events
|
||||||
|
(id, instance_id, event_type, character_id, ability_icon, timestamp, data)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
1, 100, EventHOStarted, characterID, 0, time.Now().Unix(), "")
|
||||||
|
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_op_events
|
||||||
|
(id, instance_id, event_type, character_id, ability_icon, timestamp, data)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
2, 100, EventHOCompleted, characterID, 0, time.Now().Add(10*time.Second).Unix(), "")
|
||||||
|
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_op_events
|
||||||
|
(id, instance_id, event_type, character_id, ability_icon, timestamp, data)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
3, 101, EventHOStarted, characterID, 0, time.Now().Add(20*time.Second).Unix(), "")
|
||||||
|
|
||||||
|
// Get statistics
|
||||||
|
stats, err := dhom.GetHOStatistics(ctx, characterID)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get HO statistics: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stats.TotalHOsStarted != 2 {
|
||||||
|
t.Errorf("Expected 2 HOs started, got %d", stats.TotalHOsStarted)
|
||||||
|
}
|
||||||
|
if stats.TotalHOsCompleted != 1 {
|
||||||
|
t.Errorf("Expected 1 HO completed, got %d", stats.TotalHOsCompleted)
|
||||||
|
}
|
||||||
|
if stats.SuccessRate != 50.0 {
|
||||||
|
t.Errorf("Expected success rate 50.0, got %f", stats.SuccessRate)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDatabaseHeroicOPManager_NextIDs tests ID generation
|
||||||
|
func TestDatabaseHeroicOPManager_NextIDs(t *testing.T) {
|
||||||
|
pool := createTestPool(t)
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Test next starter ID (should be 1 for empty database)
|
||||||
|
starterID, err := dhom.GetNextStarterID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get next starter ID: %v", err)
|
||||||
|
}
|
||||||
|
if starterID != 1 {
|
||||||
|
t.Errorf("Expected next starter ID 1, got %d", starterID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert a starter and test again
|
||||||
|
execSQL(t, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, ?, ?, 0, 0, 0, 0, 0.0, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
5, HOTypeStarter, 1, 100, 1, 2, 3, 4, 5, 6, "Test", "Test")
|
||||||
|
|
||||||
|
starterID, err = dhom.GetNextStarterID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get next starter ID after insert: %v", err)
|
||||||
|
}
|
||||||
|
if starterID != 6 {
|
||||||
|
t.Errorf("Expected next starter ID 6, got %d", starterID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test next wheel ID
|
||||||
|
wheelID, err := dhom.GetNextWheelID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get next wheel ID: %v", err)
|
||||||
|
}
|
||||||
|
if wheelID != 1 {
|
||||||
|
t.Errorf("Expected next wheel ID 1, got %d", wheelID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test next instance ID
|
||||||
|
instanceID, err := dhom.GetNextInstanceID(ctx)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to get next instance ID: %v", err)
|
||||||
|
}
|
||||||
|
if instanceID != 1 {
|
||||||
|
t.Errorf("Expected next instance ID 1, got %d", instanceID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDatabaseHeroicOPManager_LoadStarters benchmarks loading starters
|
||||||
|
func BenchmarkDatabaseHeroicOPManager_LoadStarters(b *testing.B) {
|
||||||
|
pool := createTestPool(&testing.T{})
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Insert test data
|
||||||
|
for i := 1; i <= 50; i++ {
|
||||||
|
execSQL(&testing.T{}, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, ?, ?, 0, 0, 0, 0, 0.0, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
i, HOTypeStarter, i%10+1, i*10,
|
||||||
|
i*1, i*2, i*3, i*4, i*5, i*6,
|
||||||
|
fmt.Sprintf("Starter %d", i), fmt.Sprintf("Description %d", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
starters, err := dhom.LoadStarters(ctx)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Failed to load starters: %v", err)
|
||||||
|
}
|
||||||
|
if len(starters) != 50 {
|
||||||
|
b.Errorf("Expected 50 starters, got %d", len(starters))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDatabaseHeroicOPManager_LoadWheels benchmarks loading wheels
|
||||||
|
func BenchmarkDatabaseHeroicOPManager_LoadWheels(b *testing.B) {
|
||||||
|
pool := createTestPool(&testing.T{})
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
// Insert test wheel data
|
||||||
|
for i := 1; i <= 100; i++ {
|
||||||
|
execSQL(&testing.T{}, pool, `INSERT INTO heroic_ops
|
||||||
|
(id, ho_type, starter_class, starter_icon, starter_link_id, chain_order,
|
||||||
|
shift_icon, spell_id, chance, ability1, ability2, ability3, ability4,
|
||||||
|
ability5, ability6, name, description)
|
||||||
|
VALUES (?, ?, 0, 0, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
|
i, HOTypeWheel, i%10+1, i%3+1, i*10, i*100+1000, float32(i%100)/100.0,
|
||||||
|
i*1, i*2, i*3, i*4, i*5, i*6,
|
||||||
|
fmt.Sprintf("Wheel %d", i), fmt.Sprintf("Wheel Description %d", i))
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
wheels, err := dhom.LoadWheels(ctx)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Failed to load wheels: %v", err)
|
||||||
|
}
|
||||||
|
if len(wheels) != 100 {
|
||||||
|
b.Errorf("Expected 100 wheels, got %d", len(wheels))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDatabaseHeroicOPManager_SaveStarter benchmarks saving starters
|
||||||
|
func BenchmarkDatabaseHeroicOPManager_SaveStarter(b *testing.B) {
|
||||||
|
pool := createTestPool(&testing.T{})
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
starter := &HeroicOPStarter{
|
||||||
|
ID: 1000,
|
||||||
|
StartClass: 5,
|
||||||
|
StarterIcon: 100,
|
||||||
|
Abilities: [6]int16{10, 20, 30, 40, 50, 60},
|
||||||
|
Name: "Benchmark Starter",
|
||||||
|
Description: "A benchmark test starter",
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := dhom.SaveStarter(ctx, starter)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Failed to save starter: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkDatabaseHeroicOPManager_SaveHOInstance benchmarks saving HO instances
|
||||||
|
func BenchmarkDatabaseHeroicOPManager_SaveHOInstance(b *testing.B) {
|
||||||
|
pool := createTestPool(&testing.T{})
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
dhom := NewDatabaseHeroicOPManager(pool)
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
ho := &HeroicOP{
|
||||||
|
ID: 5000,
|
||||||
|
EncounterID: 1000,
|
||||||
|
StarterID: 50,
|
||||||
|
WheelID: 100,
|
||||||
|
State: int8(HOStateWheelPhase),
|
||||||
|
StartTime: time.Now(),
|
||||||
|
WheelStartTime: time.Now(),
|
||||||
|
TimeRemaining: 10000,
|
||||||
|
TotalTime: 10000,
|
||||||
|
Complete: 0,
|
||||||
|
Countered: [6]int8{0, 0, 0, 0, 0, 0},
|
||||||
|
ShiftUsed: 0,
|
||||||
|
StarterProgress: 0,
|
||||||
|
CompletedBy: 0,
|
||||||
|
SpellName: "Benchmark Spell",
|
||||||
|
SpellDescription: "A benchmark spell",
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
err := dhom.SaveHOInstance(ctx, ho)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("Failed to save HO instance: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,6 +14,9 @@ func NewHeroicOPManager(masterList *MasterHeroicOPList, database HeroicOPDatabas
|
|||||||
encounterHOs: make(map[int32][]*HeroicOP),
|
encounterHOs: make(map[int32][]*HeroicOP),
|
||||||
masterList: masterList,
|
masterList: masterList,
|
||||||
database: database,
|
database: database,
|
||||||
|
clientManager: clientManager,
|
||||||
|
encounterManager: encounterManager,
|
||||||
|
playerManager: playerManager,
|
||||||
nextInstanceID: 1,
|
nextInstanceID: 1,
|
||||||
defaultWheelTimer: DefaultWheelTimerSeconds * 1000, // Convert to milliseconds
|
defaultWheelTimer: DefaultWheelTimerSeconds * 1000, // Convert to milliseconds
|
||||||
maxConcurrentHOs: MaxConcurrentHOs,
|
maxConcurrentHOs: MaxConcurrentHOs,
|
||||||
@ -415,7 +418,7 @@ func (hom *HeroicOPManager) completeHO(ctx context.Context, ho *HeroicOP, wheel
|
|||||||
|
|
||||||
// Cast completion spell
|
// Cast completion spell
|
||||||
if wheel.SpellID > 0 {
|
if wheel.SpellID > 0 {
|
||||||
participants := ho.GetParticipants()
|
_ = ho.GetParticipants() // participants will be used when spell manager is integrated
|
||||||
// TODO: Cast spell on participants through spell manager
|
// TODO: Cast spell on participants through spell manager
|
||||||
// hom.spellManager.CastSpell(completedBy, wheel.SpellID, participants)
|
// hom.spellManager.CastSpell(completedBy, wheel.SpellID, participants)
|
||||||
}
|
}
|
||||||
@ -547,9 +550,9 @@ func (hom *HeroicOPManager) sendShiftUpdate(ho *HeroicOP, oldWheelID, newWheelID
|
|||||||
participants := ho.GetParticipants()
|
participants := ho.GetParticipants()
|
||||||
packetBuilder := NewHeroicOPPacketBuilder(0)
|
packetBuilder := NewHeroicOPPacketBuilder(0)
|
||||||
|
|
||||||
for _, characterID := range participants {
|
for range participants {
|
||||||
if packet, err := packetBuilder.BuildHOShiftPacket(ho, oldWheelID, newWheelID); err == nil {
|
if packet, err := packetBuilder.BuildHOShiftPacket(ho, oldWheelID, newWheelID); err == nil {
|
||||||
// TODO: Send packet through client manager
|
// TODO: Send packet through client manager using characterID
|
||||||
_ = packet // Placeholder
|
_ = packet // Placeholder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,14 +97,17 @@ type MasterHeroicOPList struct {
|
|||||||
|
|
||||||
// HeroicOPManager manages active heroic opportunity instances
|
// HeroicOPManager manages active heroic opportunity instances
|
||||||
type HeroicOPManager struct {
|
type HeroicOPManager struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
activeHOs map[int64]*HeroicOP // instance_id -> HO
|
activeHOs map[int64]*HeroicOP // instance_id -> HO
|
||||||
encounterHOs map[int32][]*HeroicOP // encounter_id -> HOs
|
encounterHOs map[int32][]*HeroicOP // encounter_id -> HOs
|
||||||
masterList *MasterHeroicOPList
|
masterList *MasterHeroicOPList
|
||||||
database HeroicOPDatabase
|
database HeroicOPDatabase
|
||||||
eventHandler HeroicOPEventHandler
|
eventHandler HeroicOPEventHandler
|
||||||
logger LogHandler
|
logger LogHandler
|
||||||
nextInstanceID int64
|
clientManager ClientManager
|
||||||
|
encounterManager EncounterManager
|
||||||
|
playerManager PlayerManager
|
||||||
|
nextInstanceID int64
|
||||||
// Configuration
|
// Configuration
|
||||||
defaultWheelTimer int32 // milliseconds
|
defaultWheelTimer int32 // milliseconds
|
||||||
maxConcurrentHOs int
|
maxConcurrentHOs int
|
||||||
|
Loading…
x
Reference in New Issue
Block a user