9a05e2f927
- Implement DNSProviderCard component for displaying individual DNS provider details. - Create DNSProviderForm component for adding and editing DNS providers. - Add DNSProviderSelector component for selecting DNS providers in forms. - Introduce useDNSProviders hook for fetching and managing DNS provider data. - Add DNSProviders page for listing and managing DNS providers. - Update layout to include DNS Providers navigation. - Enhance UI components with new badge styles and improved layouts. - Add default provider schemas for various DNS providers. - Integrate translation strings for DNS provider management. - Update Vite configuration for improved chunking and performance.
768 lines
22 KiB
Go
768 lines
22 KiB
Go
package services
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/Wikid82/charon/backend/internal/crypto"
|
|
"github.com/Wikid82/charon/backend/internal/models"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
// setupTestDB creates an in-memory SQLite database for testing.
|
|
func setupDNSProviderTestDB(t *testing.T) (*gorm.DB, *crypto.EncryptionService) {
|
|
t.Helper()
|
|
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Auto-migrate schema
|
|
err = db.AutoMigrate(&models.DNSProvider{})
|
|
require.NoError(t, err)
|
|
|
|
// Create encryption service with test key
|
|
encryptor, err := crypto.NewEncryptionService("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") // 32-byte key in base64
|
|
require.NoError(t, err)
|
|
|
|
return db, encryptor
|
|
}
|
|
|
|
func TestDNSProviderService_Create(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
tests := []struct {
|
|
name string
|
|
req CreateDNSProviderRequest
|
|
wantErr bool
|
|
expectedErr error
|
|
}{
|
|
{
|
|
name: "valid cloudflare provider",
|
|
req: CreateDNSProviderRequest{
|
|
Name: "Test Cloudflare",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{
|
|
"api_token": "test-token-123",
|
|
},
|
|
PropagationTimeout: 120,
|
|
PollingInterval: 5,
|
|
IsDefault: true,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid route53 provider with defaults",
|
|
req: CreateDNSProviderRequest{
|
|
Name: "Test Route53",
|
|
ProviderType: "route53",
|
|
Credentials: map[string]string{
|
|
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
|
|
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
|
|
"region": "us-east-1",
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid provider type",
|
|
req: CreateDNSProviderRequest{
|
|
Name: "Invalid Provider",
|
|
ProviderType: "invalid",
|
|
Credentials: map[string]string{
|
|
"api_key": "test",
|
|
},
|
|
},
|
|
wantErr: true,
|
|
expectedErr: ErrInvalidProviderType,
|
|
},
|
|
{
|
|
name: "missing required credentials",
|
|
req: CreateDNSProviderRequest{
|
|
Name: "Incomplete Cloudflare",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{},
|
|
},
|
|
wantErr: true,
|
|
expectedErr: ErrInvalidCredentials,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
provider, err := service.Create(ctx, tt.req)
|
|
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
if tt.expectedErr != nil {
|
|
assert.ErrorIs(t, err, tt.expectedErr)
|
|
}
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.NotZero(t, provider.ID)
|
|
assert.NotEmpty(t, provider.UUID)
|
|
assert.Equal(t, tt.req.Name, provider.Name)
|
|
assert.Equal(t, tt.req.ProviderType, provider.ProviderType)
|
|
assert.True(t, provider.Enabled)
|
|
assert.NotEmpty(t, provider.CredentialsEncrypted)
|
|
|
|
// Verify defaults were set
|
|
if tt.req.PropagationTimeout == 0 {
|
|
assert.Equal(t, 120, provider.PropagationTimeout)
|
|
}
|
|
if tt.req.PollingInterval == 0 {
|
|
assert.Equal(t, 5, provider.PollingInterval)
|
|
}
|
|
|
|
// Verify credentials are encrypted (not plaintext)
|
|
assert.NotContains(t, provider.CredentialsEncrypted, "api_token")
|
|
assert.NotContains(t, provider.CredentialsEncrypted, "test-token")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDNSProviderService_DefaultProviderLogic(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create first default provider
|
|
provider1, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "First Default",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{
|
|
"api_token": "token1",
|
|
},
|
|
IsDefault: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, provider1.IsDefault)
|
|
|
|
// Create second default provider - should unset first
|
|
provider2, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Second Default",
|
|
ProviderType: "route53",
|
|
Credentials: map[string]string{
|
|
"access_key_id": "key",
|
|
"secret_access_key": "secret",
|
|
"region": "us-east-1",
|
|
},
|
|
IsDefault: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, provider2.IsDefault)
|
|
|
|
// Verify first provider is no longer default
|
|
updatedProvider1, err := service.Get(ctx, provider1.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, updatedProvider1.IsDefault)
|
|
}
|
|
|
|
func TestDNSProviderService_List(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create multiple providers
|
|
_, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Cloudflare",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Route53",
|
|
ProviderType: "route53",
|
|
Credentials: map[string]string{
|
|
"access_key_id": "key",
|
|
"secret_access_key": "secret",
|
|
"region": "us-east-1",
|
|
},
|
|
IsDefault: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// List all providers
|
|
providers, err := service.List(ctx)
|
|
require.NoError(t, err)
|
|
assert.Len(t, providers, 2)
|
|
|
|
// Verify default provider is first (ordered by is_default DESC)
|
|
assert.True(t, providers[0].IsDefault)
|
|
}
|
|
|
|
func TestDNSProviderService_Get(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create a provider
|
|
created, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test Provider",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Get the provider
|
|
provider, err := service.Get(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, created.ID, provider.ID)
|
|
assert.Equal(t, "Test Provider", provider.Name)
|
|
|
|
// Get non-existent provider
|
|
_, err = service.Get(ctx, 9999)
|
|
assert.ErrorIs(t, err, ErrDNSProviderNotFound)
|
|
}
|
|
|
|
func TestDNSProviderService_Update(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create a provider
|
|
created, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Original Name",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "original-token"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
t.Run("update name only", func(t *testing.T) {
|
|
newName := "Updated Name"
|
|
updated, err := service.Update(ctx, created.ID, UpdateDNSProviderRequest{
|
|
Name: &newName,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Updated Name", updated.Name)
|
|
assert.True(t, updated.Enabled) // Should remain unchanged
|
|
})
|
|
|
|
t.Run("update credentials", func(t *testing.T) {
|
|
newCreds := map[string]string{"api_token": "new-token"}
|
|
updated, err := service.Update(ctx, created.ID, UpdateDNSProviderRequest{
|
|
Credentials: newCreds,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify credentials were updated by decrypting
|
|
decrypted, err := service.GetDecryptedCredentials(ctx, updated.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "new-token", decrypted["api_token"])
|
|
})
|
|
|
|
t.Run("update enabled status", func(t *testing.T) {
|
|
enabled := false
|
|
updated, err := service.Update(ctx, created.ID, UpdateDNSProviderRequest{
|
|
Enabled: &enabled,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, updated.Enabled)
|
|
})
|
|
|
|
t.Run("update non-existent provider", func(t *testing.T) {
|
|
name := "Test"
|
|
_, err := service.Update(ctx, 9999, UpdateDNSProviderRequest{
|
|
Name: &name,
|
|
})
|
|
assert.ErrorIs(t, err, ErrDNSProviderNotFound)
|
|
})
|
|
|
|
t.Run("update to set default", func(t *testing.T) {
|
|
isDefault := true
|
|
updated, err := service.Update(ctx, created.ID, UpdateDNSProviderRequest{
|
|
IsDefault: &isDefault,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, updated.IsDefault)
|
|
})
|
|
}
|
|
|
|
func TestDNSProviderService_Delete(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create a provider
|
|
created, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "To Delete",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Delete the provider
|
|
err = service.Delete(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
|
|
// Verify it's deleted
|
|
_, err = service.Get(ctx, created.ID)
|
|
assert.ErrorIs(t, err, ErrDNSProviderNotFound)
|
|
|
|
// Delete non-existent provider
|
|
err = service.Delete(ctx, 9999)
|
|
assert.ErrorIs(t, err, ErrDNSProviderNotFound)
|
|
}
|
|
|
|
func TestDNSProviderService_GetDecryptedCredentials(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create a provider
|
|
testCreds := map[string]string{
|
|
"api_token": "secret-token-123",
|
|
"extra": "data",
|
|
}
|
|
created, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test Provider",
|
|
ProviderType: "cloudflare",
|
|
Credentials: testCreds,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Get decrypted credentials
|
|
decrypted, err := service.GetDecryptedCredentials(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testCreds, decrypted)
|
|
|
|
// Verify last_used_at was updated
|
|
provider, err := service.Get(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, provider.LastUsedAt)
|
|
}
|
|
|
|
func TestDNSProviderService_TestCredentials(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
t.Run("valid credentials", func(t *testing.T) {
|
|
result, err := service.TestCredentials(ctx, CreateDNSProviderRequest{
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Success)
|
|
assert.NotEmpty(t, result.Message)
|
|
})
|
|
|
|
t.Run("invalid provider type", func(t *testing.T) {
|
|
result, err := service.TestCredentials(ctx, CreateDNSProviderRequest{
|
|
Name: "Test",
|
|
ProviderType: "invalid",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, result.Success)
|
|
assert.Equal(t, "INVALID_PROVIDER_TYPE", result.Code)
|
|
})
|
|
|
|
t.Run("missing credentials", func(t *testing.T) {
|
|
result, err := service.TestCredentials(ctx, CreateDNSProviderRequest{
|
|
Name: "Test",
|
|
ProviderType: "route53",
|
|
Credentials: map[string]string{"access_key_id": "key"}, // Missing secret and region
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, result.Success)
|
|
assert.Equal(t, "INVALID_CREDENTIALS", result.Code)
|
|
})
|
|
}
|
|
|
|
func TestDNSProviderService_Test(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create a provider
|
|
created, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test Provider",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Test the provider
|
|
result, err := service.Test(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
assert.True(t, result.Success)
|
|
|
|
// Verify statistics were updated
|
|
provider, err := service.Get(ctx, created.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, 1, provider.SuccessCount)
|
|
assert.Equal(t, 0, provider.FailureCount)
|
|
assert.NotNil(t, provider.LastUsedAt)
|
|
assert.Empty(t, provider.LastError)
|
|
}
|
|
|
|
func TestValidateCredentials(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
providerType string
|
|
credentials map[string]string
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid cloudflare",
|
|
providerType: "cloudflare",
|
|
credentials: map[string]string{"api_token": "token"},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid route53",
|
|
providerType: "route53",
|
|
credentials: map[string]string{
|
|
"access_key_id": "key",
|
|
"secret_access_key": "secret",
|
|
"region": "us-east-1",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "missing field",
|
|
providerType: "route53",
|
|
credentials: map[string]string{
|
|
"access_key_id": "key",
|
|
// Missing secret_access_key and region
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "empty field value",
|
|
providerType: "cloudflare",
|
|
credentials: map[string]string{"api_token": ""},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid provider type",
|
|
providerType: "invalid",
|
|
credentials: map[string]string{"api_token": "token"},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateCredentials(tt.providerType, tt.credentials)
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsValidProviderType(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
providerType string
|
|
want bool
|
|
}{
|
|
{"cloudflare", "cloudflare", true},
|
|
{"route53", "route53", true},
|
|
{"digitalocean", "digitalocean", true},
|
|
{"invalid", "invalid", false},
|
|
{"empty", "", false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
assert.Equal(t, tt.want, isValidProviderType(tt.providerType))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCredentialEncryptionRoundtrip(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
originalCreds := map[string]string{
|
|
"api_token": "super-secret-token",
|
|
"api_key": "another-secret",
|
|
"extra_data": "sensitive",
|
|
}
|
|
|
|
// Create provider with credentials
|
|
provider, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Encryption Test",
|
|
ProviderType: "cloudflare",
|
|
Credentials: originalCreds,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify credentials are encrypted in database
|
|
var dbProvider models.DNSProvider
|
|
err = db.First(&dbProvider, provider.ID).Error
|
|
require.NoError(t, err)
|
|
assert.NotContains(t, dbProvider.CredentialsEncrypted, "super-secret-token")
|
|
assert.NotContains(t, dbProvider.CredentialsEncrypted, "another-secret")
|
|
|
|
// Decrypt and verify
|
|
decrypted, err := service.GetDecryptedCredentials(ctx, provider.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalCreds, decrypted)
|
|
}
|
|
|
|
func TestUpdatePreservesCredentials(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
originalCreds := map[string]string{"api_token": "original-token"}
|
|
|
|
// Create provider
|
|
provider, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
Credentials: originalCreds,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Update without providing credentials
|
|
newName := "Updated Name"
|
|
updated, err := service.Update(ctx, provider.ID, UpdateDNSProviderRequest{
|
|
Name: &newName,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify credentials were preserved
|
|
decrypted, err := service.GetDecryptedCredentials(ctx, updated.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, originalCreds, decrypted)
|
|
}
|
|
|
|
func TestEncryptionServiceIntegration(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
|
|
testData := map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
}
|
|
|
|
// Encrypt
|
|
jsonData, err := json.Marshal(testData)
|
|
require.NoError(t, err)
|
|
|
|
encrypted, err := encryptor.Encrypt(jsonData)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, encrypted)
|
|
|
|
// Store in database
|
|
provider := &models.DNSProvider{
|
|
UUID: "test-uuid",
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
CredentialsEncrypted: encrypted,
|
|
}
|
|
err = db.Create(provider).Error
|
|
require.NoError(t, err)
|
|
|
|
// Retrieve and decrypt
|
|
var retrieved models.DNSProvider
|
|
err = db.First(&retrieved, provider.ID).Error
|
|
require.NoError(t, err)
|
|
|
|
decrypted, err := encryptor.Decrypt(retrieved.CredentialsEncrypted)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]string
|
|
err = json.Unmarshal(decrypted, &result)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testData, result)
|
|
}
|
|
|
|
func TestDNSProviderService_TestFailure(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create provider with invalid credentials structure (will fail decryption in real scenario)
|
|
provider := &models.DNSProvider{
|
|
UUID: "test-uuid",
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
CredentialsEncrypted: "invalid-encrypted-data",
|
|
}
|
|
err := db.Create(provider).Error
|
|
require.NoError(t, err)
|
|
|
|
// Test should handle decryption failure gracefully
|
|
result, err := service.Test(ctx, provider.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, result.Success)
|
|
assert.Equal(t, "DECRYPTION_ERROR", result.Code)
|
|
}
|
|
|
|
func TestDNSProviderService_GetDecryptedCredentialsError(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create provider with invalid encrypted data
|
|
provider := &models.DNSProvider{
|
|
UUID: "test-uuid",
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
CredentialsEncrypted: "not-valid-base64",
|
|
}
|
|
err := db.Create(provider).Error
|
|
require.NoError(t, err)
|
|
|
|
// Should fail to decrypt
|
|
_, err = service.GetDecryptedCredentials(ctx, provider.ID)
|
|
assert.Error(t, err)
|
|
assert.ErrorIs(t, err, ErrDecryptionFailed)
|
|
}
|
|
|
|
func TestDNSProviderService_UpdateDefaultLogic(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create two providers, first is default
|
|
provider1, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Provider 1",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token1"},
|
|
IsDefault: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
provider2, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Provider 2",
|
|
ProviderType: "route53",
|
|
Credentials: map[string]string{
|
|
"access_key_id": "key",
|
|
"secret_access_key": "secret",
|
|
"region": "us-east-1",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Make provider2 default via update
|
|
isDefault := true
|
|
_, err = service.Update(ctx, provider2.ID, UpdateDNSProviderRequest{
|
|
IsDefault: &isDefault,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Verify provider1 is no longer default
|
|
updated1, err := service.Get(ctx, provider1.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, updated1.IsDefault)
|
|
|
|
// Verify provider2 is default
|
|
updated2, err := service.Get(ctx, provider2.ID)
|
|
require.NoError(t, err)
|
|
assert.True(t, updated2.IsDefault)
|
|
|
|
// Unset default
|
|
notDefault := false
|
|
_, err = service.Update(ctx, provider2.ID, UpdateDNSProviderRequest{
|
|
IsDefault: ¬Default,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
updated2, err = service.Get(ctx, provider2.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, updated2.IsDefault)
|
|
}
|
|
|
|
func TestAllProviderTypes(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Test all supported provider types
|
|
testCases := map[string]map[string]string{
|
|
"cloudflare": {"api_token": "token"},
|
|
"route53": {"access_key_id": "key", "secret_access_key": "secret", "region": "us-east-1"},
|
|
"digitalocean": {"auth_token": "token"},
|
|
"googleclouddns": {"service_account_json": "{}", "project": "test-project"},
|
|
"namecheap": {"api_user": "user", "api_key": "key", "client_ip": "1.2.3.4"},
|
|
"godaddy": {"api_key": "key", "api_secret": "secret"},
|
|
"azure": {
|
|
"tenant_id": "tenant",
|
|
"client_id": "client",
|
|
"client_secret": "secret",
|
|
"subscription_id": "sub",
|
|
"resource_group": "rg",
|
|
},
|
|
"hetzner": {"api_key": "key"},
|
|
"vultr": {"api_key": "key"},
|
|
"dnsimple": {"oauth_token": "token", "account_id": "12345"},
|
|
}
|
|
|
|
for providerType, creds := range testCases {
|
|
t.Run(providerType, func(t *testing.T) {
|
|
provider, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test " + providerType,
|
|
ProviderType: providerType,
|
|
Credentials: creds,
|
|
})
|
|
require.NoError(t, err, "Failed to create %s provider", providerType)
|
|
assert.Equal(t, providerType, provider.ProviderType)
|
|
|
|
// Verify credentials can be decrypted
|
|
decrypted, err := service.GetDecryptedCredentials(ctx, provider.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, creds, decrypted)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDNSProviderService_UpdateInvalidCredentials(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create a provider
|
|
provider, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "token"},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Try to update with invalid credentials
|
|
invalidCreds := map[string]string{"wrong_field": "value"}
|
|
_, err = service.Update(ctx, provider.ID, UpdateDNSProviderRequest{
|
|
Credentials: invalidCreds,
|
|
})
|
|
assert.Error(t, err)
|
|
assert.ErrorIs(t, err, ErrInvalidCredentials)
|
|
}
|
|
|
|
func TestDNSProviderService_CreateEncryptionError(t *testing.T) {
|
|
db, encryptor := setupDNSProviderTestDB(t)
|
|
service := NewDNSProviderService(db, encryptor)
|
|
ctx := context.Background()
|
|
|
|
// Create with credentials that would marshal to invalid JSON
|
|
// This is hard to test without mocking, so we test the encryption path by
|
|
// verifying that any errors during encryption are properly wrapped
|
|
provider, err := service.Create(ctx, CreateDNSProviderRequest{
|
|
Name: "Test",
|
|
ProviderType: "cloudflare",
|
|
Credentials: map[string]string{"api_token": "valid-token"},
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, provider.CredentialsEncrypted)
|
|
}
|