Skip to content

Commit

Permalink
all: add v1 dnssvc stub; refactor tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ainar-g committed Aug 11, 2022
1 parent 14fd995 commit 8cd45ba
Show file tree
Hide file tree
Showing 12 changed files with 607 additions and 139 deletions.
14 changes: 14 additions & 0 deletions internal/aghalg/aghalg.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,20 @@ import (
"golang.org/x/exp/slices"
)

// Coalesce returns the first non-zero value. It is named after the function
// COALESCE in SQL except. If values or all it's elements are empty, it returns
// a zero value.
func Coalesce[T comparable](values ...T) (res T) {
var zero T
for _, v := range values {
if v != zero {
return v
}
}

return zero
}

// UniqChecker allows validating uniqueness of comparable items.
//
// TODO(a.garipov): The Ordered constraint is only really necessary in Validate.
Expand Down
20 changes: 0 additions & 20 deletions internal/aghtest/exchanger.go

This file was deleted.

23 changes: 0 additions & 23 deletions internal/aghtest/fswatcher.go

This file was deleted.

154 changes: 154 additions & 0 deletions internal/aghtest/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package aghtest

import (
"io/fs"
"net"

"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/miekg/dns"
)

// Interface Mocks
//
// Keep entities in this file in alphabetic order.

// Standard Library

// Package io/fs

// fs.FS

// type check
var _ fs.FS = &FS{}

// FS is a mock [fs.FS] implementation for tests.
type FS struct {
OnOpen func(name string) (fs.File, error)
}

// Open implements the [fs.FS] interface for *FS.
func (fsys *FS) Open(name string) (fs.File, error) {
return fsys.OnOpen(name)
}

// fs.GlobFS

// type check
var _ fs.GlobFS = &GlobFS{}

// GlobFS is a mock [fs.GlobFS] implementation for tests.
type GlobFS struct {
// FS is embedded here to avoid implementing all it's methods.
FS
OnGlob func(pattern string) ([]string, error)
}

// Glob implements the [fs.GlobFS] interface for *GlobFS.
func (fsys *GlobFS) Glob(pattern string) ([]string, error) {
return fsys.OnGlob(pattern)
}

// fs.StatFS

// type check
var _ fs.StatFS = &StatFS{}

// StatFS is a mock [fs.StatFS] implementation for tests.
type StatFS struct {
// FS is embedded here to avoid implementing all it's methods.
FS
OnStat func(name string) (fs.FileInfo, error)
}

// Stat implements the [fs.StatFS] interface for *StatFS.
func (fsys *StatFS) Stat(name string) (fs.FileInfo, error) {
return fsys.OnStat(name)
}

// Package net

// net.Listener

// type check
var _ net.Listener = (*Listener)(nil)

// Listener is a mock [net.Listener] implementation for tests.
type Listener struct {
OnAccept func() (conn net.Conn, err error)
OnAddr func() (addr net.Addr)
OnClose func() (err error)
}

// Accept implements the [net.Listener] interface for *Listener.
func (l *Listener) Accept() (conn net.Conn, err error) {
return l.OnAccept()
}

// Addr implements the [net.Listener] interface for *Listener.
func (l *Listener) Addr() (addr net.Addr) {
return l.OnAddr()
}

// Close implements the [net.Listener] interface for *Listener.
func (l *Listener) Close() (err error) {
return l.OnClose()
}

// Module dnsproxy

// Package upstream

// upstream.Upstream

// type check
var _ upstream.Upstream = (*UpstreamMock)(nil)

// UpstreamMock is a mock [upstream.Upstream] implementation for tests.
//
// TODO(a.garipov): Replace with all uses of Upstream with UpstreamMock and
// rename it to just Upstream.
type UpstreamMock struct {
OnAddress func() (addr string)
OnExchange func(req *dns.Msg) (resp *dns.Msg, err error)
}

// Address implements the [upstream.Upstream] interface for *UpstreamMock.
func (u *UpstreamMock) Address() (addr string) {
return u.OnAddress()
}

// Exchange implements the [upstream.Upstream] interface for *UpstreamMock.
func (u *UpstreamMock) Exchange(req *dns.Msg) (resp *dns.Msg, err error) {
return u.OnExchange(req)
}

// Module AdGuardHome

// Package aghos

// aghos.FSWatcher

// Keep the type check for *FSWatcher in interface_test.go to prevent an import
// cycle.

// FSWatcher is a mock [aghos.FSWatcher] implementation for tests.
type FSWatcher struct {
OnEvents func() (e <-chan struct{})
OnAdd func(name string) (err error)
OnClose func() (err error)
}

// Events implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Events() (e <-chan struct{}) {
return w.OnEvents()
}

// Add implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Add(name string) (err error) {
return w.OnAdd(name)
}

// Close implements the [aghos.FSWatcher] interface for *FSWatcher.
func (w *FSWatcher) Close() (err error) {
return w.OnClose()
}
9 changes: 9 additions & 0 deletions internal/aghtest/interface_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package aghtest_test

import (
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
)

// type check
var _ aghos.FSWatcher = (*aghtest.FSWatcher)(nil)
46 changes: 0 additions & 46 deletions internal/aghtest/testfs.go

This file was deleted.

74 changes: 60 additions & 14 deletions internal/aghtest/upstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,18 @@ import (
"net"
"strings"
"sync"
"testing"

"github.com/AdguardTeam/golibs/errors"
"github.com/miekg/dns"
"github.com/stretchr/testify/require"
)

// Additional Upstream Testing Utilities

// Upstream is a mock implementation of upstream.Upstream.
//
// TODO(a.garipov): Replace with UpstreamMock and rename it to just Upstream.
type Upstream struct {
// CName is a map of hostname to canonical name.
CName map[string][]string
Expand All @@ -25,6 +32,43 @@ type Upstream struct {
Addr string
}

// RespondTo returns a response with answer if req has class cl, question type
// qt, and target targ.
func RespondTo(t testing.TB, req *dns.Msg, cl, qt uint16, targ, answer string) (resp *dns.Msg) {
t.Helper()

require.NotNil(t, req)
require.Len(t, req.Question, 1)

q := req.Question[0]
targ = dns.Fqdn(targ)
if q.Qclass != cl || q.Qtype != qt || q.Name != targ {
return nil
}

respHdr := dns.RR_Header{
Name: targ,
Rrtype: qt,
Class: cl,
Ttl: 60,
}

resp = new(dns.Msg).SetReply(req)
switch qt {
case dns.TypePTR:
resp.Answer = []dns.RR{
&dns.PTR{
Hdr: respHdr,
Ptr: answer,
},
}
default:
t.Fatalf("unsupported question type: %s", dns.Type(qt).String())
}

return resp
}

// Exchange implements the upstream.Upstream interface for *Upstream.
//
// TODO(a.garipov): Split further into handlers.
Expand Down Expand Up @@ -78,6 +122,8 @@ func (u *Upstream) Address() string {

// TestBlockUpstream implements upstream.Upstream interface for replacing real
// upstream in tests.
//
// TODO(a.garipov): Replace with UpstreamMock.
type TestBlockUpstream struct {
Hostname string

Expand Down Expand Up @@ -131,19 +177,19 @@ func (u *TestBlockUpstream) RequestsCount() int {
return u.reqNum
}

// TestErrUpstream implements upstream.Upstream interface for replacing real
// upstream in tests.
type TestErrUpstream struct {
// The error returned by Exchange may be unwrapped to the Err.
Err error
}

// Exchange always returns nil Msg and non-nil error.
func (u *TestErrUpstream) Exchange(*dns.Msg) (*dns.Msg, error) {
return nil, fmt.Errorf("errupstream: %w", u.Err)
}
// ErrUpstream is the error returned from the [*UpstreamMock] created by
// [NewErrorUpstream].
const ErrUpstream errors.Error = "test upstream error"

// Address always returns an empty string.
func (u *TestErrUpstream) Address() string {
return ""
// NewErrorUpstream returns an [*UpstreamMock] that returns [ErrUpstream] from
// its Exchange method.
func NewErrorUpstream() (u *UpstreamMock) {
return &UpstreamMock{
OnAddress: func() (addr string) {
return "error.upstream.example"
},
OnExchange: func(_ *dns.Msg) (resp *dns.Msg, err error) {
return nil, errors.Error("test upstream error")
},
}
}
Loading

0 comments on commit 8cd45ba

Please sign in to comment.