Complete lint remediation addressing errcheck, gosec, and staticcheck violations across backend test files. Tighten pre-commit configuration to prevent future blind spots. Key Changes: - Fix 61 Go linting issues (errcheck, gosec G115/G301/G304/G306, bodyclose) - Add proper error handling for json.Unmarshal, os.Setenv, db.Close(), w.Write() - Fix gosec G115 integer overflow with strconv.FormatUint - Add #nosec annotations with justifications for test fixtures - Fix SecurityService goroutine leaks (add Close() calls) - Fix CrowdSec tar.gz non-deterministic ordering with sorted keys Pre-commit Hardening: - Remove test file exclusion from golangci-lint hook - Add gosec to .golangci-fast.yml with critical checks (G101, G110, G305) - Replace broad .golangci.yml exclusions with targeted path-specific rules - Test files now linted on every commit Test Fixes: - Fix emergency route count assertions (1→2 for dual-port setup) - Fix DNS provider service tests with proper mock setup - Fix certificate service tests with deterministic behavior Backend: 27 packages pass, 83.5% coverage Frontend: 0 lint warnings, 0 TypeScript errors Pre-commit: All 14 hooks pass (~37s)
604 lines
17 KiB
Go
604 lines
17 KiB
Go
package services
|
|
|
|
import (
|
|
"net"
|
|
"testing"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
gormlogger "gorm.io/gorm/logger"
|
|
)
|
|
|
|
// TestCoverageBoost_ErrorPaths tests various error handling paths to increase coverage
|
|
func TestCoverageBoost_ErrorPaths(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Migrate all tables
|
|
err = db.AutoMigrate(
|
|
&models.ProxyHost{},
|
|
&models.RemoteServer{},
|
|
&models.SecurityConfig{},
|
|
&models.SecurityRuleSet{},
|
|
&models.NotificationTemplate{},
|
|
&models.Setting{},
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("ProxyHostService_GetByUUID_Error", func(t *testing.T) {
|
|
svc := NewProxyHostService(db)
|
|
|
|
// Test with non-existent UUID
|
|
_, err := svc.GetByUUID("non-existent-uuid")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("ProxyHostService_List_WithValidDB", func(t *testing.T) {
|
|
svc := NewProxyHostService(db)
|
|
|
|
// Should not error even with empty db
|
|
hosts, err := svc.List()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, hosts)
|
|
})
|
|
|
|
t.Run("RemoteServerService_GetByUUID_Error", func(t *testing.T) {
|
|
svc := NewRemoteServerService(db)
|
|
|
|
// Test with non-existent UUID
|
|
_, err := svc.GetByUUID("non-existent-uuid")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("RemoteServerService_List_WithValidDB", func(t *testing.T) {
|
|
svc := NewRemoteServerService(db)
|
|
|
|
// Should not error with empty db
|
|
servers, err := svc.List(false)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, servers)
|
|
})
|
|
|
|
t.Run("SecurityService_Get_NotFound", func(t *testing.T) {
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close()
|
|
|
|
// No config exists yet
|
|
_, err := svc.Get()
|
|
assert.ErrorIs(t, err, ErrSecurityConfigNotFound)
|
|
})
|
|
|
|
t.Run("SecurityService_ListRuleSets_EmptyDB", func(t *testing.T) {
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close()
|
|
|
|
// Should not error with empty db
|
|
rulesets, err := svc.ListRuleSets()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, rulesets)
|
|
assert.Empty(t, rulesets)
|
|
})
|
|
|
|
t.Run("SecurityService_DeleteRuleSet_NotFound", func(t *testing.T) {
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close()
|
|
|
|
// Test with non-existent ID
|
|
err := svc.DeleteRuleSet(999)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("SecurityService_VerifyBreakGlass_MissingConfig", func(t *testing.T) {
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close()
|
|
|
|
// No config exists
|
|
valid, err := svc.VerifyBreakGlassToken("default", "anytoken")
|
|
assert.Error(t, err)
|
|
assert.False(t, valid)
|
|
})
|
|
|
|
t.Run("SecurityService_GenerateBreakGlassToken_Success", func(t *testing.T) {
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close()
|
|
|
|
// Generate token
|
|
token, err := svc.GenerateBreakGlassToken("test-config")
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, token)
|
|
|
|
// Verify it was created
|
|
var cfg models.SecurityConfig
|
|
err = db.Where("name = ?", "test-config").First(&cfg).Error
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, cfg.BreakGlassHash)
|
|
})
|
|
|
|
t.Run("NotificationService_ListTemplates_EmptyDB", func(t *testing.T) {
|
|
svc := NewNotificationService(db)
|
|
|
|
// Should not error with empty db
|
|
templates, err := svc.ListTemplates()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, templates)
|
|
assert.Empty(t, templates)
|
|
})
|
|
|
|
t.Run("NotificationService_GetTemplate_NotFound", func(t *testing.T) {
|
|
svc := NewNotificationService(db)
|
|
|
|
// Test with non-existent ID
|
|
_, err := svc.GetTemplate("nonexistent")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_SecurityService_AdditionalPaths tests more security service paths
|
|
func TestCoverageBoost_SecurityService_AdditionalPaths(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.SecurityConfig{}, &models.SecurityRuleSet{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close()
|
|
|
|
t.Run("Upsert_Create", func(t *testing.T) {
|
|
// Create initial config
|
|
cfg := &models.SecurityConfig{
|
|
Name: "default",
|
|
CrowdSecMode: "local",
|
|
}
|
|
err := svc.Upsert(cfg)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("UpsertRuleSet_Create", func(t *testing.T) {
|
|
ruleset := &models.SecurityRuleSet{
|
|
Name: "test-ruleset-new",
|
|
SourceURL: "https://example.com",
|
|
}
|
|
err := svc.UpsertRuleSet(ruleset)
|
|
assert.NoError(t, err)
|
|
|
|
// Verify created
|
|
var found models.SecurityRuleSet
|
|
err = db.Where("name = ?", "test-ruleset-new").First(&found).Error
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_MinInt tests the minInt helper
|
|
func TestCoverageBoost_MinInt(t *testing.T) {
|
|
t.Run("minInt_FirstSmaller", func(t *testing.T) {
|
|
result := minInt(5, 10)
|
|
assert.Equal(t, 5, result)
|
|
})
|
|
|
|
t.Run("minInt_SecondSmaller", func(t *testing.T) {
|
|
result := minInt(10, 5)
|
|
assert.Equal(t, 5, result)
|
|
})
|
|
|
|
t.Run("minInt_Equal", func(t *testing.T) {
|
|
result := minInt(5, 5)
|
|
assert.Equal(t, 5, result)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_MailService_ErrorPaths tests mail service error handling
|
|
func TestCoverageBoost_MailService_ErrorPaths(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.Setting{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewMailService(db)
|
|
|
|
t.Run("GetSMTPConfig_EmptyDB", func(t *testing.T) {
|
|
// Empty DB should return config with defaults
|
|
config, err := svc.GetSMTPConfig()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, config)
|
|
})
|
|
|
|
t.Run("IsConfigured_NoConfig", func(t *testing.T) {
|
|
// With empty DB, should return false
|
|
configured := svc.IsConfigured()
|
|
assert.False(t, configured)
|
|
})
|
|
|
|
t.Run("TestConnection_NoConfig", func(t *testing.T) {
|
|
// With empty config, should error
|
|
err := svc.TestConnection()
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("SendEmail_NoConfig", func(t *testing.T) {
|
|
// With empty config, should error
|
|
err := svc.SendEmail("test@example.com", "Subject", "Body")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_AccessListService_Paths tests access list error paths
|
|
func TestCoverageBoost_AccessListService_Paths(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.AccessList{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewAccessListService(db)
|
|
|
|
t.Run("GetByID_NotFound", func(t *testing.T) {
|
|
_, err := svc.GetByID(999)
|
|
assert.ErrorIs(t, err, ErrAccessListNotFound)
|
|
})
|
|
|
|
t.Run("GetByUUID_NotFound", func(t *testing.T) {
|
|
_, err := svc.GetByUUID("nonexistent-uuid")
|
|
assert.ErrorIs(t, err, ErrAccessListNotFound)
|
|
})
|
|
|
|
t.Run("List_EmptyDB", func(t *testing.T) {
|
|
// Should not error with empty db
|
|
lists, err := svc.List()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, lists)
|
|
assert.Empty(t, lists)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_HelperFunctions tests utility helper functions
|
|
func TestCoverageBoost_HelperFunctions(t *testing.T) {
|
|
t.Run("extractPort_HTTP", func(t *testing.T) {
|
|
port := extractPort("http://example.com:8080/path")
|
|
assert.Equal(t, "8080", port)
|
|
})
|
|
|
|
t.Run("extractPort_HTTPS", func(t *testing.T) {
|
|
port := extractPort("https://example.com:443")
|
|
assert.Equal(t, "443", port)
|
|
})
|
|
|
|
t.Run("extractPort_Invalid", func(t *testing.T) {
|
|
port := extractPort("not-a-url")
|
|
assert.Equal(t, "", port)
|
|
})
|
|
|
|
t.Run("hasHeader_Found", func(t *testing.T) {
|
|
headers := map[string][]string{
|
|
"X-Test-Header": {"value1", "value2"},
|
|
"Content-Type": {"application/json"},
|
|
}
|
|
assert.True(t, hasHeader(headers, "X-Test-Header"))
|
|
assert.True(t, hasHeader(headers, "Content-Type"))
|
|
})
|
|
|
|
t.Run("hasHeader_NotFound", func(t *testing.T) {
|
|
headers := map[string][]string{
|
|
"X-Test-Header": {"value1"},
|
|
}
|
|
assert.False(t, hasHeader(headers, "X-Missing-Header"))
|
|
})
|
|
|
|
t.Run("hasHeader_EmptyMap", func(t *testing.T) {
|
|
headers := map[string][]string{}
|
|
assert.False(t, hasHeader(headers, "Any-Header"))
|
|
})
|
|
|
|
t.Run("isPrivateIP_PrivateRanges", func(t *testing.T) {
|
|
assert.True(t, isPrivateIP(net.ParseIP("192.168.1.1")))
|
|
assert.True(t, isPrivateIP(net.ParseIP("10.0.0.1")))
|
|
assert.True(t, isPrivateIP(net.ParseIP("172.16.0.1")))
|
|
assert.True(t, isPrivateIP(net.ParseIP("127.0.0.1")))
|
|
})
|
|
|
|
t.Run("isPrivateIP_PublicIP", func(t *testing.T) {
|
|
assert.False(t, isPrivateIP(net.ParseIP("8.8.8.8")))
|
|
assert.False(t, isPrivateIP(net.ParseIP("1.1.1.1")))
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_ProxyHostService_DB tests DB accessor
|
|
func TestCoverageBoost_ProxyHostService_DB(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewProxyHostService(db)
|
|
|
|
t.Run("DB_ReturnsValidDB", func(t *testing.T) {
|
|
dbInstance := svc.DB()
|
|
assert.NotNil(t, dbInstance)
|
|
assert.Equal(t, db, dbInstance)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_DNSProviderService_SupportedTypes tests provider type queries
|
|
func TestCoverageBoost_DNSProviderService_SupportedTypes(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.DNSProvider{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewDNSProviderService(db, nil)
|
|
|
|
t.Run("GetSupportedProviderTypes", func(t *testing.T) {
|
|
types := svc.GetSupportedProviderTypes()
|
|
assert.NotNil(t, types)
|
|
// Should include at least some built-in types
|
|
assert.NotEmpty(t, types)
|
|
})
|
|
|
|
t.Run("GetProviderCredentialFields_ValidProvider", func(t *testing.T) {
|
|
types := svc.GetSupportedProviderTypes()
|
|
if len(types) > 0 {
|
|
// Test with first available provider
|
|
fields, err := svc.GetProviderCredentialFields(types[0])
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, fields)
|
|
}
|
|
})
|
|
|
|
t.Run("GetProviderCredentialFields_InvalidProvider", func(t *testing.T) {
|
|
fields, err := svc.GetProviderCredentialFields("invalid-provider-type-12345")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, fields)
|
|
assert.Contains(t, err.Error(), "unsupported provider type")
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_SecurityService_Close tests service cleanup
|
|
func TestCoverageBoost_SecurityService_Close(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewSecurityService(db)
|
|
defer svc.Close() // Ensure cleanup even if test panics
|
|
|
|
t.Run("Close_Success", func(t *testing.T) {
|
|
svc.Close()
|
|
// Close doesn't return error, just ensure it doesn't panic
|
|
})
|
|
|
|
t.Run("Flush_Success", func(t *testing.T) {
|
|
svc.Flush()
|
|
// Flush doesn't return error, just ensure it doesn't panic
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_BackupService_GetAvailableSpace tests disk space checking
|
|
func TestCoverageBoost_BackupService_GetAvailableSpace(t *testing.T) {
|
|
// Skip these tests as they require full config setup
|
|
t.Skip("BackupService requires full config.Config, tested elsewhere")
|
|
}
|
|
|
|
// TestCoverageBoost_CertificateService_ListCertificates tests certificate listing with errors
|
|
func TestCoverageBoost_CertificateService_ListCertificates(t *testing.T) {
|
|
// Skip these tests as they require proper model imports
|
|
t.Skip("Certificate models tested in certificate_service_test.go")
|
|
}
|
|
|
|
// TestCoverageBoost_MailService_SendSSL tests SSL mail sending error paths
|
|
func TestCoverageBoost_MailService_SendSSL(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.Setting{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewMailService(db)
|
|
|
|
t.Run("SendEmail_SSL_InvalidHost", func(t *testing.T) {
|
|
// Save invalid config
|
|
config := &SMTPConfig{
|
|
Host: "invalid-mail-server-12345.example.com",
|
|
Port: 465,
|
|
Username: "test",
|
|
Password: "test",
|
|
FromAddress: "test@example.com",
|
|
Encryption: "ssl",
|
|
}
|
|
err := svc.SaveSMTPConfig(config)
|
|
require.NoError(t, err)
|
|
|
|
// Try to send - should fail with connection error
|
|
err = svc.SendEmail("test@example.com", "Test", "Body")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("SendEmail_STARTTLS_InvalidHost", func(t *testing.T) {
|
|
// Save invalid config with STARTTLS
|
|
config := &SMTPConfig{
|
|
Host: "invalid-mail-server-12345.example.com",
|
|
Port: 587,
|
|
Username: "test",
|
|
Password: "test",
|
|
FromAddress: "test@example.com",
|
|
Encryption: "starttls",
|
|
}
|
|
err := svc.SaveSMTPConfig(config)
|
|
require.NoError(t, err)
|
|
|
|
// Try to send - should fail with connection error
|
|
err = svc.SendEmail("test@example.com", "Test", "Body")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_CredentialService_ErrorPaths tests credential service error handling
|
|
func TestCoverageBoost_CredentialService_ErrorPaths(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.DNSProvider{}, &models.DNSProviderCredential{})
|
|
require.NoError(t, err)
|
|
|
|
// Note: CredentialService requires crypto.EncryptionService, tested in credential_service_test.go
|
|
t.Skip("CredentialService requires crypto.EncryptionService, tested elsewhere")
|
|
}
|
|
|
|
// TestCoverageBoost_GeoIPService_ErrorPaths tests GeoIP service error handling
|
|
func TestCoverageBoost_GeoIPService_ErrorPaths(t *testing.T) {
|
|
t.Run("NewGeoIPService_InvalidPath", func(t *testing.T) {
|
|
svc, err := NewGeoIPService("/nonexistent/path/to/geoip.mmdb")
|
|
assert.Error(t, err)
|
|
assert.Nil(t, svc)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_DockerService_ErrorPaths tests Docker service error handling
|
|
func TestCoverageBoost_DockerService_ErrorPaths(t *testing.T) {
|
|
t.Skip("Docker service tests require specific setup, tested in docker_service_test.go")
|
|
}
|
|
|
|
// TestCoverageBoost_UptimeService_FlushNotifications tests notification flushing
|
|
func TestCoverageBoost_UptimeService_FlushNotifications(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.UptimeMonitor{}, &models.UptimeHost{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewUptimeService(db, nil)
|
|
|
|
t.Run("FlushPendingNotifications", func(t *testing.T) {
|
|
// Should not error even with empty pending notifications
|
|
svc.FlushPendingNotifications()
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_LogService_NewLogService tests log service creation
|
|
func TestCoverageBoost_LogService_NewLogService(t *testing.T) {
|
|
t.Skip("LogService requires full config, tested in log_service_test.go")
|
|
}
|
|
|
|
// TestCoverageBoost_UpdateService_ClearCache tests cache clearing
|
|
func TestCoverageBoost_UpdateService_ClearCache(t *testing.T) {
|
|
svc := NewUpdateService()
|
|
|
|
t.Run("ClearCache", func(t *testing.T) {
|
|
svc.ClearCache()
|
|
})
|
|
|
|
t.Run("SetCurrentVersion", func(t *testing.T) {
|
|
svc.SetCurrentVersion("v1.2.3")
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_NotificationService_Providers tests provider management
|
|
func TestCoverageBoost_NotificationService_Providers(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.NotificationProvider{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewNotificationService(db)
|
|
|
|
t.Run("ListProviders_EmptyDB", func(t *testing.T) {
|
|
providers, err := svc.ListProviders()
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, providers)
|
|
assert.Empty(t, providers)
|
|
})
|
|
|
|
t.Run("CreateProvider", func(t *testing.T) {
|
|
provider := &models.NotificationProvider{
|
|
Name: "test-provider",
|
|
Type: "webhook",
|
|
Enabled: true,
|
|
Config: `{"url": "https://example.com/hook"}`,
|
|
}
|
|
err := svc.CreateProvider(provider)
|
|
assert.NoError(t, err)
|
|
assert.NotZero(t, provider.ID)
|
|
})
|
|
|
|
t.Run("UpdateProvider", func(t *testing.T) {
|
|
// Create a provider first
|
|
provider := &models.NotificationProvider{
|
|
Name: "update-test",
|
|
Type: "webhook",
|
|
Enabled: true,
|
|
Config: `{"url": "https://example.com/hook"}`,
|
|
}
|
|
err := svc.CreateProvider(provider)
|
|
require.NoError(t, err)
|
|
|
|
// Update it
|
|
provider.Name = "updated-name"
|
|
err = svc.UpdateProvider(provider)
|
|
assert.NoError(t, err)
|
|
})
|
|
|
|
t.Run("DeleteProvider", func(t *testing.T) {
|
|
// Create a provider first
|
|
provider := &models.NotificationProvider{
|
|
Name: "delete-test",
|
|
Type: "webhook",
|
|
Enabled: true,
|
|
Config: `{"url": "https://example.com/hook"}`,
|
|
}
|
|
err := svc.CreateProvider(provider)
|
|
require.NoError(t, err)
|
|
|
|
// Delete it
|
|
err = svc.DeleteProvider(provider.ID)
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|
|
|
|
// TestCoverageBoost_NotificationService_CRUD tests notification CRUD operations
|
|
func TestCoverageBoost_NotificationService_CRUD(t *testing.T) {
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&models.Notification{})
|
|
require.NoError(t, err)
|
|
|
|
svc := NewNotificationService(db)
|
|
|
|
t.Run("List_EmptyDB", func(t *testing.T) {
|
|
notifs, err := svc.List(false)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, notifs)
|
|
})
|
|
|
|
t.Run("MarkAllAsRead_Success", func(t *testing.T) {
|
|
err := svc.MarkAllAsRead()
|
|
assert.NoError(t, err)
|
|
})
|
|
}
|