Normalize DNS resolver validation
- commit
- ae10fc74f944812a0b5264c86783d9efc461c342
- author
- brice · · 2026-05-01 17:25 UTC
- parent
- f4879b5
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +63 -12
M internal/adapters/out/acmelego/issuer.go +34 -2
| @@ -7,7 +7,9 @@ import ( | |||
| 7 | 7 | "crypto/x509" | |
| 8 | 8 | "encoding/json" | |
| 9 | 9 | "fmt" | |
| 10 | + | "net" | |
| 10 | 11 | "net/http" | |
| 12 | + | "strconv" | |
| 11 | 13 | "strings" | |
| 12 | 14 | "sync" | |
| 13 | 15 | "time" | |
| @@ -91,9 +93,11 @@ func NewIssuer(cfg Config) (*Issuer, error) { | |||
| 91 | 93 | if cfg.Token == "" { | |
| 92 | 94 | return nil, fmt.Errorf("acmelego: %w", domain.ErrCloudflareTokenMissing) | |
| 93 | 95 | } | |
| 94 | - | if len(cfg.DNSResolvers) == 0 { | |
| 95 | - | return nil, fmt.Errorf("acmelego: %w: DNSResolvers must contain at least one resolver", domain.ErrDNSConfigInvalid) | |
| 96 | + | resolvers, err := normalizeDNSResolvers(cfg.DNSResolvers) | |
| 97 | + | if err != nil { | |
| 98 | + | return nil, err | |
| 96 | 99 | } | |
| 100 | + | cfg.DNSResolvers = resolvers | |
| 97 | 101 | if cfg.DNSPropagationTimeout <= 0 { | |
| 98 | 102 | return nil, fmt.Errorf("acmelego: %w: DNSPropagationTimeout must be positive", domain.ErrDNSConfigInvalid) | |
| 99 | 103 | } | |
| @@ -110,6 +114,34 @@ func NewIssuer(cfg Config) (*Issuer, error) { | |||
| 110 | 114 | return &Issuer{cfg: cfg}, nil | |
| 111 | 115 | } | |
| 112 | 116 | ||
| 117 | + | func normalizeDNSResolvers(resolvers []string) ([]string, error) { | |
| 118 | + | if len(resolvers) == 0 { | |
| 119 | + | return nil, fmt.Errorf("acmelego: %w: DNSResolvers must contain at least one resolver", domain.ErrDNSConfigInvalid) | |
| 120 | + | } | |
| 121 | + | ||
| 122 | + | normalized := make([]string, len(resolvers)) | |
| 123 | + | for i, resolver := range resolvers { | |
| 124 | + | trimmed := strings.TrimSpace(resolver) | |
| 125 | + | if trimmed == "" { | |
| 126 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |
| 127 | + | } | |
| 128 | + | host, port, err := net.SplitHostPort(trimmed) | |
| 129 | + | if err != nil { | |
| 130 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |
| 131 | + | } | |
| 132 | + | if host == "" { | |
| 133 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |
| 134 | + | } | |
| 135 | + | portNumber, err := strconv.Atoi(port) | |
| 136 | + | if err != nil || portNumber < 1 || portNumber > 65535 { | |
| 137 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |
| 138 | + | } | |
| 139 | + | normalized[i] = trimmed | |
| 140 | + | } | |
| 141 | + | ||
| 142 | + | return normalized, nil | |
| 143 | + | } | |
| 144 | + | ||
| 113 | 145 | // compile-time interface check | |
| 114 | 146 | var _ out.PublicCertificateIssuer = (*Issuer)(nil) | |
| 115 | 147 | ||
| @@ -7,7 +7,9 @@ import ( | |||||
| 7 | "crypto/x509" | 7 | "crypto/x509" | ||
| 8 | "encoding/json" | 8 | "encoding/json" | ||
| 9 | "fmt" | 9 | "fmt" | ||
| 10 | + | "net" | |||
| 10 | "net/http" | 11 | "net/http" | ||
| 12 | + | "strconv" | |||
| 11 | "strings" | 13 | "strings" | ||
| 12 | "sync" | 14 | "sync" | ||
| 13 | "time" | 15 | "time" | ||
| @@ -91,9 +93,11 @@ func NewIssuer(cfg Config) (*Issuer, error) { | |||||
| 91 | if cfg.Token == "" { | 93 | if cfg.Token == "" { | ||
| 92 | return nil, fmt.Errorf("acmelego: %w", domain.ErrCloudflareTokenMissing) | 94 | return nil, fmt.Errorf("acmelego: %w", domain.ErrCloudflareTokenMissing) | ||
| 93 | } | 95 | } | ||
| 94 | - | if len(cfg.DNSResolvers) == 0 { | 96 | + | resolvers, err := normalizeDNSResolvers(cfg.DNSResolvers) |
| 95 | - | return nil, fmt.Errorf("acmelego: %w: DNSResolvers must contain at least one resolver", domain.ErrDNSConfigInvalid) | 97 | + | if err != nil { |
| 98 | + | return nil, err | |||
| 96 | } | 99 | } | ||
| 100 | + | cfg.DNSResolvers = resolvers | |||
| 97 | if cfg.DNSPropagationTimeout <= 0 { | 101 | if cfg.DNSPropagationTimeout <= 0 { | ||
| 98 | return nil, fmt.Errorf("acmelego: %w: DNSPropagationTimeout must be positive", domain.ErrDNSConfigInvalid) | 102 | return nil, fmt.Errorf("acmelego: %w: DNSPropagationTimeout must be positive", domain.ErrDNSConfigInvalid) | ||
| 99 | } | 103 | } | ||
| @@ -110,6 +114,34 @@ func NewIssuer(cfg Config) (*Issuer, error) { | |||||
| 110 | return &Issuer{cfg: cfg}, nil | 114 | return &Issuer{cfg: cfg}, nil | ||
| 111 | } | 115 | } | ||
| 112 | 116 | ||||
| 117 | + | func normalizeDNSResolvers(resolvers []string) ([]string, error) { | |||
| 118 | + | if len(resolvers) == 0 { | |||
| 119 | + | return nil, fmt.Errorf("acmelego: %w: DNSResolvers must contain at least one resolver", domain.ErrDNSConfigInvalid) | |||
| 120 | + | } | |||
| 121 | + | ||||
| 122 | + | normalized := make([]string, len(resolvers)) | |||
| 123 | + | for i, resolver := range resolvers { | |||
| 124 | + | trimmed := strings.TrimSpace(resolver) | |||
| 125 | + | if trimmed == "" { | |||
| 126 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |||
| 127 | + | } | |||
| 128 | + | host, port, err := net.SplitHostPort(trimmed) | |||
| 129 | + | if err != nil { | |||
| 130 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |||
| 131 | + | } | |||
| 132 | + | if host == "" { | |||
| 133 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |||
| 134 | + | } | |||
| 135 | + | portNumber, err := strconv.Atoi(port) | |||
| 136 | + | if err != nil || portNumber < 1 || portNumber > 65535 { | |||
| 137 | + | return nil, fmt.Errorf("acmelego: %w: invalid DNSResolvers entry", domain.ErrDNSConfigInvalid) | |||
| 138 | + | } | |||
| 139 | + | normalized[i] = trimmed | |||
| 140 | + | } | |||
| 141 | + | ||||
| 142 | + | return normalized, nil | |||
| 143 | + | } | |||
| 144 | + | ||||
| 113 | // compile-time interface check | 145 | // compile-time interface check | ||
| 114 | var _ out.PublicCertificateIssuer = (*Issuer)(nil) | 146 | var _ out.PublicCertificateIssuer = (*Issuer)(nil) | ||
| 115 | 147 | ||||
M internal/adapters/out/acmelego/issuer_test.go +16 -2
| @@ -83,6 +83,19 @@ func TestNewIssuerValidatesCloudflareDNSConfig(t *testing.T) { | |||
| 83 | 83 | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | |
| 84 | 84 | assert.Contains(t, err.Error(), "DNSResolvers") | |
| 85 | 85 | ||
| 86 | + | _, err = NewIssuer(Config{ | |
| 87 | + | Email: "test@example.com", | |
| 88 | + | Challenge: domain.ACMEChallengeCloudflareDNS01, | |
| 89 | + | Token: "token", | |
| 90 | + | Store: outmocks.NewMockCertificateStore(t), | |
| 91 | + | DNSResolvers: []string{"1.1.1.1:abc"}, | |
| 92 | + | DNSPropagationTimeout: 5 * time.Minute, | |
| 93 | + | DNSPollingInterval: 5 * time.Second, | |
| 94 | + | }) | |
| 95 | + | require.Error(t, err) | |
| 96 | + | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | |
| 97 | + | assert.Contains(t, err.Error(), "invalid DNSResolvers entry") | |
| 98 | + | ||
| 86 | 99 | _, err = NewIssuer(Config{ | |
| 87 | 100 | Email: "test@example.com", | |
| 88 | 101 | Challenge: domain.ACMEChallengeCloudflareDNS01, | |
| @@ -96,16 +109,17 @@ func TestNewIssuerValidatesCloudflareDNSConfig(t *testing.T) { | |||
| 96 | 109 | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | |
| 97 | 110 | assert.Contains(t, err.Error(), "DNSPollingInterval") | |
| 98 | 111 | ||
| 99 | - | _, err = NewIssuer(Config{ | |
| 112 | + | issuer, err := NewIssuer(Config{ | |
| 100 | 113 | Email: "test@example.com", | |
| 101 | 114 | Challenge: domain.ACMEChallengeCloudflareDNS01, | |
| 102 | 115 | Token: "token", | |
| 103 | 116 | Store: outmocks.NewMockCertificateStore(t), | |
| 104 | - | DNSResolvers: []string{"1.1.1.1:53"}, | |
| 117 | + | DNSResolvers: []string{" 1.1.1.1:53 ", "\t8.8.8.8:53\n"}, | |
| 105 | 118 | DNSPropagationTimeout: 5 * time.Minute, | |
| 106 | 119 | DNSPollingInterval: 5 * time.Second, | |
| 107 | 120 | }) | |
| 108 | 121 | require.NoError(t, err) | |
| 122 | + | assert.Equal(t, []string{"1.1.1.1:53", "8.8.8.8:53"}, issuer.cfg.DNSResolvers) | |
| 109 | 123 | } | |
| 110 | 124 | ||
| 111 | 125 | func TestNewIssuerRejectsNonHTTPSURL(t *testing.T) { | |
| @@ -83,6 +83,19 @@ func TestNewIssuerValidatesCloudflareDNSConfig(t *testing.T) { | |||||
| 83 | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | 83 | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | ||
| 84 | assert.Contains(t, err.Error(), "DNSResolvers") | 84 | assert.Contains(t, err.Error(), "DNSResolvers") | ||
| 85 | 85 | ||||
| 86 | + | _, err = NewIssuer(Config{ | |||
| 87 | + | Email: "test@example.com", | |||
| 88 | + | Challenge: domain.ACMEChallengeCloudflareDNS01, | |||
| 89 | + | Token: "token", | |||
| 90 | + | Store: outmocks.NewMockCertificateStore(t), | |||
| 91 | + | DNSResolvers: []string{"1.1.1.1:abc"}, | |||
| 92 | + | DNSPropagationTimeout: 5 * time.Minute, | |||
| 93 | + | DNSPollingInterval: 5 * time.Second, | |||
| 94 | + | }) | |||
| 95 | + | require.Error(t, err) | |||
| 96 | + | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | |||
| 97 | + | assert.Contains(t, err.Error(), "invalid DNSResolvers entry") | |||
| 98 | + | ||||
| 86 | _, err = NewIssuer(Config{ | 99 | _, err = NewIssuer(Config{ | ||
| 87 | Email: "test@example.com", | 100 | Email: "test@example.com", | ||
| 88 | Challenge: domain.ACMEChallengeCloudflareDNS01, | 101 | Challenge: domain.ACMEChallengeCloudflareDNS01, | ||
| @@ -96,16 +109,17 @@ func TestNewIssuerValidatesCloudflareDNSConfig(t *testing.T) { | |||||
| 96 | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | 109 | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | ||
| 97 | assert.Contains(t, err.Error(), "DNSPollingInterval") | 110 | assert.Contains(t, err.Error(), "DNSPollingInterval") | ||
| 98 | 111 | ||||
| 99 | - | _, err = NewIssuer(Config{ | 112 | + | issuer, err := NewIssuer(Config{ |
| 100 | Email: "test@example.com", | 113 | Email: "test@example.com", | ||
| 101 | Challenge: domain.ACMEChallengeCloudflareDNS01, | 114 | Challenge: domain.ACMEChallengeCloudflareDNS01, | ||
| 102 | Token: "token", | 115 | Token: "token", | ||
| 103 | Store: outmocks.NewMockCertificateStore(t), | 116 | Store: outmocks.NewMockCertificateStore(t), | ||
| 104 | - | DNSResolvers: []string{"1.1.1.1:53"}, | 117 | + | DNSResolvers: []string{" 1.1.1.1:53 ", "\t8.8.8.8:53\n"}, |
| 105 | DNSPropagationTimeout: 5 * time.Minute, | 118 | DNSPropagationTimeout: 5 * time.Minute, | ||
| 106 | DNSPollingInterval: 5 * time.Second, | 119 | DNSPollingInterval: 5 * time.Second, | ||
| 107 | }) | 120 | }) | ||
| 108 | require.NoError(t, err) | 121 | require.NoError(t, err) | ||
| 122 | + | assert.Equal(t, []string{"1.1.1.1:53", "8.8.8.8:53"}, issuer.cfg.DNSResolvers) | |||
| 109 | } | 123 | } | ||
| 110 | 124 | ||||
| 111 | func TestNewIssuerRejectsNonHTTPSURL(t *testing.T) { | 125 | func TestNewIssuerRejectsNonHTTPSURL(t *testing.T) { | ||
M internal/usecase/publictls/dns_config.go +10 -8
| @@ -6,6 +6,8 @@ import ( | |||
| 6 | 6 | "strconv" | |
| 7 | 7 | "strings" | |
| 8 | 8 | "time" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/gordon/internal/domain" | |
| 9 | 11 | ) | |
| 10 | 12 | ||
| 11 | 13 | const ( | |
| @@ -36,34 +38,34 @@ func DefaultDNSConfig() DNSConfig { | |||
| 36 | 38 | // Validate checks DNSConfig for ACME DNS-01 usage and normalizes resolver whitespace. | |
| 37 | 39 | func (c *DNSConfig) Validate() error { | |
| 38 | 40 | if len(c.Resolvers) == 0 { | |
| 39 | - | return fmt.Errorf("dns.resolvers must contain at least one resolver") | |
| 41 | + | return fmt.Errorf("%w: dns.resolvers must contain at least one resolver", domain.ErrDNSConfigInvalid) | |
| 40 | 42 | } | |
| 41 | 43 | for i, resolver := range c.Resolvers { | |
| 42 | 44 | trimmed := strings.TrimSpace(resolver) | |
| 43 | 45 | if trimmed == "" { | |
| 44 | - | return fmt.Errorf("invalid dns.resolvers[%d]: empty resolver", i) | |
| 46 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: empty resolver", domain.ErrDNSConfigInvalid, i) | |
| 45 | 47 | } | |
| 46 | 48 | host, port, err := net.SplitHostPort(trimmed) | |
| 47 | 49 | if err != nil { | |
| 48 | - | return fmt.Errorf("invalid dns.resolvers[%d]: %q: expected host:port: %w", i, trimmed, err) | |
| 50 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: %q: expected host:port: %v", domain.ErrDNSConfigInvalid, i, trimmed, err) | |
| 49 | 51 | } | |
| 50 | 52 | if host == "" { | |
| 51 | - | return fmt.Errorf("invalid dns.resolvers[%d]: %q: host must not be empty", i, trimmed) | |
| 53 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: %q: host must not be empty", domain.ErrDNSConfigInvalid, i, trimmed) | |
| 52 | 54 | } | |
| 53 | 55 | portNumber, err := strconv.Atoi(port) | |
| 54 | 56 | if err != nil || portNumber < 1 || portNumber > 65535 { | |
| 55 | - | return fmt.Errorf("invalid dns.resolvers[%d]: %q: port must be a number between 1 and 65535", i, trimmed) | |
| 57 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: %q: port must be a number between 1 and 65535", domain.ErrDNSConfigInvalid, i, trimmed) | |
| 56 | 58 | } | |
| 57 | 59 | c.Resolvers[i] = trimmed | |
| 58 | 60 | } | |
| 59 | 61 | if c.PropagationTimeout <= 0 { | |
| 60 | - | return fmt.Errorf("dns.propagation_timeout must be positive") | |
| 62 | + | return fmt.Errorf("%w: dns.propagation_timeout must be positive", domain.ErrDNSConfigInvalid) | |
| 61 | 63 | } | |
| 62 | 64 | if c.PollingInterval <= 0 { | |
| 63 | - | return fmt.Errorf("dns.polling_interval must be positive") | |
| 65 | + | return fmt.Errorf("%w: dns.polling_interval must be positive", domain.ErrDNSConfigInvalid) | |
| 64 | 66 | } | |
| 65 | 67 | if c.PollingInterval >= c.PropagationTimeout { | |
| 66 | - | return fmt.Errorf("dns.polling_interval must be less than dns.propagation_timeout") | |
| 68 | + | return fmt.Errorf("%w: dns.polling_interval must be less than dns.propagation_timeout", domain.ErrDNSConfigInvalid) | |
| 67 | 69 | } | |
| 68 | 70 | return nil | |
| 69 | 71 | } | |
| @@ -6,6 +6,8 @@ import ( | |||||
| 6 | "strconv" | 6 | "strconv" | ||
| 7 | "strings" | 7 | "strings" | ||
| 8 | "time" | 8 | "time" | ||
| 9 | + | ||||
| 10 | + | "github.com/bnema/gordon/internal/domain" | |||
| 9 | ) | 11 | ) | ||
| 10 | 12 | ||||
| 11 | const ( | 13 | const ( | ||
| @@ -36,34 +38,34 @@ func DefaultDNSConfig() DNSConfig { | |||||
| 36 | // Validate checks DNSConfig for ACME DNS-01 usage and normalizes resolver whitespace. | 38 | // Validate checks DNSConfig for ACME DNS-01 usage and normalizes resolver whitespace. | ||
| 37 | func (c *DNSConfig) Validate() error { | 39 | func (c *DNSConfig) Validate() error { | ||
| 38 | if len(c.Resolvers) == 0 { | 40 | if len(c.Resolvers) == 0 { | ||
| 39 | - | return fmt.Errorf("dns.resolvers must contain at least one resolver") | 41 | + | return fmt.Errorf("%w: dns.resolvers must contain at least one resolver", domain.ErrDNSConfigInvalid) |
| 40 | } | 42 | } | ||
| 41 | for i, resolver := range c.Resolvers { | 43 | for i, resolver := range c.Resolvers { | ||
| 42 | trimmed := strings.TrimSpace(resolver) | 44 | trimmed := strings.TrimSpace(resolver) | ||
| 43 | if trimmed == "" { | 45 | if trimmed == "" { | ||
| 44 | - | return fmt.Errorf("invalid dns.resolvers[%d]: empty resolver", i) | 46 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: empty resolver", domain.ErrDNSConfigInvalid, i) |
| 45 | } | 47 | } | ||
| 46 | host, port, err := net.SplitHostPort(trimmed) | 48 | host, port, err := net.SplitHostPort(trimmed) | ||
| 47 | if err != nil { | 49 | if err != nil { | ||
| 48 | - | return fmt.Errorf("invalid dns.resolvers[%d]: %q: expected host:port: %w", i, trimmed, err) | 50 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: %q: expected host:port: %v", domain.ErrDNSConfigInvalid, i, trimmed, err) |
| 49 | } | 51 | } | ||
| 50 | if host == "" { | 52 | if host == "" { | ||
| 51 | - | return fmt.Errorf("invalid dns.resolvers[%d]: %q: host must not be empty", i, trimmed) | 53 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: %q: host must not be empty", domain.ErrDNSConfigInvalid, i, trimmed) |
| 52 | } | 54 | } | ||
| 53 | portNumber, err := strconv.Atoi(port) | 55 | portNumber, err := strconv.Atoi(port) | ||
| 54 | if err != nil || portNumber < 1 || portNumber > 65535 { | 56 | if err != nil || portNumber < 1 || portNumber > 65535 { | ||
| 55 | - | return fmt.Errorf("invalid dns.resolvers[%d]: %q: port must be a number between 1 and 65535", i, trimmed) | 57 | + | return fmt.Errorf("%w: invalid dns.resolvers[%d]: %q: port must be a number between 1 and 65535", domain.ErrDNSConfigInvalid, i, trimmed) |
| 56 | } | 58 | } | ||
| 57 | c.Resolvers[i] = trimmed | 59 | c.Resolvers[i] = trimmed | ||
| 58 | } | 60 | } | ||
| 59 | if c.PropagationTimeout <= 0 { | 61 | if c.PropagationTimeout <= 0 { | ||
| 60 | - | return fmt.Errorf("dns.propagation_timeout must be positive") | 62 | + | return fmt.Errorf("%w: dns.propagation_timeout must be positive", domain.ErrDNSConfigInvalid) |
| 61 | } | 63 | } | ||
| 62 | if c.PollingInterval <= 0 { | 64 | if c.PollingInterval <= 0 { | ||
| 63 | - | return fmt.Errorf("dns.polling_interval must be positive") | 65 | + | return fmt.Errorf("%w: dns.polling_interval must be positive", domain.ErrDNSConfigInvalid) |
| 64 | } | 66 | } | ||
| 65 | if c.PollingInterval >= c.PropagationTimeout { | 67 | if c.PollingInterval >= c.PropagationTimeout { | ||
| 66 | - | return fmt.Errorf("dns.polling_interval must be less than dns.propagation_timeout") | 68 | + | return fmt.Errorf("%w: dns.polling_interval must be less than dns.propagation_timeout", domain.ErrDNSConfigInvalid) |
| 67 | } | 69 | } | ||
| 68 | return nil | 70 | return nil | ||
| 69 | } | 71 | } | ||
M internal/usecase/publictls/dns_config_test.go +3 -0
| @@ -6,6 +6,8 @@ import ( | |||
| 6 | 6 | ||
| 7 | 7 | "github.com/stretchr/testify/assert" | |
| 8 | 8 | "github.com/stretchr/testify/require" | |
| 9 | + | ||
| 10 | + | "github.com/bnema/gordon/internal/domain" | |
| 9 | 11 | ) | |
| 10 | 12 | ||
| 11 | 13 | func TestDefaultDNSConfig(t *testing.T) { | |
| @@ -114,6 +116,7 @@ func TestDNSConfigValidate(t *testing.T) { | |||
| 114 | 116 | return | |
| 115 | 117 | } | |
| 116 | 118 | require.Error(t, err) | |
| 119 | + | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | |
| 117 | 120 | assert.Contains(t, err.Error(), tt.wantErr) | |
| 118 | 121 | }) | |
| 119 | 122 | } | |
| @@ -6,6 +6,8 @@ import ( | |||||
| 6 | 6 | ||||
| 7 | "github.com/stretchr/testify/assert" | 7 | "github.com/stretchr/testify/assert" | ||
| 8 | "github.com/stretchr/testify/require" | 8 | "github.com/stretchr/testify/require" | ||
| 9 | + | ||||
| 10 | + | "github.com/bnema/gordon/internal/domain" | |||
| 9 | ) | 11 | ) | ||
| 10 | 12 | ||||
| 11 | func TestDefaultDNSConfig(t *testing.T) { | 13 | func TestDefaultDNSConfig(t *testing.T) { | ||
| @@ -114,6 +116,7 @@ func TestDNSConfigValidate(t *testing.T) { | |||||
| 114 | return | 116 | return | ||
| 115 | } | 117 | } | ||
| 116 | require.Error(t, err) | 118 | require.Error(t, err) | ||
| 119 | + | assert.ErrorIs(t, err, domain.ErrDNSConfigInvalid) | |||
| 117 | assert.Contains(t, err.Error(), tt.wantErr) | 120 | assert.Contains(t, err.Error(), tt.wantErr) | ||
| 118 | }) | 121 | }) | ||
| 119 | } | 122 | } | ||