package models import ( "fmt" "strings" "rocketgit.ru/rsmon/worker/app/models/authidentity" ) // Drop removes all test data from the database. // It uses TRUNCATE ... CASCADE for join tables and deletes leaf-to-root for main tables. // Safe to call from multiple goroutines within a single test binary; concurrent calls // from separate test binaries are serialized by the advisory lock in Migrate(). func Drop() { var dbname string if err := DB().Raw("SELECT current_database()").Scan(&dbname).Error; err != nil { panic(fmt.Sprintf("Drop: cannot read database name: %v", err)) } if !strings.Contains(dbname, "test") { panic(fmt.Sprintf( "Drop() refused: database '%s' is not a test database. "+ "Set DATABASE_NAME=rsmon_test to run tests safely.", dbname, )) } // Truncate many2many join tables first to avoid FK violations. DB().Exec("TRUNCATE event_checks, event_messages, notification_contacts, notification_groups RESTART IDENTITY CASCADE") // Delete main tables in dependency order (leaf tables first). DB().Where("1=1").Delete(&NotificationDelivery{}) DB().Where("1=1").Delete(&TaskReplay{}) DB().Where("1=1").Delete(&TelegramBotMessage{}) DB().Where("1=1").Delete(&TelegramBotStatus{}) DB().Where("1=1").Delete(&Task{}) DB().Where("1=1").Delete(&Access{}) DB().Where("1=1").Delete(&Invite{}) DB().Where("1=1").Delete(&Message{}) DB().Where("1=1").Delete(&Event{}) // Durable network-diagnostic rows reference checks, monitors, and workers. DB().Where("1=1").Delete(&DiagnosticAuditEvent{}) DB().Where("1=1").Delete(&CheckAttempt{}) // CheckRegionResult FKs check_id; it must be cleared before Check. DB().Where("1=1").Delete(&CheckRegionResult{}) DB().Where("1=1").Delete(&Check{}) DB().Where("1=1").Delete(&Notification{}) // DNSRecord FKs monitor_id; it must be cleared before Monitor. DB().Where("1=1").Delete(&DNSRecord{}) DB().Where("1=1").Delete(&Monitor{}) DB().Where("1=1").Delete(&Group{}) DB().Where("1=1").Delete(&Contact{}) DB().Where("1=1").Delete(&NotificationCredential{}) DB().Where("1=1").Delete(&WorkerLogEvent{}) DB().Where("1=1").Delete(&WorkerNode{}) DB().Where("1=1").Delete(&LLM{}) DB().Where("1=1").Delete(&Region{}) // Inventory (docs/plans/inventory-management.md §6): leaf tables // (sites, deployments, server_ips) reference accounts/servers, so // they must be cleared before Server is deleted. DB().Where("1=1").Delete(&Deployment{}) DB().Where("1=1").Delete(&SiteRepo{}) DB().Where("1=1").Delete(&Site{}) DB().Where("1=1").Delete(&Repo{}) DB().Where("1=1").Delete(&ServerIp{}) DB().Where("1=1").Delete(&Domain{}) DB().Where("1=1").Delete(&Server{}) // Account-scoped tag metadata (account_id FK to accounts(id)). // Cleared before Account so a future cascade change cannot orphan // rows mid-truncate. DB().Where("1=1").Delete(&Tag{}) // Status pages (docs/plans/status-pages.md §3.1–3.5). Children // reference status_pages(id) with ON DELETE CASCADE so GORM // ordering would already wipe them, but we delete them // explicitly so the test DB stays clean even if a future model // change drops the cascade. DB().Where("1=1").Delete(&StatusPageDomain{}) DB().Unscoped().Where("1=1").Delete(&Maintenance{}) DB().Where("1=1").Delete(&StatusPageMaintenance{}) DB().Where("1=1").Delete(&StatusPageIncident{}) DB().Where("1=1").Delete(&StatusPageDelivery{}) DB().Where("1=1").Delete(&StatusPageDigestSchedule{}) DB().Where("1=1").Delete(&StatusPageSubscriber{}) DB().Unscoped().Where("1=1").Delete(&StatusPage{}) DB().Where("1=1").Delete(&SubscriptionEvent{}) DB().Where("1=1").Delete(&Subscription{}) DB().Where("1=1").Delete(&Account{}) DB().Unscoped().Where("1=1").Delete(&authidentity.AuthIdentity{}) // User has a unique email index, so test fixtures must be physically // removed rather than soft-deleted between tests. DB().Unscoped().Where("1=1").Delete(&User{}) // A few audited tables added by feature migrations can retain a user FK that // is intentionally not modeled as an association. CASCADE keeps fixture // cleanup deterministic instead of silently leaving unique emails behind. DB().Exec("TRUNCATE users CASCADE") DB().Exec("TRUNCATE regions CASCADE") }