Files
Charon/backend/pkg/dnsprovider/registry_test.go
akanealw eec8c28fb3
Some checks are pending
Go Benchmark / Performance Regression Check (push) Waiting to run
Cerberus Integration / Cerberus Security Stack Integration (push) Waiting to run
Upload Coverage to Codecov / Backend Codecov Upload (push) Waiting to run
Upload Coverage to Codecov / Frontend Codecov Upload (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (go) (push) Waiting to run
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Waiting to run
CrowdSec Integration / CrowdSec Bouncer Integration (push) Waiting to run
Docker Build, Publish & Test / build-and-push (push) Waiting to run
Docker Build, Publish & Test / Security Scan PR Image (push) Blocked by required conditions
Quality Checks / Auth Route Protection Contract (push) Waiting to run
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Waiting to run
Quality Checks / Backend (Go) (push) Waiting to run
Quality Checks / Frontend (React) (push) Waiting to run
Rate Limit integration / Rate Limiting Integration (push) Waiting to run
Security Scan (PR) / Trivy Binary Scan (push) Waiting to run
Supply Chain Verification (PR) / Verify Supply Chain (push) Waiting to run
WAF integration / Coraza WAF Integration (push) Waiting to run
changed perms
2026-04-22 18:19:14 +00:00

552 lines
12 KiB
Go
Executable File

package dnsprovider
import (
"sync"
"testing"
"time"
)
// mockProvider is a test implementation of ProviderPlugin
type mockProvider struct {
providerType string
}
func (m *mockProvider) Type() string {
return m.providerType
}
func (m *mockProvider) Metadata() ProviderMetadata {
return ProviderMetadata{
Type: m.providerType,
Name: "Mock Provider",
Version: "1.0.0",
IsBuiltIn: false,
}
}
func (m *mockProvider) Init() error {
return nil
}
func (m *mockProvider) Cleanup() error {
return nil
}
func (m *mockProvider) ValidateCredentials(creds map[string]string) error {
return nil
}
func (m *mockProvider) TestCredentials(creds map[string]string) error {
return nil
}
func (m *mockProvider) RequiredCredentialFields() []CredentialFieldSpec {
return []CredentialFieldSpec{}
}
func (m *mockProvider) OptionalCredentialFields() []CredentialFieldSpec {
return []CredentialFieldSpec{}
}
func (m *mockProvider) SupportsMultiCredential() bool {
return false
}
func (m *mockProvider) BuildCaddyConfig(creds map[string]string) map[string]any {
return map[string]any{}
}
func (m *mockProvider) BuildCaddyConfigForZone(baseDomain string, creds map[string]string) map[string]any {
return map[string]any{}
}
func (m *mockProvider) PropagationTimeout() time.Duration {
return time.Minute
}
func (m *mockProvider) PollingInterval() time.Duration {
return 2 * time.Second
}
// TestNewRegistry tests creating a new registry instance
func TestNewRegistry(t *testing.T) {
r := NewRegistry()
if r == nil {
t.Fatal("NewRegistry returned nil")
}
if r.Count() != 0 {
t.Errorf("expected empty registry, got %d providers", r.Count())
}
}
// TestGlobal tests the global registry singleton
func TestGlobal(t *testing.T) {
r1 := Global()
r2 := Global()
if r1 != r2 {
t.Error("Global() should return the same instance")
}
if r1 == nil {
t.Fatal("Global() returned nil")
}
}
// TestRegister tests registering providers
func TestRegister(t *testing.T) {
tests := []struct {
name string
provider ProviderPlugin
wantErr error
}{
{
name: "successful registration",
provider: &mockProvider{providerType: "test-provider"},
wantErr: nil,
},
{
name: "nil provider",
provider: nil,
wantErr: ErrInvalidPlugin,
},
{
name: "empty provider type",
provider: &mockProvider{providerType: ""},
wantErr: ErrInvalidPlugin,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := NewRegistry()
err := r.Register(tt.provider)
if err != tt.wantErr {
t.Errorf("Register() error = %v, wantErr %v", err, tt.wantErr)
}
// If successful, verify provider was registered
if err == nil && tt.provider != nil {
if !r.IsSupported(tt.provider.Type()) {
t.Errorf("provider %s was not registered", tt.provider.Type())
}
}
})
}
}
// TestRegister_Duplicate tests duplicate registration
func TestRegister_Duplicate(t *testing.T) {
r := NewRegistry()
provider := &mockProvider{providerType: "duplicate-test"}
// First registration should succeed
err := r.Register(provider)
if err != nil {
t.Fatalf("first registration failed: %v", err)
}
// Second registration should fail
err = r.Register(provider)
if err != ErrProviderAlreadyRegistered {
t.Errorf("expected ErrProviderAlreadyRegistered, got %v", err)
}
// Count should still be 1
if r.Count() != 1 {
t.Errorf("expected count 1, got %d", r.Count())
}
}
// TestGet tests retrieving providers
func TestGet(t *testing.T) {
r := NewRegistry()
provider := &mockProvider{providerType: "test-get"}
if err := r.Register(provider); err != nil {
t.Fatalf("failed to register provider: %v", err)
}
tests := []struct {
name string
providerType string
wantOK bool
}{
{
name: "existing provider",
providerType: "test-get",
wantOK: true,
},
{
name: "non-existent provider",
providerType: "does-not-exist",
wantOK: false,
},
{
name: "empty provider type",
providerType: "",
wantOK: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotProvider, gotOK := r.Get(tt.providerType)
if gotOK != tt.wantOK {
t.Errorf("Get() ok = %v, want %v", gotOK, tt.wantOK)
}
if tt.wantOK {
if gotProvider == nil {
t.Error("expected non-nil provider for existing type")
}
if gotProvider.Type() != tt.providerType {
t.Errorf("got provider type %s, want %s", gotProvider.Type(), tt.providerType)
}
} else if gotProvider != nil {
t.Error("expected nil provider for non-existent type")
}
})
}
}
// TestList tests listing all providers
func TestList(t *testing.T) {
r := NewRegistry()
// Test empty registry
list := r.List()
if len(list) != 0 {
t.Errorf("expected empty list, got %d providers", len(list))
}
// Register multiple providers
providers := []*mockProvider{
{providerType: "provider-c"},
{providerType: "provider-a"},
{providerType: "provider-b"},
}
for _, p := range providers {
if err := r.Register(p); err != nil {
t.Fatalf("failed to register provider %s: %v", p.Type(), err)
}
}
// Get list (should be sorted)
list = r.List()
if len(list) != 3 {
t.Fatalf("expected 3 providers, got %d", len(list))
}
// Verify alphabetical ordering
expectedOrder := []string{"provider-a", "provider-b", "provider-c"}
for i, p := range list {
if p.Type() != expectedOrder[i] {
t.Errorf("list[%d] = %s, want %s", i, p.Type(), expectedOrder[i])
}
}
}
// TestTypes tests listing provider types
func TestTypes(t *testing.T) {
r := NewRegistry()
// Test empty registry
types := r.Types()
if len(types) != 0 {
t.Errorf("expected empty types, got %d", len(types))
}
// Register multiple providers
providers := []*mockProvider{
{providerType: "zebra"},
{providerType: "alpha"},
{providerType: "beta"},
}
for _, p := range providers {
if err := r.Register(p); err != nil {
t.Fatalf("failed to register provider %s: %v", p.Type(), err)
}
}
// Get types (should be sorted)
types = r.Types()
if len(types) != 3 {
t.Fatalf("expected 3 types, got %d", len(types))
}
// Verify alphabetical ordering
expectedOrder := []string{"alpha", "beta", "zebra"}
for i, typ := range types {
if typ != expectedOrder[i] {
t.Errorf("types[%d] = %s, want %s", i, typ, expectedOrder[i])
}
}
}
// TestIsSupported tests checking provider support
func TestIsSupported(t *testing.T) {
r := NewRegistry()
// Register a provider
provider := &mockProvider{providerType: "supported-test"}
if err := r.Register(provider); err != nil {
t.Fatalf("failed to register provider: %v", err)
}
tests := []struct {
name string
providerType string
want bool
}{
{
name: "supported provider",
providerType: "supported-test",
want: true,
},
{
name: "unsupported provider",
providerType: "unsupported",
want: false,
},
{
name: "empty string",
providerType: "",
want: false,
},
{
name: "case sensitivity",
providerType: "SUPPORTED-TEST",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := r.IsSupported(tt.providerType)
if got != tt.want {
t.Errorf("IsSupported(%q) = %v, want %v", tt.providerType, got, tt.want)
}
})
}
}
// TestUnregister tests removing providers
func TestUnregister(t *testing.T) {
r := NewRegistry()
// Register a provider
provider := &mockProvider{providerType: "unregister-test"}
if err := r.Register(provider); err != nil {
t.Fatalf("failed to register provider: %v", err)
}
// Verify it's registered
if !r.IsSupported("unregister-test") {
t.Fatal("provider not registered")
}
// Unregister it
r.Unregister("unregister-test")
// Verify it's gone
if r.IsSupported("unregister-test") {
t.Error("provider still registered after unregister")
}
// Unregister non-existent (should not panic)
r.Unregister("does-not-exist")
// Count should be 0
if r.Count() != 0 {
t.Errorf("expected count 0, got %d", r.Count())
}
}
// TestCount tests counting registered providers
func TestCount(t *testing.T) {
r := NewRegistry()
// Initial count should be 0
if r.Count() != 0 {
t.Errorf("expected count 0, got %d", r.Count())
}
// Register providers
for i := 1; i <= 5; i++ {
provider := &mockProvider{providerType: "test-" + string(rune('a'+i-1))}
if err := r.Register(provider); err != nil {
t.Fatalf("failed to register provider: %v", err)
}
if r.Count() != i {
t.Errorf("after registering %d providers, count = %d", i, r.Count())
}
}
// Unregister one
r.Unregister("test-a")
if r.Count() != 4 {
t.Errorf("after unregistering, count = %d, want 4", r.Count())
}
}
// TestClear tests clearing all providers
func TestClear(t *testing.T) {
r := NewRegistry()
// Register multiple providers
for i := 0; i < 3; i++ {
provider := &mockProvider{providerType: "clear-test-" + string(rune('a'+i))}
if err := r.Register(provider); err != nil {
t.Fatalf("failed to register provider: %v", err)
}
}
// Verify count
if r.Count() != 3 {
t.Fatalf("expected count 3, got %d", r.Count())
}
// Clear registry
r.Clear()
// Verify empty
if r.Count() != 0 {
t.Errorf("after clear, count = %d, want 0", r.Count())
}
// Verify no providers are supported
if r.IsSupported("clear-test-a") {
t.Error("provider still supported after clear")
}
// List should be empty
if len(r.List()) != 0 {
t.Errorf("list not empty after clear, got %d providers", len(r.List()))
}
}
// TestConcurrency tests thread-safe operations
func TestConcurrency(t *testing.T) {
r := NewRegistry()
var wg sync.WaitGroup
// Concurrent registrations
for i := 0; i < 10; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
provider := &mockProvider{providerType: "concurrent-" + string(rune('a'+n))}
_ = r.Register(provider)
}(i)
}
// Concurrent reads
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = r.List()
_ = r.Types()
_ = r.IsSupported("concurrent-a")
_, _ = r.Get("concurrent-a")
}()
}
wg.Wait()
// Verify final state
if r.Count() != 10 {
t.Errorf("expected 10 providers after concurrent registration, got %d", r.Count())
}
}
// TestRegistry_Operations tests combined operations
func TestRegistry_Operations(t *testing.T) {
r := NewRegistry()
// Start with empty registry
if r.Count() != 0 {
t.Errorf("new registry should be empty")
}
// Register providers
p1 := &mockProvider{providerType: "provider1"}
p2 := &mockProvider{providerType: "provider2"}
p3 := &mockProvider{providerType: "provider3"}
if err := r.Register(p1); err != nil {
t.Fatalf("failed to register p1: %v", err)
}
if err := r.Register(p2); err != nil {
t.Fatalf("failed to register p2: %v", err)
}
if err := r.Register(p3); err != nil {
t.Fatalf("failed to register p3: %v", err)
}
// Verify all are registered
for _, p := range []ProviderPlugin{p1, p2, p3} {
if !r.IsSupported(p.Type()) {
t.Errorf("provider %s not registered", p.Type())
}
retrieved, ok := r.Get(p.Type())
if !ok {
t.Errorf("failed to get provider %s", p.Type())
}
if retrieved.Type() != p.Type() {
t.Errorf("retrieved wrong provider: got %s, want %s", retrieved.Type(), p.Type())
}
}
// List should contain all 3
list := r.List()
if len(list) != 3 {
t.Errorf("list length = %d, want 3", len(list))
}
// Types should contain all 3
types := r.Types()
if len(types) != 3 {
t.Errorf("types length = %d, want 3", len(types))
}
// Unregister one
r.Unregister("provider2")
// Verify count decreased
if r.Count() != 2 {
t.Errorf("after unregister, count = %d, want 2", r.Count())
}
// Verify p2 is gone
if r.IsSupported("provider2") {
t.Error("provider2 still supported after unregister")
}
// Clear all
r.Clear()
// Verify empty
if r.Count() != 0 {
t.Errorf("after clear, count = %d, want 0", r.Count())
}
if len(r.List()) != 0 {
t.Error("list not empty after clear")
}
if len(r.Types()) != 0 {
t.Error("types not empty after clear")
}
}