- Add comprehensive test suite for plugin_handler (25 tests) - Expand credential_handler error path coverage (20 tests) - Enhance caddy/config DNS challenge & security tests (23 tests) - Improve hub_sync SSRF protection & backup tests (66 tests) - Add encryption_handler, audit_log, manager tests (35+ tests) - Fix DNS provider registry initialization in test files - Configure encryption keys for credential rotation tests Coverage improvements by file: - plugin_handler: 0% → 75.67% - credential_handler: 32.83% → 84.93% - caddy/config: 79.82% → 94.5% - hub_sync: 56.78% → 78%+ - encryption_handler: 78.29% → 94.29% - manager: 76.13% → 96.46% - audit_log_handler: 78.08% → 94.25% Overall backend: 73% → 84.1% (+11.1%) All 1400+ tests passing. Security scans clean (CodeQL, Go vuln).
156 lines
4.3 KiB
Go
156 lines
4.3 KiB
Go
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
|
|
}
|
|
|
|
// OpenTestDB creates 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)
|
|
}
|
|
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
|
|
}
|