Files
AdGuardHome/internal/client/index_internal_test.go
Stanislav Chzhen 61a1403e4e Pull request 2378: AGDNS-2750-find-client
Merge in DNS/adguard-home from AGDNS-2750-find-client to master

Squashed commit of the following:

commit 98f1a8ca4622b6f502a5092273b9724203fe0bd8
Merge: 9270222d8 4ccc2a213
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 23 17:53:20 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 9270222d8e9e03038e9434b54496cbb6164463cd
Merge: 6468ceec8 c7c62ad3b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 19:40:58 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 6468ceec82d30084771a53ff6720a8c11c68bf2f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 21 19:40:52 2025 +0300

    home: imp docs

commit 3fd4735a0d6db4fdf2d46f3da9794a687fdcaa8b
Merge: 1311a5869 a8fdf1c55
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 19:43:36 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 1311a58695de00f20c9704378ee6e964a44d1c59
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 18 19:42:41 2025 +0300

    home: imp code

commit b1f2c4c883c9476c5135140abac31f8ae6609b4f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 16 16:47:59 2025 +0300

    home: imp code

commit d0a5abd66587c1ad602c2ccf6c8a45a3dfe39a5c
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 15 14:58:31 2025 +0300

    client: imp naming

commit 5accdca325551237f003f1c416891b488fe5290b
Merge: 6a00232f7 4d258972d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 19:40:40 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 6a00232f76a0fe5ce781aa01637b6e04ace7250d
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 14 19:30:32 2025 +0300

    home: imp code

commit 8633886457c6aab75f5676494b1f49d9811e9ab9
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Fri Apr 11 15:29:25 2025 +0300

    all: imp code

commit d6f16879e7b054a5ffac59131d2a6eff1da659c0
Merge: 58236fdec 6d282ae71
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:35:23 2025 +0300

    Merge branch 'master' into AGDNS-2750-find-client

commit 58236fdec5b64e83a44680ff8a89badc18ec81f1
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:23:01 2025 +0300

    all: upd ci

commit 3c4d946d7970987677d4ac984394e18987a29f9a
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 21:16:03 2025 +0300

    all: upd go

commit cc1c97734506a9ffbe70fd3c676284e58a21ba46
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Thu Apr 10 20:58:56 2025 +0300

    all: imp code

commit 8f061c933152481a4c80eef2af575efd4919d82b
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 9 16:49:11 2025 +0300

    all: imp docs

commit 8d19355f1c519211a56cec3f23d527922d4f2ee0
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Apr 7 21:35:06 2025 +0300

    all: imp code

commit f1e853f57e5d54d13bedcdab4f8e21e112f3a356
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Apr 2 14:57:40 2025 +0300

    all: imp code

commit 6a6ac7f899f29ddc90a583c80562233e646ba1d6
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 19:51:56 2025 +0300

    client: imp tests

commit 52040ee7393d0483c682f2f37d7b70f12f9cf621
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Tue Apr 1 19:28:18 2025 +0300

    all: imp code

commit 1e09208dbd2d35c3f6b2ade169324e23d1a643a5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Mar 26 15:33:02 2025 +0300

    all: imp code

... and 2 more commits
2025-04-23 18:10:52 +03:00

454 lines
9.5 KiB
Go

package client
import (
"net"
"net/netip"
"testing"
"github.com/AdguardTeam/golibs/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// newIDIndex is a helper function that returns a client index filled with
// persistent clients from the m. It also generates a UID for each client.
func newIDIndex(m []*Persistent) (ci *index) {
ci = newIndex()
for _, c := range m {
c.UID = MustNewUID()
ci.add(c)
}
return ci
}
// TODO(s.chzhen): Remove.
func TestClientIndex_Find(t *testing.T) {
const (
cliIPNone = "1.2.3.4"
cliIP1 = "1.1.1.1"
cliIP2 = "2.2.2.2"
cliIPv6 = "1:2:3::4"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
linkLocalIP = "fe80::abcd:abcd:abcd:ab%eth0"
linkLocalSubnet = "fe80::/16"
)
var (
clientWithBothFams = &Persistent{
Name: "client1",
IPs: []netip.Addr{
netip.MustParseAddr(cliIP1),
netip.MustParseAddr(cliIPv6),
},
}
clientWithSubnet = &Persistent{
Name: "client2",
IPs: []netip.Addr{netip.MustParseAddr(cliIP2)},
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}
clientWithMAC = &Persistent{
Name: "client_with_mac",
MACs: []net.HardwareAddr{errors.Must(net.ParseMAC(cliMAC))},
}
clientWithID = &Persistent{
Name: "client_with_id",
ClientIDs: []ClientID{cliID},
}
clientLinkLocal = &Persistent{
Name: "client_link_local",
Subnets: []netip.Prefix{netip.MustParsePrefix(linkLocalSubnet)},
}
)
clients := []*Persistent{
clientWithBothFams,
clientWithSubnet,
clientWithMAC,
clientWithID,
clientLinkLocal,
}
ci := newIDIndex(clients)
testCases := []struct {
want *Persistent
name string
ids []string
}{{
name: "ipv4_ipv6",
ids: []string{cliIP1, cliIPv6},
want: clientWithBothFams,
}, {
name: "ipv4_subnet",
ids: []string{cliIP2, cliSubnetIP},
want: clientWithSubnet,
}, {
name: "mac",
ids: []string{cliMAC},
want: clientWithMAC,
}, {
name: "client_id",
ids: []string{cliID},
want: clientWithID,
}, {
name: "client_link_local_subnet",
ids: []string{linkLocalIP},
want: clientLinkLocal,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := ci.find(id)
require.True(t, ok)
assert.Equal(t, tc.want, c)
}
})
}
t.Run("not_found", func(t *testing.T) {
_, ok := ci.find(cliIPNone)
assert.False(t, ok)
})
}
func TestClientIndex_Clashes(t *testing.T) {
const (
cliIP1 = "1.1.1.1"
cliSubnet = "2.2.2.0/24"
cliSubnetIP = "2.2.2.222"
cliID = "client-id"
cliMAC = "11:11:11:11:11:11"
)
clients := []*Persistent{{
Name: "client_with_ip",
IPs: []netip.Addr{netip.MustParseAddr(cliIP1)},
}, {
Name: "client_with_subnet",
Subnets: []netip.Prefix{netip.MustParsePrefix(cliSubnet)},
}, {
Name: "client_with_mac",
MACs: []net.HardwareAddr{errors.Must(net.ParseMAC(cliMAC))},
}, {
Name: "client_with_id",
ClientIDs: []ClientID{cliID},
}}
ci := newIDIndex(clients)
testCases := []struct {
client *Persistent
name string
}{{
name: "ipv4",
client: clients[0],
}, {
name: "subnet",
client: clients[1],
}, {
name: "mac",
client: clients[2],
}, {
name: "client_id",
client: clients[3],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
clone := tc.client.ShallowClone()
clone.UID = MustNewUID()
err := ci.clashes(clone)
require.Error(t, err)
ci.remove(tc.client)
err = ci.clashes(clone)
require.NoError(t, err)
})
}
}
func TestMACToKey(t *testing.T) {
testCases := []struct {
want any
name string
in string
}{{
name: "column6",
in: "00:00:5e:00:53:01",
want: [6]byte(errors.Must(net.ParseMAC("00:00:5e:00:53:01"))),
}, {
name: "column8",
in: "02:00:5e:10:00:00:00:01",
want: [8]byte(errors.Must(net.ParseMAC("02:00:5e:10:00:00:00:01"))),
}, {
name: "column20",
in: "00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01",
want: [20]byte(errors.Must(net.ParseMAC("00:00:00:00:fe:80:00:00:00:00:00:00:02:00:5e:10:00:00:00:01"))),
}, {
name: "hyphen6",
in: "00-00-5e-00-53-01",
want: [6]byte(errors.Must(net.ParseMAC("00-00-5e-00-53-01"))),
}, {
name: "hyphen8",
in: "02-00-5e-10-00-00-00-01",
want: [8]byte(errors.Must(net.ParseMAC("02-00-5e-10-00-00-00-01"))),
}, {
name: "hyphen20",
in: "00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01",
want: [20]byte(errors.Must(net.ParseMAC("00-00-00-00-fe-80-00-00-00-00-00-00-02-00-5e-10-00-00-00-01"))),
}, {
name: "dot6",
in: "0000.5e00.5301",
want: [6]byte(errors.Must(net.ParseMAC("0000.5e00.5301"))),
}, {
name: "dot8",
in: "0200.5e10.0000.0001",
want: [8]byte(errors.Must(net.ParseMAC("0200.5e10.0000.0001"))),
}, {
name: "dot20",
in: "0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001",
want: [20]byte(errors.Must(net.ParseMAC("0000.0000.fe80.0000.0000.0000.0200.5e10.0000.0001"))),
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mac := errors.Must(net.ParseMAC(tc.in))
key := macToKey(mac)
assert.Equal(t, tc.want, key)
})
}
assert.Panics(t, func() {
mac := net.HardwareAddr([]byte{1, 2, 3})
_ = macToKey(mac)
})
}
func TestIndex_FindByIPWithoutZone(t *testing.T) {
var (
ip = netip.MustParseAddr("fe80::a098:7654:32ef:ff1")
ipWithZone = netip.MustParseAddr("fe80::1ff:fe23:4567:890a%eth2")
)
var (
clientNoZone = &Persistent{
Name: "client",
IPs: []netip.Addr{ip},
}
clientWithZone = &Persistent{
Name: "client_with_zone",
IPs: []netip.Addr{ipWithZone},
}
)
ci := newIDIndex([]*Persistent{
clientNoZone,
clientWithZone,
})
testCases := []struct {
ip netip.Addr
want *Persistent
name string
}{{
name: "without_zone",
ip: ip,
want: clientNoZone,
}, {
name: "with_zone",
ip: ipWithZone,
want: clientWithZone,
}, {
name: "zero_address",
ip: netip.Addr{},
want: nil,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := ci.findByIPWithoutZone(tc.ip.WithZone(""))
require.Equal(t, tc.want, c)
})
}
}
func TestClientIndex_RangeByName(t *testing.T) {
sortedClients := []*Persistent{{
Name: "clientA",
ClientIDs: []ClientID{"A"},
}, {
Name: "clientB",
ClientIDs: []ClientID{"B"},
}, {
Name: "clientC",
ClientIDs: []ClientID{"C"},
}, {
Name: "clientD",
ClientIDs: []ClientID{"D"},
}, {
Name: "clientE",
ClientIDs: []ClientID{"E"},
}}
testCases := []struct {
name string
want []*Persistent
}{{
name: "basic",
want: sortedClients,
}, {
name: "nil",
want: nil,
}, {
name: "one_element",
want: sortedClients[:1],
}, {
name: "two_elements",
want: sortedClients[:2],
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
ci := newIDIndex(tc.want)
var got []*Persistent
ci.rangeByName(func(c *Persistent) (cont bool) {
got = append(got, c)
return true
})
assert.Equal(t, tc.want, got)
})
}
}
func TestIndex_FindByName(t *testing.T) {
const (
clientExistingName = "client_existing"
clientAnotherExistingName = "client_another_existing"
nonExistingClientName = "client_non_existing"
)
var (
clientExisting = &Persistent{
Name: clientExistingName,
IPs: []netip.Addr{netip.MustParseAddr("192.0.2.1")},
}
clientAnotherExisting = &Persistent{
Name: clientAnotherExistingName,
IPs: []netip.Addr{netip.MustParseAddr("192.0.2.2")},
}
)
clients := []*Persistent{
clientExisting,
clientAnotherExisting,
}
ci := newIDIndex(clients)
testCases := []struct {
want *Persistent
found assert.BoolAssertionFunc
name string
clientName string
}{{
want: clientExisting,
found: assert.True,
name: "existing",
clientName: clientExistingName,
}, {
want: clientAnotherExisting,
found: assert.True,
name: "another_existing",
clientName: clientAnotherExistingName,
}, {
want: nil,
found: assert.False,
name: "non_existing",
clientName: nonExistingClientName,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c, ok := ci.findByName(tc.clientName)
assert.Equal(t, tc.want, c)
tc.found(t, ok)
})
}
}
func TestIndex_FindByMAC(t *testing.T) {
var (
cliMAC = errors.Must(net.ParseMAC("11:11:11:11:11:11"))
cliAnotherMAC = errors.Must(net.ParseMAC("22:22:22:22:22:22"))
nonExistingClientMAC = errors.Must(net.ParseMAC("33:33:33:33:33:33"))
)
var (
clientExisting = &Persistent{
Name: "client",
MACs: []net.HardwareAddr{cliMAC},
}
clientAnotherExisting = &Persistent{
Name: "another_client",
MACs: []net.HardwareAddr{cliAnotherMAC},
}
)
clients := []*Persistent{
clientExisting,
clientAnotherExisting,
}
ci := newIDIndex(clients)
testCases := []struct {
want *Persistent
found assert.BoolAssertionFunc
name string
clientMAC net.HardwareAddr
}{{
want: clientExisting,
found: assert.True,
name: "existing",
clientMAC: cliMAC,
}, {
want: clientAnotherExisting,
found: assert.True,
name: "another_existing",
clientMAC: cliAnotherMAC,
}, {
want: nil,
found: assert.False,
name: "non_existing",
clientMAC: nonExistingClientMAC,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c, ok := ci.findByMAC(tc.clientMAC)
assert.Equal(t, tc.want, c)
tc.found(t, ok)
})
}
}