util/syspolicy, ipn/ipnlocal: update syspolicy package to utilize syspolicy/rsop
In this PR, we update the syspolicy package to utilize syspolicy/rsop under the hood, and remove syspolicy.CachingHandler, syspolicy.windowsHandler and related code which is no longer used. We mark the syspolicy.Handler interface and RegisterHandler/SetHandlerForTest functions as deprecated, but keep them temporarily until they are no longer used in other repos. We also update the package to register setting definitions for all existing policy settings and to register the Registry-based, Windows-specific policy stores when running on Windows. Finally, we update existing internal and external tests to use the new API and add a few more tests and benchmarks. Updates #12687 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
@ -9,57 +9,15 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||
"tailscale.com/util/syspolicy/internal/metrics"
|
||||
"tailscale.com/util/syspolicy/setting"
|
||||
"tailscale.com/util/syspolicy/source"
|
||||
)
|
||||
|
||||
// testHandler encompasses all data types returned when testing any of the syspolicy
|
||||
// methods that involve getting a policy value.
|
||||
// For keys and the corresponding values, check policy_keys.go.
|
||||
type testHandler struct {
|
||||
t *testing.T
|
||||
key Key
|
||||
s string
|
||||
u64 uint64
|
||||
b bool
|
||||
sArr []string
|
||||
err error
|
||||
calls int // used for testing reads from cache vs. handler
|
||||
}
|
||||
|
||||
var someOtherError = errors.New("error other than not found")
|
||||
|
||||
func (th *testHandler) ReadString(key string) (string, error) {
|
||||
if key != string(th.key) {
|
||||
th.t.Errorf("ReadString(%q) want %q", key, th.key)
|
||||
}
|
||||
th.calls++
|
||||
return th.s, th.err
|
||||
}
|
||||
|
||||
func (th *testHandler) ReadUInt64(key string) (uint64, error) {
|
||||
if key != string(th.key) {
|
||||
th.t.Errorf("ReadUint64(%q) want %q", key, th.key)
|
||||
}
|
||||
th.calls++
|
||||
return th.u64, th.err
|
||||
}
|
||||
|
||||
func (th *testHandler) ReadBoolean(key string) (bool, error) {
|
||||
if key != string(th.key) {
|
||||
th.t.Errorf("ReadBool(%q) want %q", key, th.key)
|
||||
}
|
||||
th.calls++
|
||||
return th.b, th.err
|
||||
}
|
||||
|
||||
func (th *testHandler) ReadStringArray(key string) ([]string, error) {
|
||||
if key != string(th.key) {
|
||||
th.t.Errorf("ReadStringArray(%q) want %q", key, th.key)
|
||||
}
|
||||
th.calls++
|
||||
return th.sArr, th.err
|
||||
}
|
||||
|
||||
func TestGetString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
@ -69,23 +27,28 @@ func TestGetString(t *testing.T) {
|
||||
defaultValue string
|
||||
wantValue string
|
||||
wantError error
|
||||
wantMetrics []metrics.TestState
|
||||
}{
|
||||
{
|
||||
name: "read existing value",
|
||||
key: AdminConsoleVisibility,
|
||||
handlerValue: "hide",
|
||||
wantValue: "hide",
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AdminConsole", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read non-existing value",
|
||||
key: EnableServerMode,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
name: "read non-existing value, non-blank default",
|
||||
key: EnableServerMode,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
defaultValue: "test",
|
||||
wantValue: "test",
|
||||
wantError: nil,
|
||||
@ -95,24 +58,43 @@ func TestGetString(t *testing.T) {
|
||||
key: NetworkDevicesVisibility,
|
||||
handlerError: someOtherError,
|
||||
wantError: someOtherError,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_NetworkDevices_error", Value: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RegisterWellKnownSettingsForTest(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
s: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
h := metrics.NewTestHandler(t)
|
||||
metrics.SetHooksForTest(t, h.AddMetric, h.SetMetric)
|
||||
|
||||
s := source.TestSetting[string]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetString(tt.key, tt.defaultValue)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if value != tt.wantValue {
|
||||
t.Errorf("value=%v, want %v", value, tt.wantValue)
|
||||
}
|
||||
wantMetrics := tt.wantMetrics
|
||||
if !metrics.ShouldReport() {
|
||||
// Check that metrics are not reported on platforms
|
||||
// where they shouldn't be reported.
|
||||
// As of 2024-09-04, syspolicy only reports metrics
|
||||
// on Windows and Android.
|
||||
wantMetrics = nil
|
||||
}
|
||||
h.MustEqual(wantMetrics...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -129,7 +111,7 @@ func TestGetUint64(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "read existing value",
|
||||
key: KeyExpirationNoticeTime,
|
||||
key: LogSCMInteractions,
|
||||
handlerValue: 1,
|
||||
wantValue: 1,
|
||||
},
|
||||
@ -137,14 +119,14 @@ func TestGetUint64(t *testing.T) {
|
||||
name: "read non-existing value",
|
||||
key: LogSCMInteractions,
|
||||
handlerValue: 0,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: 0,
|
||||
},
|
||||
{
|
||||
name: "read non-existing value, non-zero default",
|
||||
key: LogSCMInteractions,
|
||||
defaultValue: 2,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: 2,
|
||||
},
|
||||
{
|
||||
@ -157,14 +139,23 @@ func TestGetUint64(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
u64: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
// None of the policy settings tested here are integers.
|
||||
// In fact, we don't have any integer policies as of 2024-10-08.
|
||||
// However, we can register each of them as an integer policy setting
|
||||
// for the duration of the test, providing us with something to test against.
|
||||
if err := setting.SetDefinitionsForTest(t, setting.NewDefinition(tt.key, setting.DeviceSetting, setting.IntegerValue)); err != nil {
|
||||
t.Fatalf("SetDefinitionsForTest failed: %v", err)
|
||||
}
|
||||
|
||||
s := source.TestSetting[uint64]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetUint64(tt.key, tt.defaultValue)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if value != tt.wantValue {
|
||||
@ -183,45 +174,69 @@ func TestGetBoolean(t *testing.T) {
|
||||
defaultValue bool
|
||||
wantValue bool
|
||||
wantError error
|
||||
wantMetrics []metrics.TestState
|
||||
}{
|
||||
{
|
||||
name: "read existing value",
|
||||
key: FlushDNSOnSessionUnlock,
|
||||
handlerValue: true,
|
||||
wantValue: true,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_FlushDNSOnSessionUnlock", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read non-existing value",
|
||||
key: LogSCMInteractions,
|
||||
handlerValue: false,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: false,
|
||||
},
|
||||
{
|
||||
name: "reading value returns other error",
|
||||
key: FlushDNSOnSessionUnlock,
|
||||
handlerError: someOtherError,
|
||||
wantError: someOtherError,
|
||||
wantError: someOtherError, // expect error...
|
||||
defaultValue: true,
|
||||
wantValue: false,
|
||||
wantValue: true, // ...AND default value if the handler fails.
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_FlushDNSOnSessionUnlock_error", Value: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RegisterWellKnownSettingsForTest(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
b: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
h := metrics.NewTestHandler(t)
|
||||
metrics.SetHooksForTest(t, h.AddMetric, h.SetMetric)
|
||||
|
||||
s := source.TestSetting[bool]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetBoolean(tt.key, tt.defaultValue)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if value != tt.wantValue {
|
||||
t.Errorf("value=%v, want %v", value, tt.wantValue)
|
||||
}
|
||||
wantMetrics := tt.wantMetrics
|
||||
if !metrics.ShouldReport() {
|
||||
// Check that metrics are not reported on platforms
|
||||
// where they shouldn't be reported.
|
||||
// As of 2024-09-04, syspolicy only reports metrics
|
||||
// on Windows and Android.
|
||||
wantMetrics = nil
|
||||
}
|
||||
h.MustEqual(wantMetrics...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -234,29 +249,42 @@ func TestGetPreferenceOption(t *testing.T) {
|
||||
handlerError error
|
||||
wantValue setting.PreferenceOption
|
||||
wantError error
|
||||
wantMetrics []metrics.TestState
|
||||
}{
|
||||
{
|
||||
name: "always by policy",
|
||||
key: EnableIncomingConnections,
|
||||
handlerValue: "always",
|
||||
wantValue: setting.AlwaysByPolicy,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AllowIncomingConnections", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "never by policy",
|
||||
key: EnableIncomingConnections,
|
||||
handlerValue: "never",
|
||||
wantValue: setting.NeverByPolicy,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AllowIncomingConnections", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "use default",
|
||||
key: EnableIncomingConnections,
|
||||
handlerValue: "",
|
||||
wantValue: setting.ShowChoiceByPolicy,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AllowIncomingConnections", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read non-existing value",
|
||||
key: EnableIncomingConnections,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: setting.ShowChoiceByPolicy,
|
||||
},
|
||||
{
|
||||
@ -265,24 +293,43 @@ func TestGetPreferenceOption(t *testing.T) {
|
||||
handlerError: someOtherError,
|
||||
wantValue: setting.ShowChoiceByPolicy,
|
||||
wantError: someOtherError,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_AllowIncomingConnections_error", Value: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RegisterWellKnownSettingsForTest(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
s: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
h := metrics.NewTestHandler(t)
|
||||
metrics.SetHooksForTest(t, h.AddMetric, h.SetMetric)
|
||||
|
||||
s := source.TestSetting[string]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
option, err := GetPreferenceOption(tt.key)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if option != tt.wantValue {
|
||||
t.Errorf("option=%v, want %v", option, tt.wantValue)
|
||||
}
|
||||
wantMetrics := tt.wantMetrics
|
||||
if !metrics.ShouldReport() {
|
||||
// Check that metrics are not reported on platforms
|
||||
// where they shouldn't be reported.
|
||||
// As of 2024-09-04, syspolicy only reports metrics
|
||||
// on Windows and Android.
|
||||
wantMetrics = nil
|
||||
}
|
||||
h.MustEqual(wantMetrics...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -295,24 +342,33 @@ func TestGetVisibility(t *testing.T) {
|
||||
handlerError error
|
||||
wantValue setting.Visibility
|
||||
wantError error
|
||||
wantMetrics []metrics.TestState
|
||||
}{
|
||||
{
|
||||
name: "hidden by policy",
|
||||
key: AdminConsoleVisibility,
|
||||
handlerValue: "hide",
|
||||
wantValue: setting.HiddenByPolicy,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AdminConsole", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "visibility default",
|
||||
key: AdminConsoleVisibility,
|
||||
handlerValue: "show",
|
||||
wantValue: setting.VisibleByPolicy,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AdminConsole", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read non-existing value",
|
||||
key: AdminConsoleVisibility,
|
||||
handlerValue: "show",
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: setting.VisibleByPolicy,
|
||||
},
|
||||
{
|
||||
@ -322,24 +378,43 @@ func TestGetVisibility(t *testing.T) {
|
||||
handlerError: someOtherError,
|
||||
wantValue: setting.VisibleByPolicy,
|
||||
wantError: someOtherError,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_AdminConsole_error", Value: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RegisterWellKnownSettingsForTest(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
s: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
h := metrics.NewTestHandler(t)
|
||||
metrics.SetHooksForTest(t, h.AddMetric, h.SetMetric)
|
||||
|
||||
s := source.TestSetting[string]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
visibility, err := GetVisibility(tt.key)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if visibility != tt.wantValue {
|
||||
t.Errorf("visibility=%v, want %v", visibility, tt.wantValue)
|
||||
}
|
||||
wantMetrics := tt.wantMetrics
|
||||
if !metrics.ShouldReport() {
|
||||
// Check that metrics are not reported on platforms
|
||||
// where they shouldn't be reported.
|
||||
// As of 2024-09-04, syspolicy only reports metrics
|
||||
// on Windows and Android.
|
||||
wantMetrics = nil
|
||||
}
|
||||
h.MustEqual(wantMetrics...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -353,6 +428,7 @@ func TestGetDuration(t *testing.T) {
|
||||
defaultValue time.Duration
|
||||
wantValue time.Duration
|
||||
wantError error
|
||||
wantMetrics []metrics.TestState
|
||||
}{
|
||||
{
|
||||
name: "read existing value",
|
||||
@ -360,25 +436,34 @@ func TestGetDuration(t *testing.T) {
|
||||
handlerValue: "2h",
|
||||
wantValue: 2 * time.Hour,
|
||||
defaultValue: 24 * time.Hour,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_KeyExpirationNotice", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid duration value",
|
||||
key: KeyExpirationNoticeTime,
|
||||
handlerValue: "-20",
|
||||
wantValue: 24 * time.Hour,
|
||||
wantError: errors.New(`time: missing unit in duration "-20"`),
|
||||
defaultValue: 24 * time.Hour,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_KeyExpirationNotice_error", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read non-existing value",
|
||||
key: KeyExpirationNoticeTime,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: 24 * time.Hour,
|
||||
defaultValue: 24 * time.Hour,
|
||||
},
|
||||
{
|
||||
name: "read non-existing value different default",
|
||||
key: KeyExpirationNoticeTime,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantValue: 0 * time.Second,
|
||||
defaultValue: 0 * time.Second,
|
||||
},
|
||||
@ -389,24 +474,43 @@ func TestGetDuration(t *testing.T) {
|
||||
wantValue: 24 * time.Hour,
|
||||
wantError: someOtherError,
|
||||
defaultValue: 24 * time.Hour,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_KeyExpirationNotice_error", Value: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RegisterWellKnownSettingsForTest(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
s: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
h := metrics.NewTestHandler(t)
|
||||
metrics.SetHooksForTest(t, h.AddMetric, h.SetMetric)
|
||||
|
||||
s := source.TestSetting[string]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
duration, err := GetDuration(tt.key, tt.defaultValue)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if duration != tt.wantValue {
|
||||
t.Errorf("duration=%v, want %v", duration, tt.wantValue)
|
||||
}
|
||||
wantMetrics := tt.wantMetrics
|
||||
if !metrics.ShouldReport() {
|
||||
// Check that metrics are not reported on platforms
|
||||
// where they shouldn't be reported.
|
||||
// As of 2024-09-04, syspolicy only reports metrics
|
||||
// on Windows and Android.
|
||||
wantMetrics = nil
|
||||
}
|
||||
h.MustEqual(wantMetrics...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -420,23 +524,28 @@ func TestGetStringArray(t *testing.T) {
|
||||
defaultValue []string
|
||||
wantValue []string
|
||||
wantError error
|
||||
wantMetrics []metrics.TestState
|
||||
}{
|
||||
{
|
||||
name: "read existing value",
|
||||
key: AllowedSuggestedExitNodes,
|
||||
handlerValue: []string{"foo", "bar"},
|
||||
wantValue: []string{"foo", "bar"},
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_any", Value: 1},
|
||||
{Name: "$os_syspolicy_AllowedSuggestedExitNodes", Value: 1},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "read non-existing value",
|
||||
key: AllowedSuggestedExitNodes,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
wantError: nil,
|
||||
},
|
||||
{
|
||||
name: "read non-existing value, non nil default",
|
||||
key: AllowedSuggestedExitNodes,
|
||||
handlerError: ErrNoSuchKey,
|
||||
handlerError: ErrNotConfigured,
|
||||
defaultValue: []string{"foo", "bar"},
|
||||
wantValue: []string{"foo", "bar"},
|
||||
wantError: nil,
|
||||
@ -446,28 +555,68 @@ func TestGetStringArray(t *testing.T) {
|
||||
key: AllowedSuggestedExitNodes,
|
||||
handlerError: someOtherError,
|
||||
wantError: someOtherError,
|
||||
wantMetrics: []metrics.TestState{
|
||||
{Name: "$os_syspolicy_errors", Value: 1},
|
||||
{Name: "$os_syspolicy_AllowedSuggestedExitNodes_error", Value: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
RegisterWellKnownSettingsForTest(t)
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
SetHandlerForTest(t, &testHandler{
|
||||
t: t,
|
||||
key: tt.key,
|
||||
sArr: tt.handlerValue,
|
||||
err: tt.handlerError,
|
||||
})
|
||||
h := metrics.NewTestHandler(t)
|
||||
metrics.SetHooksForTest(t, h.AddMetric, h.SetMetric)
|
||||
|
||||
s := source.TestSetting[[]string]{
|
||||
Key: tt.key,
|
||||
Value: tt.handlerValue,
|
||||
Error: tt.handlerError,
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetStringArray(tt.key, tt.defaultValue)
|
||||
if err != tt.wantError {
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
if !slices.Equal(tt.wantValue, value) {
|
||||
t.Errorf("value=%v, want %v", value, tt.wantValue)
|
||||
}
|
||||
wantMetrics := tt.wantMetrics
|
||||
if !metrics.ShouldReport() {
|
||||
// Check that metrics are not reported on platforms
|
||||
// where they shouldn't be reported.
|
||||
// As of 2024-09-04, syspolicy only reports metrics
|
||||
// on Windows and Android.
|
||||
wantMetrics = nil
|
||||
}
|
||||
h.MustEqual(wantMetrics...)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func registerSingleSettingStoreForTest[T source.TestValueType](tb TB, s source.TestSetting[T]) {
|
||||
policyStore := source.NewTestStoreOf(tb, s)
|
||||
MustRegisterStoreForTest(tb, "TestStore", setting.DeviceScope, policyStore)
|
||||
}
|
||||
|
||||
func BenchmarkGetString(b *testing.B) {
|
||||
loggerx.SetForTest(b, logger.Discard, logger.Discard)
|
||||
RegisterWellKnownSettingsForTest(b)
|
||||
|
||||
wantControlURL := "https://login.tailscale.com"
|
||||
registerSingleSettingStoreForTest(b, source.TestSettingOf(ControlURL, wantControlURL))
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
gotControlURL, _ := GetString(ControlURL, "https://controlplane.tailscale.com")
|
||||
if gotControlURL != wantControlURL {
|
||||
b.Fatalf("got %v; want %v", gotControlURL, wantControlURL)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectControlURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
reg, disk, want string
|
||||
@ -499,3 +648,13 @@ func TestSelectControlURL(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func errorsMatchForTest(got, want error) bool {
|
||||
if got == nil && want == nil {
|
||||
return true
|
||||
}
|
||||
if got == nil || want == nil {
|
||||
return false
|
||||
}
|
||||
return errors.Is(got, want) || got.Error() == want.Error()
|
||||
}
|
||||
|
Reference in New Issue
Block a user