mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-03 18:53:13 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			478 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package parse
 | 
						|
 | 
						|
import (
 | 
						|
	"os"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
)
 | 
						|
 | 
						|
func TestStandardAddress(t *testing.T) {
 | 
						|
	for i, test := range []struct {
 | 
						|
		input              string
 | 
						|
		scheme, host, port string
 | 
						|
		shouldErr          bool
 | 
						|
	}{
 | 
						|
		{`localhost`, "", "localhost", "", false},
 | 
						|
		{`localhost:1234`, "", "localhost", "1234", false},
 | 
						|
		{`localhost:`, "", "localhost", "", false},
 | 
						|
		{`0.0.0.0`, "", "0.0.0.0", "", false},
 | 
						|
		{`127.0.0.1:1234`, "", "127.0.0.1", "1234", false},
 | 
						|
		{`:1234`, "", "", "1234", false},
 | 
						|
		{`[::1]`, "", "::1", "", false},
 | 
						|
		{`[::1]:1234`, "", "::1", "1234", false},
 | 
						|
		{`:`, "", "", "", false},
 | 
						|
		{`localhost:http`, "http", "localhost", "80", false},
 | 
						|
		{`localhost:https`, "https", "localhost", "443", false},
 | 
						|
		{`:http`, "http", "", "80", false},
 | 
						|
		{`:https`, "https", "", "443", false},
 | 
						|
		{`http://localhost:https`, "", "", "", true}, // conflict
 | 
						|
		{`http://localhost:http`, "", "", "", true},  // repeated scheme
 | 
						|
		{`http://localhost:443`, "", "", "", true},   // not conventional
 | 
						|
		{`https://localhost:80`, "", "", "", true},   // not conventional
 | 
						|
		{`http://localhost`, "http", "localhost", "80", false},
 | 
						|
		{`https://localhost`, "https", "localhost", "443", false},
 | 
						|
		{`http://127.0.0.1`, "http", "127.0.0.1", "80", false},
 | 
						|
		{`https://127.0.0.1`, "https", "127.0.0.1", "443", false},
 | 
						|
		{`http://[::1]`, "http", "::1", "80", false},
 | 
						|
		{`http://localhost:1234`, "http", "localhost", "1234", false},
 | 
						|
		{`https://127.0.0.1:1234`, "https", "127.0.0.1", "1234", false},
 | 
						|
		{`http://[::1]:1234`, "http", "::1", "1234", false},
 | 
						|
		{``, "", "", "", false},
 | 
						|
		{`::1`, "", "::1", "", true},
 | 
						|
		{`localhost::`, "", "localhost::", "", true},
 | 
						|
		{`#$%@`, "", "#$%@", "", true},
 | 
						|
	} {
 | 
						|
		actual, err := standardAddress(test.input)
 | 
						|
 | 
						|
		if err != nil && !test.shouldErr {
 | 
						|
			t.Errorf("Test %d (%s): Expected no error, but had error: %v", i, test.input, err)
 | 
						|
		}
 | 
						|
		if err == nil && test.shouldErr {
 | 
						|
			t.Errorf("Test %d (%s): Expected error, but had none", i, test.input)
 | 
						|
		}
 | 
						|
 | 
						|
		if actual.Scheme != test.scheme {
 | 
						|
			t.Errorf("Test %d (%s): Expected scheme '%s', got '%s'", i, test.input, test.scheme, actual.Scheme)
 | 
						|
		}
 | 
						|
		if actual.Host != test.host {
 | 
						|
			t.Errorf("Test %d (%s): Expected host '%s', got '%s'", i, test.input, test.host, actual.Host)
 | 
						|
		}
 | 
						|
		if actual.Port != test.port {
 | 
						|
			t.Errorf("Test %d (%s): Expected port '%s', got '%s'", i, test.input, test.port, actual.Port)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestParseOneAndImport(t *testing.T) {
 | 
						|
	setupParseTests()
 | 
						|
 | 
						|
	testParseOne := func(input string) (ServerBlock, error) {
 | 
						|
		p := testParser(input)
 | 
						|
		p.Next() // parseOne doesn't call Next() to start, so we must
 | 
						|
		err := p.parseOne()
 | 
						|
		return p.block, err
 | 
						|
	}
 | 
						|
 | 
						|
	for i, test := range []struct {
 | 
						|
		input     string
 | 
						|
		shouldErr bool
 | 
						|
		addresses []address
 | 
						|
		tokens    map[string]int // map of directive name to number of tokens expected
 | 
						|
	}{
 | 
						|
		{`localhost`, false, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{}},
 | 
						|
 | 
						|
		{`localhost
 | 
						|
		  dir1`, false, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 1,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost:1234
 | 
						|
		  dir1 foo bar`, false, []address{
 | 
						|
			{"localhost:1234", "", "localhost", "1234"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost {
 | 
						|
		    dir1
 | 
						|
		  }`, false, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 1,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost:1234 {
 | 
						|
		    dir1 foo bar
 | 
						|
		    dir2
 | 
						|
		  }`, false, []address{
 | 
						|
			{"localhost:1234", "", "localhost", "1234"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
			"dir2": 1,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`http://localhost https://localhost
 | 
						|
		  dir1 foo bar`, false, []address{
 | 
						|
			{"http://localhost", "http", "localhost", "80"},
 | 
						|
			{"https://localhost", "https", "localhost", "443"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`http://localhost https://localhost {
 | 
						|
		    dir1 foo bar
 | 
						|
		  }`, false, []address{
 | 
						|
			{"http://localhost", "http", "localhost", "80"},
 | 
						|
			{"https://localhost", "https", "localhost", "443"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`http://localhost, https://localhost {
 | 
						|
		    dir1 foo bar
 | 
						|
		  }`, false, []address{
 | 
						|
			{"http://localhost", "http", "localhost", "80"},
 | 
						|
			{"https://localhost", "https", "localhost", "443"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`http://localhost, {
 | 
						|
		  }`, true, []address{
 | 
						|
			{"http://localhost", "http", "localhost", "80"},
 | 
						|
		}, map[string]int{}},
 | 
						|
 | 
						|
		{`host1:80, http://host2.com
 | 
						|
		  dir1 foo bar
 | 
						|
		  dir2 baz`, false, []address{
 | 
						|
			{"host1:80", "", "host1", "80"},
 | 
						|
			{"http://host2.com", "http", "host2.com", "80"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
			"dir2": 2,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`http://host1.com,
 | 
						|
		  http://host2.com,
 | 
						|
		  https://host3.com`, false, []address{
 | 
						|
			{"http://host1.com", "http", "host1.com", "80"},
 | 
						|
			{"http://host2.com", "http", "host2.com", "80"},
 | 
						|
			{"https://host3.com", "https", "host3.com", "443"},
 | 
						|
		}, map[string]int{}},
 | 
						|
 | 
						|
		{`http://host1.com:1234, https://host2.com
 | 
						|
		  dir1 foo {
 | 
						|
		    bar baz
 | 
						|
		  }
 | 
						|
		  dir2`, false, []address{
 | 
						|
			{"http://host1.com:1234", "http", "host1.com", "1234"},
 | 
						|
			{"https://host2.com", "https", "host2.com", "443"},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 6,
 | 
						|
			"dir2": 1,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`127.0.0.1
 | 
						|
		  dir1 {
 | 
						|
		    bar baz
 | 
						|
		  }
 | 
						|
		  dir2 {
 | 
						|
		    foo bar
 | 
						|
		  }`, false, []address{
 | 
						|
			{"127.0.0.1", "", "127.0.0.1", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 5,
 | 
						|
			"dir2": 5,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`127.0.0.1
 | 
						|
		  unknown_directive`, true, []address{
 | 
						|
			{"127.0.0.1", "", "127.0.0.1", ""},
 | 
						|
		}, map[string]int{}},
 | 
						|
 | 
						|
		{`localhost
 | 
						|
		  dir1 {
 | 
						|
		    foo`, true, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost
 | 
						|
		  dir1 {
 | 
						|
		  }`, false, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost
 | 
						|
		  dir1 {
 | 
						|
		  } }`, true, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost
 | 
						|
		  dir1 {
 | 
						|
		    nested {
 | 
						|
		      foo
 | 
						|
		    }
 | 
						|
		  }
 | 
						|
		  dir2 foo bar`, false, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 7,
 | 
						|
			"dir2": 3,
 | 
						|
		}},
 | 
						|
 | 
						|
		{``, false, []address{}, map[string]int{}},
 | 
						|
 | 
						|
		{`localhost
 | 
						|
		  dir1 arg1
 | 
						|
		  import import_test1.txt`, false, []address{
 | 
						|
			{"localhost", "", "localhost", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 2,
 | 
						|
			"dir2": 3,
 | 
						|
			"dir3": 1,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`import import_test2.txt`, false, []address{
 | 
						|
			{"host1", "", "host1", ""},
 | 
						|
		}, map[string]int{
 | 
						|
			"dir1": 1,
 | 
						|
			"dir2": 2,
 | 
						|
		}},
 | 
						|
 | 
						|
		{`import import_test1.txt import_test2.txt`, true, []address{}, map[string]int{}},
 | 
						|
 | 
						|
		{`import not_found.txt`, true, []address{}, map[string]int{}},
 | 
						|
 | 
						|
		{`""`, false, []address{}, map[string]int{}},
 | 
						|
 | 
						|
		{``, false, []address{}, map[string]int{}},
 | 
						|
	} {
 | 
						|
		result, err := testParseOne(test.input)
 | 
						|
 | 
						|
		if test.shouldErr && err == nil {
 | 
						|
			t.Errorf("Test %d: Expected an error, but didn't get one", i)
 | 
						|
		}
 | 
						|
		if !test.shouldErr && err != nil {
 | 
						|
			t.Errorf("Test %d: Expected no error, but got: %v", i, err)
 | 
						|
		}
 | 
						|
 | 
						|
		if len(result.Addresses) != len(test.addresses) {
 | 
						|
			t.Errorf("Test %d: Expected %d addresses, got %d",
 | 
						|
				i, len(test.addresses), len(result.Addresses))
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		for j, addr := range result.Addresses {
 | 
						|
			if addr.Host != test.addresses[j].Host {
 | 
						|
				t.Errorf("Test %d, address %d: Expected host to be '%s', but was '%s'",
 | 
						|
					i, j, test.addresses[j].Host, addr.Host)
 | 
						|
			}
 | 
						|
			if addr.Port != test.addresses[j].Port {
 | 
						|
				t.Errorf("Test %d, address %d: Expected port to be '%s', but was '%s'",
 | 
						|
					i, j, test.addresses[j].Port, addr.Port)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if len(result.Tokens) != len(test.tokens) {
 | 
						|
			t.Errorf("Test %d: Expected %d directives, had %d",
 | 
						|
				i, len(test.tokens), len(result.Tokens))
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		for directive, tokens := range result.Tokens {
 | 
						|
			if len(tokens) != test.tokens[directive] {
 | 
						|
				t.Errorf("Test %d, directive '%s': Expected %d tokens, counted %d",
 | 
						|
					i, directive, test.tokens[directive], len(tokens))
 | 
						|
				continue
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestParseAll(t *testing.T) {
 | 
						|
	setupParseTests()
 | 
						|
 | 
						|
	for i, test := range []struct {
 | 
						|
		input     string
 | 
						|
		shouldErr bool
 | 
						|
		addresses [][]address // addresses per server block, in order
 | 
						|
	}{
 | 
						|
		{`localhost`, false, [][]address{
 | 
						|
			{{"localhost", "", "localhost", ""}},
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost:1234`, false, [][]address{
 | 
						|
			{{"localhost:1234", "", "localhost", "1234"}},
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost:1234 {
 | 
						|
		  }
 | 
						|
		  localhost:2015 {
 | 
						|
		  }`, false, [][]address{
 | 
						|
			{{"localhost:1234", "", "localhost", "1234"}},
 | 
						|
			{{"localhost:2015", "", "localhost", "2015"}},
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost:1234, http://host2`, false, [][]address{
 | 
						|
			{{"localhost:1234", "", "localhost", "1234"}, {"http://host2", "http", "host2", "80"}},
 | 
						|
		}},
 | 
						|
 | 
						|
		{`localhost:1234, http://host2,`, true, [][]address{}},
 | 
						|
 | 
						|
		{`http://host1.com, http://host2.com {
 | 
						|
		  }
 | 
						|
		  https://host3.com, https://host4.com {
 | 
						|
		  }`, false, [][]address{
 | 
						|
			{{"http://host1.com", "http", "host1.com", "80"}, {"http://host2.com", "http", "host2.com", "80"}},
 | 
						|
			{{"https://host3.com", "https", "host3.com", "443"}, {"https://host4.com", "https", "host4.com", "443"}},
 | 
						|
		}},
 | 
						|
 | 
						|
		{`import import_glob*.txt`, false, [][]address{
 | 
						|
			{{"glob0.host0", "", "glob0.host0", ""}},
 | 
						|
			{{"glob0.host1", "", "glob0.host1", ""}},
 | 
						|
			{{"glob1.host0", "", "glob1.host0", ""}},
 | 
						|
			{{"glob2.host0", "", "glob2.host0", ""}},
 | 
						|
		}},
 | 
						|
	} {
 | 
						|
		p := testParser(test.input)
 | 
						|
		blocks, err := p.parseAll()
 | 
						|
 | 
						|
		if test.shouldErr && err == nil {
 | 
						|
			t.Errorf("Test %d: Expected an error, but didn't get one", i)
 | 
						|
		}
 | 
						|
		if !test.shouldErr && err != nil {
 | 
						|
			t.Errorf("Test %d: Expected no error, but got: %v", i, err)
 | 
						|
		}
 | 
						|
 | 
						|
		if len(blocks) != len(test.addresses) {
 | 
						|
			t.Errorf("Test %d: Expected %d server blocks, got %d",
 | 
						|
				i, len(test.addresses), len(blocks))
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		for j, block := range blocks {
 | 
						|
			if len(block.Addresses) != len(test.addresses[j]) {
 | 
						|
				t.Errorf("Test %d: Expected %d addresses in block %d, got %d",
 | 
						|
					i, len(test.addresses[j]), j, len(block.Addresses))
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			for k, addr := range block.Addresses {
 | 
						|
				if addr.Host != test.addresses[j][k].Host {
 | 
						|
					t.Errorf("Test %d, block %d, address %d: Expected host to be '%s', but was '%s'",
 | 
						|
						i, j, k, test.addresses[j][k].Host, addr.Host)
 | 
						|
				}
 | 
						|
				if addr.Port != test.addresses[j][k].Port {
 | 
						|
					t.Errorf("Test %d, block %d, address %d: Expected port to be '%s', but was '%s'",
 | 
						|
						i, j, k, test.addresses[j][k].Port, addr.Port)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestEnvironmentReplacement(t *testing.T) {
 | 
						|
	setupParseTests()
 | 
						|
 | 
						|
	os.Setenv("PORT", "8080")
 | 
						|
	os.Setenv("ADDRESS", "servername.com")
 | 
						|
	os.Setenv("FOOBAR", "foobar")
 | 
						|
 | 
						|
	// basic test; unix-style env vars
 | 
						|
	p := testParser(`{$ADDRESS}`)
 | 
						|
	blocks, _ := p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Host, "servername.com"; expected != actual {
 | 
						|
		t.Errorf("Expected host to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// multiple vars per token
 | 
						|
	p = testParser(`{$ADDRESS}:{$PORT}`)
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Host, "servername.com"; expected != actual {
 | 
						|
		t.Errorf("Expected host to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Port, "8080"; expected != actual {
 | 
						|
		t.Errorf("Expected port to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// windows-style var and unix style in same token
 | 
						|
	p = testParser(`{%ADDRESS%}:{$PORT}`)
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Host, "servername.com"; expected != actual {
 | 
						|
		t.Errorf("Expected host to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Port, "8080"; expected != actual {
 | 
						|
		t.Errorf("Expected port to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// reverse order
 | 
						|
	p = testParser(`{$ADDRESS}:{%PORT%}`)
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Host, "servername.com"; expected != actual {
 | 
						|
		t.Errorf("Expected host to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Port, "8080"; expected != actual {
 | 
						|
		t.Errorf("Expected port to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// env var in server block body as argument
 | 
						|
	p = testParser(":{%PORT%}\ndir1 {$FOOBAR}")
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Port, "8080"; expected != actual {
 | 
						|
		t.Errorf("Expected port to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
	if actual, expected := blocks[0].Tokens["dir1"][1].text, "foobar"; expected != actual {
 | 
						|
		t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// combined windows env vars in argument
 | 
						|
	p = testParser(":{%PORT%}\ndir1 {%ADDRESS%}/{%FOOBAR%}")
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Tokens["dir1"][1].text, "servername.com/foobar"; expected != actual {
 | 
						|
		t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// malformed env var (windows)
 | 
						|
	p = testParser(":1234\ndir1 {%ADDRESS}")
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Tokens["dir1"][1].text, "{%ADDRESS}"; expected != actual {
 | 
						|
		t.Errorf("Expected host to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// malformed (non-existent) env var (unix)
 | 
						|
	p = testParser(`:{$PORT$}`)
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Addresses[0].Port, ""; expected != actual {
 | 
						|
		t.Errorf("Expected port to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
 | 
						|
	// in quoted field
 | 
						|
	p = testParser(":1234\ndir1 \"Test {$FOOBAR} test\"")
 | 
						|
	blocks, _ = p.parseAll()
 | 
						|
	if actual, expected := blocks[0].Tokens["dir1"][1].text, "Test foobar test"; expected != actual {
 | 
						|
		t.Errorf("Expected argument to be '%s' but was '%s'", expected, actual)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func setupParseTests() {
 | 
						|
	// Set up some bogus directives for testing
 | 
						|
	ValidDirectives = map[string]struct{}{
 | 
						|
		"dir1": {},
 | 
						|
		"dir2": {},
 | 
						|
		"dir3": {},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func testParser(input string) parser {
 | 
						|
	buf := strings.NewReader(input)
 | 
						|
	p := parser{Dispenser: NewDispenser("Test", buf), checkDirectives: true}
 | 
						|
	return p
 | 
						|
}
 |