Files
Charon/backend/internal/api/handlers/testdb.go
T
akanealw eec8c28fb3
Go Benchmark / Performance Regression Check (push) Has been cancelled
Cerberus Integration / Cerberus Security Stack Integration (push) Has been cancelled
Upload Coverage to Codecov / Backend Codecov Upload (push) Has been cancelled
Upload Coverage to Codecov / Frontend Codecov Upload (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (go) (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Has been cancelled
CrowdSec Integration / CrowdSec Bouncer Integration (push) Has been cancelled
Docker Build, Publish & Test / build-and-push (push) Has been cancelled
Quality Checks / Auth Route Protection Contract (push) Has been cancelled
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Has been cancelled
Quality Checks / Backend (Go) (push) Has been cancelled
Quality Checks / Frontend (React) (push) Has been cancelled
Rate Limit integration / Rate Limiting Integration (push) Has been cancelled
Security Scan (PR) / Trivy Binary Scan (push) Has been cancelled
Supply Chain Verification (PR) / Verify Supply Chain (push) Has been cancelled
WAF integration / Coraza WAF Integration (push) Has been cancelled
Docker Build, Publish & Test / Security Scan PR Image (push) Has been cancelled
Repo Health Check / Repo health (push) Has been cancelled
History Rewrite Dry-Run / Dry-run preview for history rewrite (push) Has been cancelled
Prune Renovate Branches / prune (push) Has been cancelled
Renovate / renovate (push) Has been cancelled
Nightly Build & Package / sync-development-to-nightly (push) Has been cancelled
Nightly Build & Package / Trigger Nightly Validation Workflows (push) Has been cancelled
Nightly Build & Package / build-and-push-nightly (push) Has been cancelled
Nightly Build & Package / test-nightly-image (push) Has been cancelled
Nightly Build & Package / verify-nightly-supply-chain (push) Has been cancelled
Update GeoLite2 Checksum / update-checksum (push) Has been cancelled
Container Registry Prune / prune-ghcr (push) Has been cancelled
Container Registry Prune / prune-dockerhub (push) Has been cancelled
Container Registry Prune / summarize (push) Has been cancelled
Supply Chain Verification / Verify SBOM (push) Has been cancelled
Supply Chain Verification / Verify Release Artifacts (push) Has been cancelled
Supply Chain Verification / Verify Docker Image Supply Chain (push) Has been cancelled
Monitor Caddy Major Release / check-caddy-major (push) Has been cancelled
Weekly Nightly to Main Promotion / Verify Nightly Branch Health (push) Has been cancelled
Weekly Nightly to Main Promotion / Create Promotion PR (push) Has been cancelled
Weekly Nightly to Main Promotion / Trigger Missing Required Checks (push) Has been cancelled
Weekly Nightly to Main Promotion / Notify on Failure (push) Has been cancelled
Weekly Nightly to Main Promotion / Workflow Summary (push) Has been cancelled
Weekly Security Rebuild / Security Rebuild & Scan (push) Has been cancelled
changed perms
2026-04-22 18:19:14 +00:00

163 lines
4.4 KiB
Go
Executable File

package handlers
import (
crand "crypto/rand"
"fmt"
"math/big"
"strings"
"sync"
"testing"
"time"
"github.com/Wikid82/charon/backend/internal/models"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var (
templateDBOnce sync.Once
templateDB *gorm.DB
templateErr error
)
// initTemplateDB creates a pre-migrated database template (called once).
// This eliminates repeated AutoMigrate calls across tests.
func initTemplateDB() {
templateDB, templateErr = gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if templateErr != nil {
return
}
// Migrate ALL models once
templateErr = templateDB.AutoMigrate(
&models.User{},
&models.ProxyHost{},
&models.Location{},
&models.RemoteServer{},
&models.Notification{},
&models.NotificationProvider{},
&models.NotificationTemplate{},
&models.NotificationConfig{},
&models.Setting{},
&models.SecurityConfig{},
&models.SecurityDecision{},
&models.SecurityAudit{},
&models.SecurityRuleSet{},
&models.SecurityHeaderProfile{},
&models.SSLCertificate{},
&models.AccessList{},
&models.UptimeMonitor{},
&models.UptimeHeartbeat{},
&models.UptimeHost{},
&models.UptimeNotificationEvent{},
&models.ImportSession{},
&models.CaddyConfig{},
&models.Domain{},
&models.CrowdsecConsoleEnrollment{},
&models.Plugin{},
&models.DNSProvider{},
&models.DNSProviderCredential{},
)
}
// GetTemplateDB returns the pre-migrated template database.
// Tests can use this to copy the schema instead of running AutoMigrate each time.
func GetTemplateDB() (*gorm.DB, error) {
templateDBOnce.Do(initTemplateDB)
return templateDB, templateErr
}
// Opens a SQLite in-memory DB unique per test and applies
// a busy timeout and WAL journal mode to reduce SQLITE locking during parallel tests.
func OpenTestDB(t *testing.T) *gorm.DB {
t.Helper()
// Append a timestamp/random suffix to ensure uniqueness even across parallel runs
dsnName := strings.ReplaceAll(t.Name(), "/", "_")
// Use crypto/rand for suffix generation in tests to avoid static analysis warnings
n, _ := crand.Int(crand.Reader, big.NewInt(10000))
uniqueSuffix := fmt.Sprintf("%d%d", time.Now().UnixNano(), n.Int64())
dsn := fmt.Sprintf("file:%s_%s?mode=memory&cache=shared&_journal_mode=WAL&_busy_timeout=5000", dsnName, uniqueSuffix)
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
t.Fatalf("failed to open test db: %v", err)
}
// Register cleanup to close database connection
t.Cleanup(func() {
sqlDB, err := db.DB()
if err == nil {
_ = sqlDB.Close()
}
})
return db
}
// OpenTestDBWithMigrations creates a SQLite in-memory DB and runs AutoMigrate
// for all commonly used models. This is faster than individual test migrations
// because it uses the template database schema when available.
func OpenTestDBWithMigrations(t *testing.T) *gorm.DB {
t.Helper()
db := OpenTestDB(t)
// Try to get template DB and copy schema
if tmpl, err := GetTemplateDB(); err == nil && tmpl != nil {
// Copy all table schemas from template
// For SQLite, we can use the template's schema info
rows, err := tmpl.Raw("SELECT sql FROM sqlite_master WHERE type='table' AND sql IS NOT NULL").Rows()
if err == nil {
defer func() {
if closeErr := rows.Close(); closeErr != nil {
t.Logf("warning: failed to close rows: %v", closeErr)
}
}()
for rows.Next() {
var sql string
if rows.Scan(&sql) == nil && sql != "" {
db.Exec(sql)
}
}
return db
}
}
// Fallback: run AutoMigrate directly if template not available
if err := db.AutoMigrate(
&models.User{},
&models.ProxyHost{},
&models.Location{},
&models.RemoteServer{},
&models.Notification{},
&models.NotificationProvider{},
&models.NotificationTemplate{},
&models.NotificationConfig{},
&models.Setting{},
&models.SecurityConfig{},
&models.SecurityDecision{},
&models.SecurityAudit{},
&models.SecurityRuleSet{},
&models.SecurityHeaderProfile{},
&models.SSLCertificate{},
&models.AccessList{},
&models.UptimeMonitor{},
&models.UptimeHeartbeat{},
&models.UptimeHost{},
&models.UptimeNotificationEvent{},
&models.ImportSession{},
&models.CaddyConfig{},
&models.Domain{},
&models.CrowdsecConsoleEnrollment{},
&models.Plugin{},
&models.DNSProvider{},
&models.DNSProviderCredential{},
); err != nil {
t.Fatalf("failed to migrate test db: %v", err)
}
return db
}