Files
Charon/backend/internal/testutil/db_test.go
GitHub Actions 3169b05156 fix: skip incomplete system log viewer tests
- Marked 12 tests as skip pending feature implementation
- Features tracked in GitHub issue #686 (system log viewer feature completion)
- Tests cover sorting by timestamp/level/method/URI/status, pagination controls, filtering by text/level, download functionality
- Unblocks Phase 2 at 91.7% pass rate to proceed to Phase 3 security enforcement validation
- TODO comments in code reference GitHub #686 for feature completion tracking
- Tests skipped: Pagination (3), Search/Filter (2), Download (2), Sorting (1), Log Display (4)
2026-02-09 21:55:55 +00:00

305 lines
8.1 KiB
Go

package testutil
import (
"testing"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
// testModel is a simple model for testing database operations
type testModel struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"not null"`
}
// setupTestDB creates a fresh in-memory SQLite database for testing
func setupTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open test database: %v", err)
}
// Run migrations
if err := db.AutoMigrate(&testModel{}); err != nil {
t.Fatalf("Failed to migrate test database: %v", err)
}
return db
}
// TestWithTx_Success verifies that WithTx executes the function and rolls back the transaction
func TestWithTx_Success(t *testing.T) {
db := setupTestDB(t)
// Insert data within transaction
WithTx(t, db, func(tx *gorm.DB) {
record := &testModel{Name: "test-record"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
// Verify record exists within transaction
var count int64
tx.Model(&testModel{}).Count(&count)
if count != 1 {
t.Errorf("Expected 1 record in transaction, got %d", count)
}
})
// Verify record was rolled back
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after rollback, got %d", count)
}
}
// TestWithTx_Panic verifies that WithTx rolls back on panic and propagates the panic
func TestWithTx_Panic(t *testing.T) {
db := setupTestDB(t)
defer func() {
if r := recover(); r == nil {
t.Error("Expected panic to be propagated, but no panic occurred")
} else if r != "test panic" {
t.Errorf("Expected panic value 'test panic', got %v", r)
}
// Verify record was rolled back after panic
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after panic rollback, got %d", count)
}
}()
WithTx(t, db, func(tx *gorm.DB) {
// Insert data
record := &testModel{Name: "panic-test"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
// Trigger panic
panic("test panic")
})
}
// TestWithTx_MultipleOperations verifies WithTx works with multiple database operations
func TestWithTx_MultipleOperations(t *testing.T) {
db := setupTestDB(t)
WithTx(t, db, func(tx *gorm.DB) {
// Create multiple records
records := []testModel{
{Name: "record1"},
{Name: "record2"},
{Name: "record3"},
}
for _, record := range records {
if err := tx.Create(&record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
}
// Update a record
if err := tx.Model(&testModel{}).Where("name = ?", "record2").Update("name", "updated").Error; err != nil {
t.Fatalf("Failed to update record: %v", err)
}
// Verify updates within transaction
var updated testModel
tx.Where("name = ?", "updated").First(&updated)
if updated.Name != "updated" {
t.Error("Update not visible within transaction")
}
})
// Verify all operations were rolled back
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after rollback, got %d", count)
}
}
// TestGetTestTx_Cleanup verifies that GetTestTx registers cleanup and rolls back
func TestGetTestTx_Cleanup(t *testing.T) {
db := setupTestDB(t)
// Create a subtest to isolate cleanup
t.Run("Subtest", func(t *testing.T) {
tx := GetTestTx(t, db)
// Insert data
record := &testModel{Name: "cleanup-test"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
// Verify record exists
var count int64
tx.Model(&testModel{}).Count(&count)
if count != 1 {
t.Errorf("Expected 1 record in transaction, got %d", count)
}
// When this subtest finishes, t.Cleanup should roll back the transaction
})
// Verify record was rolled back after subtest cleanup
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after cleanup rollback, got %d", count)
}
}
// TestGetTestTx_MultipleTransactions verifies that multiple GetTestTx calls are isolated
func TestGetTestTx_MultipleTransactions(t *testing.T) {
db := setupTestDB(t)
// First transaction
t.Run("Transaction1", func(t *testing.T) {
tx := GetTestTx(t, db)
record := &testModel{Name: "tx1-record"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
})
// Second transaction
t.Run("Transaction2", func(t *testing.T) {
tx := GetTestTx(t, db)
record := &testModel{Name: "tx2-record"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
})
// Verify both transactions were rolled back
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after all cleanups, got %d", count)
}
}
// TestGetTestTx_UsageInMultipleFunctions demonstrates passing tx between functions
func TestGetTestTx_UsageInMultipleFunctions(t *testing.T) {
db := setupTestDB(t)
t.Run("MultiFunction", func(t *testing.T) {
tx := GetTestTx(t, db)
// Helper function 1: Create
createRecord := func(tx *gorm.DB, name string) error {
return tx.Create(&testModel{Name: name}).Error
}
// Helper function 2: Count
countRecords := func(tx *gorm.DB) int64 {
var count int64
tx.Model(&testModel{}).Count(&count)
return count
}
// Use helper functions with the same transaction
if err := createRecord(tx, "func-test"); err != nil {
t.Fatalf("Failed to create record: %v", err)
}
count := countRecords(tx)
if count != 1 {
t.Errorf("Expected 1 record, got %d", count)
}
})
// Verify cleanup happened
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after cleanup, got %d", count)
}
}
// TestGetTestTx_Parallel verifies isolation with multiple GetTestTx calls
// Note: SQLite doesn't handle concurrent writes well, so we test isolation without t.Parallel()
func TestGetTestTx_Parallel(t *testing.T) {
// Use shared database for isolation tests
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open shared test database: %v", err)
}
if err := db.AutoMigrate(&testModel{}); err != nil {
t.Fatalf("Failed to migrate test database: %v", err)
}
// Run isolated tests (demonstrating isolation without actual parallelism due to SQLite limitations)
t.Run("Isolation1", func(t *testing.T) {
tx := GetTestTx(t, db)
record := &testModel{Name: "isolation1"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
var count int64
tx.Model(&testModel{}).Count(&count)
if count != 1 {
t.Errorf("Expected 1 record in isolation1 transaction, got %d", count)
}
})
t.Run("Isolation2", func(t *testing.T) {
tx := GetTestTx(t, db)
record := &testModel{Name: "isolation2"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
var count int64
tx.Model(&testModel{}).Count(&count)
if count != 1 {
t.Errorf("Expected 1 record in isolation2 transaction, got %d", count)
}
})
// After all tests complete, verify all rolled back
var finalCount int64
db.Model(&testModel{}).Count(&finalCount)
if finalCount != 0 {
t.Errorf("Expected 0 records after isolated tests, got %d", finalCount)
}
}
// TestGetTestTx_WithActualTestFailure verifies cleanup happens even on test failure
func TestGetTestTx_WithActualTestFailure(t *testing.T) {
db := setupTestDB(t)
// This subtest will fail, but cleanup should still happen
t.Run("FailingSubtest", func(t *testing.T) {
tx := GetTestTx(t, db)
record := &testModel{Name: "will-be-rolled-back"}
if err := tx.Create(record).Error; err != nil {
t.Fatalf("Failed to create record: %v", err)
}
// Even though this test "fails" conceptually, cleanup should still run
// (We're not actually failing here to avoid failing the test suite)
})
// Verify cleanup happened despite the "failure"
var count int64
db.Model(&testModel{}).Count(&count)
if count != 0 {
t.Errorf("Expected 0 records after cleanup on failure, got %d", count)
}
}