mirror of
https://github.com/coredns/coredns.git
synced 2025-09-25 23:51:06 +02:00
test(dnsserver): improve core/dnsserver test coverage (#7317)
Add comprehensive tests for multiple components including server blocks inspection, configuration handling, DoH/DoQ writers, and server startup functions. Increases overall test coverage from 27% to 38.4% with particular focus on register.go, https.go, quic.go, and config.go. Signed-off-by: Ville Vesilehto <ville@vesilehto.fi>
This commit is contained in:
parent
32ea433a29
commit
a070d22bc3
67
core/dnsserver/config_test.go
Normal file
67
core/dnsserver/config_test.go
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
package dnsserver
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coredns/caddy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKeyForConfig(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
blockIndex int
|
||||||
|
blockKeyIndex int
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{"zero_indices", 0, 0, "0:0"},
|
||||||
|
{"positive_indices", 1, 2, "1:2"},
|
||||||
|
{"larger_indices", 10, 5, "10:5"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
result := keyForConfig(tc.blockIndex, tc.blockKeyIndex)
|
||||||
|
if result != tc.expected {
|
||||||
|
t.Errorf("Expected %s, got %s for blockIndex %d and blockKeyIndex %d",
|
||||||
|
tc.expected, result, tc.blockIndex, tc.blockKeyIndex)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetConfig(t *testing.T) {
|
||||||
|
controller := caddy.NewTestController("dns", "")
|
||||||
|
initialCtx := controller.Context()
|
||||||
|
dnsCtx, ok := initialCtx.(*dnsContext)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("controller.Context() did not return a *dnsContext, got %T", initialCtx)
|
||||||
|
}
|
||||||
|
if dnsCtx.keysToConfigs == nil {
|
||||||
|
t.Fatal("dnsCtx.keysToConfigs is nil; it should have been initialized by newContext")
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("returns and saves default config when config missing", func(t *testing.T) {
|
||||||
|
controller.ServerBlockIndex = 0
|
||||||
|
controller.ServerBlockKeyIndex = 0
|
||||||
|
key := keyForConfig(controller.ServerBlockIndex, controller.ServerBlockKeyIndex)
|
||||||
|
|
||||||
|
// Ensure config doesn't exist initially for this specific key
|
||||||
|
delete(dnsCtx.keysToConfigs, key)
|
||||||
|
|
||||||
|
cfg := GetConfig(controller)
|
||||||
|
if cfg == nil {
|
||||||
|
t.Fatal("GetConfig returned nil (should create and return a default)")
|
||||||
|
}
|
||||||
|
if len(cfg.ListenHosts) != 1 || cfg.ListenHosts[0] != "" {
|
||||||
|
t.Errorf("Expected default ListenHosts [\"\"] for auto-created config, got %v", cfg.ListenHosts)
|
||||||
|
}
|
||||||
|
|
||||||
|
savedCfg, found := dnsCtx.keysToConfigs[key]
|
||||||
|
if !found {
|
||||||
|
t.Fatal("fallback did not save the default config into the context")
|
||||||
|
}
|
||||||
|
if savedCfg != cfg {
|
||||||
|
t.Fatal("config is not the same instance as the one saved in the context")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -88,3 +88,72 @@ func TestDoHWriter_Request(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDoHWriter_Write(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid DNS message",
|
||||||
|
// A minimal valid DNS query message
|
||||||
|
input: []byte{
|
||||||
|
0x00, 0x01, /* ID */
|
||||||
|
0x01, 0x00, /* Flags: query, recursion desired */
|
||||||
|
0x00, 0x01, /* Questions: 1 */
|
||||||
|
0x00, 0x00, /* Answer RRs: 0 */
|
||||||
|
0x00, 0x00, /* Authority RRs: 0 */
|
||||||
|
0x00, 0x00, /* Additional RRs: 0 */
|
||||||
|
0x03, 'w', 'w', 'w',
|
||||||
|
0x07, 'e', 'x', 'a', 'm', 'p', 'l', 'e',
|
||||||
|
0x03, 'c', 'o', 'm',
|
||||||
|
0x00, /* Null terminator for domain name */
|
||||||
|
0x00, 0x01, /* Type: A */
|
||||||
|
0x00, 0x01, /* Class: IN */
|
||||||
|
},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty message",
|
||||||
|
input: []byte{},
|
||||||
|
wantErr: true, // Expect an error because unpacking an empty message will fail
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "invalid DNS message",
|
||||||
|
input: []byte{0x00, 0x01, 0x02}, // Truncated message
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
d := &DoHWriter{}
|
||||||
|
n, err := d.Write(tt.input)
|
||||||
|
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("Write() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !tt.wantErr && n != len(tt.input) {
|
||||||
|
t.Errorf("Write() bytes written = %v, want %v", n, len(tt.input))
|
||||||
|
}
|
||||||
|
if !tt.wantErr && d.Msg == nil {
|
||||||
|
t.Errorf("Write() d.Msg is nil, expected a parsed message")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoHWriter_Close(t *testing.T) {
|
||||||
|
d := &DoHWriter{}
|
||||||
|
if err := d.Close(); err != nil {
|
||||||
|
t.Errorf("Close() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoHWriter_TsigStatus(t *testing.T) {
|
||||||
|
d := &DoHWriter{}
|
||||||
|
if err := d.TsigStatus(); err != nil {
|
||||||
|
t.Errorf("TsigStatus() error = %v, want nil", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -37,3 +37,110 @@ func TestRegex1035PrefSyntax(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStartUpZones(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
protocol string
|
||||||
|
addr string
|
||||||
|
zones map[string][]*Config
|
||||||
|
expectedOutput string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no zones",
|
||||||
|
protocol: "dns://",
|
||||||
|
addr: "127.0.0.1:53",
|
||||||
|
zones: map[string][]*Config{},
|
||||||
|
expectedOutput: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single zone valid syntax ip and port",
|
||||||
|
protocol: "dns://",
|
||||||
|
addr: "127.0.0.1:53",
|
||||||
|
zones: map[string][]*Config{"example.com.": nil},
|
||||||
|
expectedOutput: "dns://example.com.:53 on 127.0.0.1\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single zone valid syntax port only",
|
||||||
|
protocol: "http://",
|
||||||
|
addr: ":8080",
|
||||||
|
zones: map[string][]*Config{"example.org.": nil},
|
||||||
|
expectedOutput: "http://example.org.:8080\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "single zone invalid syntax",
|
||||||
|
protocol: "tls://",
|
||||||
|
addr: "10.0.0.1:853",
|
||||||
|
zones: map[string][]*Config{"invalid-zone": nil},
|
||||||
|
expectedOutput: "Warning: Domain \"invalid-zone\" does not follow RFC1035 preferred syntax\n" +
|
||||||
|
"tls://invalid-zone:853 on 10.0.0.1\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple zones sorted order",
|
||||||
|
protocol: "dns://",
|
||||||
|
addr: "localhost:5353",
|
||||||
|
zones: map[string][]*Config{
|
||||||
|
"c-zone.com.": nil,
|
||||||
|
"a-zone.org.": nil,
|
||||||
|
"b-zone.net.": nil,
|
||||||
|
},
|
||||||
|
expectedOutput: "dns://a-zone.org.:5353 on localhost\n" +
|
||||||
|
"dns://b-zone.net.:5353 on localhost\n" +
|
||||||
|
"dns://c-zone.com.:5353 on localhost\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "addr parse error",
|
||||||
|
protocol: "grpc://",
|
||||||
|
addr: "[::1]:8080:extra", // Malformed, should cause SplitProtocolHostPort to error
|
||||||
|
zones: map[string][]*Config{"error.example.": nil},
|
||||||
|
expectedOutput: "grpc://error.example.:[::1]:8080:extra\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "root zone",
|
||||||
|
protocol: "dns://",
|
||||||
|
addr: "192.168.1.1:53",
|
||||||
|
zones: map[string][]*Config{".": nil},
|
||||||
|
expectedOutput: "dns://.:53 on 192.168.1.1\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reverse zone",
|
||||||
|
protocol: "dns://",
|
||||||
|
addr: ":53",
|
||||||
|
zones: map[string][]*Config{"1.0.168.192.in-addr.arpa.": nil},
|
||||||
|
expectedOutput: "dns://1.0.168.192.in-addr.arpa.:53\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple zones mixed syntax and addr handling",
|
||||||
|
protocol: "quic://",
|
||||||
|
addr: "coolserver.local:784",
|
||||||
|
zones: map[string][]*Config{
|
||||||
|
"valid.net.": nil,
|
||||||
|
"_tcp.service.": nil, // Invalid syntax
|
||||||
|
"another.valid.com.": nil,
|
||||||
|
},
|
||||||
|
expectedOutput: "Warning: Domain \"_tcp.service.\" does not follow RFC1035 preferred syntax\n" +
|
||||||
|
"quic://_tcp.service.:784 on coolserver.local\n" +
|
||||||
|
"quic://another.valid.com.:784 on coolserver.local\n" +
|
||||||
|
"quic://valid.net.:784 on coolserver.local\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "zone with leading dash invalid",
|
||||||
|
protocol: "dns://",
|
||||||
|
addr: "127.0.0.1:53",
|
||||||
|
zones: map[string][]*Config{"-leadingdash.com.": nil},
|
||||||
|
expectedOutput: "Warning: Domain \"-leadingdash.com.\" does not follow RFC1035 preferred syntax\n" +
|
||||||
|
"dns://-leadingdash.com.:53 on 127.0.0.1\n",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got := startUpZones(tc.protocol, tc.addr, tc.zones)
|
||||||
|
if got != tc.expectedOutput {
|
||||||
|
// Use %q for expected and got to make differences in whitespace/newlines visible.
|
||||||
|
t.Errorf("startUpZones(%q, %q, ...) mismatch for test '%s':\nGot:\n%q\nExpected:\n%q",
|
||||||
|
tc.protocol, tc.addr, tc.name, got, tc.expectedOutput)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
package dnsserver
|
package dnsserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"github.com/quic-go/quic-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestDoQWriterAddPrefix(t *testing.T) {
|
func TestDoQWriterAddPrefix(t *testing.T) {
|
||||||
@ -18,3 +26,239 @@ func TestDoQWriterAddPrefix(t *testing.T) {
|
|||||||
t.Errorf("Expected prefixed size to be 3, got: %d", size)
|
t.Errorf("Expected prefixed size to be 3, got: %d", size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDoQWriter_ResponseWriterMethods(t *testing.T) {
|
||||||
|
localAddr := &net.UDPAddr{IP: net.ParseIP("127.0.0.1"), Port: 1234}
|
||||||
|
remoteAddr := &net.UDPAddr{IP: net.ParseIP("8.8.8.8"), Port: 53}
|
||||||
|
|
||||||
|
writer := &DoQWriter{
|
||||||
|
localAddr: localAddr,
|
||||||
|
remoteAddr: remoteAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.TsigStatus(); err != nil {
|
||||||
|
t.Errorf("TsigStatus() returned an error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a no-op, just call it
|
||||||
|
writer.TsigTimersOnly(true)
|
||||||
|
writer.TsigTimersOnly(false)
|
||||||
|
|
||||||
|
// this is a no-op, just call it
|
||||||
|
writer.Hijack()
|
||||||
|
|
||||||
|
if addr := writer.LocalAddr(); addr != localAddr {
|
||||||
|
t.Errorf("LocalAddr() = %v, want %v", addr, localAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr := writer.RemoteAddr(); addr != remoteAddr {
|
||||||
|
t.Errorf("RemoteAddr() = %v, want %v", addr, remoteAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// mockQuicStream is a mock implementation of quic.Stream for testing.
|
||||||
|
type mockQuicStream struct {
|
||||||
|
writer func(p []byte) (n int, err error)
|
||||||
|
closer func() error
|
||||||
|
closed bool
|
||||||
|
data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockQuicStream) Write(p []byte) (n int, err error) {
|
||||||
|
m.data = append(m.data, p...)
|
||||||
|
if m.writer != nil {
|
||||||
|
return m.writer(p)
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockQuicStream) Close() error {
|
||||||
|
m.closed = true
|
||||||
|
if m.closer != nil {
|
||||||
|
return m.closer()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required by quic.Stream interface, but not used in these tests
|
||||||
|
func (m *mockQuicStream) Read(p []byte) (n int, err error) { return 0, nil }
|
||||||
|
func (m *mockQuicStream) CancelRead(code quic.StreamErrorCode) {}
|
||||||
|
func (m *mockQuicStream) CancelWrite(code quic.StreamErrorCode) {}
|
||||||
|
func (m *mockQuicStream) SetReadDeadline(t time.Time) error { return nil }
|
||||||
|
func (m *mockQuicStream) SetWriteDeadline(t time.Time) error { return nil }
|
||||||
|
func (m *mockQuicStream) SetDeadline(t time.Time) error { return nil }
|
||||||
|
func (m *mockQuicStream) StreamID() quic.StreamID { return 0 }
|
||||||
|
func (m *mockQuicStream) Context() context.Context { return nil }
|
||||||
|
|
||||||
|
func TestDoQWriter_Write(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input []byte
|
||||||
|
streamWriter func(p []byte) (n int, err error)
|
||||||
|
expectErr bool
|
||||||
|
expectedData []byte
|
||||||
|
expectedN int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "successful write",
|
||||||
|
input: []byte{0x1, 0x2, 0x3},
|
||||||
|
streamWriter: func(p []byte) (n int, err error) {
|
||||||
|
return len(p), nil
|
||||||
|
},
|
||||||
|
expectErr: false,
|
||||||
|
expectedData: []byte{0x0, 0x3, 0x1, 0x2, 0x3}, // 3-byte length prefix
|
||||||
|
expectedN: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stream write error",
|
||||||
|
input: []byte{0x4, 0x5},
|
||||||
|
streamWriter: func(p []byte) (n int, err error) {
|
||||||
|
return 0, errors.New("stream error")
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
expectedData: []byte{0x0, 0x2, 0x4, 0x5}, // 2-byte length prefix
|
||||||
|
expectedN: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
mockStream := &mockQuicStream{writer: tt.streamWriter}
|
||||||
|
writer := &DoQWriter{stream: mockStream}
|
||||||
|
|
||||||
|
n, err := writer.Write(tt.input)
|
||||||
|
|
||||||
|
if (err != nil) != tt.expectErr {
|
||||||
|
t.Errorf("Write() error = %v, expectErr %v", err, tt.expectErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if n != tt.expectedN {
|
||||||
|
t.Errorf("Write() n = %v, want %v", n, tt.expectedN)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(mockStream.data, tt.expectedData) {
|
||||||
|
t.Errorf("Write() data written to stream = %X, want %X", mockStream.data, tt.expectedData)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoQWriter_WriteMsg(t *testing.T) {
|
||||||
|
newMsg := func() *dns.Msg {
|
||||||
|
m := new(dns.Msg)
|
||||||
|
m.SetQuestion("example.com.", dns.TypeA)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
msg *dns.Msg
|
||||||
|
mockStream *mockQuicStream
|
||||||
|
expectErr bool
|
||||||
|
expectClosed bool
|
||||||
|
expectedData []byte // Expected data written to stream (packed msg with prefix)
|
||||||
|
packErr bool // Simulate error during msg.Pack()
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "successful write and close",
|
||||||
|
msg: newMsg(),
|
||||||
|
mockStream: &mockQuicStream{},
|
||||||
|
expectErr: false,
|
||||||
|
expectClosed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "msg.Pack() error",
|
||||||
|
msg: new(dns.Msg),
|
||||||
|
mockStream: &mockQuicStream{},
|
||||||
|
expectErr: true,
|
||||||
|
packErr: true, // We'll make msg.Pack() fail by corrupting the msg or using a mock
|
||||||
|
expectClosed: false, // Close should not be called if Pack fails
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stream write error",
|
||||||
|
msg: newMsg(),
|
||||||
|
mockStream: &mockQuicStream{
|
||||||
|
writer: func(p []byte) (n int, err error) {
|
||||||
|
return 0, errors.New("stream write failed")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
expectClosed: false, // Close should not be called if Write fails
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stream close error",
|
||||||
|
msg: newMsg(),
|
||||||
|
mockStream: &mockQuicStream{
|
||||||
|
closer: func() error {
|
||||||
|
return errors.New("stream close failed")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
expectClosed: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if tt.packErr {
|
||||||
|
// Intentionally make the message invalid to cause a pack error.
|
||||||
|
// Invalid Rcode to ensure Pack fails.
|
||||||
|
tt.msg.Rcode = 1337
|
||||||
|
}
|
||||||
|
|
||||||
|
writer := &DoQWriter{stream: tt.mockStream, Msg: tt.msg}
|
||||||
|
err := writer.WriteMsg(tt.msg)
|
||||||
|
|
||||||
|
if (err != nil) != tt.expectErr {
|
||||||
|
t.Errorf("WriteMsg() error = %v, expectErr %v", err, tt.expectErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.mockStream.closed != tt.expectClosed {
|
||||||
|
t.Errorf("WriteMsg() stream closed = %v, want %v", tt.mockStream.closed, tt.expectClosed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tt.packErr {
|
||||||
|
if len(tt.mockStream.data) != 0 {
|
||||||
|
t.Errorf("WriteMsg() data written to stream on pack error = %X, want empty", tt.mockStream.data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoQWriter_Close(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
mockStream *mockQuicStream
|
||||||
|
expectErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "successful close",
|
||||||
|
mockStream: &mockQuicStream{},
|
||||||
|
expectErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "stream close error",
|
||||||
|
mockStream: &mockQuicStream{
|
||||||
|
closer: func() error {
|
||||||
|
return errors.New("stream close error")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
writer := &DoQWriter{stream: tt.mockStream}
|
||||||
|
err := writer.Close()
|
||||||
|
|
||||||
|
if (err != nil) != tt.expectErr {
|
||||||
|
t.Errorf("Close() error = %v, expectErr %v", err, tt.expectErr)
|
||||||
|
}
|
||||||
|
if !tt.mockStream.closed {
|
||||||
|
t.Errorf("Close() stream not marked as closed")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ package dnsserver
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coredns/caddy/caddyfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandler(t *testing.T) {
|
func TestHandler(t *testing.T) {
|
||||||
@ -118,3 +120,215 @@ func TestGroupingServers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInspectServerBlocks(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
serverBlocks []caddyfile.ServerBlock
|
||||||
|
expectedServerBlocks []caddyfile.ServerBlock
|
||||||
|
expectedConfigsLen int
|
||||||
|
expectedZoneAddrs map[string]zoneAddr
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "simple dns",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"example.org"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"dns://example.org.:53"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"dns://example.org.:53": {Zone: "example.org.", Port: "53", Transport: "dns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dns with port",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"example.org:1053"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"dns://example.org.:1053"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"dns://example.org.:1053": {Zone: "example.org.", Port: "1053", Transport: "dns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tls",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"tls://example.org"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"tls://example.org.:853"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"tls://example.org.:853": {Zone: "example.org.", Port: "853", Transport: "tls"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "quic",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"quic://example.org"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"quic://example.org.:853"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"quic://example.org.:853": {Zone: "example.org.", Port: "853", Transport: "quic"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "grpc",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"grpc://example.org"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"grpc://example.org.:443"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"grpc://example.org.:443": {Zone: "example.org.", Port: "443", Transport: "grpc"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "https",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"https://example.org."}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"https://example.org.:443"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"https://example.org.:443": {Zone: "example.org.", Port: "443", Transport: "https"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple hosts same key",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"example.org,example.com:1053"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"dns://example.org,example.com.:1053"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"dns://example.org,example.com.:1053": {Zone: "example.org,example.com.", Port: "1053", Transport: "dns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple keys",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"example.org", "example.com:1053"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"dns://example.org.:53", "dns://example.com.:1053"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 2,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"dns://example.org.:53": {Zone: "example.org.", Port: "53", Transport: "dns"},
|
||||||
|
"dns://example.com.:1053": {Zone: "example.com.", Port: "1053", Transport: "dns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "fqdn input",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"example.org."}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"dns://example.org.:53"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 1,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"dns://example.org.:53": {Zone: "example.org.", Port: "53", Transport: "dns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "multiple server blocks",
|
||||||
|
serverBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"example.org"}},
|
||||||
|
{Keys: []string{"sub.example.org:1054"}},
|
||||||
|
},
|
||||||
|
expectedServerBlocks: []caddyfile.ServerBlock{
|
||||||
|
{Keys: []string{"dns://example.org.:53"}},
|
||||||
|
{Keys: []string{"dns://sub.example.org.:1054"}},
|
||||||
|
},
|
||||||
|
expectedConfigsLen: 2,
|
||||||
|
expectedZoneAddrs: map[string]zoneAddr{
|
||||||
|
"dns://example.org.:53": {Zone: "example.org.", Port: "53", Transport: "dns"},
|
||||||
|
"dns://sub.example.org.:1054": {Zone: "sub.example.org.", Port: "1054", Transport: "dns"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range tests {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
ctx := newContext(nil).(*dnsContext)
|
||||||
|
processedBlocks, err := ctx.InspectServerBlocks("TestInspectServerBlocks", tc.serverBlocks)
|
||||||
|
|
||||||
|
if (err != nil) != tc.wantErr {
|
||||||
|
t.Fatalf("InspectServerBlocks() error = %v, wantErr %v", err, tc.wantErr)
|
||||||
|
}
|
||||||
|
if tc.wantErr {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(processedBlocks) != len(tc.expectedServerBlocks) {
|
||||||
|
t.Fatalf("Expected %d processed blocks, got %d", len(tc.expectedServerBlocks), len(processedBlocks))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, block := range processedBlocks {
|
||||||
|
expectedBlock := tc.expectedServerBlocks[i]
|
||||||
|
if len(block.Keys) != len(expectedBlock.Keys) {
|
||||||
|
t.Errorf("Block %d: expected %d keys, got %d. Expected: %v, Got: %v", i, len(expectedBlock.Keys), len(block.Keys), expectedBlock.Keys, block.Keys)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for j, key := range block.Keys {
|
||||||
|
if key != expectedBlock.Keys[j] {
|
||||||
|
t.Errorf("Block %d, Key %d: expected key '%s', got '%s'", i, j, expectedBlock.Keys[j], key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ctx.configs) != tc.expectedConfigsLen {
|
||||||
|
t.Errorf("Expected %d configs to be created, got %d", tc.expectedConfigsLen, len(ctx.configs))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tc.expectedZoneAddrs != nil {
|
||||||
|
configIndex := 0
|
||||||
|
for ib := range processedBlocks {
|
||||||
|
for ik, key := range processedBlocks[ib].Keys {
|
||||||
|
if configIndex >= len(ctx.configs) {
|
||||||
|
t.Fatalf("Not enough configs stored, expected at least %d, processed block %d key %d", configIndex+1, ib, ik)
|
||||||
|
}
|
||||||
|
cfg := ctx.configs[configIndex]
|
||||||
|
expectedZa, ok := tc.expectedZoneAddrs[key]
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("No expected zoneAddr for processed key '%s'", key)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Zone != expectedZa.Zone {
|
||||||
|
t.Errorf("Config for key '%s': expected Zone '%s', got '%s'", key, expectedZa.Zone, cfg.Zone)
|
||||||
|
}
|
||||||
|
if cfg.Port != expectedZa.Port {
|
||||||
|
t.Errorf("Config for key '%s': expected Port '%s', got '%s'", key, expectedZa.Port, cfg.Port)
|
||||||
|
}
|
||||||
|
if cfg.Transport != expectedZa.Transport {
|
||||||
|
t.Errorf("Config for key '%s': expected Transport '%s', got '%s'", key, expectedZa.Transport, cfg.Transport)
|
||||||
|
}
|
||||||
|
if len(cfg.ListenHosts) != 1 || cfg.ListenHosts[0] != "" {
|
||||||
|
t.Errorf("Config for key '%s': expected ListenHosts [''], got %v", key, cfg.ListenHosts)
|
||||||
|
}
|
||||||
|
configIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user