test: Add comprehensive test suite for database functionality
- Add unit tests for database operations and optimization - Test external data source loading and caching - Add callsign manager functionality tests - Create test helpers for database testing utilities - Ensure database reliability and performance validation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
0f16748224
commit
5733209251
5 changed files with 955 additions and 0 deletions
307
internal/database/optimization_test.go
Normal file
307
internal/database/optimization_test.go
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestOptimizationManager_VacuumDatabase(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
config := &Config{Path: db.config.Path}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
err := optimizer.VacuumDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("VacuumDatabase failed:", err)
|
||||
}
|
||||
|
||||
// Verify vacuum was successful by checking database integrity
|
||||
conn := db.GetConnection()
|
||||
var result string
|
||||
err = conn.QueryRow("PRAGMA integrity_check").Scan(&result)
|
||||
if err != nil {
|
||||
t.Error("Failed to run integrity check:", err)
|
||||
}
|
||||
if result != "ok" {
|
||||
t.Errorf("Database integrity check failed: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationManager_OptimizeDatabase(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
config := &Config{Path: db.config.Path}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
err := optimizer.OptimizeDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("OptimizeDatabase failed:", err)
|
||||
}
|
||||
|
||||
// Check that auto_vacuum was set
|
||||
conn := db.GetConnection()
|
||||
var autoVacuum int
|
||||
err = conn.QueryRow("PRAGMA auto_vacuum").Scan(&autoVacuum)
|
||||
if err != nil {
|
||||
t.Error("Failed to check auto_vacuum setting:", err)
|
||||
}
|
||||
// Should be 2 (INCREMENTAL) after optimization
|
||||
if autoVacuum != 2 {
|
||||
t.Errorf("Expected auto_vacuum = 2 (INCREMENTAL), got %d", autoVacuum)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationManager_OptimizePageSize(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
config := &Config{Path: db.config.Path}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
// Get current page size
|
||||
conn := db.GetConnection()
|
||||
var currentPageSize int
|
||||
err := conn.QueryRow("PRAGMA page_size").Scan(¤tPageSize)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get current page size:", err)
|
||||
}
|
||||
|
||||
// Set a different page size
|
||||
targetPageSize := 8192
|
||||
if currentPageSize == targetPageSize {
|
||||
targetPageSize = 4096 // Use different size if already at target
|
||||
}
|
||||
|
||||
err = optimizer.OptimizePageSize(targetPageSize)
|
||||
if err != nil {
|
||||
t.Fatal("OptimizePageSize failed:", err)
|
||||
}
|
||||
|
||||
// Verify page size was changed
|
||||
var newPageSize int
|
||||
err = conn.QueryRow("PRAGMA page_size").Scan(&newPageSize)
|
||||
if err != nil {
|
||||
t.Error("Failed to get new page size:", err)
|
||||
}
|
||||
if newPageSize != targetPageSize {
|
||||
t.Errorf("Expected page size %d, got %d", targetPageSize, newPageSize)
|
||||
}
|
||||
|
||||
// Test setting same page size (should be no-op)
|
||||
err = optimizer.OptimizePageSize(targetPageSize)
|
||||
if err != nil {
|
||||
t.Error("OptimizePageSize failed for same page size:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationManager_GetOptimizationStats(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
config := &Config{Path: db.config.Path}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
// Insert some test data to make stats more meaningful
|
||||
conn := db.GetConnection()
|
||||
_, err := conn.Exec(`
|
||||
INSERT INTO airlines (id, name, alias, iata_code, icao_code, callsign, country, active, data_source)
|
||||
VALUES (1, 'Test Airways', 'Test', 'TA', 'TST', 'TESTAIR', 'United States', 1, 'test')
|
||||
`)
|
||||
if err != nil {
|
||||
t.Error("Failed to insert test data:", err)
|
||||
}
|
||||
|
||||
stats, err := optimizer.GetOptimizationStats()
|
||||
if err != nil {
|
||||
t.Fatal("GetOptimizationStats failed:", err)
|
||||
}
|
||||
|
||||
if stats == nil {
|
||||
t.Fatal("Expected stats, got nil")
|
||||
}
|
||||
|
||||
// Check basic stats
|
||||
if stats.DatabaseSize <= 0 {
|
||||
t.Error("Database size should be greater than 0")
|
||||
}
|
||||
if stats.PageSize <= 0 {
|
||||
t.Error("Page size should be greater than 0")
|
||||
}
|
||||
if stats.PageCount <= 0 {
|
||||
t.Error("Page count should be greater than 0")
|
||||
}
|
||||
if stats.UsedPages < 0 {
|
||||
t.Error("Used pages should be non-negative")
|
||||
}
|
||||
if stats.FreePages < 0 {
|
||||
t.Error("Free pages should be non-negative")
|
||||
}
|
||||
if stats.Efficiency < 0 || stats.Efficiency > 100 {
|
||||
t.Errorf("Efficiency should be between 0-100%%, got %.2f%%", stats.Efficiency)
|
||||
}
|
||||
|
||||
t.Logf("Database stats: Size=%d bytes, Pages=%d (used=%d, free=%d), Efficiency=%.1f%%",
|
||||
stats.DatabaseSize, stats.PageCount, stats.UsedPages, stats.FreePages, stats.Efficiency)
|
||||
}
|
||||
|
||||
func TestOptimizationManager_PerformMaintenance(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
config := &Config{
|
||||
Path: db.config.Path,
|
||||
VacuumInterval: time.Millisecond, // Very short interval for testing
|
||||
}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
// Should perform vacuum due to short interval
|
||||
err := optimizer.PerformMaintenance()
|
||||
if err != nil {
|
||||
t.Fatal("PerformMaintenance failed:", err)
|
||||
}
|
||||
|
||||
// Check that lastVacuum was updated
|
||||
if optimizer.lastVacuum.IsZero() {
|
||||
t.Error("lastVacuum should be set after maintenance")
|
||||
}
|
||||
|
||||
// Wait a bit and run again with longer interval
|
||||
config.VacuumInterval = time.Hour // Long interval
|
||||
err = optimizer.PerformMaintenance()
|
||||
if err != nil {
|
||||
t.Fatal("Second PerformMaintenance failed:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationManager_getDatabaseSize(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
config := &Config{Path: db.config.Path}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
size, err := optimizer.getDatabaseSize()
|
||||
if err != nil {
|
||||
t.Fatal("getDatabaseSize failed:", err)
|
||||
}
|
||||
|
||||
if size <= 0 {
|
||||
t.Error("Database size should be greater than 0")
|
||||
}
|
||||
|
||||
// Verify size matches actual file size
|
||||
stat, err := os.Stat(db.config.Path)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to stat database file:", err)
|
||||
}
|
||||
|
||||
if size != stat.Size() {
|
||||
t.Errorf("getDatabaseSize returned %d, but file size is %d", size, stat.Size())
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationManager_InvalidPath(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
// Test with invalid path
|
||||
config := &Config{Path: "/nonexistent/path/database.db"}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
_, err := optimizer.getDatabaseSize()
|
||||
if err == nil {
|
||||
t.Error("getDatabaseSize should fail with invalid path")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationStats_JSON(t *testing.T) {
|
||||
stats := &OptimizationStats{
|
||||
DatabaseSize: 1024000,
|
||||
PageSize: 4096,
|
||||
PageCount: 250,
|
||||
UsedPages: 200,
|
||||
FreePages: 50,
|
||||
Efficiency: 80.0,
|
||||
AutoVacuumEnabled: true,
|
||||
LastVacuum: time.Now(),
|
||||
}
|
||||
|
||||
// Test that all fields are accessible
|
||||
if stats.DatabaseSize != 1024000 {
|
||||
t.Error("DatabaseSize not preserved")
|
||||
}
|
||||
if stats.PageSize != 4096 {
|
||||
t.Error("PageSize not preserved")
|
||||
}
|
||||
if stats.Efficiency != 80.0 {
|
||||
t.Error("Efficiency not preserved")
|
||||
}
|
||||
if !stats.AutoVacuumEnabled {
|
||||
t.Error("AutoVacuumEnabled not preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimizationManager_WithRealData(t *testing.T) {
|
||||
db, cleanup := setupTestDatabase(t)
|
||||
defer cleanup()
|
||||
|
||||
// Load some real data to make optimization more realistic
|
||||
// Skip actual data loading in tests as it requires network access
|
||||
// Just insert minimal test data
|
||||
conn := db.GetConnection()
|
||||
_, err := conn.Exec(`INSERT INTO airlines (id, name, alias, iata_code, icao_code, callsign, country, active, data_source)
|
||||
VALUES (1, 'Test Airways', 'Test', 'TA', 'TST', 'TESTAIR', 'United States', 1, 'test')`)
|
||||
if err != nil {
|
||||
t.Fatal("Failed to insert test data:", err)
|
||||
}
|
||||
|
||||
config := &Config{Path: db.config.Path}
|
||||
optimizer := NewOptimizationManager(db, config)
|
||||
|
||||
// Get stats before optimization
|
||||
statsBefore, err := optimizer.GetOptimizationStats()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get stats before optimization:", err)
|
||||
}
|
||||
|
||||
// Run optimization
|
||||
err = optimizer.OptimizeDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("OptimizeDatabase failed:", err)
|
||||
}
|
||||
|
||||
err = optimizer.VacuumDatabase()
|
||||
if err != nil {
|
||||
t.Fatal("VacuumDatabase failed:", err)
|
||||
}
|
||||
|
||||
// Get stats after optimization
|
||||
statsAfter, err := optimizer.GetOptimizationStats()
|
||||
if err != nil {
|
||||
t.Fatal("Failed to get stats after optimization:", err)
|
||||
}
|
||||
|
||||
// Compare efficiency
|
||||
t.Logf("Optimization results: %.2f%% → %.2f%% efficiency",
|
||||
statsBefore.Efficiency, statsAfter.Efficiency)
|
||||
|
||||
// After optimization, we should have auto-vacuum enabled
|
||||
if !statsAfter.AutoVacuumEnabled {
|
||||
t.Error("Auto-vacuum should be enabled after optimization")
|
||||
}
|
||||
|
||||
// Database should still be functional
|
||||
conn = db.GetConnection()
|
||||
var count int
|
||||
err = conn.QueryRow("SELECT COUNT(*) FROM airlines").Scan(&count)
|
||||
if err != nil {
|
||||
t.Error("Database not functional after optimization:", err)
|
||||
}
|
||||
if count == 0 {
|
||||
t.Error("Data lost during optimization")
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue