mirror of
https://github.com/AdguardTeam/AdGuardHome.git
synced 2025-12-20 01:11:03 +08:00
dhcpsvc: add tests
This commit is contained in:
@@ -121,7 +121,7 @@ func TestConfig_Validate(t *testing.T) {
|
||||
}, {
|
||||
conf: &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
Logger: testLogger,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
||||
"eth0": {
|
||||
@@ -136,7 +136,7 @@ func TestConfig_Validate(t *testing.T) {
|
||||
}, {
|
||||
conf: &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
Logger: testLogger,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
||||
"eth0": {
|
||||
@@ -151,7 +151,7 @@ func TestConfig_Validate(t *testing.T) {
|
||||
}, {
|
||||
conf: &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
Logger: testLogger,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
||||
"eth0": {
|
||||
@@ -167,7 +167,7 @@ func TestConfig_Validate(t *testing.T) {
|
||||
}, {
|
||||
conf: &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
Logger: testLogger,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: map[string]*dhcpsvc.InterfaceConfig{
|
||||
"eth0": {
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
package dhcpsvc_test
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"io/fs"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||
"github.com/AdguardTeam/golibs/testutil"
|
||||
"github.com/AdguardTeam/golibs/timeutil"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testLocalTLD is a common local TLD for tests.
|
||||
@@ -15,8 +23,11 @@ const testLocalTLD = "local"
|
||||
// testTimeout is a common timeout for tests and contexts.
|
||||
const testTimeout time.Duration = 10 * time.Second
|
||||
|
||||
// discardLog is a logger to discard test output.
|
||||
var discardLog = slogutil.NewDiscardLogger()
|
||||
// testLogger is a common logger for tests.
|
||||
var testLogger = slogutil.NewDiscardLogger()
|
||||
|
||||
// testdata is a filesystem containing data for tests.
|
||||
var testdata = os.DirFS("testdata")
|
||||
|
||||
// testInterfaceConf is a common set of interface configurations for tests.
|
||||
var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{
|
||||
@@ -57,3 +68,50 @@ var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// newTempDB copies the leases database file located in the testdata FS, under
|
||||
// tb.Name()/leases.json, to a temporary directory and returns the path to the
|
||||
// copied file.
|
||||
func newTempDB(tb testing.TB) (dst string) {
|
||||
tb.Helper()
|
||||
|
||||
const filename = "leases.json"
|
||||
|
||||
data, err := fs.ReadFile(testdata, path.Join(tb.Name(), filename))
|
||||
require.NoError(tb, err)
|
||||
|
||||
dst = filepath.Join(tb.TempDir(), filename)
|
||||
|
||||
err = os.WriteFile(dst, data, dhcpsvc.DatabasePerm)
|
||||
require.NoError(tb, err)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
// newTestDHCPServer creates a new DHCPServer for testing. It uses the default
|
||||
// values of config in case it's nil or some of its fields aren't set.
|
||||
func newTestDHCPServer(t *testing.T, conf *dhcpsvc.Config) (srv *dhcpsvc.DHCPServer) {
|
||||
t.Helper()
|
||||
|
||||
conf = cmp.Or(conf, &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
})
|
||||
if conf.Interfaces == nil {
|
||||
conf.Interfaces = testInterfaceConf
|
||||
}
|
||||
conf.NetworkDeviceManager = cmp.Or[dhcpsvc.NetworkDeviceManager](
|
||||
conf.NetworkDeviceManager,
|
||||
dhcpsvc.EmptyNetworkDeviceManager{},
|
||||
)
|
||||
conf.Logger = cmp.Or(conf.Logger, testLogger)
|
||||
conf.LocalDomainName = cmp.Or(conf.LocalDomainName, testLocalTLD)
|
||||
if conf.DBFilePath == "" {
|
||||
conf.DBFilePath = filepath.Join(t.TempDir(), "leases.json")
|
||||
}
|
||||
conf.ICMPTimeout = cmp.Or(conf.ICMPTimeout, testTimeout)
|
||||
|
||||
srv, err := dhcpsvc.New(testutil.ContextWithTimeout(t, testTimeout), conf)
|
||||
require.NoError(t, err)
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
41
internal/dhcpsvc/handler4_test.go
Normal file
41
internal/dhcpsvc/handler4_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package dhcpsvc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/AdguardTeam/golibs/testutil/servicetest"
|
||||
)
|
||||
|
||||
func TestDHCPServer_ServeEther4(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ndMgr := &testNetworkDeviceManager{
|
||||
onOpen: func(
|
||||
ctx context.Context,
|
||||
conf *dhcpsvc.NetworkDeviceConfig,
|
||||
) (nd dhcpsvc.NetworkDevice, err error) {
|
||||
return &testNetworkDevice{
|
||||
// TODO(e.burkov): !! implement ReadPacketData, WritePacketData, and LinkType
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
NetworkDeviceManager: ndMgr,
|
||||
Enabled: true,
|
||||
})
|
||||
servicetest.RequireRun(t, srv, testTimeout)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
// TODO(e.burkov): !! define other fields.
|
||||
}{}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// TODO(e.burkov): !! implement a test
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,22 @@ type NetworkDeviceManager interface {
|
||||
Open(ctx context.Context, conf *NetworkDeviceConfig) (dev NetworkDevice, err error)
|
||||
}
|
||||
|
||||
// EmptyNetworkDeviceManager is an empty implementation of
|
||||
// [NetworkDeviceManager].
|
||||
type EmptyNetworkDeviceManager struct{}
|
||||
|
||||
// type check
|
||||
var _ NetworkDeviceManager = EmptyNetworkDeviceManager{}
|
||||
|
||||
// Open implements the [NetworkDeviceManager] interface for
|
||||
// [EmptyNetworkDeviceManager]. It always returns [EmptyNetworkDevice].
|
||||
func (EmptyNetworkDeviceManager) Open(
|
||||
_ context.Context,
|
||||
_ *NetworkDeviceConfig,
|
||||
) (nd NetworkDevice, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NetworkDevice provides reading and writing packets to a network interface.
|
||||
type NetworkDevice interface {
|
||||
gopacket.PacketDataSource
|
||||
@@ -45,6 +61,31 @@ type NetworkDevice interface {
|
||||
WritePacketData(data []byte) (err error)
|
||||
}
|
||||
|
||||
// EmptyNetworkDevice is an empty implementation of NetworkDevice.
|
||||
type EmptyNetworkDevice struct{}
|
||||
|
||||
// type check
|
||||
var _ NetworkDevice = EmptyNetworkDevice{}
|
||||
|
||||
// ReadPacketData implements the [gopacket.PacketDataSource] interface for
|
||||
// [EmptyNetworkDevice]. It always returns no data, empty capture info and a
|
||||
// nil error.
|
||||
func (EmptyNetworkDevice) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
return nil, gopacket.CaptureInfo{}, nil
|
||||
}
|
||||
|
||||
// LinkType implements the [NetworkDevice] interface for [EmptyNetworkDevice].
|
||||
// It always returns [layers.LinkTypeNull].
|
||||
func (EmptyNetworkDevice) LinkType() (lt layers.LinkType) {
|
||||
return layers.LinkTypeNull
|
||||
}
|
||||
|
||||
// WritePacketData implements the [NetworkDevice] interface for
|
||||
// [EmptyNetworkDevice]. It always returns nil.
|
||||
func (EmptyNetworkDevice) WritePacketData(_ []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// frameData stores the Ethernet and IPv4 layers of the incoming packet, and
|
||||
// the network device that the packet was received from.
|
||||
type frameData struct {
|
||||
|
||||
57
internal/dhcpsvc/networkdevice_test.go
Normal file
57
internal/dhcpsvc/networkdevice_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package dhcpsvc_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||
"github.com/google/gopacket"
|
||||
"github.com/google/gopacket/layers"
|
||||
)
|
||||
|
||||
// testNetworkDeviceManager is a mock implementation of the
|
||||
// [dhcpsvc.NetworkDeviceManager] interface.
|
||||
//
|
||||
// TODO(e.burkov): Move to aghtest.
|
||||
type testNetworkDeviceManager struct {
|
||||
onOpen func(
|
||||
ctx context.Context,
|
||||
conf *dhcpsvc.NetworkDeviceConfig,
|
||||
) (nd dhcpsvc.NetworkDevice, err error)
|
||||
}
|
||||
|
||||
// Open implements the [NetworkDeviceManager] interface for
|
||||
// *testNetworkDeviceManager.
|
||||
func (ndm *testNetworkDeviceManager) Open(
|
||||
ctx context.Context,
|
||||
conf *dhcpsvc.NetworkDeviceConfig,
|
||||
) (dev dhcpsvc.NetworkDevice, err error) {
|
||||
return ndm.onOpen(ctx, conf)
|
||||
}
|
||||
|
||||
// testNetworkDevice is a mock implementation of the [dhcpsvc.NetworkDevice]
|
||||
// interface.
|
||||
//
|
||||
// TODO(e.burkov): Move to aghtest.
|
||||
type testNetworkDevice struct {
|
||||
onReadPacketData func() (data []byte, ci gopacket.CaptureInfo, err error)
|
||||
onLinkType func() (lt layers.LinkType)
|
||||
onWritePacketData func(data []byte) (err error)
|
||||
}
|
||||
|
||||
// ReadPacketData implements the [dhcpsvc.NetworkDevice] interface for
|
||||
// *testNetworkDevice.
|
||||
func (nd *testNetworkDevice) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
|
||||
return nd.onReadPacketData()
|
||||
}
|
||||
|
||||
// WritePacketData implements the [dhcpsvc.NetworkDevice] interface for
|
||||
// *testNetworkDevice.
|
||||
func (nd *testNetworkDevice) WritePacketData(data []byte) (err error) {
|
||||
return nd.onWritePacketData(data)
|
||||
}
|
||||
|
||||
// LinkType implements the [dhcpsvc.NetworkDevice] interface for
|
||||
// *testNetworkDevice.
|
||||
func (nd *testNetworkDevice) LinkType() (lt layers.LinkType) {
|
||||
return nd.onLinkType()
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
package dhcpsvc_test
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"net"
|
||||
"net/netip"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -18,40 +15,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// testdata is a filesystem containing data for tests.
|
||||
var testdata = os.DirFS("testdata")
|
||||
|
||||
// newTempDB copies the leases database file located in the testdata FS, under
|
||||
// tb.Name()/leases.json, to a temporary directory and returns the path to the
|
||||
// copied file.
|
||||
func newTempDB(tb testing.TB) (dst string) {
|
||||
tb.Helper()
|
||||
|
||||
const filename = "leases.json"
|
||||
|
||||
data, err := fs.ReadFile(testdata, path.Join(tb.Name(), filename))
|
||||
require.NoError(tb, err)
|
||||
|
||||
dst = filepath.Join(tb.TempDir(), filename)
|
||||
|
||||
err = os.WriteFile(dst, data, dhcpsvc.DatabasePerm)
|
||||
require.NoError(tb, err)
|
||||
|
||||
return dst
|
||||
}
|
||||
|
||||
func TestDHCPServer_AddLease(t *testing.T) {
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
leasesPath := filepath.Join(t.TempDir(), "leases.json")
|
||||
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: testInterfaceConf,
|
||||
DBFilePath: leasesPath,
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
DBFilePath: leasesPath,
|
||||
Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
const (
|
||||
existHost = "host1"
|
||||
@@ -69,6 +38,7 @@ func TestDHCPServer_AddLease(t *testing.T) {
|
||||
ipv6MAC = errors.Must(net.ParseMAC("02:03:04:05:06:07"))
|
||||
)
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
require.NoError(t, srv.AddLease(ctx, &dhcpsvc.Lease{
|
||||
Hostname: existHost,
|
||||
IP: existIP,
|
||||
@@ -144,6 +114,7 @@ func TestDHCPServer_AddLease(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx = testutil.ContextWithTimeout(t, testTimeout)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.AddLease(ctx, tc.lease))
|
||||
})
|
||||
}
|
||||
@@ -153,17 +124,11 @@ func TestDHCPServer_AddLease(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDHCPServer_index(t *testing.T) {
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
leasesPath := newTempDB(t)
|
||||
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: testInterfaceConf,
|
||||
DBFilePath: leasesPath,
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
DBFilePath: leasesPath,
|
||||
Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
const (
|
||||
host1 = "host1"
|
||||
@@ -210,17 +175,11 @@ func TestDHCPServer_index(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDHCPServer_UpdateStaticLease(t *testing.T) {
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
leasesPath := newTempDB(t)
|
||||
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: testInterfaceConf,
|
||||
DBFilePath: leasesPath,
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
DBFilePath: leasesPath,
|
||||
Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
const (
|
||||
host1 = "host1"
|
||||
@@ -309,6 +268,7 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.UpdateStaticLease(ctx, tc.lease))
|
||||
})
|
||||
}
|
||||
@@ -317,17 +277,11 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDHCPServer_RemoveLease(t *testing.T) {
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
leasesPath := newTempDB(t)
|
||||
srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: testInterfaceConf,
|
||||
DBFilePath: leasesPath,
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
DBFilePath: leasesPath,
|
||||
Enabled: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
const (
|
||||
host1 = "host1"
|
||||
@@ -393,6 +347,7 @@ func TestDHCPServer_RemoveLease(t *testing.T) {
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, srv.RemoveLease(ctx, tc.lease))
|
||||
})
|
||||
}
|
||||
@@ -403,22 +358,16 @@ func TestDHCPServer_RemoveLease(t *testing.T) {
|
||||
|
||||
func TestDHCPServer_Reset(t *testing.T) {
|
||||
leasesPath := newTempDB(t)
|
||||
conf := &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: testInterfaceConf,
|
||||
DBFilePath: leasesPath,
|
||||
}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
srv, err := dhcpsvc.New(ctx, conf)
|
||||
require.NoError(t, err)
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
DBFilePath: leasesPath,
|
||||
Enabled: true,
|
||||
})
|
||||
|
||||
const leasesNum = 4
|
||||
|
||||
require.Len(t, srv.Leases(), leasesNum)
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
require.NoError(t, srv.Reset(ctx))
|
||||
|
||||
assert.FileExists(t, leasesPath)
|
||||
@@ -427,18 +376,10 @@ func TestDHCPServer_Reset(t *testing.T) {
|
||||
|
||||
func TestServer_Leases(t *testing.T) {
|
||||
leasesPath := newTempDB(t)
|
||||
conf := &dhcpsvc.Config{
|
||||
Enabled: true,
|
||||
Logger: discardLog,
|
||||
LocalDomainName: testLocalTLD,
|
||||
Interfaces: testInterfaceConf,
|
||||
DBFilePath: leasesPath,
|
||||
}
|
||||
|
||||
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||
|
||||
srv, err := dhcpsvc.New(ctx, conf)
|
||||
require.NoError(t, err)
|
||||
srv := newTestDHCPServer(t, &dhcpsvc.Config{
|
||||
DBFilePath: leasesPath,
|
||||
Enabled: true,
|
||||
})
|
||||
|
||||
expiry, err := time.Parse(time.RFC3339, "2042-01-02T03:04:05Z")
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user