mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-25 19:15:34 +00:00
util/syspolicy/setting: make setting.RawItem JSON-marshallable
We add setting.RawValue, a new type that facilitates unmarshalling JSON numbers and arrays as uint64 and []string (instead of float64 and []any) for policy setting values. We then use it to make setting.RawItem JSON-marshallable and update the tests. Updates #12687 Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
parent
2cc1100d24
commit
2a2228f97b
@ -36,7 +36,7 @@ func ValueOf[T any](v T) Value[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// String implements [fmt.Stringer].
|
// String implements [fmt.Stringer].
|
||||||
func (o *Value[T]) String() string {
|
func (o Value[T]) String() string {
|
||||||
if !o.set {
|
if !o.set {
|
||||||
return fmt.Sprintf("(empty[%T])", o.value)
|
return fmt.Sprintf("(empty[%T])", o.value)
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
jsonv2 "github.com/go-json-experiment/json"
|
||||||
|
"github.com/go-json-experiment/json/jsontext"
|
||||||
|
"tailscale.com/types/opt"
|
||||||
"tailscale.com/types/structs"
|
"tailscale.com/types/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,10 +21,15 @@
|
|||||||
// or converted from strings, these setting types predate the typed policy
|
// or converted from strings, these setting types predate the typed policy
|
||||||
// hierarchies, and must be supported at this layer.
|
// hierarchies, and must be supported at this layer.
|
||||||
type RawItem struct {
|
type RawItem struct {
|
||||||
_ structs.Incomparable
|
_ structs.Incomparable
|
||||||
value any
|
data rawItemJSON
|
||||||
err *ErrorText
|
}
|
||||||
origin *Origin // or nil
|
|
||||||
|
// rawItemJSON holds JSON-marshallable data for [RawItem].
|
||||||
|
type rawItemJSON struct {
|
||||||
|
Value RawValue `json:",omitzero"`
|
||||||
|
Error *ErrorText `json:",omitzero"` // or nil
|
||||||
|
Origin *Origin `json:",omitzero"` // or nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RawItemOf returns a [RawItem] with the specified value.
|
// RawItemOf returns a [RawItem] with the specified value.
|
||||||
@ -30,20 +39,20 @@ func RawItemOf(value any) RawItem {
|
|||||||
|
|
||||||
// RawItemWith returns a [RawItem] with the specified value, error and origin.
|
// RawItemWith returns a [RawItem] with the specified value, error and origin.
|
||||||
func RawItemWith(value any, err *ErrorText, origin *Origin) RawItem {
|
func RawItemWith(value any, err *ErrorText, origin *Origin) RawItem {
|
||||||
return RawItem{value: value, err: err, origin: origin}
|
return RawItem{data: rawItemJSON{Value: RawValue{opt.ValueOf(value)}, Error: err, Origin: origin}}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Value returns the value of the policy setting, or nil if the policy setting
|
// Value returns the value of the policy setting, or nil if the policy setting
|
||||||
// is not configured, or an error occurred while reading it.
|
// is not configured, or an error occurred while reading it.
|
||||||
func (i RawItem) Value() any {
|
func (i RawItem) Value() any {
|
||||||
return i.value
|
return i.data.Value.Get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error returns the error that occurred when reading the policy setting,
|
// Error returns the error that occurred when reading the policy setting,
|
||||||
// or nil if no error occurred.
|
// or nil if no error occurred.
|
||||||
func (i RawItem) Error() error {
|
func (i RawItem) Error() error {
|
||||||
if i.err != nil {
|
if i.data.Error != nil {
|
||||||
return i.err
|
return i.data.Error
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -51,17 +60,103 @@ func (i RawItem) Error() error {
|
|||||||
// Origin returns an optional [Origin] indicating where the policy setting is
|
// Origin returns an optional [Origin] indicating where the policy setting is
|
||||||
// configured.
|
// configured.
|
||||||
func (i RawItem) Origin() *Origin {
|
func (i RawItem) Origin() *Origin {
|
||||||
return i.origin
|
return i.data.Origin
|
||||||
}
|
}
|
||||||
|
|
||||||
// String implements [fmt.Stringer].
|
// String implements [fmt.Stringer].
|
||||||
func (i RawItem) String() string {
|
func (i RawItem) String() string {
|
||||||
var suffix string
|
var suffix string
|
||||||
if i.origin != nil {
|
if i.data.Origin != nil {
|
||||||
suffix = fmt.Sprintf(" - {%v}", i.origin)
|
suffix = fmt.Sprintf(" - {%v}", i.data.Origin)
|
||||||
}
|
}
|
||||||
if i.err != nil {
|
if i.data.Error != nil {
|
||||||
return fmt.Sprintf("Error{%q}%s", i.err.Error(), suffix)
|
return fmt.Sprintf("Error{%q}%s", i.data.Error.Error(), suffix)
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%v%s", i.value, suffix)
|
return fmt.Sprintf("%v%s", i.data.Value.Value, suffix)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSONV2 implements [jsonv2.MarshalerV2].
|
||||||
|
func (i RawItem) MarshalJSONV2(out *jsontext.Encoder, opts jsonv2.Options) error {
|
||||||
|
return jsonv2.MarshalEncode(out, &i.data, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSONV2 implements [jsonv2.UnmarshalerV2].
|
||||||
|
func (i *RawItem) UnmarshalJSONV2(in *jsontext.Decoder, opts jsonv2.Options) error {
|
||||||
|
return jsonv2.UnmarshalDecode(in, &i.data, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements [json.Marshaler].
|
||||||
|
func (i RawItem) MarshalJSON() ([]byte, error) {
|
||||||
|
return jsonv2.Marshal(i) // uses MarshalJSONV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements [json.Unmarshaler].
|
||||||
|
func (i *RawItem) UnmarshalJSON(b []byte) error {
|
||||||
|
return jsonv2.Unmarshal(b, i) // uses UnmarshalJSONV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawValue represents a raw policy setting value read from a policy store.
|
||||||
|
// It is JSON-marshallable and facilitates unmarshalling of JSON values
|
||||||
|
// into corresponding policy setting types, with special handling for JSON numbers
|
||||||
|
// (unmarshalled as float64) and JSON string arrays (unmarshalled as []string).
|
||||||
|
// See also [RawValue.UnmarshalJSONV2].
|
||||||
|
type RawValue struct {
|
||||||
|
opt.Value[any]
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawValueType is a constraint that permits raw setting value types.
|
||||||
|
type RawValueType interface {
|
||||||
|
bool | uint64 | string | []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawValueOf returns a new [RawValue] holding the specified value.
|
||||||
|
func RawValueOf[T RawValueType](v T) RawValue {
|
||||||
|
return RawValue{opt.ValueOf[any](v)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSONV2 implements [jsonv2.MarshalerV2].
|
||||||
|
func (v RawValue) MarshalJSONV2(out *jsontext.Encoder, opts jsonv2.Options) error {
|
||||||
|
return jsonv2.MarshalEncode(out, v.Value, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSONV2 implements [jsonv2.UnmarshalerV2] by attempting to unmarshal
|
||||||
|
// a JSON value as one of the supported policy setting value types (bool, string, uint64, or []string),
|
||||||
|
// based on the JSON value type. It fails if the JSON value is an object, if it's a JSON number that
|
||||||
|
// cannot be represented as a uint64, or if a JSON array contains anything other than strings.
|
||||||
|
func (v *RawValue) UnmarshalJSONV2(in *jsontext.Decoder, opts jsonv2.Options) error {
|
||||||
|
var valPtr any
|
||||||
|
switch k := in.PeekKind(); k {
|
||||||
|
case 't', 'f':
|
||||||
|
valPtr = new(bool)
|
||||||
|
case '"':
|
||||||
|
valPtr = new(string)
|
||||||
|
case '0':
|
||||||
|
valPtr = new(uint64) // unmarshal JSON numbers as uint64
|
||||||
|
case '[', 'n':
|
||||||
|
valPtr = new([]string) // unmarshal arrays as string slices
|
||||||
|
case '{':
|
||||||
|
return fmt.Errorf("unexpected token: %v", k)
|
||||||
|
default:
|
||||||
|
panic("unreachable")
|
||||||
|
}
|
||||||
|
if err := jsonv2.UnmarshalDecode(in, valPtr, opts); err != nil {
|
||||||
|
v.Value.Clear()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
value := reflect.ValueOf(valPtr).Elem().Interface()
|
||||||
|
v.Value = opt.ValueOf(value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements [json.Marshaler].
|
||||||
|
func (v RawValue) MarshalJSON() ([]byte, error) {
|
||||||
|
return jsonv2.Marshal(v) // uses MarshalJSONV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON implements [json.Unmarshaler].
|
||||||
|
func (v *RawValue) UnmarshalJSON(b []byte) error {
|
||||||
|
return jsonv2.Unmarshal(b, v) // uses UnmarshalJSONV2
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawValues is a map of keyed setting values that can be read from a JSON.
|
||||||
|
type RawValues map[Key]RawValue
|
||||||
|
101
util/syspolicy/setting/raw_item_test.go
Normal file
101
util/syspolicy/setting/raw_item_test.go
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
package setting
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
jsonv2 "github.com/go-json-experiment/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMarshalUnmarshalRawValue(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
json string
|
||||||
|
want RawValue
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Bool/True",
|
||||||
|
json: `true`,
|
||||||
|
want: RawValueOf(true),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Bool/False",
|
||||||
|
json: `false`,
|
||||||
|
want: RawValueOf(false),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "String/Empty",
|
||||||
|
json: `""`,
|
||||||
|
want: RawValueOf(""),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "String/NonEmpty",
|
||||||
|
json: `"Test"`,
|
||||||
|
want: RawValueOf("Test"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "StringSlice/Null",
|
||||||
|
json: `null`,
|
||||||
|
want: RawValueOf([]string(nil)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "StringSlice/Empty",
|
||||||
|
json: `[]`,
|
||||||
|
want: RawValueOf([]string{}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "StringSlice/NonEmpty",
|
||||||
|
json: `["A", "B", "C"]`,
|
||||||
|
want: RawValueOf([]string{"A", "B", "C"}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "StringSlice/NonStrings",
|
||||||
|
json: `[1, 2, 3]`,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Number/Integer/0",
|
||||||
|
json: `0`,
|
||||||
|
want: RawValueOf(uint64(0)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Number/Integer/1",
|
||||||
|
json: `1`,
|
||||||
|
want: RawValueOf(uint64(1)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Number/Integer/MaxUInt64",
|
||||||
|
json: strconv.FormatUint(math.MaxUint64, 10),
|
||||||
|
want: RawValueOf(uint64(math.MaxUint64)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Number/Integer/Negative",
|
||||||
|
json: `-1`,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Object",
|
||||||
|
json: `{}`,
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
var got RawValue
|
||||||
|
gotErr := jsonv2.Unmarshal([]byte(tt.json), &got)
|
||||||
|
if (gotErr != nil) != tt.wantErr {
|
||||||
|
t.Fatalf("Error: got %v; want %v", gotErr, tt.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !tt.wantErr && !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Fatalf("Value: got %v; want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -30,134 +30,134 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
name: "first-nil",
|
name: "first-nil",
|
||||||
s1: nil,
|
s1: nil,
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "first-empty",
|
name: "first-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[Key]RawItem{}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "second-nil",
|
name: "second-nil",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
s2: nil,
|
s2: nil,
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "second-empty",
|
name: "second-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[Key]RawItem{}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-conflicts",
|
name: "no-conflicts",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
"Setting5": {value: VisibleByPolicy},
|
"Setting5": RawItemOf(VisibleByPolicy),
|
||||||
"Setting6": {value: ShowChoiceByPolicy},
|
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
"Setting5": {value: VisibleByPolicy},
|
"Setting5": RawItemOf(VisibleByPolicy),
|
||||||
"Setting6": {value: ShowChoiceByPolicy},
|
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-conflicts",
|
name: "with-conflicts",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 456},
|
"Setting1": RawItemOf(456),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 456},
|
"Setting1": RawItemOf(456),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-first-wins",
|
name: "with-scope-first-wins",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 456},
|
"Setting1": RawItemOf(456),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-second-wins",
|
name: "with-scope-second-wins",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 456},
|
"Setting1": RawItemOf(456),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 456},
|
"Setting1": RawItemOf(456),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -170,28 +170,27 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
name: "with-scope-first-empty",
|
name: "with-scope-first-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}, CurrentUserScope),
|
s1: NewSnapshot(map[Key]RawItem{}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true}},
|
"Setting3": RawItemOf(true)}, DeviceScope, NewNamedOrigin("TestPolicy", DeviceScope)),
|
||||||
DeviceScope, NewNamedOrigin("TestPolicy", DeviceScope)),
|
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}, CurrentUserScope, NewNamedOrigin("TestPolicy", DeviceScope)),
|
}, CurrentUserScope, NewNamedOrigin("TestPolicy", DeviceScope)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-second-empty",
|
name: "with-scope-second-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[Key]RawItem{}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -244,9 +243,9 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
name: "first-nil",
|
name: "first-nil",
|
||||||
s1: nil,
|
s1: nil,
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
wantEqualItems: false,
|
wantEqualItems: false,
|
||||||
@ -255,9 +254,9 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
name: "first-empty",
|
name: "first-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[Key]RawItem{}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
wantEqualItems: false,
|
wantEqualItems: false,
|
||||||
@ -265,9 +264,9 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "second-nil",
|
name: "second-nil",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: true},
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
s2: nil,
|
s2: nil,
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
@ -276,9 +275,9 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "second-empty",
|
name: "second-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[Key]RawItem{}),
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
@ -287,14 +286,14 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "same-items-same-order-no-scope",
|
name: "same-items-same-order-no-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
wantEqual: true,
|
wantEqual: true,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
@ -302,14 +301,14 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "same-items-same-order-same-scope",
|
name: "same-items-same-order-same-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
wantEqual: true,
|
wantEqual: true,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
@ -317,14 +316,14 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "same-items-different-order-same-scope",
|
name: "same-items-different-order-same-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
wantEqual: true,
|
wantEqual: true,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
@ -332,14 +331,14 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "same-items-same-order-different-scope",
|
name: "same-items-same-order-different-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
@ -347,14 +346,14 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "different-items-same-scope",
|
name: "different-items-same-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 123},
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": {value: "String"},
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": {value: false},
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting4": {value: 2 * time.Hour},
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
"Setting5": {value: VisibleByPolicy},
|
"Setting5": RawItemOf(VisibleByPolicy),
|
||||||
"Setting6": {value: ShowChoiceByPolicy},
|
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
wantEqualItems: false,
|
wantEqualItems: false,
|
||||||
@ -401,9 +400,9 @@ func TestSnapshotString(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "non-empty",
|
name: "non-empty",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{
|
snapshot: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 2 * time.Hour},
|
"Setting1": RawItemOf(2 * time.Hour),
|
||||||
"Setting2": {value: VisibleByPolicy},
|
"Setting2": RawItemOf(VisibleByPolicy),
|
||||||
"Setting3": {value: ShowChoiceByPolicy},
|
"Setting3": RawItemOf(ShowChoiceByPolicy),
|
||||||
}, NewNamedOrigin("Test Policy", DeviceScope)),
|
}, NewNamedOrigin("Test Policy", DeviceScope)),
|
||||||
wantString: `{Test Policy (Device)}
|
wantString: `{Test Policy (Device)}
|
||||||
Setting1 = 2h0m0s
|
Setting1 = 2h0m0s
|
||||||
@ -413,14 +412,14 @@ func TestSnapshotString(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "non-empty-with-item-origin",
|
name: "non-empty-with-item-origin",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{
|
snapshot: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {value: 42, origin: NewNamedOrigin("Test Policy", DeviceScope)},
|
"Setting1": RawItemWith(42, nil, NewNamedOrigin("Test Policy", DeviceScope)),
|
||||||
}),
|
}),
|
||||||
wantString: `Setting1 = 42 - {Test Policy (Device)}`,
|
wantString: `Setting1 = 42 - {Test Policy (Device)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "non-empty-with-item-error",
|
name: "non-empty-with-item-error",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{
|
snapshot: NewSnapshot(map[Key]RawItem{
|
||||||
"Setting1": {err: NewErrorText("bang!")},
|
"Setting1": RawItemWith(nil, NewErrorText("bang!"), nil),
|
||||||
}),
|
}),
|
||||||
wantString: `Setting1 = Error{"bang!"}`,
|
wantString: `Setting1 = Error{"bang!"}`,
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user