mirror of
https://github.com/AdguardTeam/AdGuardDNS.git
synced 2025-12-23 23:38:37 -05:00
842 lines
21 KiB
Go
842 lines
21 KiB
Go
package profiledb_test
|
|
|
|
import (
|
|
"context"
|
|
"net/netip"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/agd"
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/agdtest"
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/dnsmsg"
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb"
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal"
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/filecachepb"
|
|
"github.com/AdguardTeam/AdGuardDNS/internal/profiledb/internal/profiledbtest"
|
|
"github.com/AdguardTeam/golibs/container"
|
|
"github.com/AdguardTeam/golibs/errors"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestDefault(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
const (
|
|
devIdxDefault = iota
|
|
devIdxAuto
|
|
)
|
|
|
|
devices := []*agd.Device{
|
|
devIdxDefault: {
|
|
ID: profiledbtest.DeviceID,
|
|
LinkedIP: testClientIPv4,
|
|
DedicatedIPs: []netip.Addr{
|
|
testDedicatedIPv4,
|
|
},
|
|
},
|
|
devIdxAuto: {
|
|
ID: profiledbtest.DeviceIDAuto,
|
|
HumanIDLower: profiledbtest.HumanIDLower,
|
|
},
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
devicesCh <- devices
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
t.Run("by_dedicated_ip", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
p, d, err := db.ProfileByDedicatedIP(ctx, testDedicatedIPv4)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, profiledbtest.ProfileID, p.ID)
|
|
assert.Equal(t, d, devices[devIdxDefault])
|
|
})
|
|
|
|
t.Run("by_device_id", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
p, d, err := db.ProfileByDeviceID(ctx, profiledbtest.DeviceID)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, profiledbtest.ProfileID, p.ID)
|
|
assert.Equal(t, d, devices[devIdxDefault])
|
|
})
|
|
|
|
t.Run("by_human_id", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
p, d, err := db.ProfileByHumanID(ctx, profiledbtest.ProfileID, profiledbtest.HumanIDLower)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, profiledbtest.ProfileID, p.ID)
|
|
assert.Equal(t, d, devices[devIdxAuto])
|
|
})
|
|
|
|
t.Run("by_linked_ip", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
p, d, err := db.ProfileByLinkedIP(ctx, testClientIPv4)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, profiledbtest.ProfileID, p.ID)
|
|
assert.Equal(t, d, devices[devIdxDefault])
|
|
})
|
|
}
|
|
|
|
func TestDefault_ProfileByDedicatedIP_removedDevice(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
DedicatedIPs: []netip.Addr{
|
|
testDedicatedIPv4,
|
|
},
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
|
|
// The first response, the device is still there.
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
_, d, err := db.ProfileByDedicatedIP(ctx, testDedicatedIPv4)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, d, dev)
|
|
|
|
// The second response, the device is removed.
|
|
devicesCh <- nil
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
err = db.Refresh(ctx)
|
|
require.NoError(t, err)
|
|
|
|
assert.Eventually(t, func() (ok bool) {
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
_, d, err = db.ProfileByDedicatedIP(ctx, testDedicatedIPv4)
|
|
|
|
return errors.Is(err, profiledb.ErrDeviceNotFound)
|
|
}, profiledbtest.Timeout, profiledbtest.Timeout/10)
|
|
}
|
|
|
|
func TestDefault_ProfileByDedicatedIP_deviceNewIP(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
DedicatedIPs: []netip.Addr{
|
|
testDedicatedIPv4,
|
|
},
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
|
|
// The first response, the device is still there.
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
_, d, err := db.ProfileByDedicatedIP(ctx, testDedicatedIPv4)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, d, dev)
|
|
|
|
// The second response, the device has a new IP.
|
|
dev.DedicatedIPs[0] = testOtherDedicatedIPv4
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
err = db.Refresh(ctx)
|
|
require.NoError(t, err)
|
|
|
|
assert.Eventually(t, func() (ok bool) {
|
|
_, _, err = db.ProfileByDedicatedIP(ctx, testDedicatedIPv4)
|
|
|
|
if !errors.Is(err, profiledb.ErrDeviceNotFound) {
|
|
return false
|
|
}
|
|
|
|
_, d, err = db.ProfileByDedicatedIP(ctx, testOtherDedicatedIPv4)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return d != nil && d.ID == dev.ID
|
|
}, profiledbtest.Timeout, profiledbtest.Timeout/10)
|
|
}
|
|
|
|
func TestDefault_ProfileByHumanID_removedDevice(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceIDAuto,
|
|
HumanIDLower: profiledbtest.HumanIDLower,
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
|
|
// The first response, the device is still there.
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
_, d, err := db.ProfileByHumanID(ctx, profiledbtest.ProfileID, profiledbtest.HumanIDLower)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, d, dev)
|
|
|
|
// The second response, the device is removed.
|
|
devicesCh <- nil
|
|
|
|
err = db.Refresh(ctx)
|
|
require.NoError(t, err)
|
|
|
|
assert.Eventually(t, func() (ok bool) {
|
|
_, d, err = db.ProfileByHumanID(ctx, profiledbtest.ProfileID, profiledbtest.HumanIDLower)
|
|
|
|
return errors.Is(err, profiledb.ErrDeviceNotFound)
|
|
}, profiledbtest.Timeout, profiledbtest.Timeout/10)
|
|
}
|
|
|
|
func TestDefault_ProfileByHumanID_deviceNotAuto(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceIDAuto,
|
|
HumanIDLower: profiledbtest.HumanIDLower,
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
|
|
// The first response, the device is still there.
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
_, d, err := db.ProfileByHumanID(ctx, profiledbtest.ProfileID, profiledbtest.HumanIDLower)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, d, dev)
|
|
|
|
// The second response, the device is now a non-auto device.
|
|
devicesCh <- []*agd.Device{{
|
|
ID: profiledbtest.DeviceIDAuto,
|
|
}}
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
err = db.Refresh(ctx)
|
|
require.NoError(t, err)
|
|
|
|
assert.Eventually(t, func() (ok bool) {
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
_, d, err = db.ProfileByHumanID(ctx, profiledbtest.ProfileID, profiledbtest.HumanIDLower)
|
|
|
|
return errors.Is(err, profiledb.ErrDeviceNotFound)
|
|
}, profiledbtest.Timeout, profiledbtest.Timeout/10)
|
|
}
|
|
|
|
func TestDefault_ProfileByLinkedIP_removedDevice(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
LinkedIP: testClientIPv4,
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
|
|
// The first response, the device is still there.
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
_, d, err := db.ProfileByLinkedIP(ctx, testClientIPv4)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, d, dev)
|
|
|
|
// The second response, the device is removed.
|
|
devicesCh <- nil
|
|
|
|
err = db.Refresh(ctx)
|
|
require.NoError(t, err)
|
|
|
|
assert.Eventually(t, func() (ok bool) {
|
|
_, d, err = db.ProfileByLinkedIP(ctx, testClientIPv4)
|
|
|
|
return errors.Is(err, profiledb.ErrDeviceNotFound)
|
|
}, profiledbtest.Timeout, profiledbtest.Timeout/10)
|
|
}
|
|
|
|
func TestDefault_ProfileByLinkedIP_deviceNewIP(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
LinkedIP: testClientIPv4,
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 2)
|
|
|
|
// The first response, the device is still there.
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
db := newDefaultProfileDB(t, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
_, d, err := db.ProfileByLinkedIP(ctx, testClientIPv4)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, d, dev)
|
|
|
|
// The second response, the device has a new IP.
|
|
dev.LinkedIP = testOtherClientIPv4
|
|
devicesCh <- []*agd.Device{dev}
|
|
|
|
err = db.Refresh(ctx)
|
|
require.NoError(t, err)
|
|
|
|
assert.Eventually(t, func() (ok bool) {
|
|
_, _, err = db.ProfileByLinkedIP(ctx, testClientIPv4)
|
|
|
|
if !errors.Is(err, profiledb.ErrDeviceNotFound) {
|
|
return false
|
|
}
|
|
|
|
_, d, err = db.ProfileByLinkedIP(ctx, testOtherClientIPv4)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
return d != nil && d.ID == dev.ID
|
|
}, profiledbtest.Timeout, profiledbtest.Timeout/10)
|
|
}
|
|
|
|
func TestDefault_fileCache_success(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var gotSyncTime time.Time
|
|
|
|
ps := agdtest.NewProfileStorage()
|
|
ps.OnProfiles = func(
|
|
_ context.Context,
|
|
req *profiledb.StorageProfilesRequest,
|
|
) (resp *profiledb.StorageProfilesResponse, err error) {
|
|
gotSyncTime = req.SyncTime
|
|
|
|
return &profiledb.StorageProfilesResponse{}, nil
|
|
}
|
|
|
|
// Use the time with monotonic clocks stripped down.
|
|
wantSyncTime := time.Now().Round(0).UTC()
|
|
|
|
prof, dev := profiledbtest.NewProfile(t)
|
|
|
|
cacheFilePath := filepath.Join(t.TempDir(), "profiles.pb")
|
|
pbCache := filecachepb.New(&filecachepb.Config{
|
|
Logger: profiledbtest.Logger,
|
|
BaseCustomLogger: profiledbtest.Logger,
|
|
ProfileAccessConstructor: profiledbtest.ProfileAccessConstructor,
|
|
CacheFilePath: cacheFilePath,
|
|
ResponseSizeEstimate: profiledbtest.RespSzEst,
|
|
})
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
err := pbCache.Store(ctx, &internal.FileCache{
|
|
SyncTime: wantSyncTime,
|
|
Profiles: []*agd.Profile{prof},
|
|
Devices: []*agd.Device{dev},
|
|
Version: internal.FileCacheVersion,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
db := newProfileDB(t, &profiledb.Config{
|
|
Storage: ps,
|
|
CacheFilePath: cacheFilePath,
|
|
})
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.Refresh(ctx))
|
|
|
|
assert.Equal(t, wantSyncTime, gotSyncTime)
|
|
|
|
p, d, err := db.ProfileByDeviceID(context.Background(), dev.ID)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, dev, d)
|
|
agdtest.AssertEqualProfile(t, prof, p)
|
|
}
|
|
|
|
func TestDefault_fileCache_badVersion(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
storageCalled := false
|
|
ps := agdtest.NewProfileStorage()
|
|
ps.OnProfiles = func(
|
|
_ context.Context,
|
|
_ *profiledb.StorageProfilesRequest,
|
|
) (resp *profiledb.StorageProfilesResponse, err error) {
|
|
storageCalled = true
|
|
|
|
return &profiledb.StorageProfilesResponse{}, nil
|
|
}
|
|
|
|
cacheFilePath := filepath.Join(t.TempDir(), "profiles.pb")
|
|
pbCache := filecachepb.New(&filecachepb.Config{
|
|
Logger: profiledbtest.Logger,
|
|
BaseCustomLogger: profiledbtest.Logger,
|
|
ProfileAccessConstructor: profiledbtest.ProfileAccessConstructor,
|
|
CacheFilePath: cacheFilePath,
|
|
ResponseSizeEstimate: profiledbtest.RespSzEst,
|
|
})
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
err := pbCache.Store(ctx, &internal.FileCache{
|
|
Version: 10000,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
db := newProfileDB(t, &profiledb.Config{
|
|
Storage: ps,
|
|
CacheFilePath: cacheFilePath,
|
|
})
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.Refresh(ctx))
|
|
|
|
assert.True(t, storageCalled)
|
|
}
|
|
|
|
func TestDefault_CreateAutoDevice(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
wantDev := &agd.Device{
|
|
ID: profiledbtest.DeviceIDAuto,
|
|
HumanIDLower: profiledbtest.HumanIDLower,
|
|
}
|
|
wantProf := &agd.Profile{
|
|
CustomDomains: &agd.AccountCustomDomains{},
|
|
AdultBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
BlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
SafeBrowsingBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
ID: profiledbtest.ProfileID,
|
|
DeviceIDs: nil,
|
|
AutoDevicesEnabled: true,
|
|
}
|
|
|
|
ps := &agdtest.ProfileStorage{
|
|
OnCreateAutoDevice: func(
|
|
_ context.Context,
|
|
_ *profiledb.StorageCreateAutoDeviceRequest,
|
|
) (resp *profiledb.StorageCreateAutoDeviceResponse, err error) {
|
|
return &profiledb.StorageCreateAutoDeviceResponse{
|
|
Device: wantDev,
|
|
}, nil
|
|
},
|
|
OnProfiles: func(
|
|
_ context.Context,
|
|
_ *profiledb.StorageProfilesRequest,
|
|
) (resp *profiledb.StorageProfilesResponse, err error) {
|
|
return &profiledb.StorageProfilesResponse{
|
|
Profiles: []*agd.Profile{wantProf},
|
|
Devices: nil,
|
|
}, nil
|
|
},
|
|
}
|
|
|
|
db := newProfileDB(t, &profiledb.Config{
|
|
Storage: ps,
|
|
})
|
|
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.Refresh(ctx))
|
|
|
|
p, d, err := db.CreateAutoDevice(
|
|
ctx,
|
|
profiledbtest.ProfileID,
|
|
profiledbtest.HumanID,
|
|
agd.DeviceTypeOther,
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, wantDev, d)
|
|
assert.Equal(t, wantProf, p)
|
|
}
|
|
|
|
func TestDefault_deviceChanges(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
devPrevName = &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
Name: "Name 1",
|
|
}
|
|
|
|
devNewName = &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
Name: "Name 2",
|
|
}
|
|
|
|
devAuto = &agd.Device{
|
|
ID: profiledbtest.DeviceIDAuto,
|
|
HumanIDLower: profiledbtest.HumanIDLower,
|
|
}
|
|
)
|
|
|
|
profBefore := &agd.Profile{
|
|
CustomDomains: &agd.AccountCustomDomains{},
|
|
AdultBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
BlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
SafeBrowsingBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
ID: profiledbtest.ProfileID,
|
|
DeviceIDs: container.NewMapSet(
|
|
profiledbtest.DeviceID,
|
|
profiledbtest.DeviceIDAuto,
|
|
),
|
|
AutoDevicesEnabled: true,
|
|
}
|
|
|
|
profAfter := &agd.Profile{
|
|
CustomDomains: &agd.AccountCustomDomains{},
|
|
AdultBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
BlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
SafeBrowsingBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
ID: profiledbtest.ProfileID,
|
|
DeviceIDs: container.NewMapSet(
|
|
profiledbtest.DeviceID,
|
|
),
|
|
AutoDevicesEnabled: true,
|
|
}
|
|
|
|
var (
|
|
strgRespFull = &profiledb.StorageProfilesResponse{
|
|
SyncTime: time.Now(),
|
|
Profiles: []*agd.Profile{profBefore},
|
|
Devices: []*agd.Device{devPrevName, devAuto},
|
|
}
|
|
|
|
strgRespChg = &profiledb.StorageDeviceChange{
|
|
DeletedDeviceIDs: []agd.DeviceID{profiledbtest.DeviceIDAuto},
|
|
IsPartial: true,
|
|
}
|
|
|
|
strgRespPartial = &profiledb.StorageProfilesResponse{
|
|
SyncTime: time.Now(),
|
|
DeviceChanges: map[agd.ProfileID]*profiledb.StorageDeviceChange{
|
|
profiledbtest.ProfileID: strgRespChg,
|
|
},
|
|
Profiles: []*agd.Profile{profAfter},
|
|
Devices: []*agd.Device{devNewName},
|
|
}
|
|
)
|
|
|
|
ps := agdtest.NewProfileStorage()
|
|
ps.OnProfiles = func(
|
|
_ context.Context,
|
|
req *profiledb.StorageProfilesRequest,
|
|
) (resp *profiledb.StorageProfilesResponse, err error) {
|
|
if req.SyncTime.IsZero() {
|
|
return strgRespFull, nil
|
|
} else {
|
|
return strgRespPartial, nil
|
|
}
|
|
}
|
|
|
|
db := newProfileDB(t, &profiledb.Config{
|
|
Storage: ps,
|
|
})
|
|
|
|
require.True(t, t.Run("after_full", func(t *testing.T) {
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.RefreshFull(ctx))
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
p, d, dbErr := db.ProfileByDeviceID(ctx, profiledbtest.DeviceID)
|
|
require.NoError(t, dbErr)
|
|
|
|
assert.Equal(t, profBefore, p)
|
|
assert.Equal(t, devPrevName, d)
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
p, d, dbErr = db.ProfileByDeviceID(ctx, profiledbtest.DeviceIDAuto)
|
|
require.NoError(t, dbErr)
|
|
|
|
assert.Equal(t, profBefore, p)
|
|
assert.Equal(t, devAuto, d)
|
|
}))
|
|
|
|
require.True(t, t.Run("after_partial", func(t *testing.T) {
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.Refresh(ctx))
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
p, d, dbErr := db.ProfileByDeviceID(ctx, profiledbtest.DeviceID)
|
|
require.NoError(t, dbErr)
|
|
|
|
assert.Equal(t, profAfter, p)
|
|
assert.Equal(t, devNewName, d)
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
p, d, dbErr = db.ProfileByDeviceID(ctx, profiledbtest.DeviceIDAuto)
|
|
assert.ErrorIs(t, dbErr, profiledb.ErrDeviceNotFound)
|
|
assert.Nil(t, p)
|
|
assert.Nil(t, d)
|
|
}))
|
|
}
|
|
|
|
func TestDefault_noDeviceChanges(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
dev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
}
|
|
|
|
profBefore := &agd.Profile{
|
|
CustomDomains: &agd.AccountCustomDomains{},
|
|
AdultBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
BlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
SafeBrowsingBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
ID: profiledbtest.ProfileID,
|
|
FilteringEnabled: true,
|
|
DeviceIDs: container.NewMapSet(profiledbtest.DeviceID),
|
|
}
|
|
|
|
profAfter := &agd.Profile{
|
|
CustomDomains: &agd.AccountCustomDomains{},
|
|
AdultBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
BlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
SafeBrowsingBlockingMode: &dnsmsg.BlockingModeNullIP{},
|
|
ID: profiledbtest.ProfileID,
|
|
FilteringEnabled: false,
|
|
DeviceIDs: container.NewMapSet(profiledbtest.DeviceID),
|
|
}
|
|
|
|
var (
|
|
strgRespFull = &profiledb.StorageProfilesResponse{
|
|
SyncTime: time.Now(),
|
|
Profiles: []*agd.Profile{profBefore},
|
|
Devices: []*agd.Device{dev},
|
|
}
|
|
|
|
strgRespChg = &profiledb.StorageDeviceChange{
|
|
DeletedDeviceIDs: []agd.DeviceID{},
|
|
IsPartial: true,
|
|
}
|
|
|
|
strgRespPartial = &profiledb.StorageProfilesResponse{
|
|
SyncTime: time.Now(),
|
|
DeviceChanges: map[agd.ProfileID]*profiledb.StorageDeviceChange{
|
|
profiledbtest.ProfileID: strgRespChg,
|
|
},
|
|
Profiles: []*agd.Profile{profAfter},
|
|
Devices: []*agd.Device{},
|
|
}
|
|
)
|
|
|
|
ps := agdtest.NewProfileStorage()
|
|
ps.OnProfiles = func(
|
|
_ context.Context,
|
|
req *profiledb.StorageProfilesRequest,
|
|
) (resp *profiledb.StorageProfilesResponse, err error) {
|
|
if req.SyncTime.IsZero() {
|
|
return strgRespFull, nil
|
|
} else {
|
|
return strgRespPartial, nil
|
|
}
|
|
}
|
|
|
|
db := newProfileDB(t, &profiledb.Config{
|
|
Storage: ps,
|
|
})
|
|
|
|
require.True(t, t.Run("after_full", func(t *testing.T) {
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.RefreshFull(ctx))
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
p, d, dbErr := db.ProfileByDeviceID(ctx, profiledbtest.DeviceID)
|
|
require.NoError(t, dbErr)
|
|
|
|
assert.Equal(t, profBefore, p)
|
|
assert.Equal(t, dev, d)
|
|
}))
|
|
|
|
require.True(t, t.Run("after_partial", func(t *testing.T) {
|
|
ctx := profiledbtest.ContextWithTimeout(t)
|
|
require.NoError(t, db.Refresh(ctx))
|
|
|
|
ctx = profiledbtest.ContextWithTimeout(t)
|
|
p, d, dbErr := db.ProfileByDeviceID(ctx, profiledbtest.DeviceID)
|
|
require.NoError(t, dbErr)
|
|
|
|
assert.Equal(t, profAfter, p)
|
|
assert.Equal(t, dev, d)
|
|
}))
|
|
}
|
|
|
|
func BenchmarkDefault_ProfileByDeviceID(b *testing.B) {
|
|
existingDev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 1)
|
|
devicesCh <- []*agd.Device{existingDev}
|
|
db := newDefaultProfileDB(b, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
|
|
var (
|
|
prof *agd.Profile
|
|
dev *agd.Device
|
|
err error
|
|
)
|
|
|
|
b.Run("success", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
prof, dev, err = db.ProfileByDeviceID(ctx, profiledbtest.DeviceID)
|
|
}
|
|
|
|
assert.NotNil(b, prof)
|
|
assert.NotNil(b, dev)
|
|
assert.NoError(b, err)
|
|
})
|
|
|
|
const wrongDevID = profiledbtest.DeviceID + "_bad"
|
|
|
|
b.Run("not_found", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
prof, dev, err = db.ProfileByDeviceID(ctx, wrongDevID)
|
|
}
|
|
|
|
assert.Nil(b, prof)
|
|
assert.Nil(b, dev)
|
|
assert.ErrorIs(b, err, profiledb.ErrDeviceNotFound)
|
|
})
|
|
|
|
// Most recent results:
|
|
//
|
|
// goos: linux
|
|
// goarch: amd64
|
|
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/profiledb
|
|
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
|
// BenchmarkDefault_ProfileByDeviceID/success-16 31104525 37.61 ns/op 0 B/op 0 allocs/op
|
|
// BenchmarkDefault_ProfileByDeviceID/not_found-16 60978588 18.41 ns/op 0 B/op 0 allocs/op
|
|
}
|
|
|
|
func BenchmarkDefault_ProfileByLinkedIP(b *testing.B) {
|
|
existingDev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
LinkedIP: testClientIPv4,
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 1)
|
|
devicesCh <- []*agd.Device{existingDev}
|
|
db := newDefaultProfileDB(b, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
|
|
var (
|
|
prof *agd.Profile
|
|
dev *agd.Device
|
|
err error
|
|
)
|
|
|
|
b.Run("success", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
prof, dev, err = db.ProfileByLinkedIP(ctx, testClientIPv4)
|
|
}
|
|
|
|
assert.NotNil(b, prof)
|
|
assert.NotNil(b, dev)
|
|
assert.NoError(b, err)
|
|
})
|
|
|
|
b.Run("not_found", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
prof, dev, err = db.ProfileByLinkedIP(ctx, testOtherClientIPv4)
|
|
}
|
|
|
|
assert.Nil(b, prof)
|
|
assert.Nil(b, dev)
|
|
assert.ErrorIs(b, err, profiledb.ErrDeviceNotFound)
|
|
})
|
|
|
|
// Most recent results:
|
|
//
|
|
// goos: linux
|
|
// goarch: amd64
|
|
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/profiledb
|
|
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
|
// BenchmarkDefault_ProfileByLinkedIP/success-16 18498074 63.25 ns/op 0 B/op 0 allocs/op
|
|
// BenchmarkDefault_ProfileByLinkedIP/not_found-16 46330509 23.94 ns/op 0 B/op 0 allocs/op
|
|
}
|
|
|
|
func BenchmarkDefault_ProfileByDedicatedIP(b *testing.B) {
|
|
existingDev := &agd.Device{
|
|
ID: profiledbtest.DeviceID,
|
|
DedicatedIPs: []netip.Addr{
|
|
testClientIPv4,
|
|
},
|
|
}
|
|
|
|
devicesCh := make(chan []*agd.Device, 1)
|
|
devicesCh <- []*agd.Device{existingDev}
|
|
db := newDefaultProfileDB(b, devicesCh)
|
|
|
|
ctx := context.Background()
|
|
|
|
var (
|
|
prof *agd.Profile
|
|
dev *agd.Device
|
|
err error
|
|
)
|
|
|
|
b.Run("success", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
prof, dev, err = db.ProfileByDedicatedIP(ctx, testClientIPv4)
|
|
}
|
|
|
|
assert.NotNil(b, prof)
|
|
assert.NotNil(b, dev)
|
|
assert.NoError(b, err)
|
|
})
|
|
|
|
b.Run("not_found", func(b *testing.B) {
|
|
b.ReportAllocs()
|
|
for b.Loop() {
|
|
prof, dev, err = db.ProfileByDedicatedIP(ctx, testOtherClientIPv4)
|
|
}
|
|
|
|
assert.Nil(b, prof)
|
|
assert.Nil(b, dev)
|
|
assert.ErrorIs(b, err, profiledb.ErrDeviceNotFound)
|
|
})
|
|
|
|
// Most recent results:
|
|
//
|
|
// goos: linux
|
|
// goarch: amd64
|
|
// pkg: github.com/AdguardTeam/AdGuardDNS/internal/profiledb
|
|
// cpu: AMD Ryzen 7 PRO 4750U with Radeon Graphics
|
|
// BenchmarkDefault_ProfileByDedicatedIP/success-16 18668960 63.51 ns/op 0 B/op 0 allocs/op
|
|
// BenchmarkDefault_ProfileByDedicatedIP/not_found-16 57252513 19.94 ns/op 0 B/op 0 allocs/op
|
|
}
|