Files
Charon/backend/internal/api/handlers/testdb.go
GitHub Actions 0022b43c8d fix(lint): resolve 20 gocritic, eslint, and type safety issues
Backend (Go):
- Add named return parameters for improved readability
- Modernize octal literals (0755 → 0o755, 0644 → 0o644)
- Replace nil with http.NoBody in test requests (3 instances)
- Add error handling for rows.Close() in test helper
- Close HTTP response bodies in network tests (3 instances)

Frontend (React/TypeScript):
- Add Fast Refresh export suppressions for UI components
- Replace 'any' types with proper TypeScript types (6 instances)
- Add missing useEffect dependency (calculateScore)
- Remove unused variable in Playwright test

Testing:
- Backend coverage: 87.3% (threshold: 85%)
- Frontend coverage: 87.75% (threshold: 85%)
- All tests passing with race detection
- Zero type errors

Security:
- CodeQL scans: Zero HIGH/CRITICAL findings
- Trivy scan: Zero vulnerabilities
- Pre-commit hooks: All passing
2025-12-31 05:21:11 +00:00

150 lines
4.1 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{},
)
}
// 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{},
); err != nil {
t.Fatalf("failed to migrate test db: %v", err)
}
return db
}