- Created a comprehensive documentation file for DNS provider types, including RFC 2136, Webhook, and Script providers, detailing their use cases, configurations, and security notes. - Updated the DNSProviderForm component to handle new field types including select and textarea for better user input management. - Enhanced the DNS provider schemas to include new fields for script execution, webhook authentication, and RFC 2136 configurations, improving flexibility and usability.
715 lines
18 KiB
Go
715 lines
18 KiB
Go
package custom
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Wikid82/charon/backend/pkg/dnsprovider"
|
|
)
|
|
|
|
func TestNewRFC2136Provider(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
if provider == nil {
|
|
t.Fatal("NewRFC2136Provider() returned nil")
|
|
}
|
|
|
|
if provider.propagationTimeout != RFC2136DefaultPropagationTimeout {
|
|
t.Errorf("propagationTimeout = %v, want %v", provider.propagationTimeout, RFC2136DefaultPropagationTimeout)
|
|
}
|
|
|
|
if provider.pollingInterval != RFC2136DefaultPollingInterval {
|
|
t.Errorf("pollingInterval = %v, want %v", provider.pollingInterval, RFC2136DefaultPollingInterval)
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_Type(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
if got := provider.Type(); got != "rfc2136" {
|
|
t.Errorf("Type() = %q, want %q", got, "rfc2136")
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_Metadata(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
metadata := provider.Metadata()
|
|
|
|
if metadata.Type != "rfc2136" {
|
|
t.Errorf("Metadata().Type = %q, want %q", metadata.Type, "rfc2136")
|
|
}
|
|
|
|
if metadata.Name != "RFC 2136 (Dynamic DNS)" {
|
|
t.Errorf("Metadata().Name = %q, want %q", metadata.Name, "RFC 2136 (Dynamic DNS)")
|
|
}
|
|
|
|
if metadata.IsBuiltIn {
|
|
t.Error("Metadata().IsBuiltIn = true, want false")
|
|
}
|
|
|
|
if metadata.Version != "1.0.0" {
|
|
t.Errorf("Metadata().Version = %q, want %q", metadata.Version, "1.0.0")
|
|
}
|
|
|
|
if metadata.InterfaceVersion != dnsprovider.InterfaceVersion {
|
|
t.Errorf("Metadata().InterfaceVersion = %q, want %q", metadata.InterfaceVersion, dnsprovider.InterfaceVersion)
|
|
}
|
|
|
|
if metadata.DocumentationURL == "" {
|
|
t.Error("Metadata().DocumentationURL is empty")
|
|
}
|
|
|
|
if metadata.Description == "" {
|
|
t.Error("Metadata().Description is empty")
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_InitAndCleanup(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
if err := provider.Init(); err != nil {
|
|
t.Errorf("Init() returned error: %v", err)
|
|
}
|
|
|
|
if err := provider.Cleanup(); err != nil {
|
|
t.Errorf("Cleanup() returned error: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_RequiredCredentialFields(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
fields := provider.RequiredCredentialFields()
|
|
|
|
expectedFields := map[string]bool{
|
|
"nameserver": false,
|
|
"tsig_key_name": false,
|
|
"tsig_key_secret": false,
|
|
}
|
|
|
|
if len(fields) != len(expectedFields) {
|
|
t.Errorf("RequiredCredentialFields() returned %d fields, want %d", len(fields), len(expectedFields))
|
|
}
|
|
|
|
for _, field := range fields {
|
|
if _, ok := expectedFields[field.Name]; !ok {
|
|
t.Errorf("Unexpected required field: %q", field.Name)
|
|
}
|
|
expectedFields[field.Name] = true
|
|
|
|
if field.Label == "" {
|
|
t.Errorf("Field %q has empty label", field.Name)
|
|
}
|
|
if field.Type == "" {
|
|
t.Errorf("Field %q has empty type", field.Name)
|
|
}
|
|
}
|
|
|
|
for name, found := range expectedFields {
|
|
if !found {
|
|
t.Errorf("Missing required field: %q", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_OptionalCredentialFields(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
fields := provider.OptionalCredentialFields()
|
|
|
|
expectedFields := map[string]bool{
|
|
"port": false,
|
|
"tsig_algorithm": false,
|
|
"zone": false,
|
|
}
|
|
|
|
if len(fields) != len(expectedFields) {
|
|
t.Errorf("OptionalCredentialFields() returned %d fields, want %d", len(fields), len(expectedFields))
|
|
}
|
|
|
|
for _, field := range fields {
|
|
if _, ok := expectedFields[field.Name]; !ok {
|
|
t.Errorf("Unexpected optional field: %q", field.Name)
|
|
}
|
|
expectedFields[field.Name] = true
|
|
|
|
if field.Label == "" {
|
|
t.Errorf("Field %q has empty label", field.Name)
|
|
}
|
|
|
|
// Verify tsig_algorithm has select options
|
|
if field.Name == "tsig_algorithm" {
|
|
if field.Type != "select" {
|
|
t.Errorf("tsig_algorithm type = %q, want %q", field.Type, "select")
|
|
}
|
|
if len(field.Options) == 0 {
|
|
t.Error("tsig_algorithm has no select options")
|
|
}
|
|
|
|
// Verify all valid algorithms are present
|
|
optionValues := make(map[string]bool)
|
|
for _, opt := range field.Options {
|
|
optionValues[opt.Value] = true
|
|
}
|
|
for alg := range ValidTSIGAlgorithms {
|
|
if !optionValues[alg] {
|
|
t.Errorf("Missing algorithm option: %q", alg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for name, found := range expectedFields {
|
|
if !found {
|
|
t.Errorf("Missing optional field: %q", name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_ValidateCredentials(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
// Valid base64 secret (example)
|
|
validSecret := "c2VjcmV0a2V5MTIzNDU2Nzg5MA==" // "secretkey1234567890" in base64
|
|
|
|
tests := []struct {
|
|
name string
|
|
creds map[string]string
|
|
wantErr bool
|
|
errMsg string
|
|
}{
|
|
{
|
|
name: "valid credentials with defaults",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key.example.com",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid credentials with all fields",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key.example.com",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "5353",
|
|
"tsig_algorithm": "hmac-sha512",
|
|
"zone": "example.com",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid credentials with uppercase algorithm",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key.example.com",
|
|
"tsig_key_secret": validSecret,
|
|
"tsig_algorithm": "HMAC-SHA256",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid with IP address nameserver",
|
|
creds: map[string]string{
|
|
"nameserver": "192.168.1.1",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "valid with whitespace trimming",
|
|
creds: map[string]string{
|
|
"nameserver": " ns1.example.com ",
|
|
"tsig_key_name": " acme-key ",
|
|
"tsig_key_secret": " " + validSecret + " ",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "missing nameserver",
|
|
creds: map[string]string{
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: true,
|
|
errMsg: "nameserver is required",
|
|
},
|
|
{
|
|
name: "empty nameserver",
|
|
creds: map[string]string{
|
|
"nameserver": "",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: true,
|
|
errMsg: "nameserver is required",
|
|
},
|
|
{
|
|
name: "whitespace-only nameserver",
|
|
creds: map[string]string{
|
|
"nameserver": " ",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: true,
|
|
errMsg: "nameserver is required",
|
|
},
|
|
{
|
|
name: "missing tsig_key_name",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: true,
|
|
errMsg: "tsig_key_name is required",
|
|
},
|
|
{
|
|
name: "empty tsig_key_name",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
wantErr: true,
|
|
errMsg: "tsig_key_name is required",
|
|
},
|
|
{
|
|
name: "missing tsig_key_secret",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "tsig_key_secret is required",
|
|
},
|
|
{
|
|
name: "empty tsig_key_secret",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": "",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "tsig_key_secret is required",
|
|
},
|
|
{
|
|
name: "invalid base64 secret",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": "not-valid-base64!!!",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "tsig_key_secret must be valid base64",
|
|
},
|
|
{
|
|
name: "invalid port - not a number",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "abc",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "port must be a number",
|
|
},
|
|
{
|
|
name: "invalid port - too low",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "0",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "port must be between",
|
|
},
|
|
{
|
|
name: "invalid port - too high",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "65536",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "port must be between",
|
|
},
|
|
{
|
|
name: "invalid algorithm",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"tsig_algorithm": "invalid-algorithm",
|
|
},
|
|
wantErr: true,
|
|
errMsg: "tsig_algorithm must be one of",
|
|
},
|
|
{
|
|
name: "all empty credentials",
|
|
creds: map[string]string{},
|
|
wantErr: true,
|
|
errMsg: "nameserver is required",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := provider.ValidateCredentials(tt.creds)
|
|
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Error("ValidateCredentials() expected error but got nil")
|
|
return
|
|
}
|
|
if tt.errMsg != "" && !contains(err.Error(), tt.errMsg) {
|
|
t.Errorf("ValidateCredentials() error = %q, want to contain %q", err.Error(), tt.errMsg)
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("ValidateCredentials() unexpected error: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_TestCredentials(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
validSecret := "c2VjcmV0a2V5MTIzNDU2Nzg5MA=="
|
|
|
|
// TestCredentials should behave the same as ValidateCredentials
|
|
validCreds := map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
}
|
|
|
|
if err := provider.TestCredentials(validCreds); err != nil {
|
|
t.Errorf("TestCredentials() with valid creds returned error: %v", err)
|
|
}
|
|
|
|
invalidCreds := map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
}
|
|
|
|
if err := provider.TestCredentials(invalidCreds); err == nil {
|
|
t.Error("TestCredentials() with invalid creds expected error but got nil")
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_SupportsMultiCredential(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
if !provider.SupportsMultiCredential() {
|
|
t.Error("SupportsMultiCredential() = false, want true")
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_BuildCaddyConfig(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
validSecret := "c2VjcmV0a2V5MTIzNDU2Nzg5MA=="
|
|
|
|
tests := []struct {
|
|
name string
|
|
creds map[string]string
|
|
expected map[string]any
|
|
}{
|
|
{
|
|
name: "minimal config with defaults",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
expected: map[string]any{
|
|
"name": "rfc2136",
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "53",
|
|
"tsig_algorithm": "hmac-sha256",
|
|
},
|
|
},
|
|
{
|
|
name: "full config with all options",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "5353",
|
|
"tsig_algorithm": "hmac-sha512",
|
|
"zone": "example.com",
|
|
},
|
|
expected: map[string]any{
|
|
"name": "rfc2136",
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "5353",
|
|
"tsig_algorithm": "hmac-sha512",
|
|
"zone": "example.com",
|
|
},
|
|
},
|
|
{
|
|
name: "algorithm normalization to lowercase",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"tsig_algorithm": "HMAC-SHA384",
|
|
},
|
|
expected: map[string]any{
|
|
"name": "rfc2136",
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "53",
|
|
"tsig_algorithm": "hmac-sha384",
|
|
},
|
|
},
|
|
{
|
|
name: "whitespace trimming",
|
|
creds: map[string]string{
|
|
"nameserver": " ns1.example.com ",
|
|
"tsig_key_name": " acme-key ",
|
|
"tsig_key_secret": " " + validSecret + " ",
|
|
"port": " 5353 ",
|
|
"zone": " example.com ",
|
|
},
|
|
expected: map[string]any{
|
|
"name": "rfc2136",
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"port": "5353",
|
|
"tsig_algorithm": "hmac-sha256",
|
|
"zone": "example.com",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
config := provider.BuildCaddyConfig(tt.creds)
|
|
|
|
for key, expectedValue := range tt.expected {
|
|
actualValue, ok := config[key]
|
|
if !ok {
|
|
t.Errorf("BuildCaddyConfig() missing key %q", key)
|
|
continue
|
|
}
|
|
if actualValue != expectedValue {
|
|
t.Errorf("BuildCaddyConfig()[%q] = %v, want %v", key, actualValue, expectedValue)
|
|
}
|
|
}
|
|
|
|
// Check no unexpected keys (except for zone which is optional)
|
|
for key := range config {
|
|
if _, ok := tt.expected[key]; !ok {
|
|
t.Errorf("BuildCaddyConfig() unexpected key %q", key)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_BuildCaddyConfigForZone(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
validSecret := "c2VjcmV0a2V5MTIzNDU2Nzg5MA=="
|
|
|
|
tests := []struct {
|
|
name string
|
|
baseDomain string
|
|
creds map[string]string
|
|
expectedZone string
|
|
}{
|
|
{
|
|
name: "zone auto-set from baseDomain",
|
|
baseDomain: "example.org",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
},
|
|
expectedZone: "example.org",
|
|
},
|
|
{
|
|
name: "explicit zone takes precedence",
|
|
baseDomain: "example.org",
|
|
creds: map[string]string{
|
|
"nameserver": "ns1.example.com",
|
|
"tsig_key_name": "acme-key",
|
|
"tsig_key_secret": validSecret,
|
|
"zone": "custom.zone.com",
|
|
},
|
|
expectedZone: "custom.zone.com",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
config := provider.BuildCaddyConfigForZone(tt.baseDomain, tt.creds)
|
|
|
|
zone, ok := config["zone"]
|
|
if !ok {
|
|
t.Error("BuildCaddyConfigForZone() missing 'zone' key")
|
|
return
|
|
}
|
|
if zone != tt.expectedZone {
|
|
t.Errorf("BuildCaddyConfigForZone() zone = %v, want %v", zone, tt.expectedZone)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_PropagationTimeout(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
timeout := provider.PropagationTimeout()
|
|
|
|
if timeout != 60*time.Second {
|
|
t.Errorf("PropagationTimeout() = %v, want %v", timeout, 60*time.Second)
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_PollingInterval(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
interval := provider.PollingInterval()
|
|
|
|
if interval != 2*time.Second {
|
|
t.Errorf("PollingInterval() = %v, want %v", interval, 2*time.Second)
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_GetPort(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
tests := []struct {
|
|
name string
|
|
creds map[string]string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "default port when not set",
|
|
creds: map[string]string{},
|
|
expected: "53",
|
|
},
|
|
{
|
|
name: "default port when empty",
|
|
creds: map[string]string{"port": ""},
|
|
expected: "53",
|
|
},
|
|
{
|
|
name: "custom port",
|
|
creds: map[string]string{"port": "5353"},
|
|
expected: "5353",
|
|
},
|
|
{
|
|
name: "custom port with whitespace",
|
|
creds: map[string]string{"port": " 5353 "},
|
|
expected: "5353",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
port := provider.GetPort(tt.creds)
|
|
if port != tt.expected {
|
|
t.Errorf("GetPort() = %q, want %q", port, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_GetAlgorithm(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
tests := []struct {
|
|
name string
|
|
creds map[string]string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "default algorithm when not set",
|
|
creds: map[string]string{},
|
|
expected: "hmac-sha256",
|
|
},
|
|
{
|
|
name: "default algorithm when empty",
|
|
creds: map[string]string{"tsig_algorithm": ""},
|
|
expected: "hmac-sha256",
|
|
},
|
|
{
|
|
name: "custom algorithm",
|
|
creds: map[string]string{"tsig_algorithm": "hmac-sha512"},
|
|
expected: "hmac-sha512",
|
|
},
|
|
{
|
|
name: "uppercase algorithm normalized",
|
|
creds: map[string]string{"tsig_algorithm": "HMAC-SHA384"},
|
|
expected: "hmac-sha384",
|
|
},
|
|
{
|
|
name: "algorithm with whitespace",
|
|
creds: map[string]string{"tsig_algorithm": " hmac-sha1 "},
|
|
expected: "hmac-sha1",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
algorithm := provider.GetAlgorithm(tt.creds)
|
|
if algorithm != tt.expected {
|
|
t.Errorf("GetAlgorithm() = %q, want %q", algorithm, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_ValidTSIGAlgorithms(t *testing.T) {
|
|
expectedAlgorithms := []string{
|
|
"hmac-sha256",
|
|
"hmac-sha384",
|
|
"hmac-sha512",
|
|
"hmac-sha1",
|
|
"hmac-md5",
|
|
}
|
|
|
|
for _, alg := range expectedAlgorithms {
|
|
if !ValidTSIGAlgorithms[alg] {
|
|
t.Errorf("ValidTSIGAlgorithms missing %q", alg)
|
|
}
|
|
}
|
|
|
|
if len(ValidTSIGAlgorithms) != len(expectedAlgorithms) {
|
|
t.Errorf("ValidTSIGAlgorithms has %d entries, want %d", len(ValidTSIGAlgorithms), len(expectedAlgorithms))
|
|
}
|
|
}
|
|
|
|
func TestRFC2136Provider_ImplementsInterface(t *testing.T) {
|
|
provider := NewRFC2136Provider()
|
|
|
|
// Compile-time interface check
|
|
var _ dnsprovider.ProviderPlugin = provider
|
|
}
|
|
|
|
// Helper function to check if string contains substring
|
|
func contains(s, substr string) bool {
|
|
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsHelper(s, substr))
|
|
}
|
|
|
|
func containsHelper(s, substr string) bool {
|
|
for i := 0; i <= len(s)-len(substr); i++ {
|
|
if s[i:i+len(substr)] == substr {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|