From 083da3671320f7774db9c5b854e663162da9d214 Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Tue, 9 Jul 2024 15:28:52 +0300 Subject: [PATCH] dhcpsvc: imp code, tests --- internal/dhcpsvc/config.go | 2 + internal/dhcpsvc/db.go | 43 ++- internal/dhcpsvc/db_internal_test.go | 4 + internal/dhcpsvc/db_test.go | 55 ---- internal/dhcpsvc/dhcpsvc_test.go | 66 +++++ internal/dhcpsvc/server.go | 5 +- internal/dhcpsvc/server_test.go | 266 +++++++++--------- .../leases.json | 5 + 8 files changed, 227 insertions(+), 219 deletions(-) create mode 100644 internal/dhcpsvc/db_internal_test.go delete mode 100644 internal/dhcpsvc/db_test.go create mode 100644 internal/dhcpsvc/dhcpsvc_test.go rename internal/dhcpsvc/testdata/{TestServer_loadDatabase => TestServer_Leases}/leases.json (60%) diff --git a/internal/dhcpsvc/config.go b/internal/dhcpsvc/config.go index 2a35a977..464c497d 100644 --- a/internal/dhcpsvc/config.go +++ b/internal/dhcpsvc/config.go @@ -66,6 +66,8 @@ func (conf *Config) Validate() (err error) { errs = append(errs, err) } + // This is a best-effort check for the file accessibility. The file will be + // checked again when it is opened later. if _, err = os.Stat(conf.DBFilePath); err != nil && !errors.Is(err, os.ErrNotExist) { errs = append(errs, fmt.Errorf("db file path %q: %w", conf.DBFilePath, err)) } diff --git a/internal/dhcpsvc/db.go b/internal/dhcpsvc/db.go index 5fb4b23f..b247e653 100644 --- a/internal/dhcpsvc/db.go +++ b/internal/dhcpsvc/db.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "io/fs" "net" "net/netip" "os" @@ -19,6 +20,9 @@ import ( // dataVersion is the current version of the stored DHCP leases structure. const dataVersion = 1 +// databasePerm is the permissions for the database file. +const databasePerm fs.FileMode = 0o640 + // dataLeases is the structure of the stored DHCP leases. type dataLeases struct { // Leases is the list containing stored DHCP leases. @@ -43,8 +47,8 @@ func (dl *dbLease) compareNames(other *dbLease) (res int) { return strings.Compare(dl.Hostname, other.Hostname) } -// fromLease converts *Lease to *dbLease. -func fromLease(l *Lease) (dl *dbLease) { +// toDBLease converts *Lease to *dbLease. +func toDBLease(l *Lease) (dl *dbLease) { var expiryStr string if !l.IsStatic { // The front-end is waiting for RFC 3999 format of the time value. It @@ -63,8 +67,8 @@ func fromLease(l *Lease) (dl *dbLease) { } } -// toLease converts dl to *Lease. -func (dl *dbLease) toLease() (l *Lease, err error) { +// toInternal converts dl to *Lease. +func (dl *dbLease) toInternal() (l *Lease, err error) { mac, err := net.ParseMAC(dl.HWAddr) if err != nil { return nil, fmt.Errorf("parsing hardware address: %w", err) @@ -117,47 +121,38 @@ func (srv *DHCPServer) dbLoad(ctx context.Context) (err error) { // addDBLeases adds leases to the server. func (srv *DHCPServer) addDBLeases(ctx context.Context, leases []*dbLease) { - const logMsg = "loading lease" - var v4, v6 uint for i, l := range leases { - var lease *Lease - lease, err := l.toLease() + lease, err := l.toInternal() if err != nil { - srv.logger.DebugContext(ctx, logMsg, "idx", i, slogutil.KeyError, err) + srv.logger.WarnContext(ctx, "converting lease", "idx", i, slogutil.KeyError, err) continue } - addr := l.IP - iface, err := srv.ifaceForAddr(addr) + iface, err := srv.ifaceForAddr(l.IP) if err != nil { - srv.logger.DebugContext(ctx, logMsg, "idx", i, slogutil.KeyError, err) + srv.logger.WarnContext(ctx, "searching lease iface", "idx", i, slogutil.KeyError, err) continue } err = srv.leases.add(lease, iface) if err != nil { - srv.logger.DebugContext(ctx, logMsg, "idx", i, slogutil.KeyError, err) + srv.logger.WarnContext(ctx, "adding lease", "idx", i, slogutil.KeyError, err) continue } - if lease.IP.Is4() { + if l.IP.Is4() { v4++ } else { v6++ } } - srv.logger.InfoContext( - ctx, - "loaded leases", - "v4", v4, - "v6", v6, - "total", len(leases), - ) + // TODO(e.burkov): Group by interface. + srv.logger.InfoContext(ctx, "loaded leases", "v4", v4, "v6", v6, "total", len(leases)) } // writeDB writes leases to the database file. It expects the @@ -166,13 +161,13 @@ func (srv *DHCPServer) dbStore(ctx context.Context) (err error) { defer func() { err = errors.Annotate(err, "writing db: %w") }() dl := &dataLeases{ - // Avoid writing "null" into the database file if there are no leases. + // Avoid writing null into the database file if there are no leases. Leases: make([]*dbLease, 0, srv.leases.len()), Version: dataVersion, } srv.leases.rangeLeases(func(l *Lease) (cont bool) { - lease := fromLease(l) + lease := toDBLease(l) i, _ := slices.BinarySearchFunc(dl.Leases, lease, (*dbLease).compareNames) dl.Leases = slices.Insert(dl.Leases, i, lease) @@ -185,7 +180,7 @@ func (srv *DHCPServer) dbStore(ctx context.Context) (err error) { return err } - err = maybe.WriteFile(srv.dbFilePath, buf, 0o644) + err = maybe.WriteFile(srv.dbFilePath, buf, databasePerm) if err != nil { // Don't wrap the error since it's informative enough as is. return err diff --git a/internal/dhcpsvc/db_internal_test.go b/internal/dhcpsvc/db_internal_test.go new file mode 100644 index 00000000..aa47c0da --- /dev/null +++ b/internal/dhcpsvc/db_internal_test.go @@ -0,0 +1,4 @@ +package dhcpsvc + +// DatabasePerm is the permissions for the test database file. +const DatabasePerm = databasePerm diff --git a/internal/dhcpsvc/db_test.go b/internal/dhcpsvc/db_test.go deleted file mode 100644 index f3a94766..00000000 --- a/internal/dhcpsvc/db_test.go +++ /dev/null @@ -1,55 +0,0 @@ -package dhcpsvc_test - -import ( - "net/netip" - "path/filepath" - "testing" - "time" - - "github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc" - "github.com/AdguardTeam/golibs/testutil" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestServer_loadDatabase(t *testing.T) { - leasesPath := filepath.Join("testdata", t.Name(), "leases.json") - - ipv4Conf := &dhcpsvc.IPv4Config{ - Enabled: true, - GatewayIP: netip.MustParseAddr("192.168.0.1"), - SubnetMask: netip.MustParseAddr("255.255.255.0"), - RangeStart: netip.MustParseAddr("192.168.0.2"), - RangeEnd: netip.MustParseAddr("192.168.0.254"), - LeaseDuration: 1 * time.Hour, - } - conf := &dhcpsvc.Config{ - Enabled: true, - LocalDomainName: "local", - Interfaces: map[string]*dhcpsvc.InterfaceConfig{ - "eth0": { - IPv4: ipv4Conf, - IPv6: &dhcpsvc.IPv6Config{Enabled: false}, - }, - }, - DBFilePath: leasesPath, - Logger: discardLog, - } - - ctx := testutil.ContextWithTimeout(t, testTimeout) - - srv, err := dhcpsvc.New(ctx, conf) - require.NoError(t, err) - - expiry, err := time.Parse(time.RFC3339, "2042-01-02T03:04:05Z") - require.NoError(t, err) - - wantLeases := []*dhcpsvc.Lease{{ - Expiry: expiry, - IP: netip.MustParseAddr("192.168.0.3"), - Hostname: "example.host", - HWAddr: mustParseMAC(t, "AA:AA:AA:AA:AA:AA"), - IsStatic: false, - }} - assert.Equal(t, wantLeases, srv.Leases()) -} diff --git a/internal/dhcpsvc/dhcpsvc_test.go b/internal/dhcpsvc/dhcpsvc_test.go new file mode 100644 index 00000000..f8b993f6 --- /dev/null +++ b/internal/dhcpsvc/dhcpsvc_test.go @@ -0,0 +1,66 @@ +package dhcpsvc_test + +import ( + "net" + "net/netip" + "time" + + "github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc" + "github.com/AdguardTeam/golibs/logutil/slogutil" + "github.com/stretchr/testify/require" +) + +// testLocalTLD is a common local TLD for tests. +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() + +// testInterfaceConf is a common set of interface configurations for tests. +var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{ + "eth0": { + IPv4: &dhcpsvc.IPv4Config{ + Enabled: true, + GatewayIP: netip.MustParseAddr("192.168.0.1"), + SubnetMask: netip.MustParseAddr("255.255.255.0"), + RangeStart: netip.MustParseAddr("192.168.0.2"), + RangeEnd: netip.MustParseAddr("192.168.0.254"), + LeaseDuration: 1 * time.Hour, + }, + IPv6: &dhcpsvc.IPv6Config{ + Enabled: true, + RangeStart: netip.MustParseAddr("2001:db8::1"), + LeaseDuration: 1 * time.Hour, + RAAllowSLAAC: true, + RASLAACOnly: true, + }, + }, + "eth1": { + IPv4: &dhcpsvc.IPv4Config{ + Enabled: true, + GatewayIP: netip.MustParseAddr("172.16.0.1"), + SubnetMask: netip.MustParseAddr("255.255.255.0"), + RangeStart: netip.MustParseAddr("172.16.0.2"), + RangeEnd: netip.MustParseAddr("172.16.0.255"), + LeaseDuration: 1 * time.Hour, + }, + IPv6: &dhcpsvc.IPv6Config{ + Enabled: true, + RangeStart: netip.MustParseAddr("2001:db9::1"), + LeaseDuration: 1 * time.Hour, + RAAllowSLAAC: true, + RASLAACOnly: true, + }, + }, +} + +// mustParseMAC parses a hardware address from s and requires no errors. +func mustParseMAC(t require.TestingT, s string) (mac net.HardwareAddr) { + mac, err := net.ParseMAC(s) + require.NoError(t, err) + + return mac +} diff --git a/internal/dhcpsvc/server.go b/internal/dhcpsvc/server.go index 43c1bfe7..c9c02979 100644 --- a/internal/dhcpsvc/server.go +++ b/internal/dhcpsvc/server.go @@ -29,8 +29,9 @@ type DHCPServer struct { // dbFilePath is the path to the database file containing the DHCP leases. // - // TODO(e.burkov): Perhaps, extract leases and database into a separate - // type. + // TODO(e.burkov): Consider extracting the database logic into a separate + // interface to prevent packages that only need lease data from depending on + // the entire server and to simplify testing. dbFilePath string // leasesMu protects the leases index as well as leases in the interfaces. diff --git a/internal/dhcpsvc/server_test.go b/internal/dhcpsvc/server_test.go index 7ee1a44a..0166a9b7 100644 --- a/internal/dhcpsvc/server_test.go +++ b/internal/dhcpsvc/server_test.go @@ -2,7 +2,6 @@ package dhcpsvc_test import ( "io/fs" - "net" "net/netip" "os" "path/filepath" @@ -11,84 +10,33 @@ import ( "time" "github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc" - "github.com/AdguardTeam/golibs/logutil/slogutil" "github.com/AdguardTeam/golibs/testutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -// testLocalTLD is a common local TLD for tests. -const testLocalTLD = "local" +// testdata is a filesystem containing data for tests. +var testdata = os.DirFS("testdata") -// testTimeout is a common timeout for tests and contexts. -const testTimeout time.Duration = 10 * time.Second +// newTempDB copies the leases database file located in the testdata FS, under +// tb.Name()/leases.db, to a temporary directory and returns the path to the +// copied file. +func newTempDB(tb testing.TB) (dst string) { + tb.Helper() -// discardLog is a logger to discard test output. -var discardLog = slogutil.NewDiscardLogger() + const filename = "leases.json" -// copyDB copies the leases database file located in the testdata directory, -// under tb.Name() directory, to a temporary directory and returns the path to -// the copied file. -func copyDB(tb testing.TB) (dst string) { - testdata := os.DirFS("testdata") - - data, err := fs.ReadFile(testdata, filepath.Join(tb.Name(), "leases.json")) + data, err := fs.ReadFile(testdata, filepath.Join(tb.Name(), filename)) require.NoError(tb, err) - dst = filepath.Join(tb.TempDir(), "leases.json") + dst = filepath.Join(tb.TempDir(), filename) - err = os.WriteFile(dst, data, 0o644) + err = os.WriteFile(dst, data, dhcpsvc.DatabasePerm) require.NoError(tb, err) return dst } -// testInterfaceConf is a common set of interface configurations for tests. -var testInterfaceConf = map[string]*dhcpsvc.InterfaceConfig{ - "eth0": { - IPv4: &dhcpsvc.IPv4Config{ - Enabled: true, - GatewayIP: netip.MustParseAddr("192.168.0.1"), - SubnetMask: netip.MustParseAddr("255.255.255.0"), - RangeStart: netip.MustParseAddr("192.168.0.2"), - RangeEnd: netip.MustParseAddr("192.168.0.254"), - LeaseDuration: 1 * time.Hour, - }, - IPv6: &dhcpsvc.IPv6Config{ - Enabled: true, - RangeStart: netip.MustParseAddr("2001:db8::1"), - LeaseDuration: 1 * time.Hour, - RAAllowSLAAC: true, - RASLAACOnly: true, - }, - }, - "eth1": { - IPv4: &dhcpsvc.IPv4Config{ - Enabled: true, - GatewayIP: netip.MustParseAddr("172.16.0.1"), - SubnetMask: netip.MustParseAddr("255.255.255.0"), - RangeStart: netip.MustParseAddr("172.16.0.2"), - RangeEnd: netip.MustParseAddr("172.16.0.255"), - LeaseDuration: 1 * time.Hour, - }, - IPv6: &dhcpsvc.IPv6Config{ - Enabled: true, - RangeStart: netip.MustParseAddr("2001:db9::1"), - LeaseDuration: 1 * time.Hour, - RAAllowSLAAC: true, - RASLAACOnly: true, - }, - }, -} - -// mustParseMAC parses a hardware address from s and requires no errors. -func mustParseMAC(t require.TestingT, s string) (mac net.HardwareAddr) { - mac, err := net.ParseMAC(s) - require.NoError(t, err) - - return mac -} - func TestNew(t *testing.T) { validIPv4Conf := &dhcpsvc.IPv4Config{ Enabled: true, @@ -217,23 +165,25 @@ func TestDHCPServer_AddLease(t *testing.T) { require.NoError(t, err) const ( - host1 = "host1" - host2 = "host2" - host3 = "host3" + existHost = "host1" + newHost = "host2" + ipv6Host = "host3" ) - ip1 := netip.MustParseAddr("192.168.0.2") - ip2 := netip.MustParseAddr("192.168.0.3") - ip3 := netip.MustParseAddr("2001:db8::2") + var ( + existIP = netip.MustParseAddr("192.168.0.2") + newIP = netip.MustParseAddr("192.168.0.3") + newIPv6 = netip.MustParseAddr("2001:db8::2") - mac1 := mustParseMAC(t, "01:02:03:04:05:06") - mac2 := mustParseMAC(t, "06:05:04:03:02:01") - mac3 := mustParseMAC(t, "02:03:04:05:06:07") + existMAC = mustParseMAC(t, "01:02:03:04:05:06") + newMAC = mustParseMAC(t, "06:05:04:03:02:01") + ipv6MAC = mustParseMAC(t, "02:03:04:05:06:07") + ) require.NoError(t, srv.AddLease(ctx, &dhcpsvc.Lease{ - Hostname: host1, - IP: ip1, - HWAddr: mac1, + Hostname: existHost, + IP: existIP, + HWAddr: existMAC, IsStatic: true, })) @@ -244,61 +194,61 @@ func TestDHCPServer_AddLease(t *testing.T) { }{{ name: "outside_range", lease: &dhcpsvc.Lease{ - Hostname: host2, + Hostname: newHost, IP: netip.MustParseAddr("1.2.3.4"), - HWAddr: mac2, + HWAddr: newMAC, }, wantErrMsg: "adding lease: no interface for ip 1.2.3.4", }, { name: "duplicate_ip", lease: &dhcpsvc.Lease{ - Hostname: host2, - IP: ip1, - HWAddr: mac2, + Hostname: newHost, + IP: existIP, + HWAddr: newMAC, }, - wantErrMsg: "adding lease: lease for ip " + ip1.String() + + wantErrMsg: "adding lease: lease for ip " + existIP.String() + " already exists", }, { name: "duplicate_hostname", lease: &dhcpsvc.Lease{ - Hostname: host1, - IP: ip2, - HWAddr: mac2, + Hostname: existHost, + IP: newIP, + HWAddr: newMAC, }, - wantErrMsg: "adding lease: lease for hostname " + host1 + + wantErrMsg: "adding lease: lease for hostname " + existHost + " already exists", }, { name: "duplicate_hostname_case", lease: &dhcpsvc.Lease{ - Hostname: strings.ToUpper(host1), - IP: ip2, - HWAddr: mac2, + Hostname: strings.ToUpper(existHost), + IP: newIP, + HWAddr: newMAC, }, wantErrMsg: "adding lease: lease for hostname " + - strings.ToUpper(host1) + " already exists", + strings.ToUpper(existHost) + " already exists", }, { name: "duplicate_mac", lease: &dhcpsvc.Lease{ - Hostname: host2, - IP: ip2, - HWAddr: mac1, + Hostname: newHost, + IP: newIP, + HWAddr: existMAC, }, - wantErrMsg: "adding lease: lease for mac " + mac1.String() + + wantErrMsg: "adding lease: lease for mac " + existMAC.String() + " already exists", }, { name: "valid", lease: &dhcpsvc.Lease{ - Hostname: host2, - IP: ip2, - HWAddr: mac2, + Hostname: newHost, + IP: newIP, + HWAddr: newMAC, }, wantErrMsg: "", }, { name: "valid_v6", lease: &dhcpsvc.Lease{ - Hostname: host3, - IP: ip3, - HWAddr: mac3, + Hostname: ipv6Host, + IP: newIPv6, + HWAddr: ipv6MAC, }, wantErrMsg: "", }} @@ -316,7 +266,7 @@ func TestDHCPServer_AddLease(t *testing.T) { func TestDHCPServer_index(t *testing.T) { ctx := testutil.ContextWithTimeout(t, testTimeout) - leasesPath := copyDB(t) + leasesPath := newTempDB(t) srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{ Enabled: true, Logger: discardLog, @@ -334,21 +284,23 @@ func TestDHCPServer_index(t *testing.T) { host5 = "host5" ) - ip1 := netip.MustParseAddr("192.168.0.2") - ip2 := netip.MustParseAddr("192.168.0.3") - ip3 := netip.MustParseAddr("172.16.0.3") - ip4 := netip.MustParseAddr("172.16.0.4") + var ( + ip1 = netip.MustParseAddr("192.168.0.2") + ip2 = netip.MustParseAddr("192.168.0.3") + ip3 = netip.MustParseAddr("172.16.0.3") + ip4 = netip.MustParseAddr("172.16.0.4") - mac1 := mustParseMAC(t, "01:02:03:04:05:06") - mac2 := mustParseMAC(t, "06:05:04:03:02:01") - mac3 := mustParseMAC(t, "02:03:04:05:06:07") + mac1 = mustParseMAC(t, "01:02:03:04:05:06") + mac2 = mustParseMAC(t, "06:05:04:03:02:01") + mac3 = mustParseMAC(t, "02:03:04:05:06:07") + ) t.Run("ip_idx", func(t *testing.T) { assert.Equal(t, ip1, srv.IPByHost(host1)) assert.Equal(t, ip2, srv.IPByHost(host2)) assert.Equal(t, ip3, srv.IPByHost(host3)) assert.Equal(t, ip4, srv.IPByHost(host4)) - assert.Equal(t, netip.Addr{}, srv.IPByHost(host5)) + assert.Zero(t, srv.IPByHost(host5)) }) t.Run("name_idx", func(t *testing.T) { @@ -356,7 +308,7 @@ func TestDHCPServer_index(t *testing.T) { assert.Equal(t, host2, srv.HostByIP(ip2)) assert.Equal(t, host3, srv.HostByIP(ip3)) assert.Equal(t, host4, srv.HostByIP(ip4)) - assert.Equal(t, "", srv.HostByIP(netip.Addr{})) + assert.Zero(t, srv.HostByIP(netip.Addr{})) }) t.Run("mac_idx", func(t *testing.T) { @@ -364,14 +316,14 @@ func TestDHCPServer_index(t *testing.T) { assert.Equal(t, mac2, srv.MACByIP(ip2)) assert.Equal(t, mac3, srv.MACByIP(ip3)) assert.Equal(t, mac1, srv.MACByIP(ip4)) - assert.Nil(t, srv.MACByIP(netip.Addr{})) + assert.Zero(t, srv.MACByIP(netip.Addr{})) }) } func TestDHCPServer_UpdateStaticLease(t *testing.T) { ctx := testutil.ContextWithTimeout(t, testTimeout) - leasesPath := copyDB(t) + leasesPath := newTempDB(t) srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{ Enabled: true, Logger: discardLog, @@ -390,14 +342,16 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) { host6 = "host6" ) - ip1 := netip.MustParseAddr("192.168.0.2") - ip2 := netip.MustParseAddr("192.168.0.3") - ip3 := netip.MustParseAddr("192.168.0.4") - ip4 := netip.MustParseAddr("2001:db8::3") + var ( + ip1 = netip.MustParseAddr("192.168.0.2") + ip2 = netip.MustParseAddr("192.168.0.3") + ip3 = netip.MustParseAddr("192.168.0.4") + ip4 = netip.MustParseAddr("2001:db8::3") - mac1 := mustParseMAC(t, "01:02:03:04:05:06") - mac2 := mustParseMAC(t, "06:05:04:03:02:01") - mac3 := mustParseMAC(t, "06:05:04:03:02:02") + mac1 = mustParseMAC(t, "01:02:03:04:05:06") + mac2 = mustParseMAC(t, "06:05:04:03:02:01") + mac3 = mustParseMAC(t, "06:05:04:03:02:02") + ) testCases := []struct { name string @@ -476,7 +430,7 @@ func TestDHCPServer_UpdateStaticLease(t *testing.T) { func TestDHCPServer_RemoveLease(t *testing.T) { ctx := testutil.ContextWithTimeout(t, testTimeout) - leasesPath := copyDB(t) + leasesPath := newTempDB(t) srv, err := dhcpsvc.New(ctx, &dhcpsvc.Config{ Enabled: true, Logger: discardLog, @@ -492,13 +446,15 @@ func TestDHCPServer_RemoveLease(t *testing.T) { host3 = "host3" ) - ip1 := netip.MustParseAddr("192.168.0.2") - ip2 := netip.MustParseAddr("192.168.0.3") - ip3 := netip.MustParseAddr("2001:db8::2") + var ( + existIP = netip.MustParseAddr("192.168.0.2") + newIP = netip.MustParseAddr("192.168.0.3") + newIPv6 = netip.MustParseAddr("2001:db8::2") - mac1 := mustParseMAC(t, "01:02:03:04:05:06") - mac2 := mustParseMAC(t, "02:03:04:05:06:07") - mac3 := mustParseMAC(t, "06:05:04:03:02:01") + existMAC = mustParseMAC(t, "01:02:03:04:05:06") + newMAC = mustParseMAC(t, "02:03:04:05:06:07") + ipv6MAC = mustParseMAC(t, "06:05:04:03:02:01") + ) testCases := []struct { name string @@ -508,40 +464,40 @@ func TestDHCPServer_RemoveLease(t *testing.T) { name: "not_found_mac", lease: &dhcpsvc.Lease{ Hostname: host1, - IP: ip1, - HWAddr: mac2, + IP: existIP, + HWAddr: newMAC, }, - wantErrMsg: "removing lease: no lease for mac " + mac2.String(), + wantErrMsg: "removing lease: no lease for mac " + newMAC.String(), }, { name: "not_found_ip", lease: &dhcpsvc.Lease{ Hostname: host1, - IP: ip2, - HWAddr: mac1, + IP: newIP, + HWAddr: existMAC, }, - wantErrMsg: "removing lease: no lease for ip " + ip2.String(), + wantErrMsg: "removing lease: no lease for ip " + newIP.String(), }, { name: "not_found_host", lease: &dhcpsvc.Lease{ Hostname: host2, - IP: ip1, - HWAddr: mac1, + IP: existIP, + HWAddr: existMAC, }, wantErrMsg: "removing lease: no lease for hostname " + host2, }, { name: "valid", lease: &dhcpsvc.Lease{ Hostname: host1, - IP: ip1, - HWAddr: mac1, + IP: existIP, + HWAddr: existMAC, }, wantErrMsg: "", }, { name: "valid_v6", lease: &dhcpsvc.Lease{ Hostname: host3, - IP: ip3, - HWAddr: mac3, + IP: newIPv6, + HWAddr: ipv6MAC, }, wantErrMsg: "", }} @@ -557,7 +513,7 @@ func TestDHCPServer_RemoveLease(t *testing.T) { } func TestDHCPServer_Reset(t *testing.T) { - leasesPath := copyDB(t) + leasesPath := newTempDB(t) conf := &dhcpsvc.Config{ Enabled: true, Logger: discardLog, @@ -579,3 +535,37 @@ func TestDHCPServer_Reset(t *testing.T) { assert.FileExists(t, leasesPath) assert.Empty(t, srv.Leases()) } + +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) + + expiry, err := time.Parse(time.RFC3339, "2042-01-02T03:04:05Z") + require.NoError(t, err) + + wantLeases := []*dhcpsvc.Lease{{ + Expiry: expiry, + IP: netip.MustParseAddr("192.168.0.3"), + Hostname: "example.host", + HWAddr: mustParseMAC(t, "AA:AA:AA:AA:AA:AA"), + IsStatic: false, + }, { + Expiry: time.Time{}, + IP: netip.MustParseAddr("192.168.0.4"), + Hostname: "example.static.host", + HWAddr: mustParseMAC(t, "BB:BB:BB:BB:BB:BB"), + IsStatic: true, + }} + assert.Equal(t, wantLeases, srv.Leases()) +} diff --git a/internal/dhcpsvc/testdata/TestServer_loadDatabase/leases.json b/internal/dhcpsvc/testdata/TestServer_Leases/leases.json similarity index 60% rename from internal/dhcpsvc/testdata/TestServer_loadDatabase/leases.json rename to internal/dhcpsvc/testdata/TestServer_Leases/leases.json index 069f3595..c5ccad0d 100644 --- a/internal/dhcpsvc/testdata/TestServer_loadDatabase/leases.json +++ b/internal/dhcpsvc/testdata/TestServer_Leases/leases.json @@ -5,6 +5,11 @@ "hostname": "example.host", "mac": "AA:AA:AA:AA:AA:AA", "static": false + }, { + "ip": "192.168.0.4", + "hostname": "example.static.host", + "mac": "BB:BB:BB:BB:BB:BB", + "static": true }], "version": 1 }