package tests import ( "context" "fmt" "omega-server/local/model" "omega-server/local/utl/logging" "os" "testing" "go.uber.org/dig" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" ) // TestConfig holds configuration for testing type TestConfig struct { DBHost string DBPort string DBUser string DBPassword string DBName string DBSSLMode string } // TestSuite provides a base structure for test suites type TestSuite struct { DB *gorm.DB DI *dig.Container Config *TestConfig } // NewTestSuite creates a new test suite func NewTestSuite() *TestSuite { config := &TestConfig{ DBHost: getEnvOrDefault("TEST_DB_HOST", "localhost"), DBPort: getEnvOrDefault("TEST_DB_PORT", "5432"), DBUser: getEnvOrDefault("TEST_DB_USER", "postgres"), DBPassword: getEnvOrDefault("TEST_DB_PASSWORD", "password"), DBName: getEnvOrDefault("TEST_DB_NAME", "omega_test"), DBSSLMode: getEnvOrDefault("TEST_DB_SSL_MODE", "disable"), } return &TestSuite{ Config: config, DI: dig.New(), } } // Setup initializes the test environment func (ts *TestSuite) Setup(t *testing.T) { // Initialize logger for tests logger, err := logging.Initialize() if err != nil { t.Fatalf("Failed to initialize logger: %v", err) } defer logger.Close() // Setup test database ts.setupTestDatabase(t) // Setup dependency injection ts.setupDI(t) // Migrate database ts.migrateDatabase(t) } // Teardown cleans up the test environment func (ts *TestSuite) Teardown(t *testing.T) { if ts.DB != nil { // Clean up test data ts.cleanupDatabase(t) // Close database connection sqlDB, err := ts.DB.DB() if err == nil { sqlDB.Close() } } } // setupTestDatabase initializes the test database connection func (ts *TestSuite) setupTestDatabase(t *testing.T) { dsn := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=%s TimeZone=UTC", ts.Config.DBHost, ts.Config.DBUser, ts.Config.DBPassword, ts.Config.DBName, ts.Config.DBPort, ts.Config.DBSSLMode, ) // Use silent logger for tests gormLogger := logger.Default.LogMode(logger.Silent) db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ Logger: gormLogger, }) if err != nil { t.Skipf("Failed to connect to test database: %v", err) } ts.DB = db } // setupDI initializes dependency injection for tests func (ts *TestSuite) setupDI(t *testing.T) { err := ts.DI.Provide(func() *gorm.DB { return ts.DB }) if err != nil { t.Fatalf("Failed to provide database to DI: %v", err) } } // migrateDatabase runs database migrations for tests func (ts *TestSuite) migrateDatabase(t *testing.T) { err := ts.DB.AutoMigrate( &model.User{}, &model.Role{}, &model.Permission{}, &model.Type{}, &model.Project{}, &model.Task{}, &model.Integration{}, &model.ProjectMember{}, &model.TaskAssignee{}, &model.SystemConfig{}, &model.AuditLog{}, &model.SecurityEvent{}, ) if err != nil { t.Fatalf("Failed to migrate test database: %v", err) } } // cleanupDatabase cleans up test data func (ts *TestSuite) cleanupDatabase(t *testing.T) { // Clean up in reverse order of dependencies tables := []string{ "task_assignees", "project_members", "integrations", "tasks", "projects", "types", "user_roles", "role_permissions", "users", "roles", "permissions", "system_configs", "audit_logs", "security_events", } for _, table := range tables { err := ts.DB.Exec(fmt.Sprintf("TRUNCATE TABLE %s RESTART IDENTITY CASCADE", table)).Error if err != nil { t.Logf("Warning: Failed to truncate table %s: %v", table, err) } } } // CreateTestUser creates a test user func (ts *TestSuite) CreateTestUser(t *testing.T, email, fullName string) *model.User { user := &model.User{ Email: email, FullName: fullName, } user.Init() err := user.SetPassword("testpassword123") if err != nil { t.Fatalf("Failed to set password for test user: %v", err) } err = ts.DB.Create(user).Error if err != nil { t.Fatalf("Failed to create test user: %v", err) } return user } // CreateTestRole creates a test role func (ts *TestSuite) CreateTestRole(t *testing.T, name, description string) *model.Role { role := &model.Role{ Name: name, Description: description, Active: true, } role.Init() err := ts.DB.Create(role).Error if err != nil { t.Fatalf("Failed to create test role: %v", err) } return role } // CreateTestPermission creates a test permission func (ts *TestSuite) CreateTestPermission(t *testing.T, name, description, category string) *model.Permission { permission := &model.Permission{ Name: name, Description: description, Category: category, Active: true, } permission.Init() err := ts.DB.Create(permission).Error if err != nil { t.Fatalf("Failed to create test permission: %v", err) } return permission } // CreateTestType creates a test project type func (ts *TestSuite) CreateTestType(t *testing.T, name, description string, userID *string) *model.Type { projectType := &model.Type{ Name: name, Description: description, UserID: userID, } projectType.Init() err := ts.DB.Create(projectType).Error if err != nil { t.Fatalf("Failed to create test type: %v", err) } return projectType } // CreateTestProject creates a test project func (ts *TestSuite) CreateTestProject(t *testing.T, name, description, ownerID, typeID string) *model.Project { project := &model.Project{ Name: name, Description: description, OwnerID: ownerID, TypeID: typeID, } project.Init() err := ts.DB.Create(project).Error if err != nil { t.Fatalf("Failed to create test project: %v", err) } return project } // CreateTestTask creates a test task func (ts *TestSuite) CreateTestTask(t *testing.T, title, description, projectID string) *model.Task { task := &model.Task{ Title: title, Description: description, Status: model.TaskStatusTodo, Priority: model.TaskPriorityMedium, ProjectID: projectID, } task.Init() err := ts.DB.Create(task).Error if err != nil { t.Fatalf("Failed to create test task: %v", err) } return task } // WithTransaction runs a function within a database transaction func (ts *TestSuite) WithTransaction(t *testing.T, fn func(tx *gorm.DB) error) { tx := ts.DB.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() t.Fatalf("Transaction panicked: %v", r) } }() if err := fn(tx); err != nil { tx.Rollback() t.Fatalf("Transaction failed: %v", err) } if err := tx.Commit().Error; err != nil { t.Fatalf("Failed to commit transaction: %v", err) } } // AssertUserExists asserts that a user exists in the database func (ts *TestSuite) AssertUserExists(t *testing.T, email string) *model.User { var user model.User err := ts.DB.Where("email = ?", email).First(&user).Error if err != nil { t.Fatalf("Expected user with email %s to exist, but not found: %v", email, err) } return &user } // AssertUserNotExists asserts that a user does not exist in the database func (ts *TestSuite) AssertUserNotExists(t *testing.T, email string) { var user model.User err := ts.DB.Where("email = ?", email).First(&user).Error if err == nil { t.Fatalf("Expected user with email %s to not exist, but found: %+v", email, user) } } // AssertProjectExists asserts that a project exists in the database func (ts *TestSuite) AssertProjectExists(t *testing.T, name string) *model.Project { var project model.Project err := ts.DB.Where("name = ?", name).First(&project).Error if err != nil { t.Fatalf("Expected project with name %s to exist, but not found: %v", name, err) } return &project } // AssertTaskExists asserts that a task exists in the database func (ts *TestSuite) AssertTaskExists(t *testing.T, title string) *model.Task { var task model.Task err := ts.DB.Where("title = ?", title).First(&task).Error if err != nil { t.Fatalf("Expected task with title %s to exist, but not found: %v", title, err) } return &task } // TestContext creates a test context func (ts *TestSuite) TestContext() context.Context { return context.Background() } // getEnvOrDefault returns environment variable value or default func getEnvOrDefault(key, defaultValue string) string { if value := os.Getenv(key); value != "" { return value } return defaultValue } // SkipIfNoTestDB skips test if test database is not available func (ts *TestSuite) SkipIfNoTestDB(t *testing.T) { if ts.DB == nil { t.Skip("Test database not available, skipping test") } } // RunInTestTransaction runs a test function in a transaction that gets rolled back func (ts *TestSuite) RunInTestTransaction(t *testing.T, testFn func(tx *gorm.DB)) { tx := ts.DB.Begin() defer tx.Rollback() testFn(tx) }