package bootstrap import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/testsupport" "context" "log" "os" "path/filepath" "testing" "time" "github.com/testcontainers/testcontainers-go" postgres_module "github.com/testcontainers/testcontainers-go/modules/postgres" "github.com/testcontainers/testcontainers-go/wait" gorm_postgres "gorm.io/driver/postgres" "gorm.io/gorm" ) func TestSanitizeLegacyUserMetadataRemovesClassificationFlags(t *testing.T) { db := openBootstrapPostgresTestDB(t) if err := db.AutoMigrate(&domain.User{}); err != nil { t.Fatalf("failed to migrate users table: %v", err) } user := domain.User{ ID: "10000000-0000-0000-0000-000000000001", Email: "legacy@example.com", Name: "Legacy User", Role: domain.RoleUser, Status: domain.UserStatusActive, Metadata: domain.JSONMap{ "hanmacFamily": true, "userType": "hanmac", "employeeId": "E001", "nested": map[string]any{ "userType": "must stay nested", }, }, } if err := db.Create(&user).Error; err != nil { t.Fatalf("failed to create user: %v", err) } if err := SanitizeLegacyUserMetadata(db); err != nil { t.Fatalf("SanitizeLegacyUserMetadata returned error: %v", err) } if err := SanitizeLegacyUserMetadata(db); err != nil { t.Fatalf("SanitizeLegacyUserMetadata must be idempotent: %v", err) } var got domain.User if err := db.First(&got, "id = ?", user.ID).Error; err != nil { t.Fatalf("failed to load sanitized user: %v", err) } if _, ok := got.Metadata["hanmacFamily"]; ok { t.Fatalf("hanmacFamily must be removed from metadata: %#v", got.Metadata) } if _, ok := got.Metadata["userType"]; ok { t.Fatalf("userType must be removed from metadata: %#v", got.Metadata) } if got.Metadata["employeeId"] != "E001" { t.Fatalf("employeeId = %#v, want E001", got.Metadata["employeeId"]) } nested, ok := got.Metadata["nested"].(map[string]any) if !ok || nested["userType"] != "must stay nested" { t.Fatalf("nested metadata must be preserved: %#v", got.Metadata["nested"]) } } func TestCanonicalizeLegacyUserStatuses(t *testing.T) { db := openBootstrapPostgresTestDB(t) if err := db.AutoMigrate(&domain.User{}); err != nil { t.Fatalf("failed to migrate users table: %v", err) } users := []domain.User{ {ID: "11000000-0000-0000-0000-000000000001", Email: "inactive@example.com", Name: "Inactive", Role: domain.RoleUser, Status: "inactive"}, {ID: "11000000-0000-0000-0000-000000000002", Email: "leave@example.com", Name: "Leave", Role: domain.RoleUser, Status: "leave_of_absence"}, {ID: "11000000-0000-0000-0000-000000000003", Email: "baron-only@example.com", Name: "Baron Only", Role: domain.RoleUser, Status: "baron_only"}, {ID: "11000000-0000-0000-0000-000000000004", Email: "active@example.com", Name: "Active", Role: domain.RoleUser, Status: domain.UserStatusActive}, } if err := db.Create(&users).Error; err != nil { t.Fatalf("failed to create users: %v", err) } if err := CanonicalizeLegacyUserStatuses(db); err != nil { t.Fatalf("CanonicalizeLegacyUserStatuses returned error: %v", err) } if err := CanonicalizeLegacyUserStatuses(db); err != nil { t.Fatalf("CanonicalizeLegacyUserStatuses must be idempotent: %v", err) } got := map[string]string{} var loaded []domain.User if err := db.Find(&loaded).Error; err != nil { t.Fatalf("failed to load users: %v", err) } for _, user := range loaded { got[user.Email] = user.Status } if got["inactive@example.com"] != domain.UserStatusPreboarding { t.Fatalf("inactive status = %q, want %q", got["inactive@example.com"], domain.UserStatusPreboarding) } if got["leave@example.com"] != domain.UserStatusTemporaryLeave { t.Fatalf("leave status = %q, want %q", got["leave@example.com"], domain.UserStatusTemporaryLeave) } if got["baron-only@example.com"] != domain.UserStatusBaronGuest { t.Fatalf("baron_only status = %q, want %q", got["baron-only@example.com"], domain.UserStatusBaronGuest) } if got["active@example.com"] != domain.UserStatusActive { t.Fatalf("active status = %q, want %q", got["active@example.com"], domain.UserStatusActive) } } func TestRunSanitizesLegacyUserMetadata(t *testing.T) { db := openBootstrapPostgresTestDB(t) if err := db.AutoMigrate(&domain.User{}); err != nil { t.Fatalf("failed to migrate users table: %v", err) } user := domain.User{ ID: "20000000-0000-0000-0000-000000000001", Email: "run-legacy@example.com", Name: "Run Legacy User", Role: domain.RoleUser, Status: domain.UserStatusActive, Metadata: domain.JSONMap{ "hanmacFamily": true, "userType": "external", "employeeId": "E002", }, } if err := db.Create(&user).Error; err != nil { t.Fatalf("failed to create user: %v", err) } dir := t.TempDir() path := filepath.Join(dir, "seed-tenant.csv") csv := "id,name,type,parent_tenant_slug,slug,memo,email_domain\n" + "30000000-0000-0000-0000-000000000001,Seed Root,COMPANY_GROUP,,seed-root,seed root,\n" if err := os.WriteFile(path, []byte(csv), 0o600); err != nil { t.Fatalf("failed to write seed csv: %v", err) } t.Setenv(seedTenantCSVPathEnv, path) if err := Run(db); err != nil { t.Fatalf("Run returned error: %v", err) } var got domain.User if err := db.First(&got, "id = ?", user.ID).Error; err != nil { t.Fatalf("failed to load sanitized user: %v", err) } if _, ok := got.Metadata["hanmacFamily"]; ok { t.Fatalf("Run must remove hanmacFamily from metadata: %#v", got.Metadata) } if _, ok := got.Metadata["userType"]; ok { t.Fatalf("Run must remove userType from metadata: %#v", got.Metadata) } if got.Metadata["employeeId"] != "E002" { t.Fatalf("employeeId = %#v, want E002", got.Metadata["employeeId"]) } } func openBootstrapPostgresTestDB(t *testing.T) *gorm.DB { t.Helper() if !testsupport.DockerAvailable() { t.Skip("Docker provider is unavailable in this environment") } ctx := context.Background() postgresContainer, err := postgres_module.Run(ctx, "postgres:16-alpine", postgres_module.WithDatabase("testdb"), postgres_module.WithUsername("user"), postgres_module.WithPassword("password"), testcontainers.WithWaitStrategy( wait.ForLog("database system is ready to accept connections"). WithOccurrence(2). WithStartupTimeout(30*time.Second), ), ) if err != nil { t.Fatalf("failed to start postgres container: %v", err) } t.Cleanup(func() { if err := postgresContainer.Terminate(ctx); err != nil { log.Printf("failed to terminate postgres container: %v", err) } }) connStr, err := postgresContainer.ConnectionString(ctx, "sslmode=disable") if err != nil { t.Fatalf("failed to get postgres connection string: %v", err) } db, err := gorm.Open(gorm_postgres.Open(connStr), &gorm.Config{}) if err != nil { t.Fatalf("failed to open postgres connection: %v", err) } return db }