|
4 | 4 | package setting |
5 | 5 |
|
6 | 6 | import ( |
| 7 | + "cmp" |
| 8 | + "encoding/json" |
7 | 9 | "testing" |
8 | 10 | "time" |
| 11 | + |
| 12 | + jsonv2 "github.com/go-json-experiment/json" |
| 13 | + "tailscale.com/util/syspolicy/internal" |
9 | 14 | ) |
10 | 15 |
|
11 | 16 | func TestMergeSnapshots(t *testing.T) { |
@@ -432,3 +437,133 @@ Setting3 = user-decides`, |
432 | 437 | }) |
433 | 438 | } |
434 | 439 | } |
| 440 | + |
| 441 | +func TestMarshalUnmarshalSnapshot(t *testing.T) { |
| 442 | + tests := []struct { |
| 443 | + name string |
| 444 | + snapshot *Snapshot |
| 445 | + wantJSON string |
| 446 | + wantBack *Snapshot |
| 447 | + }{ |
| 448 | + { |
| 449 | + name: "Nil", |
| 450 | + snapshot: (*Snapshot)(nil), |
| 451 | + wantJSON: "null", |
| 452 | + wantBack: NewSnapshot(nil), |
| 453 | + }, |
| 454 | + { |
| 455 | + name: "Zero", |
| 456 | + snapshot: &Snapshot{}, |
| 457 | + wantJSON: "{}", |
| 458 | + }, |
| 459 | + { |
| 460 | + name: "Bool/True", |
| 461 | + snapshot: NewSnapshot(map[Key]RawItem{"BoolPolicy": RawItemOf(true)}), |
| 462 | + wantJSON: `{"Settings": {"BoolPolicy": {"Value": true}}}`, |
| 463 | + }, |
| 464 | + { |
| 465 | + name: "Bool/False", |
| 466 | + snapshot: NewSnapshot(map[Key]RawItem{"BoolPolicy": RawItemOf(false)}), |
| 467 | + wantJSON: `{"Settings": {"BoolPolicy": {"Value": false}}}`, |
| 468 | + }, |
| 469 | + { |
| 470 | + name: "String/Non-Empty", |
| 471 | + snapshot: NewSnapshot(map[Key]RawItem{"StringPolicy": RawItemOf("StringValue")}), |
| 472 | + wantJSON: `{"Settings": {"StringPolicy": {"Value": "StringValue"}}}`, |
| 473 | + }, |
| 474 | + { |
| 475 | + name: "String/Empty", |
| 476 | + snapshot: NewSnapshot(map[Key]RawItem{"StringPolicy": RawItemOf("")}), |
| 477 | + wantJSON: `{"Settings": {"StringPolicy": {"Value": ""}}}`, |
| 478 | + }, |
| 479 | + { |
| 480 | + name: "Integer/NonZero", |
| 481 | + snapshot: NewSnapshot(map[Key]RawItem{"IntPolicy": RawItemOf(uint64(42))}), |
| 482 | + wantJSON: `{"Settings": {"IntPolicy": {"Value": 42}}}`, |
| 483 | + }, |
| 484 | + { |
| 485 | + name: "Integer/Zero", |
| 486 | + snapshot: NewSnapshot(map[Key]RawItem{"IntPolicy": RawItemOf(uint64(0))}), |
| 487 | + wantJSON: `{"Settings": {"IntPolicy": {"Value": 0}}}`, |
| 488 | + }, |
| 489 | + { |
| 490 | + name: "String-List", |
| 491 | + snapshot: NewSnapshot(map[Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}), |
| 492 | + wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`, |
| 493 | + }, |
| 494 | + { |
| 495 | + name: "Empty/With-Summary", |
| 496 | + snapshot: NewSnapshot( |
| 497 | + map[Key]RawItem{}, |
| 498 | + SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)), |
| 499 | + ), |
| 500 | + wantJSON: `{"Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"}}`, |
| 501 | + }, |
| 502 | + { |
| 503 | + name: "Setting/With-Summary", |
| 504 | + snapshot: NewSnapshot( |
| 505 | + map[Key]RawItem{"PolicySetting": RawItemOf(uint64(42))}, |
| 506 | + SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)), |
| 507 | + ), |
| 508 | + wantJSON: `{ |
| 509 | + "Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"}, |
| 510 | + "Settings": {"PolicySetting": {"Value": 42}} |
| 511 | + }`, |
| 512 | + }, |
| 513 | + { |
| 514 | + name: "Settings/With-Origins", |
| 515 | + snapshot: NewSnapshot( |
| 516 | + map[Key]RawItem{ |
| 517 | + "SettingA": RawItemWith(uint64(42), nil, NewNamedOrigin("SourceA", DeviceScope)), |
| 518 | + "SettingB": RawItemWith("B", nil, NewNamedOrigin("SourceB", CurrentProfileScope)), |
| 519 | + "SettingC": RawItemWith(true, nil, NewNamedOrigin("SourceC", CurrentUserScope)), |
| 520 | + }, |
| 521 | + ), |
| 522 | + wantJSON: `{ |
| 523 | + "Settings": { |
| 524 | + "SettingA": {"Value": 42, "Origin": {"Name": "SourceA", "Scope": "Device"}}, |
| 525 | + "SettingB": {"Value": "B", "Origin": {"Name": "SourceB", "Scope": "Profile"}}, |
| 526 | + "SettingC": {"Value": true, "Origin": {"Name": "SourceC", "Scope": "User"}} |
| 527 | + } |
| 528 | + }`, |
| 529 | + }, |
| 530 | + } |
| 531 | + |
| 532 | + for _, tt := range tests { |
| 533 | + t.Run(tt.name, func(t *testing.T) { |
| 534 | + doTest := func(t *testing.T, useJSONv2 bool) { |
| 535 | + var gotJSON []byte |
| 536 | + var err error |
| 537 | + if useJSONv2 { |
| 538 | + gotJSON, err = jsonv2.Marshal(tt.snapshot) |
| 539 | + } else { |
| 540 | + gotJSON, err = json.Marshal(tt.snapshot) |
| 541 | + } |
| 542 | + if err != nil { |
| 543 | + t.Fatal(err) |
| 544 | + } |
| 545 | + |
| 546 | + if got, want, equal := internal.EqualJSONForTest(t, gotJSON, []byte(tt.wantJSON)); !equal { |
| 547 | + t.Errorf("JSON: got %s; want %s", got, want) |
| 548 | + } |
| 549 | + |
| 550 | + gotBack := &Snapshot{} |
| 551 | + if useJSONv2 { |
| 552 | + err = jsonv2.Unmarshal(gotJSON, &gotBack) |
| 553 | + } else { |
| 554 | + err = json.Unmarshal(gotJSON, &gotBack) |
| 555 | + } |
| 556 | + if err != nil { |
| 557 | + t.Fatal(err) |
| 558 | + } |
| 559 | + |
| 560 | + if wantBack := cmp.Or(tt.wantBack, tt.snapshot); !gotBack.Equal(wantBack) { |
| 561 | + t.Errorf("Snapshot: got %+v; want %+v", gotBack, wantBack) |
| 562 | + } |
| 563 | + } |
| 564 | + |
| 565 | + t.Run("json", func(t *testing.T) { doTest(t, false) }) |
| 566 | + t.Run("jsonv2", func(t *testing.T) { doTest(t, true) }) |
| 567 | + }) |
| 568 | + } |
| 569 | +} |
0 commit comments