added tests to config and database
This commit is contained in:
@@ -0,0 +1,405 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bamort/config"
|
||||
"database/sql/driver"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// setupTestEnvironment sets up test environment variables
|
||||
func setupTestEnvironment(t *testing.T) {
|
||||
// Save original values
|
||||
origEnv := os.Getenv("ENVIRONMENT")
|
||||
origDevTesting := os.Getenv("DEV_TESTING")
|
||||
origDatabaseType := os.Getenv("DATABASE_TYPE")
|
||||
origDatabaseURL := os.Getenv("DATABASE_URL")
|
||||
|
||||
// Cleanup function to restore original values
|
||||
t.Cleanup(func() {
|
||||
if origEnv != "" {
|
||||
os.Setenv("ENVIRONMENT", origEnv)
|
||||
} else {
|
||||
os.Unsetenv("ENVIRONMENT")
|
||||
}
|
||||
if origDevTesting != "" {
|
||||
os.Setenv("DEV_TESTING", origDevTesting)
|
||||
} else {
|
||||
os.Unsetenv("DEV_TESTING")
|
||||
}
|
||||
if origDatabaseType != "" {
|
||||
os.Setenv("DATABASE_TYPE", origDatabaseType)
|
||||
} else {
|
||||
os.Unsetenv("DATABASE_TYPE")
|
||||
}
|
||||
if origDatabaseURL != "" {
|
||||
os.Setenv("DATABASE_URL", origDatabaseURL)
|
||||
} else {
|
||||
os.Unsetenv("DATABASE_URL")
|
||||
}
|
||||
|
||||
// Reset global DB variable
|
||||
DB = nil
|
||||
|
||||
// Reload configuration
|
||||
config.LoadConfig()
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetBackendDir(t *testing.T) {
|
||||
// Test that getBackendDir returns a valid path
|
||||
backendDir := getBackendDir()
|
||||
|
||||
// Should be an absolute path
|
||||
assert.True(t, filepath.IsAbs(backendDir), "getBackendDir should return an absolute path")
|
||||
|
||||
// Should end with "backend"
|
||||
assert.True(t, strings.HasSuffix(backendDir, "backend"), "getBackendDir should return path ending with 'backend'")
|
||||
|
||||
// The directory should exist
|
||||
info, err := os.Stat(backendDir)
|
||||
assert.NoError(t, err, "Backend directory should exist")
|
||||
assert.True(t, info.IsDir(), "Backend path should be a directory")
|
||||
|
||||
// Should contain expected subdirectories
|
||||
expectedDirs := []string{"database", "models", "config"}
|
||||
for _, expectedDir := range expectedDirs {
|
||||
dirPath := filepath.Join(backendDir, expectedDir)
|
||||
info, err := os.Stat(dirPath)
|
||||
assert.NoError(t, err, "Expected directory %s should exist", expectedDir)
|
||||
if err == nil {
|
||||
assert.True(t, info.IsDir(), "%s should be a directory", expectedDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreparedTestDBPath(t *testing.T) {
|
||||
// Test that PreparedTestDB contains the correct path
|
||||
assert.True(t, strings.Contains(PreparedTestDB, "testdata"), "PreparedTestDB should contain testdata directory")
|
||||
assert.True(t, strings.HasSuffix(PreparedTestDB, "prepared_test_data.db"), "PreparedTestDB should end with prepared_test_data.db")
|
||||
assert.True(t, filepath.IsAbs(PreparedTestDB), "PreparedTestDB should be an absolute path")
|
||||
}
|
||||
|
||||
func TestTestDataDirPath(t *testing.T) {
|
||||
// Test that TestDataDir contains the correct path
|
||||
assert.True(t, strings.Contains(TestDataDir, "maintenance"), "TestDataDir should contain maintenance directory")
|
||||
assert.True(t, strings.Contains(TestDataDir, "testdata"), "TestDataDir should contain testdata directory")
|
||||
assert.True(t, filepath.IsAbs(TestDataDir), "TestDataDir should be an absolute path")
|
||||
}
|
||||
|
||||
func TestConnectDatabase_TestEnvironment(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Set environment to test
|
||||
os.Setenv("ENVIRONMENT", "test")
|
||||
config.LoadConfig()
|
||||
|
||||
// Reset DB to ensure fresh connection
|
||||
DB = nil
|
||||
|
||||
// ConnectDatabase should use test database when environment is "test"
|
||||
db := ConnectDatabase()
|
||||
|
||||
assert.NotNil(t, db, "ConnectDatabase should return a valid database connection")
|
||||
assert.Equal(t, db, DB, "ConnectDatabase should set global DB variable")
|
||||
}
|
||||
|
||||
func TestConnectDatabase_DevTestingYes(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Set dev testing to yes
|
||||
os.Setenv("ENVIRONMENT", "production")
|
||||
os.Setenv("DEV_TESTING", "yes")
|
||||
config.LoadConfig()
|
||||
|
||||
// Reset DB to ensure fresh connection
|
||||
DB = nil
|
||||
|
||||
// ConnectDatabase should use test database when DEV_TESTING=yes
|
||||
db := ConnectDatabase()
|
||||
|
||||
assert.NotNil(t, db, "ConnectDatabase should return a valid database connection")
|
||||
assert.Equal(t, db, DB, "ConnectDatabase should set global DB variable")
|
||||
}
|
||||
|
||||
func TestConnectDatabaseOrig_SQLite(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Create a temporary SQLite file
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "test.db")
|
||||
|
||||
// Set up configuration for SQLite
|
||||
os.Setenv("DATABASE_TYPE", "sqlite")
|
||||
os.Setenv("DATABASE_URL", dbPath)
|
||||
config.LoadConfig()
|
||||
|
||||
// Reset DB to ensure fresh connection
|
||||
DB = nil
|
||||
|
||||
// Test ConnectDatabaseOrig with SQLite
|
||||
db := ConnectDatabaseOrig()
|
||||
|
||||
assert.NotNil(t, db, "ConnectDatabaseOrig should return a valid SQLite connection")
|
||||
assert.Equal(t, db, DB, "ConnectDatabaseOrig should set global DB variable")
|
||||
|
||||
// Verify we can perform basic operations
|
||||
sqlDB, err := db.DB()
|
||||
assert.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping the database")
|
||||
}
|
||||
|
||||
func TestConnectDatabaseOrig_DefaultMySQL(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Set up configuration with empty DATABASE_URL to trigger default MySQL
|
||||
os.Setenv("DATABASE_TYPE", "mysql")
|
||||
os.Setenv("DATABASE_URL", "")
|
||||
config.LoadConfig()
|
||||
|
||||
// Reset DB to ensure fresh connection
|
||||
DB = nil
|
||||
|
||||
// Note: This test will fail to connect since we don't have a real MySQL server
|
||||
// But we can test that it attempts to use the default configuration
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Expected to panic since we don't have a real MySQL server
|
||||
assert.Contains(t, r.(string), "Failed to connect to database", "Should panic with connection error")
|
||||
}
|
||||
}()
|
||||
|
||||
ConnectDatabaseOrig()
|
||||
|
||||
// If we reach here, the connection surprisingly succeeded
|
||||
// This could happen in a test environment with MySQL available
|
||||
if DB != nil {
|
||||
assert.NotNil(t, DB, "If connection succeeds, DB should be set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConnectDatabaseOrig_UnsupportedDatabaseType(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Set up configuration with unsupported database type
|
||||
os.Setenv("DATABASE_TYPE", "postgresql")
|
||||
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
||||
config.LoadConfig()
|
||||
|
||||
// Reset DB to ensure fresh connection
|
||||
DB = nil
|
||||
|
||||
// Should fall back to MySQL for unsupported database types
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Expected to panic since we don't have a real MySQL server
|
||||
assert.Contains(t, r.(string), "Failed to connect to database", "Should panic with connection error")
|
||||
}
|
||||
}()
|
||||
|
||||
ConnectDatabaseOrig()
|
||||
}
|
||||
|
||||
func TestGetDB(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Reset DB to ensure fresh connection
|
||||
DB = nil
|
||||
|
||||
// First call should initialize DB
|
||||
db1 := GetDB()
|
||||
assert.NotNil(t, db1, "GetDB should return a valid database connection")
|
||||
assert.Equal(t, db1, DB, "GetDB should set global DB variable")
|
||||
|
||||
// Second call should return the same instance
|
||||
db2 := GetDB()
|
||||
assert.Equal(t, db1, db2, "GetDB should return the same instance on subsequent calls")
|
||||
}
|
||||
|
||||
func TestGetDB_AlreadyInitialized(t *testing.T) {
|
||||
setupTestEnvironment(t)
|
||||
|
||||
// Set up a mock database connection
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "test.db")
|
||||
mockDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create mock database")
|
||||
|
||||
// Set DB to the mock instance
|
||||
DB = mockDB
|
||||
|
||||
// GetDB should return the existing instance
|
||||
db := GetDB()
|
||||
assert.Equal(t, mockDB, db, "GetDB should return the existing DB instance")
|
||||
}
|
||||
|
||||
func TestStringArray_Value(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input StringArray
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "Empty array",
|
||||
input: StringArray{},
|
||||
expected: "[]",
|
||||
},
|
||||
{
|
||||
name: "Single element",
|
||||
input: StringArray{"test"},
|
||||
expected: `["test"]`,
|
||||
},
|
||||
{
|
||||
name: "Multiple elements",
|
||||
input: StringArray{"one", "two", "three"},
|
||||
expected: `["one","two","three"]`,
|
||||
},
|
||||
{
|
||||
name: "Array with special characters",
|
||||
input: StringArray{"test\"quote", "test\nnewline", "test\\backslash"},
|
||||
expected: `["test\"quote","test\nnewline","test\\backslash"]`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
value, err := tt.input.Value()
|
||||
assert.NoError(t, err, "Value() should not return an error")
|
||||
|
||||
// Convert the returned driver.Value to string
|
||||
bytes, ok := value.([]byte)
|
||||
require.True(t, ok, "Value() should return []byte")
|
||||
|
||||
assert.JSONEq(t, tt.expected, string(bytes), "Value() should return correct JSON")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringArray_Scan(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input interface{}
|
||||
expected StringArray
|
||||
hasError bool
|
||||
}{
|
||||
{
|
||||
name: "Nil input",
|
||||
input: nil,
|
||||
expected: StringArray{},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "Empty JSON array",
|
||||
input: []byte("[]"),
|
||||
expected: StringArray{},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "Single element JSON array",
|
||||
input: []byte(`["test"]`),
|
||||
expected: StringArray{"test"},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "Multiple elements JSON array",
|
||||
input: []byte(`["one","two","three"]`),
|
||||
expected: StringArray{"one", "two", "three"},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "JSON array with special characters",
|
||||
input: []byte(`["test\"quote","test\nnewline","test\\backslash"]`),
|
||||
expected: StringArray{"test\"quote", "test\nnewline", "test\\backslash"},
|
||||
hasError: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid input type",
|
||||
input: "not a byte slice",
|
||||
expected: StringArray{},
|
||||
hasError: true,
|
||||
},
|
||||
{
|
||||
name: "Invalid JSON",
|
||||
input: []byte(`invalid json`),
|
||||
expected: StringArray{},
|
||||
hasError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var sa StringArray
|
||||
err := sa.Scan(tt.input)
|
||||
|
||||
if tt.hasError {
|
||||
assert.Error(t, err, "Scan() should return an error for invalid input")
|
||||
} else {
|
||||
assert.NoError(t, err, "Scan() should not return an error for valid input")
|
||||
assert.Equal(t, tt.expected, sa, "Scan() should set correct values")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringArray_ValueScanRoundTrip(t *testing.T) {
|
||||
// Test that Value() and Scan() work together correctly
|
||||
original := StringArray{"test1", "test2", "test3"}
|
||||
|
||||
// Convert to driver.Value
|
||||
value, err := original.Value()
|
||||
assert.NoError(t, err, "Value() should not error")
|
||||
|
||||
// Scan back to StringArray
|
||||
var result StringArray
|
||||
err = result.Scan(value)
|
||||
assert.NoError(t, err, "Scan() should not error")
|
||||
|
||||
// Should be equal to original
|
||||
assert.Equal(t, original, result, "Value/Scan round trip should preserve data")
|
||||
}
|
||||
|
||||
func TestStringArray_DatabaseCompatibility(t *testing.T) {
|
||||
// Test that StringArray implements the required database interfaces
|
||||
var sa StringArray
|
||||
|
||||
// Should implement driver.Valuer
|
||||
_, ok := interface{}(sa).(driver.Valuer)
|
||||
assert.True(t, ok, "StringArray should implement driver.Valuer interface")
|
||||
|
||||
// Should have Scan method for sql.Scanner interface
|
||||
assert.True(t, true, "StringArray has Scan method for sql.Scanner interface")
|
||||
}
|
||||
|
||||
// Benchmark tests for performance-critical functions
|
||||
func BenchmarkGetBackendDir(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
getBackendDir()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringArray_Value(b *testing.B) {
|
||||
sa := StringArray{"test1", "test2", "test3", "test4", "test5"}
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = sa.Value()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStringArray_Scan(b *testing.B) {
|
||||
data := []byte(`["test1","test2","test3","test4","test5"]`)
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
var sa StringArray
|
||||
_ = sa.Scan(data)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Test model for migration testing
|
||||
type TestModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
Name string `gorm:"size:100;not null"`
|
||||
Age int `gorm:"default:0"`
|
||||
}
|
||||
|
||||
// Another test model for migration testing
|
||||
type AnotherTestModel struct {
|
||||
ID uint `gorm:"primarykey"`
|
||||
Description string `gorm:"size:200"`
|
||||
Active bool `gorm:"default:true"`
|
||||
}
|
||||
|
||||
// setupTestDB creates a test database for migration testing
|
||||
func setupTestDB(t *testing.T) *gorm.DB {
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "test_migrate.db")
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create test database")
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
// MockDB creates a mock database that can be configured to return errors
|
||||
type MockDB struct {
|
||||
*gorm.DB
|
||||
shouldError bool
|
||||
errorMsg string
|
||||
}
|
||||
|
||||
func (m *MockDB) AutoMigrate(dst ...interface{}) error {
|
||||
if m.shouldError {
|
||||
return errors.New(m.errorMsg)
|
||||
}
|
||||
return m.DB.AutoMigrate(dst...)
|
||||
}
|
||||
|
||||
func TestMigrateStructure_WithProvidedDB(t *testing.T) {
|
||||
// Create a test database
|
||||
testDB := setupTestDB(t)
|
||||
|
||||
// Test migration with provided database
|
||||
err := MigrateStructure(testDB)
|
||||
assert.NoError(t, err, "MigrateStructure should succeed with valid provided database")
|
||||
|
||||
// Verify that the database connection is working
|
||||
sqlDB, err := testDB.DB()
|
||||
assert.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping the database")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_WithGlobalDB(t *testing.T) {
|
||||
// Save original global DB
|
||||
originalDB := DB
|
||||
defer func() {
|
||||
DB = originalDB
|
||||
}()
|
||||
|
||||
// Set up global test database
|
||||
DB = setupTestDB(t)
|
||||
|
||||
// Test migration without providing database (should use global DB)
|
||||
err := MigrateStructure()
|
||||
assert.NoError(t, err, "MigrateStructure should succeed with global database")
|
||||
|
||||
// Verify that the global database connection is working
|
||||
sqlDB, err := DB.DB()
|
||||
assert.NoError(t, err, "Should be able to get underlying sql.DB from global DB")
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping the global database")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_WithNilProvidedDB(t *testing.T) {
|
||||
// Save original global DB
|
||||
originalDB := DB
|
||||
defer func() {
|
||||
DB = originalDB
|
||||
}()
|
||||
|
||||
// Set up global test database
|
||||
DB = setupTestDB(t)
|
||||
|
||||
// Test migration with nil provided database (should fall back to global DB)
|
||||
err := MigrateStructure(nil)
|
||||
assert.NoError(t, err, "MigrateStructure should succeed when nil is provided and global DB exists")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_WithMultipleDBParams(t *testing.T) {
|
||||
// Create two test databases
|
||||
testDB1 := setupTestDB(t)
|
||||
testDB2 := setupTestDB(t)
|
||||
|
||||
// Test that it uses the first provided database
|
||||
err := MigrateStructure(testDB1, testDB2)
|
||||
assert.NoError(t, err, "MigrateStructure should succeed with multiple DB parameters")
|
||||
|
||||
// The function should have used testDB1 (the first parameter)
|
||||
// We can't directly verify this, but the test should pass if the first DB is valid
|
||||
}
|
||||
|
||||
func TestMigrateStructure_WithActualModels(t *testing.T) {
|
||||
// Create a test database
|
||||
testDB := setupTestDB(t)
|
||||
|
||||
// Register models for migration
|
||||
err := testDB.AutoMigrate(&TestModel{}, &AnotherTestModel{})
|
||||
require.NoError(t, err, "Should be able to register test models")
|
||||
|
||||
// Test migration (AutoMigrate is called without models, which is valid in GORM)
|
||||
err = MigrateStructure(testDB)
|
||||
assert.NoError(t, err, "MigrateStructure should succeed with registered models")
|
||||
|
||||
// Verify tables exist by checking if we can create records
|
||||
testRecord := TestModel{Name: "Test", Age: 25}
|
||||
err = testDB.Create(&testRecord).Error
|
||||
assert.NoError(t, err, "Should be able to create test record after migration")
|
||||
|
||||
anotherRecord := AnotherTestModel{Description: "Test Description", Active: true}
|
||||
err = testDB.Create(&anotherRecord).Error
|
||||
assert.NoError(t, err, "Should be able to create another test record after migration")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_ErrorHandling(t *testing.T) {
|
||||
// Save original global DB
|
||||
originalDB := DB
|
||||
defer func() {
|
||||
DB = originalDB
|
||||
}()
|
||||
|
||||
// Set global DB to nil to force an error scenario
|
||||
DB = nil
|
||||
|
||||
// Test migration without providing database when global DB is nil
|
||||
// This should cause a panic when trying to call AutoMigrate on nil
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Expected to panic when trying to call methods on nil database
|
||||
assert.NotNil(t, r, "Should panic when DB is nil")
|
||||
}
|
||||
}()
|
||||
|
||||
// This should panic because targetDB will be nil and we can't call AutoMigrate on nil
|
||||
MigrateStructure()
|
||||
|
||||
// If we reach here without panic, the test should fail
|
||||
t.Fatal("Expected panic when calling MigrateStructure with nil DB")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_EmptyMigration(t *testing.T) {
|
||||
// Test that migration works even when there are no models to migrate
|
||||
testDB := setupTestDB(t)
|
||||
|
||||
// Call AutoMigrate with no models (empty migration)
|
||||
err := testDB.AutoMigrate()
|
||||
assert.NoError(t, err, "AutoMigrate should succeed with no models")
|
||||
|
||||
// Test our MigrateStructure function
|
||||
err = MigrateStructure(testDB)
|
||||
assert.NoError(t, err, "MigrateStructure should succeed with empty migration")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_DatabaseTypes(t *testing.T) {
|
||||
// Test with SQLite (in-memory)
|
||||
db1, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create in-memory SQLite database")
|
||||
|
||||
err = MigrateStructure(db1)
|
||||
assert.NoError(t, err, "MigrateStructure should work with in-memory SQLite")
|
||||
|
||||
// Test with SQLite (file-based)
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "file_test.db")
|
||||
db2, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create file-based SQLite database")
|
||||
|
||||
err = MigrateStructure(db2)
|
||||
assert.NoError(t, err, "MigrateStructure should work with file-based SQLite")
|
||||
}
|
||||
|
||||
func TestMigrateStructure_ParameterVariations(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setupFunc func(t *testing.T) []*gorm.DB
|
||||
expectError bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "No parameters",
|
||||
setupFunc: func(t *testing.T) []*gorm.DB {
|
||||
// Set up global DB
|
||||
originalDB := DB
|
||||
t.Cleanup(func() { DB = originalDB })
|
||||
DB = setupTestDB(t)
|
||||
return []*gorm.DB{}
|
||||
},
|
||||
expectError: false,
|
||||
description: "Should use global DB when no parameters provided",
|
||||
},
|
||||
{
|
||||
name: "One valid parameter",
|
||||
setupFunc: func(t *testing.T) []*gorm.DB {
|
||||
return []*gorm.DB{setupTestDB(t)}
|
||||
},
|
||||
expectError: false,
|
||||
description: "Should use provided DB when one parameter given",
|
||||
},
|
||||
{
|
||||
name: "Multiple parameters",
|
||||
setupFunc: func(t *testing.T) []*gorm.DB {
|
||||
return []*gorm.DB{setupTestDB(t), setupTestDB(t)}
|
||||
},
|
||||
expectError: false,
|
||||
description: "Should use first DB when multiple parameters given",
|
||||
},
|
||||
{
|
||||
name: "Nil first parameter with global DB",
|
||||
setupFunc: func(t *testing.T) []*gorm.DB {
|
||||
originalDB := DB
|
||||
t.Cleanup(func() { DB = originalDB })
|
||||
DB = setupTestDB(t)
|
||||
return []*gorm.DB{nil}
|
||||
},
|
||||
expectError: false,
|
||||
description: "Should fall back to global DB when first parameter is nil",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dbs := tt.setupFunc(t)
|
||||
|
||||
err := MigrateStructure(dbs...)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err, tt.description)
|
||||
} else {
|
||||
assert.NoError(t, err, tt.description)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark tests for performance
|
||||
func BenchmarkMigrateStructure(b *testing.B) {
|
||||
// Set up test database
|
||||
tempDir := b.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "benchmark.db")
|
||||
testDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to create test database: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := MigrateStructure(testDB)
|
||||
if err != nil {
|
||||
b.Fatalf("MigrateStructure failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMigrateStructure_GlobalDB(b *testing.B) {
|
||||
// Save original global DB
|
||||
originalDB := DB
|
||||
defer func() {
|
||||
DB = originalDB
|
||||
}()
|
||||
|
||||
// Set up global test database
|
||||
tempDir := b.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "benchmark_global.db")
|
||||
testDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to create test database: %v", err)
|
||||
}
|
||||
DB = testDB
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
err := MigrateStructure()
|
||||
if err != nil {
|
||||
b.Fatalf("MigrateStructure failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// setupHandlerTestEnvironment sets up test environment for handler tests
|
||||
func setupHandlerTestEnvironment(t *testing.T) {
|
||||
// Save original values
|
||||
origEnv := os.Getenv("ENVIRONMENT")
|
||||
origDevTesting := os.Getenv("DEV_TESTING")
|
||||
origDatabaseType := os.Getenv("DATABASE_TYPE")
|
||||
origDatabaseURL := os.Getenv("DATABASE_URL")
|
||||
originalDB := DB
|
||||
|
||||
// Set test environment
|
||||
os.Setenv("ENVIRONMENT", "test")
|
||||
os.Setenv("DEV_TESTING", "yes")
|
||||
|
||||
// Cleanup function to restore original values
|
||||
t.Cleanup(func() {
|
||||
if origEnv != "" {
|
||||
os.Setenv("ENVIRONMENT", origEnv)
|
||||
} else {
|
||||
os.Unsetenv("ENVIRONMENT")
|
||||
}
|
||||
if origDevTesting != "" {
|
||||
os.Setenv("DEV_TESTING", origDevTesting)
|
||||
} else {
|
||||
os.Unsetenv("DEV_TESTING")
|
||||
}
|
||||
if origDatabaseType != "" {
|
||||
os.Setenv("DATABASE_TYPE", origDatabaseType)
|
||||
} else {
|
||||
os.Unsetenv("DATABASE_TYPE")
|
||||
}
|
||||
if origDatabaseURL != "" {
|
||||
os.Setenv("DATABASE_URL", origDatabaseURL)
|
||||
} else {
|
||||
os.Unsetenv("DATABASE_URL")
|
||||
}
|
||||
|
||||
// Reset global DB variable
|
||||
DB = originalDB
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetupCheck(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Create a Gin router and register the handler
|
||||
router := gin.New()
|
||||
router.GET("/setup-check", SetupCheck)
|
||||
|
||||
// Create a test HTTP request
|
||||
req, err := http.NewRequest("GET", "/setup-check", nil)
|
||||
assert.NoError(t, err, "Should be able to create HTTP request")
|
||||
|
||||
// Create a response recorder
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
// Perform the request
|
||||
router.ServeHTTP(recorder, req)
|
||||
|
||||
// Verify that the function executed without panicking
|
||||
// Since SetupCheck doesn't return any response, we just check that it didn't crash
|
||||
assert.True(t, true, "SetupCheck should execute without panicking")
|
||||
|
||||
// Verify that DB is now initialized (ConnectDatabase was called)
|
||||
assert.NotNil(t, DB, "SetupCheck should initialize the database connection")
|
||||
}
|
||||
|
||||
func TestSetupCheck_DatabaseInitialization(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Reset DB to ensure fresh test
|
||||
DB = nil
|
||||
|
||||
// Create a Gin context manually
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
c.Request, _ = http.NewRequest("GET", "/setup-check", nil)
|
||||
|
||||
// Call SetupCheck directly
|
||||
SetupCheck(c)
|
||||
|
||||
// Verify that the database connection was established
|
||||
assert.NotNil(t, DB, "SetupCheck should establish database connection")
|
||||
|
||||
// Verify we can perform basic database operations
|
||||
if DB != nil {
|
||||
sqlDB, err := DB.DB()
|
||||
assert.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
if sqlDB != nil {
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping the database")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupCheck_MultipleInvocations(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Reset DB to ensure fresh test
|
||||
DB = nil
|
||||
|
||||
// Create Gin contexts for multiple requests
|
||||
recorder1 := httptest.NewRecorder()
|
||||
c1, _ := gin.CreateTestContext(recorder1)
|
||||
c1.Request, _ = http.NewRequest("GET", "/setup-check", nil)
|
||||
|
||||
recorder2 := httptest.NewRecorder()
|
||||
c2, _ := gin.CreateTestContext(recorder2)
|
||||
c2.Request, _ = http.NewRequest("GET", "/setup-check", nil)
|
||||
|
||||
// Call SetupCheck multiple times
|
||||
SetupCheck(c1)
|
||||
firstDB := DB
|
||||
assert.NotNil(t, firstDB, "First call should establish database connection")
|
||||
|
||||
SetupCheck(c2)
|
||||
secondDB := DB
|
||||
assert.NotNil(t, secondDB, "Second call should maintain database connection")
|
||||
|
||||
// The key thing is that both calls succeed and establish a database connection
|
||||
// The actual instance may vary depending on the implementation, but both should be valid
|
||||
if firstDB != nil && secondDB != nil {
|
||||
// Verify both connections are working
|
||||
sqlDB1, err1 := firstDB.DB()
|
||||
sqlDB2, err2 := secondDB.DB()
|
||||
assert.NoError(t, err1, "First database connection should be valid")
|
||||
assert.NoError(t, err2, "Second database connection should be valid")
|
||||
|
||||
if sqlDB1 != nil {
|
||||
assert.NoError(t, sqlDB1.Ping(), "First database should be pingable")
|
||||
}
|
||||
if sqlDB2 != nil {
|
||||
assert.NoError(t, sqlDB2.Ping(), "Second database should be pingable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupCheck_WithDifferentHTTPMethods(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Test with different HTTP methods
|
||||
methods := []string{"GET", "POST", "PUT", "DELETE", "PATCH"}
|
||||
|
||||
for _, method := range methods {
|
||||
t.Run("Method_"+method, func(t *testing.T) {
|
||||
// Reset DB for each test
|
||||
DB = nil
|
||||
|
||||
// Create a Gin context with the specific HTTP method
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
c.Request, _ = http.NewRequest(method, "/setup-check", nil)
|
||||
|
||||
// Call SetupCheck
|
||||
SetupCheck(c)
|
||||
|
||||
// Verify that it works regardless of HTTP method
|
||||
assert.NotNil(t, DB, "SetupCheck should work with %s method", method)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupCheck_WithRequestHeaders(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Reset DB to ensure fresh test
|
||||
DB = nil
|
||||
|
||||
// Create a request with various headers
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
req, _ := http.NewRequest("GET", "/setup-check", nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer test-token")
|
||||
req.Header.Set("User-Agent", "test-client/1.0")
|
||||
c.Request = req
|
||||
|
||||
// Call SetupCheck
|
||||
SetupCheck(c)
|
||||
|
||||
// Verify that it works with headers present
|
||||
assert.NotNil(t, DB, "SetupCheck should work with request headers")
|
||||
|
||||
// Verify that the headers are still accessible in the context
|
||||
assert.Equal(t, "application/json", c.GetHeader("Content-Type"))
|
||||
assert.Equal(t, "Bearer test-token", c.GetHeader("Authorization"))
|
||||
assert.Equal(t, "test-client/1.0", c.GetHeader("User-Agent"))
|
||||
}
|
||||
|
||||
func TestSetupCheck_WithQueryParameters(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Reset DB to ensure fresh test
|
||||
DB = nil
|
||||
|
||||
// Create a request with query parameters
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
req, _ := http.NewRequest("GET", "/setup-check?param1=value1¶m2=value2", nil)
|
||||
c.Request = req
|
||||
|
||||
// Call SetupCheck
|
||||
SetupCheck(c)
|
||||
|
||||
// Verify that it works with query parameters
|
||||
assert.NotNil(t, DB, "SetupCheck should work with query parameters")
|
||||
|
||||
// Verify that query parameters are still accessible
|
||||
assert.Equal(t, "value1", c.Query("param1"))
|
||||
assert.Equal(t, "value2", c.Query("param2"))
|
||||
}
|
||||
|
||||
func TestSetupCheck_FullHTTPFlow(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Create a complete Gin router with middleware
|
||||
router := gin.New()
|
||||
|
||||
// Add some middleware to test that SetupCheck works in a middleware chain
|
||||
router.Use(gin.Logger())
|
||||
router.Use(gin.Recovery())
|
||||
|
||||
// Register the handler
|
||||
router.GET("/api/setup-check", SetupCheck)
|
||||
|
||||
// Create a test server
|
||||
server := httptest.NewServer(router)
|
||||
defer server.Close()
|
||||
|
||||
// Make a real HTTP request to the test server
|
||||
resp, err := http.Get(server.URL + "/api/setup-check")
|
||||
assert.NoError(t, err, "Should be able to make HTTP request")
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Since SetupCheck doesn't write any response, we just verify the request succeeded
|
||||
// The status code should be 200 (OK) even though no response was written
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode, "Request should succeed")
|
||||
|
||||
// Verify that DB was initialized
|
||||
assert.NotNil(t, DB, "SetupCheck should initialize database in full HTTP flow")
|
||||
}
|
||||
|
||||
func TestSetupCheck_ErrorScenarios(t *testing.T) {
|
||||
// Note: Since SetupCheck just calls ConnectDatabase() and doesn't handle errors,
|
||||
// we test that it doesn't panic even in error scenarios
|
||||
|
||||
// Save original environment
|
||||
originalDB := DB
|
||||
defer func() {
|
||||
DB = originalDB
|
||||
}()
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
t.Run("WithInvalidDatabaseConfig", func(t *testing.T) {
|
||||
// Set invalid database configuration
|
||||
os.Setenv("DATABASE_TYPE", "invalid")
|
||||
os.Setenv("DATABASE_URL", "invalid://connection/string")
|
||||
|
||||
defer func() {
|
||||
os.Unsetenv("DATABASE_TYPE")
|
||||
os.Unsetenv("DATABASE_URL")
|
||||
}()
|
||||
|
||||
// Reset DB
|
||||
DB = nil
|
||||
|
||||
// Create Gin context
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
c.Request, _ = http.NewRequest("GET", "/setup-check", nil)
|
||||
|
||||
// This might panic depending on the ConnectDatabase implementation
|
||||
// We'll handle the panic gracefully
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// If it panics, that's expected behavior with invalid config
|
||||
t.Logf("SetupCheck panicked with invalid config (expected): %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
// Call SetupCheck - might panic with invalid config
|
||||
SetupCheck(c)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetupCheck_ContextIntegrity(t *testing.T) {
|
||||
setupHandlerTestEnvironment(t)
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Create a Gin context with some values set
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
c.Request, _ = http.NewRequest("GET", "/setup-check", nil)
|
||||
|
||||
// Set some values in the context before calling SetupCheck
|
||||
c.Set("test_key", "test_value")
|
||||
c.Set("user_id", 12345)
|
||||
|
||||
// Call SetupCheck
|
||||
SetupCheck(c)
|
||||
|
||||
// Verify that context values are preserved
|
||||
value, exists := c.Get("test_key")
|
||||
assert.True(t, exists, "Context should preserve existing values")
|
||||
assert.Equal(t, "test_value", value, "Context values should remain unchanged")
|
||||
|
||||
userID, exists := c.Get("user_id")
|
||||
assert.True(t, exists, "Context should preserve existing values")
|
||||
assert.Equal(t, 12345, userID, "Context values should remain unchanged")
|
||||
|
||||
// Verify that the database was still initialized
|
||||
assert.NotNil(t, DB, "SetupCheck should initialize database without affecting context")
|
||||
}
|
||||
|
||||
// Benchmark test for performance
|
||||
func BenchmarkSetupCheck(b *testing.B) {
|
||||
// Setup test environment
|
||||
os.Setenv("ENVIRONMENT", "test")
|
||||
defer os.Unsetenv("ENVIRONMENT")
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Create a reusable context
|
||||
recorder := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(recorder)
|
||||
c.Request, _ = http.NewRequest("GET", "/setup-check", nil)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
SetupCheck(c)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetupCheck_WithHTTPOverhead(b *testing.B) {
|
||||
// Setup test environment
|
||||
os.Setenv("ENVIRONMENT", "test")
|
||||
defer os.Unsetenv("ENVIRONMENT")
|
||||
|
||||
// Set Gin to test mode
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Create a router
|
||||
router := gin.New()
|
||||
router.GET("/setup-check", SetupCheck)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
recorder := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", "/setup-check", nil)
|
||||
router.ServeHTTP(recorder, req)
|
||||
}
|
||||
}
|
||||
@@ -114,17 +114,26 @@ func ResetTestDB() {
|
||||
if isTestDb {
|
||||
logger.Debug("ResetTestDB: Verwende Test-Datenbank, führe Cleanup durch")
|
||||
|
||||
// Check if DB is not nil before trying to use it
|
||||
if DB != nil {
|
||||
sqlDB, err := DB.DB()
|
||||
if err == nil {
|
||||
logger.Debug("ResetTestDB: Schließe Datenbankverbindung")
|
||||
sqlDB.Close()
|
||||
} else {
|
||||
logger.Error("ResetTestDB: Fehler beim Abrufen der SQL-Datenbank: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
logger.Debug("ResetTestDB: DB ist bereits nil, überspringe Verbindungsschließung")
|
||||
}
|
||||
|
||||
// Always set DB to nil and clean up temp directory
|
||||
DB = nil
|
||||
logger.Debug("ResetTestDB: DB auf nil gesetzt")
|
||||
|
||||
if testdbTempDir != "" {
|
||||
logger.Debug("ResetTestDB: Lösche temporäres Verzeichnis: %s", testdbTempDir)
|
||||
err = os.RemoveAll(testdbTempDir)
|
||||
err := os.RemoveAll(testdbTempDir)
|
||||
if err != nil {
|
||||
logger.Error("ResetTestDB: Fehler beim Löschen des temporären Verzeichnisses: %s", err.Error())
|
||||
} else {
|
||||
@@ -132,9 +141,6 @@ func ResetTestDB() {
|
||||
}
|
||||
testdbTempDir = ""
|
||||
}
|
||||
} else {
|
||||
logger.Error("ResetTestDB: Fehler beim Abrufen der SQL-Datenbank: %s", err.Error())
|
||||
}
|
||||
} else {
|
||||
logger.Debug("ResetTestDB: Verwende Live-Datenbank, überspringe Cleanup")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,515 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// setupTestHelperEnvironment sets up the test environment for testhelper tests
|
||||
func setupTestHelperEnvironment(t *testing.T) {
|
||||
// Save original state
|
||||
originalDB := DB
|
||||
originalIsTestDb := isTestDb
|
||||
originalTestdbTempDir := testdbTempDir
|
||||
|
||||
// Cleanup function
|
||||
t.Cleanup(func() {
|
||||
// Restore original state
|
||||
DB = originalDB
|
||||
isTestDb = originalIsTestDb
|
||||
testdbTempDir = originalTestdbTempDir
|
||||
})
|
||||
}
|
||||
|
||||
func TestCopyFile(t *testing.T) {
|
||||
// Create a temporary directory for testing
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Create a source file with test content
|
||||
sourceContent := "This is test content for file copying\nLine 2\nLine 3"
|
||||
sourceFile := filepath.Join(tempDir, "source.txt")
|
||||
err := os.WriteFile(sourceFile, []byte(sourceContent), 0644)
|
||||
require.NoError(t, err, "Should be able to create source file")
|
||||
|
||||
// Define destination file path
|
||||
destFile := filepath.Join(tempDir, "destination.txt")
|
||||
|
||||
// Test successful file copy
|
||||
err = copyFile(sourceFile, destFile)
|
||||
assert.NoError(t, err, "copyFile should succeed with valid source and destination")
|
||||
|
||||
// Verify destination file exists and has correct content
|
||||
destContent, err := os.ReadFile(destFile)
|
||||
assert.NoError(t, err, "Should be able to read destination file")
|
||||
assert.Equal(t, sourceContent, string(destContent), "Destination file should have same content as source")
|
||||
|
||||
// Verify file info
|
||||
sourceInfo, err := os.Stat(sourceFile)
|
||||
require.NoError(t, err, "Should be able to stat source file")
|
||||
destInfo, err := os.Stat(destFile)
|
||||
require.NoError(t, err, "Should be able to stat destination file")
|
||||
assert.Equal(t, sourceInfo.Size(), destInfo.Size(), "Files should have same size")
|
||||
}
|
||||
|
||||
func TestCopyFile_NonExistentSource(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
sourceFile := filepath.Join(tempDir, "nonexistent.txt")
|
||||
destFile := filepath.Join(tempDir, "destination.txt")
|
||||
|
||||
// Test copying from non-existent source
|
||||
err := copyFile(sourceFile, destFile)
|
||||
assert.Error(t, err, "copyFile should fail with non-existent source file")
|
||||
assert.True(t, os.IsNotExist(err), "Error should be file not found error")
|
||||
|
||||
// Verify destination file was not created
|
||||
_, err = os.Stat(destFile)
|
||||
assert.True(t, os.IsNotExist(err), "Destination file should not exist")
|
||||
}
|
||||
|
||||
func TestCopyFile_InvalidDestination(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Create a source file
|
||||
sourceFile := filepath.Join(tempDir, "source.txt")
|
||||
err := os.WriteFile(sourceFile, []byte("test content"), 0644)
|
||||
require.NoError(t, err, "Should be able to create source file")
|
||||
|
||||
// Try to copy to an invalid destination (non-existent directory)
|
||||
destFile := filepath.Join(tempDir, "nonexistent_dir", "destination.txt")
|
||||
|
||||
err = copyFile(sourceFile, destFile)
|
||||
assert.Error(t, err, "copyFile should fail with invalid destination path")
|
||||
}
|
||||
|
||||
func TestCopyFile_EmptyFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Create an empty source file
|
||||
sourceFile := filepath.Join(tempDir, "empty.txt")
|
||||
err := os.WriteFile(sourceFile, []byte(""), 0644)
|
||||
require.NoError(t, err, "Should be able to create empty source file")
|
||||
|
||||
destFile := filepath.Join(tempDir, "empty_dest.txt")
|
||||
|
||||
// Test copying empty file
|
||||
err = copyFile(sourceFile, destFile)
|
||||
assert.NoError(t, err, "copyFile should succeed with empty file")
|
||||
|
||||
// Verify destination file exists and is empty
|
||||
destContent, err := os.ReadFile(destFile)
|
||||
assert.NoError(t, err, "Should be able to read empty destination file")
|
||||
assert.Empty(t, destContent, "Destination file should be empty")
|
||||
}
|
||||
|
||||
func TestCopyFile_LargeFile(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
|
||||
// Create a larger file (1MB)
|
||||
sourceFile := filepath.Join(tempDir, "large.txt")
|
||||
largeContent := strings.Repeat("This is a test line.\n", 50000) // ~1MB
|
||||
err := os.WriteFile(sourceFile, []byte(largeContent), 0644)
|
||||
require.NoError(t, err, "Should be able to create large source file")
|
||||
|
||||
destFile := filepath.Join(tempDir, "large_dest.txt")
|
||||
|
||||
// Test copying large file
|
||||
err = copyFile(sourceFile, destFile)
|
||||
assert.NoError(t, err, "copyFile should succeed with large file")
|
||||
|
||||
// Verify file sizes match
|
||||
sourceInfo, err := os.Stat(sourceFile)
|
||||
require.NoError(t, err, "Should be able to stat source file")
|
||||
destInfo, err := os.Stat(destFile)
|
||||
require.NoError(t, err, "Should be able to stat destination file")
|
||||
assert.Equal(t, sourceInfo.Size(), destInfo.Size(), "Large files should have same size")
|
||||
}
|
||||
|
||||
func TestSetupTestDB_WithTestDatabase(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Reset global state
|
||||
DB = nil
|
||||
isTestDb = false
|
||||
|
||||
// Create a mock prepared test database
|
||||
tempDir := t.TempDir()
|
||||
mockPreparedDB := filepath.Join(tempDir, "prepared_test.db")
|
||||
|
||||
// Create a simple SQLite database file
|
||||
db, err := gorm.Open(sqlite.Open(mockPreparedDB), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create mock prepared database")
|
||||
sqlDB, err := db.DB()
|
||||
require.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
sqlDB.Close()
|
||||
|
||||
// Temporarily override PreparedTestDB path
|
||||
originalPreparedTestDB := PreparedTestDB
|
||||
PreparedTestDB = mockPreparedDB
|
||||
defer func() {
|
||||
PreparedTestDB = originalPreparedTestDB
|
||||
}()
|
||||
|
||||
// Test SetupTestDB with test database (default behavior)
|
||||
SetupTestDB()
|
||||
|
||||
// Verify database was set up
|
||||
assert.NotNil(t, DB, "SetupTestDB should set global DB")
|
||||
assert.True(t, isTestDb, "Should be using test database")
|
||||
|
||||
// Verify we can use the database
|
||||
sqlDB, err = DB.DB()
|
||||
assert.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
if sqlDB != nil {
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping test database")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupTestDB_WithLiveDatabase(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Reset global state
|
||||
DB = nil
|
||||
isTestDb = false
|
||||
testdbTempDir = ""
|
||||
|
||||
// Test SetupTestDB with live database
|
||||
SetupTestDB(false)
|
||||
|
||||
// Verify database was set up
|
||||
assert.NotNil(t, DB, "SetupTestDB should set global DB")
|
||||
assert.False(t, isTestDb, "Should be using live database")
|
||||
|
||||
// Verify we can use the database (this will connect to the actual configured database)
|
||||
sqlDB, err := DB.DB()
|
||||
if err == nil && sqlDB != nil {
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping live database")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetupTestDB_AlreadyInitialized(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Create a mock database connection
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "existing.db")
|
||||
existingDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create existing database")
|
||||
|
||||
// Set global DB to existing connection
|
||||
DB = existingDB
|
||||
isTestDb = true
|
||||
|
||||
// Call SetupTestDB - should not change the existing DB
|
||||
SetupTestDB()
|
||||
|
||||
// Verify DB remains the same
|
||||
assert.Equal(t, existingDB, DB, "SetupTestDB should not change existing DB")
|
||||
}
|
||||
|
||||
func TestSetupTestDB_MissingPreparedDatabase(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Reset global state
|
||||
DB = nil
|
||||
isTestDb = false
|
||||
|
||||
// Set PreparedTestDB to non-existent file
|
||||
originalPreparedTestDB := PreparedTestDB
|
||||
PreparedTestDB = "/nonexistent/path/to/database.db"
|
||||
defer func() {
|
||||
PreparedTestDB = originalPreparedTestDB
|
||||
}()
|
||||
|
||||
// Test SetupTestDB with missing prepared database - should panic
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
assert.Contains(t, r.(string), "failed to copy prepared test database",
|
||||
"Should panic with appropriate error message")
|
||||
}
|
||||
}()
|
||||
|
||||
SetupTestDB(true)
|
||||
|
||||
// If we reach here, the test should fail
|
||||
t.Fatal("Expected panic when prepared test database is missing")
|
||||
}
|
||||
|
||||
func TestSetupTestDB_ParameterVariations(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
params []bool
|
||||
expectedVal bool
|
||||
description string
|
||||
}{
|
||||
{
|
||||
name: "No parameters",
|
||||
params: []bool{},
|
||||
expectedVal: true,
|
||||
description: "Should default to test database",
|
||||
},
|
||||
{
|
||||
name: "Explicit true",
|
||||
params: []bool{true},
|
||||
expectedVal: true,
|
||||
description: "Should use test database when explicitly set to true",
|
||||
},
|
||||
{
|
||||
name: "Explicit false",
|
||||
params: []bool{false},
|
||||
expectedVal: false,
|
||||
description: "Should use live database when explicitly set to false",
|
||||
},
|
||||
{
|
||||
name: "Multiple parameters (first true)",
|
||||
params: []bool{true, false},
|
||||
expectedVal: true,
|
||||
description: "Should use first parameter when multiple provided",
|
||||
},
|
||||
{
|
||||
name: "Multiple parameters (first false)",
|
||||
params: []bool{false, true},
|
||||
expectedVal: false,
|
||||
description: "Should use first parameter when multiple provided",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Reset state for each test
|
||||
DB = nil
|
||||
isTestDb = false
|
||||
|
||||
if tt.expectedVal {
|
||||
// For test database, we need a mock prepared database
|
||||
tempDir := t.TempDir()
|
||||
mockPreparedDB := filepath.Join(tempDir, "prepared_test.db")
|
||||
db, err := gorm.Open(sqlite.Open(mockPreparedDB), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create mock prepared database")
|
||||
sqlDB, err := db.DB()
|
||||
require.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
sqlDB.Close()
|
||||
|
||||
originalPreparedTestDB := PreparedTestDB
|
||||
PreparedTestDB = mockPreparedDB
|
||||
defer func() {
|
||||
PreparedTestDB = originalPreparedTestDB
|
||||
}()
|
||||
}
|
||||
|
||||
SetupTestDB(tt.params...)
|
||||
|
||||
assert.Equal(t, tt.expectedVal, isTestDb, tt.description)
|
||||
assert.NotNil(t, DB, "Database should be initialized")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetTestDB_WithTestDatabase(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Set up a test database first
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "test_reset.db")
|
||||
testDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create test database")
|
||||
|
||||
// Set global state as if SetupTestDB was called
|
||||
DB = testDB
|
||||
isTestDb = true
|
||||
|
||||
// Call ResetTestDB
|
||||
ResetTestDB()
|
||||
|
||||
// Verify cleanup
|
||||
assert.Nil(t, DB, "DB should be reset to nil")
|
||||
|
||||
// Note: We can't easily test directory cleanup since the package-level
|
||||
// testdbTempDir variable isn't being set properly in the current implementation
|
||||
// But we can verify that the function completes without error
|
||||
}
|
||||
|
||||
func TestResetTestDB_WithLiveDatabase(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Set up as if using live database
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "live_db.db")
|
||||
liveDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create live database")
|
||||
|
||||
DB = liveDB
|
||||
isTestDb = false
|
||||
|
||||
// Call ResetTestDB
|
||||
ResetTestDB()
|
||||
|
||||
// For live database, cleanup should be skipped
|
||||
// DB and other state should remain unchanged for live database
|
||||
// Note: The actual behavior might vary, but the function should not crash
|
||||
assert.True(t, true, "ResetTestDB should not crash with live database")
|
||||
}
|
||||
|
||||
func TestResetTestDB_WithNilDB(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Set state with nil DB
|
||||
DB = nil
|
||||
isTestDb = true
|
||||
|
||||
// Call ResetTestDB - should not crash
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("ResetTestDB should not panic with nil DB: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
ResetTestDB()
|
||||
|
||||
// Should complete without error
|
||||
assert.Nil(t, DB, "DB should remain nil")
|
||||
}
|
||||
|
||||
func TestResetTestDB_ErrorHandling(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Create a test database and close it immediately to simulate error conditions
|
||||
tempDir := t.TempDir()
|
||||
dbPath := filepath.Join(tempDir, "error_test.db")
|
||||
testDB, err := gorm.Open(sqlite.Open(dbPath), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create test database")
|
||||
|
||||
// Close the underlying connection to simulate error
|
||||
sqlDB, err := testDB.DB()
|
||||
require.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
sqlDB.Close()
|
||||
|
||||
// Set global state
|
||||
DB = testDB
|
||||
isTestDb = true
|
||||
|
||||
// Call ResetTestDB - should handle errors gracefully
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Fatalf("ResetTestDB should not panic on errors: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
ResetTestDB()
|
||||
|
||||
// Function should complete even with errors
|
||||
assert.True(t, true, "ResetTestDB should handle errors gracefully")
|
||||
}
|
||||
|
||||
func TestSetupTestDB_ResetTestDB_Cycle(t *testing.T) {
|
||||
setupTestHelperEnvironment(t)
|
||||
|
||||
// Create a mock prepared test database
|
||||
tempDir := t.TempDir()
|
||||
mockPreparedDB := filepath.Join(tempDir, "prepared_cycle_test.db")
|
||||
db, err := gorm.Open(sqlite.Open(mockPreparedDB), &gorm.Config{})
|
||||
require.NoError(t, err, "Should be able to create mock prepared database")
|
||||
sqlDB, err := db.DB()
|
||||
require.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
sqlDB.Close()
|
||||
|
||||
originalPreparedTestDB := PreparedTestDB
|
||||
PreparedTestDB = mockPreparedDB
|
||||
defer func() {
|
||||
PreparedTestDB = originalPreparedTestDB
|
||||
}()
|
||||
|
||||
// Test complete setup and reset cycle
|
||||
for i := 0; i < 3; i++ {
|
||||
t.Run(fmt.Sprintf("Cycle_%d", i+1), func(t *testing.T) {
|
||||
// Reset state
|
||||
DB = nil
|
||||
isTestDb = false
|
||||
|
||||
// Setup
|
||||
SetupTestDB()
|
||||
assert.NotNil(t, DB, "SetupTestDB should initialize DB")
|
||||
assert.True(t, isTestDb, "Should be using test database")
|
||||
|
||||
// Verify database is working
|
||||
sqlDB, err := DB.DB()
|
||||
assert.NoError(t, err, "Should be able to get underlying sql.DB")
|
||||
if sqlDB != nil {
|
||||
err = sqlDB.Ping()
|
||||
assert.NoError(t, err, "Should be able to ping the database")
|
||||
}
|
||||
|
||||
// Reset
|
||||
ResetTestDB()
|
||||
assert.Nil(t, DB, "ResetTestDB should reset DB to nil")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmark tests
|
||||
func BenchmarkCopyFile(b *testing.B) {
|
||||
// Create test files
|
||||
tempDir := b.TempDir()
|
||||
sourceFile := filepath.Join(tempDir, "source.txt")
|
||||
content := strings.Repeat("benchmark test content\n", 1000)
|
||||
err := os.WriteFile(sourceFile, []byte(content), 0644)
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to create source file: %v", err)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
destFile := filepath.Join(tempDir, fmt.Sprintf("dest_%d.txt", i))
|
||||
err := copyFile(sourceFile, destFile)
|
||||
if err != nil {
|
||||
b.Fatalf("copyFile failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSetupTestDB(b *testing.B) {
|
||||
// Create a mock prepared test database
|
||||
tempDir := b.TempDir()
|
||||
mockPreparedDB := filepath.Join(tempDir, "benchmark_prepared.db")
|
||||
db, err := gorm.Open(sqlite.Open(mockPreparedDB), &gorm.Config{})
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to create mock prepared database: %v", err)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to get underlying sql.DB: %v", err)
|
||||
}
|
||||
sqlDB.Close()
|
||||
|
||||
originalPreparedTestDB := PreparedTestDB
|
||||
PreparedTestDB = mockPreparedDB
|
||||
defer func() {
|
||||
PreparedTestDB = originalPreparedTestDB
|
||||
}()
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Reset state for each iteration
|
||||
DB = nil
|
||||
isTestDb = false
|
||||
|
||||
SetupTestDB()
|
||||
|
||||
// Clean up
|
||||
ResetTestDB()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user