util/syspolicy/setting: make setting.Snapshot JSON-marshallable

We make setting.Snapshot JSON-marshallable in preparation for returning it from the LocalAPI.

Updates #12687

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl
2024-10-29 11:29:02 -05:00
committed by Nick Khyl
parent 2a2228f97b
commit 540e4c83d0
2 changed files with 180 additions and 0 deletions
+135
View File
@@ -4,8 +4,13 @@
package setting
import (
"cmp"
"encoding/json"
"testing"
"time"
jsonv2 "github.com/go-json-experiment/json"
"tailscale.com/util/syspolicy/internal"
)
func TestMergeSnapshots(t *testing.T) {
@@ -432,3 +437,133 @@ Setting3 = user-decides`,
})
}
}
func TestMarshalUnmarshalSnapshot(t *testing.T) {
tests := []struct {
name string
snapshot *Snapshot
wantJSON string
wantBack *Snapshot
}{
{
name: "Nil",
snapshot: (*Snapshot)(nil),
wantJSON: "null",
wantBack: NewSnapshot(nil),
},
{
name: "Zero",
snapshot: &Snapshot{},
wantJSON: "{}",
},
{
name: "Bool/True",
snapshot: NewSnapshot(map[Key]RawItem{"BoolPolicy": RawItemOf(true)}),
wantJSON: `{"Settings": {"BoolPolicy": {"Value": true}}}`,
},
{
name: "Bool/False",
snapshot: NewSnapshot(map[Key]RawItem{"BoolPolicy": RawItemOf(false)}),
wantJSON: `{"Settings": {"BoolPolicy": {"Value": false}}}`,
},
{
name: "String/Non-Empty",
snapshot: NewSnapshot(map[Key]RawItem{"StringPolicy": RawItemOf("StringValue")}),
wantJSON: `{"Settings": {"StringPolicy": {"Value": "StringValue"}}}`,
},
{
name: "String/Empty",
snapshot: NewSnapshot(map[Key]RawItem{"StringPolicy": RawItemOf("")}),
wantJSON: `{"Settings": {"StringPolicy": {"Value": ""}}}`,
},
{
name: "Integer/NonZero",
snapshot: NewSnapshot(map[Key]RawItem{"IntPolicy": RawItemOf(uint64(42))}),
wantJSON: `{"Settings": {"IntPolicy": {"Value": 42}}}`,
},
{
name: "Integer/Zero",
snapshot: NewSnapshot(map[Key]RawItem{"IntPolicy": RawItemOf(uint64(0))}),
wantJSON: `{"Settings": {"IntPolicy": {"Value": 0}}}`,
},
{
name: "String-List",
snapshot: NewSnapshot(map[Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}),
wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`,
},
{
name: "Empty/With-Summary",
snapshot: NewSnapshot(
map[Key]RawItem{},
SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
),
wantJSON: `{"Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"}}`,
},
{
name: "Setting/With-Summary",
snapshot: NewSnapshot(
map[Key]RawItem{"PolicySetting": RawItemOf(uint64(42))},
SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
),
wantJSON: `{
"Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"},
"Settings": {"PolicySetting": {"Value": 42}}
}`,
},
{
name: "Settings/With-Origins",
snapshot: NewSnapshot(
map[Key]RawItem{
"SettingA": RawItemWith(uint64(42), nil, NewNamedOrigin("SourceA", DeviceScope)),
"SettingB": RawItemWith("B", nil, NewNamedOrigin("SourceB", CurrentProfileScope)),
"SettingC": RawItemWith(true, nil, NewNamedOrigin("SourceC", CurrentUserScope)),
},
),
wantJSON: `{
"Settings": {
"SettingA": {"Value": 42, "Origin": {"Name": "SourceA", "Scope": "Device"}},
"SettingB": {"Value": "B", "Origin": {"Name": "SourceB", "Scope": "Profile"}},
"SettingC": {"Value": true, "Origin": {"Name": "SourceC", "Scope": "User"}}
}
}`,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
doTest := func(t *testing.T, useJSONv2 bool) {
var gotJSON []byte
var err error
if useJSONv2 {
gotJSON, err = jsonv2.Marshal(tt.snapshot)
} else {
gotJSON, err = json.Marshal(tt.snapshot)
}
if err != nil {
t.Fatal(err)
}
if got, want, equal := internal.EqualJSONForTest(t, gotJSON, []byte(tt.wantJSON)); !equal {
t.Errorf("JSON: got %s; want %s", got, want)
}
gotBack := &Snapshot{}
if useJSONv2 {
err = jsonv2.Unmarshal(gotJSON, &gotBack)
} else {
err = json.Unmarshal(gotJSON, &gotBack)
}
if err != nil {
t.Fatal(err)
}
if wantBack := cmp.Or(tt.wantBack, tt.snapshot); !gotBack.Equal(wantBack) {
t.Errorf("Snapshot: got %+v; want %+v", gotBack, wantBack)
}
}
t.Run("json", func(t *testing.T) { doTest(t, false) })
t.Run("jsonv2", func(t *testing.T) { doTest(t, true) })
})
}
}