author | Alberto Bertogli
<albertito@blitiri.com.ar> 2016-05-22 23:11:36 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2016-05-22 23:20:48 UTC |
parent | 7372ac64587dad0d02daee305bbf5d5ed25b8e6c |
testing/grpc/grpc.go | +4 | -0 |
dnss_test.go => testing/grpc/grpc_test.go | +6 | -32 |
testing/https/https.go | +4 | -0 |
testing/https/https_test.go | +163 | -0 |
testing/util/util.go | +40 | -0 |
diff --git a/testing/grpc/grpc.go b/testing/grpc/grpc.go new file mode 100644 index 0000000..268365b --- /dev/null +++ b/testing/grpc/grpc.go @@ -0,0 +1,4 @@ +package grpc + +// Dummy file so "go build ./..." does not complain about the directory not +// having buildable files. diff --git a/dnss_test.go b/testing/grpc/grpc_test.go similarity index 84% rename from dnss_test.go rename to testing/grpc/grpc_test.go index f7ca6d6..95c3a36 100644 --- a/dnss_test.go +++ b/testing/grpc/grpc_test.go @@ -1,5 +1,5 @@ -// Tests for dnss. -package main +// Tests for dnss in GRPC modes. +package grpc import ( "crypto/rand" @@ -18,6 +18,7 @@ import ( "blitiri.com.ar/go/dnss/internal/dnstox" "blitiri.com.ar/go/dnss/internal/grpctodns" + "blitiri.com.ar/go/dnss/testing/util" "github.com/golang/glog" "github.com/miekg/dns" @@ -75,11 +76,11 @@ func manyDNSQueries(b *testing.B, addr string) { } } -func BenchmarkDirect(b *testing.B) { +func BenchmarkGRPCDirect(b *testing.B) { manyDNSQueries(b, dnsSrvAddr) } -func BenchmarkWithProxy(b *testing.B) { +func BenchmarkGRPCWithProxy(b *testing.B) { manyDNSQueries(b, dnsToGrpcAddr) } @@ -182,33 +183,6 @@ func generateCert(path string) error { return nil } -// waitForServers waits 5 seconds for the servers to start, and returns an -// error if they fail to do so. -// It does this by repeatedly querying the dns-to-grpc server until it either -// replies or times out. Note we do not do any validation of the reply. -func waitForServers() error { - conn, err := dns.DialTimeout("udp", dnsToGrpcAddr, 1*time.Second) - if err != nil { - return fmt.Errorf("dns.Dial error: %v", err) - } - defer conn.Close() - - after := time.After(5 * time.Second) - tick := time.Tick(100 * time.Millisecond) - select { - case <-after: - return fmt.Errorf("timed out") - case <-tick: - conn.SetDeadline(time.Now().Add(1 * time.Second)) - err := dnsQuery(conn) - if err == nil { - return nil - } - } - - return fmt.Errorf("not reachable") -} - // realMain is the real main function, which returns the value to pass to // os.Exit(). We have to do this so we can use defer. func realMain(m *testing.M) int { @@ -251,7 +225,7 @@ func realMain(m *testing.M) int { go dnsSrv.ListenAndServe() // Wait for the servers to start up. - err = waitForServers() + err = util.WaitForDNSServer(dnsToGrpcAddr) if err != nil { fmt.Printf("Error waiting for the test servers to start: %v\n", err) fmt.Printf("Check the INFO logs for more details\n") diff --git a/testing/https/https.go b/testing/https/https.go new file mode 100644 index 0000000..cc8acf1 --- /dev/null +++ b/testing/https/https.go @@ -0,0 +1,4 @@ +package https + +// Dummy file so "go build ./..." does not complain about the directory not +// having buildable files. diff --git a/testing/https/https_test.go b/testing/https/https_test.go new file mode 100644 index 0000000..1794d56 --- /dev/null +++ b/testing/https/https_test.go @@ -0,0 +1,163 @@ +// Tests for dnss in HTTPS mode. +package https + +import ( + "flag" + "fmt" + "net/http" + "net/http/httptest" + "os" + "testing" + + "blitiri.com.ar/go/dnss/internal/dnstox" + "blitiri.com.ar/go/dnss/testing/util" + + "github.com/golang/glog" + "github.com/miekg/dns" +) + +// +// === Tests === +// +func dnsQuery(addr string, qtype uint16) (*dns.Msg, dns.RR, error) { + m := new(dns.Msg) + m.SetQuestion(addr, qtype) + in, err := dns.Exchange(m, DNSAddr) + + if err != nil { + return nil, nil, err + } else if len(in.Answer) > 0 { + return in, in.Answer[0], nil + } else { + return in, nil, nil + } +} + +func TestSimple(t *testing.T) { + _, ans, err := dnsQuery("test.blah.", dns.TypeA) + if err != nil { + t.Errorf("dns query returned error: %v", err) + } + if ans.(*dns.A).A.String() != "1.2.3.4" { + t.Errorf("unexpected result: %q", ans) + } + + _, ans, err = dnsQuery("test.blah.", dns.TypeMX) + if err != nil { + t.Errorf("dns query returned error: %v", err) + } + if ans.(*dns.MX).Mx != "mail.test.blah." { + t.Errorf("unexpected result: %q", ans.(*dns.MX).Mx) + } + + in, _, err := dnsQuery("unknown.", dns.TypeA) + if err != nil { + t.Errorf("dns query returned error: %v", err) + } + if in.Rcode != dns.RcodeNameError { + t.Errorf("unexpected result: %q", in) + } +} + +// +// === Benchmarks === +// + +func BenchmarkHTTPSimple(b *testing.B) { + var err error + for i := 0; i < b.N; i++ { + _, _, err = dnsQuery("test.blah.", dns.TypeA) + if err != nil { + b.Errorf("dns query returned error: %v", err) + } + } +} + +// +// === Test environment === +// + +// DNSHandler handles DNS-over-HTTP requests, and returns json data. +// This is used as the test server for our resolver. +func DNSHandler(w http.ResponseWriter, r *http.Request) { + err := r.ParseForm() + if err != nil { + panic(err) + } + + w.Header().Set("Content-Type", "text/json") + + resp := jsonNXDOMAIN + + if r.Form["name"][0] == "test.blah." { + switch r.Form["type"][0] { + case "1", "A": + resp = jsonA + case "15", "MX": + resp = jsonMX + default: + resp = jsonNXDOMAIN + } + } + + w.Write([]byte(resp)) +} + +// A record. +const jsonA = ` { + "Status": 0, "TC": false, "RD": true, "RA": true, "AD": false, "CD": false, + "Question": [ { "name": "test.blah.", "type": 1 } + ], + "Answer": [ { "name": "test.blah.", "type": 1, "TTL": 21599, + "data": "1.2.3.4" } ] } +` + +// MX record. +const jsonMX = ` { + "Status": 0, "TC": false, "RD": true, "RA": true, "AD": false, "CD": false, + "Question": [ { "name": "test.blah.", "type": 15 } ], + "Answer": [ { "name": "test.blah.", "type": 15, "TTL": 21599, + "data": "10 mail.test.blah." } ] } +` + +// NXDOMAIN error. +const jsonNXDOMAIN = ` { + "Status": 3, "TC": false, "RD": true, "RA": true, "AD": true, "CD": false, + "Question": [ { "name": "doesnotexist.", "type": 15 } ], + "Authority": [ { "name": ".", "type": 6, "TTL": 1798, + "data": "root. nstld. 2016052201 1800 900 604800 86400" } ] } +` + +const ( + // TODO: don't hardcode these. + DNSAddr string = "127.0.0.1:19251" +) + +// realMain is the real main function, which returns the value to pass to +// os.Exit(). We have to do this so we can use defer. +func realMain(m *testing.M) int { + flag.Parse() + defer glog.Flush() + + // Test http server. + httpsrv := httptest.NewServer(http.HandlerFunc(DNSHandler)) + + // DNS to HTTPS server. + r := dnstox.NewHTTPSResolver(httpsrv.URL, "") + dth := dnstox.New(DNSAddr, r, "") + go dth.ListenAndServe() + + // Wait for the servers to start up. + err := util.WaitForDNSServer(DNSAddr) + if err != nil { + fmt.Printf("Error waiting for the test servers to start: %v\n", err) + fmt.Printf("Check the INFO logs for more details\n") + return 1 + } + + return m.Run() +} + +func TestMain(m *testing.M) { + os.Exit(realMain(m)) +} diff --git a/testing/util/util.go b/testing/util/util.go new file mode 100644 index 0000000..9964691 --- /dev/null +++ b/testing/util/util.go @@ -0,0 +1,40 @@ +// Package util implements common testing utilities. +package util + +import ( + "fmt" + "time" + + "github.com/miekg/dns" +) + +// WaitForDNSServer waits 5 seconds for a DNS server to start, and returns an +// error if it fails to do so. +// It does this by repeatedly querying the DNS server until it either replies +// or times out. Note we do not do any validation of the reply. +func WaitForDNSServer(addr string) error { + conn, err := dns.DialTimeout("udp", addr, 1*time.Second) + if err != nil { + return fmt.Errorf("dns.Dial error: %v", err) + } + defer conn.Close() + + m := &dns.Msg{} + m.SetQuestion("unused.", dns.TypeA) + + after := time.After(5 * time.Second) + tick := time.Tick(100 * time.Millisecond) + select { + case <-after: + return fmt.Errorf("timed out") + case <-tick: + conn.SetDeadline(time.Now().Add(1 * time.Second)) + conn.WriteMsg(m) + _, err := conn.ReadMsg() + if err == nil { + return nil + } + } + + return fmt.Errorf("not reachable") +}