From 4165e0ef3a751383f1dd28b13a4086e3c1eb8cba Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Thu, 22 Apr 2021 13:38:24 +0300 Subject: [PATCH] Pull request: 2574 external tests vol.4 Merge in DNS/adguard-home from 2574-external-tests-4 to master Close #2574. Squashed commit of the following: commit 0d06fce604750f76f4a319b2539105e936a248ce Author: Eugene Burkov Date: Thu Apr 22 13:26:25 2021 +0300 home: imp tests, docs commit fc7b7f13f19bb8f183522a13d5726253eaae83d0 Author: Eugene Burkov Date: Thu Apr 22 12:20:15 2021 +0300 home: fix whois test --- internal/home/home.go | 4 +- internal/home/whois.go | 9 +++- internal/home/whois_test.go | 85 +++++++++++++++++++++++++------------ 3 files changed, 69 insertions(+), 29 deletions(-) diff --git a/internal/home/home.go b/internal/home/home.go index e9542011..21c78fe9 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -640,7 +640,9 @@ func detectFirstRun() bool { return errors.Is(err, os.ErrNotExist) } -// Connect to a remote server resolving hostname using our own DNS server +// Connect to a remote server resolving hostname using our own DNS server. +// +// TODO(e.burkov): This messy logic should be decomposed and clarified. func customDialContext(ctx context.Context, network, addr string) (conn net.Conn, err error) { log.Tracef("network:%v addr:%v", network, addr) diff --git a/internal/home/whois.go b/internal/home/whois.go index 469f2e5d..7c77a903 100644 --- a/internal/home/whois.go +++ b/internal/home/whois.go @@ -27,6 +27,10 @@ type Whois struct { clients *clientsContainer ipChan chan net.IP + // dialContext specifies the dial function for creating unencrypted TCP + // connections. + dialContext func(ctx context.Context, network, addr string) (conn net.Conn, err error) + // Contains IP addresses of clients // An active IP address is resolved once again after it expires. // If IP address couldn't be resolved, it stays here for some time to prevent further attempts to resolve the same IP. @@ -45,7 +49,8 @@ func initWhois(clients *clientsContainer) *Whois { EnableLRU: true, MaxCount: 10000, }), - ipChan: make(chan net.IP, 255), + dialContext: customDialContext, + ipChan: make(chan net.IP, 255), } go w.workerLoop() @@ -124,7 +129,7 @@ func (w *Whois) query(ctx context.Context, target, serverAddr string) (string, e if addr == "whois.arin.net" { target = "n + " + target } - conn, err := customDialContext(ctx, "tcp", serverAddr) + conn, err := w.dialContext(ctx, "tcp", serverAddr) if err != nil { return "", err } diff --git a/internal/home/whois_test.go b/internal/home/whois_test.go index 550cc297..12511bbd 100644 --- a/internal/home/whois_test.go +++ b/internal/home/whois_test.go @@ -2,44 +2,77 @@ package home import ( "context" + "io" + "net" "testing" + "time" - "github.com/AdguardTeam/AdGuardHome/internal/dnsforward" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func prepareTestDNSServer(t *testing.T) { - t.Helper() - - config.DNS.Port = 1234 - - var err error - Context.dnsServer, err = dnsforward.NewServer(dnsforward.DNSCreateParams{}) - require.NoError(t, err) - - conf := &dnsforward.ServerConfig{} - conf.UpstreamDNS = []string{"8.8.8.8"} - - err = Context.dnsServer.Prepare(conf) - require.NoError(t, err) +// fakeConn is a mock implementation of net.Conn to simplify testing. +// +// TODO(e.burkov): Search for other places in code where it may be used. Move +// into aghtest then. +type fakeConn struct { + // Conn is embedded here simply to make *fakeConn a net.Conn without + // actually implementing all methods. + net.Conn + data []byte } -// TODO(e.burkov): It's kind of complicated to get rid of network access in this -// test. The thing is that *Whois creates new *net.Dialer each time it requests -// the server, so it becomes hard to simulate handling of request from test even -// with substituted upstream. However, it must be done. -func TestWhois(t *testing.T) { - prepareTestDNSServer(t) +// Write implements net.Conn interface for *fakeConn. It always returns 0 and a +// nil error without mutating the slice. +func (c *fakeConn) Write(_ []byte) (n int, err error) { + return 0, nil +} - w := Whois{timeoutMsec: 5000} - resp, err := w.queryAll(context.Background(), "8.8.8.8") +// Read implements net.Conn interface for *fakeConn. It puts the content of +// c.data field into b up to the b's capacity. +func (c *fakeConn) Read(b []byte) (n int, err error) { + return copy(b, c.data), io.EOF +} + +// Close implements net.Conn interface for *fakeConn. It always returns nil. +func (c *fakeConn) Close() (err error) { + return nil +} + +// SetReadDeadline implements net.Conn interface for *fakeConn. It always +// returns nil. +func (c *fakeConn) SetReadDeadline(_ time.Time) (err error) { + return nil +} + +// fakeDial is a mock implementation of customDialContext to simplify testing. +func (c *fakeConn) fakeDial(ctx context.Context, network, addr string) (conn net.Conn, err error) { + return c, nil +} + +func TestWhois(t *testing.T) { + const ( + nl = "\n" + data = `OrgName: FakeOrg LLC` + nl + + `City: Nonreal` + nl + + `Country: Imagiland` + nl + ) + + fc := &fakeConn{ + data: []byte(data), + } + + w := Whois{ + timeoutMsec: 5000, + dialContext: fc.fakeDial, + } + resp, err := w.queryAll(context.Background(), "1.2.3.4") assert.NoError(t, err) m := whoisParse(resp) require.NotEmpty(t, m) - assert.Equal(t, "Google LLC", m["orgname"]) - assert.Equal(t, "US", m["country"]) - assert.Equal(t, "Mountain View", m["city"]) + assert.Equal(t, "FakeOrg LLC", m["orgname"]) + assert.Equal(t, "Imagiland", m["country"]) + assert.Equal(t, "Nonreal", m["city"]) }