snapshot_test.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package setting
  4. import (
  5. "cmp"
  6. "encoding/json"
  7. "testing"
  8. "time"
  9. jsonv2 "github.com/go-json-experiment/json"
  10. "tailscale.com/util/syspolicy/internal"
  11. "tailscale.com/util/syspolicy/pkey"
  12. "tailscale.com/util/syspolicy/ptype"
  13. )
  14. const (
  15. VisibleByPolicy = ptype.VisibleByPolicy
  16. ShowChoiceByPolicy = ptype.ShowChoiceByPolicy
  17. )
  18. func TestMergeSnapshots(t *testing.T) {
  19. tests := []struct {
  20. name string
  21. s1, s2 *Snapshot
  22. want *Snapshot
  23. }{
  24. {
  25. name: "both-nil",
  26. s1: nil,
  27. s2: nil,
  28. want: NewSnapshot(map[pkey.Key]RawItem{}),
  29. },
  30. {
  31. name: "both-empty",
  32. s1: NewSnapshot(map[pkey.Key]RawItem{}),
  33. s2: NewSnapshot(map[pkey.Key]RawItem{}),
  34. want: NewSnapshot(map[pkey.Key]RawItem{}),
  35. },
  36. {
  37. name: "first-nil",
  38. s1: nil,
  39. s2: NewSnapshot(map[pkey.Key]RawItem{
  40. "Setting1": RawItemOf(123),
  41. "Setting2": RawItemOf("String"),
  42. "Setting3": RawItemOf(true),
  43. }),
  44. want: NewSnapshot(map[pkey.Key]RawItem{
  45. "Setting1": RawItemOf(123),
  46. "Setting2": RawItemOf("String"),
  47. "Setting3": RawItemOf(true),
  48. }),
  49. },
  50. {
  51. name: "first-empty",
  52. s1: NewSnapshot(map[pkey.Key]RawItem{}),
  53. s2: NewSnapshot(map[pkey.Key]RawItem{
  54. "Setting1": RawItemOf(123),
  55. "Setting2": RawItemOf("String"),
  56. "Setting3": RawItemOf(false),
  57. }),
  58. want: NewSnapshot(map[pkey.Key]RawItem{
  59. "Setting1": RawItemOf(123),
  60. "Setting2": RawItemOf("String"),
  61. "Setting3": RawItemOf(false),
  62. }),
  63. },
  64. {
  65. name: "second-nil",
  66. s1: NewSnapshot(map[pkey.Key]RawItem{
  67. "Setting1": RawItemOf(123),
  68. "Setting2": RawItemOf("String"),
  69. "Setting3": RawItemOf(true),
  70. }),
  71. s2: nil,
  72. want: NewSnapshot(map[pkey.Key]RawItem{
  73. "Setting1": RawItemOf(123),
  74. "Setting2": RawItemOf("String"),
  75. "Setting3": RawItemOf(true),
  76. }),
  77. },
  78. {
  79. name: "second-empty",
  80. s1: NewSnapshot(map[pkey.Key]RawItem{
  81. "Setting1": RawItemOf(123),
  82. "Setting2": RawItemOf("String"),
  83. "Setting3": RawItemOf(false),
  84. }),
  85. s2: NewSnapshot(map[pkey.Key]RawItem{}),
  86. want: NewSnapshot(map[pkey.Key]RawItem{
  87. "Setting1": RawItemOf(123),
  88. "Setting2": RawItemOf("String"),
  89. "Setting3": RawItemOf(false),
  90. }),
  91. },
  92. {
  93. name: "no-conflicts",
  94. s1: NewSnapshot(map[pkey.Key]RawItem{
  95. "Setting1": RawItemOf(123),
  96. "Setting2": RawItemOf("String"),
  97. "Setting3": RawItemOf(false),
  98. }),
  99. s2: NewSnapshot(map[pkey.Key]RawItem{
  100. "Setting4": RawItemOf(2 * time.Hour),
  101. "Setting5": RawItemOf(VisibleByPolicy),
  102. "Setting6": RawItemOf(ShowChoiceByPolicy),
  103. }),
  104. want: NewSnapshot(map[pkey.Key]RawItem{
  105. "Setting1": RawItemOf(123),
  106. "Setting2": RawItemOf("String"),
  107. "Setting3": RawItemOf(false),
  108. "Setting4": RawItemOf(2 * time.Hour),
  109. "Setting5": RawItemOf(VisibleByPolicy),
  110. "Setting6": RawItemOf(ShowChoiceByPolicy),
  111. }),
  112. },
  113. {
  114. name: "with-conflicts",
  115. s1: NewSnapshot(map[pkey.Key]RawItem{
  116. "Setting1": RawItemOf(123),
  117. "Setting2": RawItemOf("String"),
  118. "Setting3": RawItemOf(true),
  119. }),
  120. s2: NewSnapshot(map[pkey.Key]RawItem{
  121. "Setting1": RawItemOf(456),
  122. "Setting3": RawItemOf(false),
  123. "Setting4": RawItemOf(2 * time.Hour),
  124. }),
  125. want: NewSnapshot(map[pkey.Key]RawItem{
  126. "Setting1": RawItemOf(456),
  127. "Setting2": RawItemOf("String"),
  128. "Setting3": RawItemOf(false),
  129. "Setting4": RawItemOf(2 * time.Hour),
  130. }),
  131. },
  132. {
  133. name: "with-scope-first-wins",
  134. s1: NewSnapshot(map[pkey.Key]RawItem{
  135. "Setting1": RawItemOf(123),
  136. "Setting2": RawItemOf("String"),
  137. "Setting3": RawItemOf(true),
  138. }, DeviceScope),
  139. s2: NewSnapshot(map[pkey.Key]RawItem{
  140. "Setting1": RawItemOf(456),
  141. "Setting3": RawItemOf(false),
  142. "Setting4": RawItemOf(2 * time.Hour),
  143. }, CurrentUserScope),
  144. want: NewSnapshot(map[pkey.Key]RawItem{
  145. "Setting1": RawItemOf(123),
  146. "Setting2": RawItemOf("String"),
  147. "Setting3": RawItemOf(true),
  148. "Setting4": RawItemOf(2 * time.Hour),
  149. }, CurrentUserScope),
  150. },
  151. {
  152. name: "with-scope-second-wins",
  153. s1: NewSnapshot(map[pkey.Key]RawItem{
  154. "Setting1": RawItemOf(123),
  155. "Setting2": RawItemOf("String"),
  156. "Setting3": RawItemOf(true),
  157. }, CurrentUserScope),
  158. s2: NewSnapshot(map[pkey.Key]RawItem{
  159. "Setting1": RawItemOf(456),
  160. "Setting3": RawItemOf(false),
  161. "Setting4": RawItemOf(2 * time.Hour),
  162. }, DeviceScope),
  163. want: NewSnapshot(map[pkey.Key]RawItem{
  164. "Setting1": RawItemOf(456),
  165. "Setting2": RawItemOf("String"),
  166. "Setting3": RawItemOf(false),
  167. "Setting4": RawItemOf(2 * time.Hour),
  168. }, CurrentUserScope),
  169. },
  170. {
  171. name: "with-scope-both-empty",
  172. s1: NewSnapshot(map[pkey.Key]RawItem{}, CurrentUserScope),
  173. s2: NewSnapshot(map[pkey.Key]RawItem{}, DeviceScope),
  174. want: NewSnapshot(map[pkey.Key]RawItem{}, CurrentUserScope),
  175. },
  176. {
  177. name: "with-scope-first-empty",
  178. s1: NewSnapshot(map[pkey.Key]RawItem{}, CurrentUserScope),
  179. s2: NewSnapshot(map[pkey.Key]RawItem{
  180. "Setting1": RawItemOf(123),
  181. "Setting2": RawItemOf("String"),
  182. "Setting3": RawItemOf(true)}, DeviceScope, NewNamedOrigin("TestPolicy", DeviceScope)),
  183. want: NewSnapshot(map[pkey.Key]RawItem{
  184. "Setting1": RawItemOf(123),
  185. "Setting2": RawItemOf("String"),
  186. "Setting3": RawItemOf(true),
  187. }, CurrentUserScope, NewNamedOrigin("TestPolicy", DeviceScope)),
  188. },
  189. {
  190. name: "with-scope-second-empty",
  191. s1: NewSnapshot(map[pkey.Key]RawItem{
  192. "Setting1": RawItemOf(123),
  193. "Setting2": RawItemOf("String"),
  194. "Setting3": RawItemOf(true),
  195. }, CurrentUserScope),
  196. s2: NewSnapshot(map[pkey.Key]RawItem{}),
  197. want: NewSnapshot(map[pkey.Key]RawItem{
  198. "Setting1": RawItemOf(123),
  199. "Setting2": RawItemOf("String"),
  200. "Setting3": RawItemOf(true),
  201. }, CurrentUserScope),
  202. },
  203. }
  204. for _, tt := range tests {
  205. t.Run(tt.name, func(t *testing.T) {
  206. got := MergeSnapshots(tt.s1, tt.s2)
  207. if !got.Equal(tt.want) {
  208. t.Errorf("got %v, want %v", got, tt.want)
  209. }
  210. })
  211. }
  212. }
  213. func TestSnapshotEqual(t *testing.T) {
  214. tests := []struct {
  215. name string
  216. s1, s2 *Snapshot
  217. wantEqual bool
  218. wantEqualItems bool
  219. }{
  220. {
  221. name: "nil-nil",
  222. s1: nil,
  223. s2: nil,
  224. wantEqual: true,
  225. wantEqualItems: true,
  226. },
  227. {
  228. name: "nil-empty",
  229. s1: nil,
  230. s2: NewSnapshot(map[pkey.Key]RawItem{}),
  231. wantEqual: true,
  232. wantEqualItems: true,
  233. },
  234. {
  235. name: "empty-nil",
  236. s1: NewSnapshot(map[pkey.Key]RawItem{}),
  237. s2: nil,
  238. wantEqual: true,
  239. wantEqualItems: true,
  240. },
  241. {
  242. name: "empty-empty",
  243. s1: NewSnapshot(map[pkey.Key]RawItem{}),
  244. s2: NewSnapshot(map[pkey.Key]RawItem{}),
  245. wantEqual: true,
  246. wantEqualItems: true,
  247. },
  248. {
  249. name: "first-nil",
  250. s1: nil,
  251. s2: NewSnapshot(map[pkey.Key]RawItem{
  252. "Setting1": RawItemOf(123),
  253. "Setting2": RawItemOf("String"),
  254. "Setting3": RawItemOf(false),
  255. }),
  256. wantEqual: false,
  257. wantEqualItems: false,
  258. },
  259. {
  260. name: "first-empty",
  261. s1: NewSnapshot(map[pkey.Key]RawItem{}),
  262. s2: NewSnapshot(map[pkey.Key]RawItem{
  263. "Setting1": RawItemOf(123),
  264. "Setting2": RawItemOf("String"),
  265. "Setting3": RawItemOf(false),
  266. }),
  267. wantEqual: false,
  268. wantEqualItems: false,
  269. },
  270. {
  271. name: "second-nil",
  272. s1: NewSnapshot(map[pkey.Key]RawItem{
  273. "Setting1": RawItemOf(123),
  274. "Setting2": RawItemOf("String"),
  275. "Setting3": RawItemOf(true),
  276. }),
  277. s2: nil,
  278. wantEqual: false,
  279. wantEqualItems: false,
  280. },
  281. {
  282. name: "second-empty",
  283. s1: NewSnapshot(map[pkey.Key]RawItem{
  284. "Setting1": RawItemOf(123),
  285. "Setting2": RawItemOf("String"),
  286. "Setting3": RawItemOf(false),
  287. }),
  288. s2: NewSnapshot(map[pkey.Key]RawItem{}),
  289. wantEqual: false,
  290. wantEqualItems: false,
  291. },
  292. {
  293. name: "same-items-same-order-no-scope",
  294. s1: NewSnapshot(map[pkey.Key]RawItem{
  295. "Setting1": RawItemOf(123),
  296. "Setting2": RawItemOf("String"),
  297. "Setting3": RawItemOf(false),
  298. }),
  299. s2: NewSnapshot(map[pkey.Key]RawItem{
  300. "Setting1": RawItemOf(123),
  301. "Setting2": RawItemOf("String"),
  302. "Setting3": RawItemOf(false),
  303. }),
  304. wantEqual: true,
  305. wantEqualItems: true,
  306. },
  307. {
  308. name: "same-items-same-order-same-scope",
  309. s1: NewSnapshot(map[pkey.Key]RawItem{
  310. "Setting1": RawItemOf(123),
  311. "Setting2": RawItemOf("String"),
  312. "Setting3": RawItemOf(false),
  313. }, DeviceScope),
  314. s2: NewSnapshot(map[pkey.Key]RawItem{
  315. "Setting1": RawItemOf(123),
  316. "Setting2": RawItemOf("String"),
  317. "Setting3": RawItemOf(false),
  318. }, DeviceScope),
  319. wantEqual: true,
  320. wantEqualItems: true,
  321. },
  322. {
  323. name: "same-items-different-order-same-scope",
  324. s1: NewSnapshot(map[pkey.Key]RawItem{
  325. "Setting1": RawItemOf(123),
  326. "Setting2": RawItemOf("String"),
  327. "Setting3": RawItemOf(false),
  328. }, DeviceScope),
  329. s2: NewSnapshot(map[pkey.Key]RawItem{
  330. "Setting3": RawItemOf(false),
  331. "Setting1": RawItemOf(123),
  332. "Setting2": RawItemOf("String"),
  333. }, DeviceScope),
  334. wantEqual: true,
  335. wantEqualItems: true,
  336. },
  337. {
  338. name: "same-items-same-order-different-scope",
  339. s1: NewSnapshot(map[pkey.Key]RawItem{
  340. "Setting1": RawItemOf(123),
  341. "Setting2": RawItemOf("String"),
  342. "Setting3": RawItemOf(false),
  343. }, DeviceScope),
  344. s2: NewSnapshot(map[pkey.Key]RawItem{
  345. "Setting1": RawItemOf(123),
  346. "Setting2": RawItemOf("String"),
  347. "Setting3": RawItemOf(false),
  348. }, CurrentUserScope),
  349. wantEqual: false,
  350. wantEqualItems: true,
  351. },
  352. {
  353. name: "different-items-same-scope",
  354. s1: NewSnapshot(map[pkey.Key]RawItem{
  355. "Setting1": RawItemOf(123),
  356. "Setting2": RawItemOf("String"),
  357. "Setting3": RawItemOf(false),
  358. }, DeviceScope),
  359. s2: NewSnapshot(map[pkey.Key]RawItem{
  360. "Setting4": RawItemOf(2 * time.Hour),
  361. "Setting5": RawItemOf(VisibleByPolicy),
  362. "Setting6": RawItemOf(ShowChoiceByPolicy),
  363. }, DeviceScope),
  364. wantEqual: false,
  365. wantEqualItems: false,
  366. },
  367. }
  368. for _, tt := range tests {
  369. t.Run(tt.name, func(t *testing.T) {
  370. if gotEqual := tt.s1.Equal(tt.s2); gotEqual != tt.wantEqual {
  371. t.Errorf("WantEqual: got %v, want %v", gotEqual, tt.wantEqual)
  372. }
  373. if gotEqualItems := tt.s1.EqualItems(tt.s2); gotEqualItems != tt.wantEqualItems {
  374. t.Errorf("WantEqualItems: got %v, want %v", gotEqualItems, tt.wantEqualItems)
  375. }
  376. })
  377. }
  378. }
  379. func TestSnapshotString(t *testing.T) {
  380. tests := []struct {
  381. name string
  382. snapshot *Snapshot
  383. wantString string
  384. }{
  385. {
  386. name: "nil",
  387. snapshot: nil,
  388. wantString: "{Empty}",
  389. },
  390. {
  391. name: "empty",
  392. snapshot: NewSnapshot(nil),
  393. wantString: "{Empty}",
  394. },
  395. {
  396. name: "empty-with-scope",
  397. snapshot: NewSnapshot(nil, DeviceScope),
  398. wantString: "{Empty, Device}",
  399. },
  400. {
  401. name: "empty-with-origin",
  402. snapshot: NewSnapshot(nil, NewNamedOrigin("Test Policy", DeviceScope)),
  403. wantString: "{Empty, Test Policy (Device)}",
  404. },
  405. {
  406. name: "non-empty",
  407. snapshot: NewSnapshot(map[pkey.Key]RawItem{
  408. "Setting1": RawItemOf(2 * time.Hour),
  409. "Setting2": RawItemOf(VisibleByPolicy),
  410. "Setting3": RawItemOf(ShowChoiceByPolicy),
  411. }, NewNamedOrigin("Test Policy", DeviceScope)),
  412. wantString: `{Test Policy (Device)}
  413. Setting1 = 2h0m0s
  414. Setting2 = show
  415. Setting3 = user-decides`,
  416. },
  417. {
  418. name: "non-empty-with-item-origin",
  419. snapshot: NewSnapshot(map[pkey.Key]RawItem{
  420. "Setting1": RawItemWith(42, nil, NewNamedOrigin("Test Policy", DeviceScope)),
  421. }),
  422. wantString: `Setting1 = 42 - {Test Policy (Device)}`,
  423. },
  424. {
  425. name: "non-empty-with-item-error",
  426. snapshot: NewSnapshot(map[pkey.Key]RawItem{
  427. "Setting1": RawItemWith(nil, NewErrorText("bang!"), nil),
  428. }),
  429. wantString: `Setting1 = Error{"bang!"}`,
  430. },
  431. }
  432. for _, tt := range tests {
  433. t.Run(tt.name, func(t *testing.T) {
  434. if gotString := tt.snapshot.String(); gotString != tt.wantString {
  435. t.Errorf("got %v\nwant %v", gotString, tt.wantString)
  436. }
  437. })
  438. }
  439. }
  440. func TestMarshalUnmarshalSnapshot(t *testing.T) {
  441. tests := []struct {
  442. name string
  443. snapshot *Snapshot
  444. wantJSON string
  445. wantBack *Snapshot
  446. }{
  447. {
  448. name: "Nil",
  449. snapshot: (*Snapshot)(nil),
  450. wantJSON: "null",
  451. wantBack: NewSnapshot(nil),
  452. },
  453. {
  454. name: "Zero",
  455. snapshot: &Snapshot{},
  456. wantJSON: "{}",
  457. },
  458. {
  459. name: "Bool/True",
  460. snapshot: NewSnapshot(map[pkey.Key]RawItem{"BoolPolicy": RawItemOf(true)}),
  461. wantJSON: `{"Settings": {"BoolPolicy": {"Value": true}}}`,
  462. },
  463. {
  464. name: "Bool/False",
  465. snapshot: NewSnapshot(map[pkey.Key]RawItem{"BoolPolicy": RawItemOf(false)}),
  466. wantJSON: `{"Settings": {"BoolPolicy": {"Value": false}}}`,
  467. },
  468. {
  469. name: "String/Non-Empty",
  470. snapshot: NewSnapshot(map[pkey.Key]RawItem{"StringPolicy": RawItemOf("StringValue")}),
  471. wantJSON: `{"Settings": {"StringPolicy": {"Value": "StringValue"}}}`,
  472. },
  473. {
  474. name: "String/Empty",
  475. snapshot: NewSnapshot(map[pkey.Key]RawItem{"StringPolicy": RawItemOf("")}),
  476. wantJSON: `{"Settings": {"StringPolicy": {"Value": ""}}}`,
  477. },
  478. {
  479. name: "Integer/NonZero",
  480. snapshot: NewSnapshot(map[pkey.Key]RawItem{"IntPolicy": RawItemOf(uint64(42))}),
  481. wantJSON: `{"Settings": {"IntPolicy": {"Value": 42}}}`,
  482. },
  483. {
  484. name: "Integer/Zero",
  485. snapshot: NewSnapshot(map[pkey.Key]RawItem{"IntPolicy": RawItemOf(uint64(0))}),
  486. wantJSON: `{"Settings": {"IntPolicy": {"Value": 0}}}`,
  487. },
  488. {
  489. name: "String-List",
  490. snapshot: NewSnapshot(map[pkey.Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}),
  491. wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`,
  492. },
  493. {
  494. name: "Duration/Zero",
  495. snapshot: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf(time.Duration(0))}),
  496. wantJSON: `{"Settings": {"DurationPolicy": {"Value": "0s"}}}`,
  497. wantBack: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf("0s")}),
  498. },
  499. {
  500. name: "Duration/NonZero",
  501. snapshot: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf(2 * time.Hour)}),
  502. wantJSON: `{"Settings": {"DurationPolicy": {"Value": "2h0m0s"}}}`,
  503. wantBack: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf("2h0m0s")}),
  504. },
  505. {
  506. name: "Empty/With-Summary",
  507. snapshot: NewSnapshot(
  508. map[pkey.Key]RawItem{},
  509. SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
  510. ),
  511. wantJSON: `{"Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"}}`,
  512. },
  513. {
  514. name: "Setting/With-Summary",
  515. snapshot: NewSnapshot(
  516. map[pkey.Key]RawItem{"PolicySetting": RawItemOf(uint64(42))},
  517. SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
  518. ),
  519. wantJSON: `{
  520. "Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"},
  521. "Settings": {"PolicySetting": {"Value": 42}}
  522. }`,
  523. },
  524. {
  525. name: "Settings/With-Origins",
  526. snapshot: NewSnapshot(
  527. map[pkey.Key]RawItem{
  528. "SettingA": RawItemWith(uint64(42), nil, NewNamedOrigin("SourceA", DeviceScope)),
  529. "SettingB": RawItemWith("B", nil, NewNamedOrigin("SourceB", CurrentProfileScope)),
  530. "SettingC": RawItemWith(true, nil, NewNamedOrigin("SourceC", CurrentUserScope)),
  531. },
  532. ),
  533. wantJSON: `{
  534. "Settings": {
  535. "SettingA": {"Value": 42, "Origin": {"Name": "SourceA", "Scope": "Device"}},
  536. "SettingB": {"Value": "B", "Origin": {"Name": "SourceB", "Scope": "Profile"}},
  537. "SettingC": {"Value": true, "Origin": {"Name": "SourceC", "Scope": "User"}}
  538. }
  539. }`,
  540. },
  541. }
  542. for _, tt := range tests {
  543. t.Run(tt.name, func(t *testing.T) {
  544. doTest := func(t *testing.T, useJSONv2 bool) {
  545. var gotJSON []byte
  546. var err error
  547. if useJSONv2 {
  548. gotJSON, err = jsonv2.Marshal(tt.snapshot)
  549. } else {
  550. gotJSON, err = json.Marshal(tt.snapshot)
  551. }
  552. if err != nil {
  553. t.Fatal(err)
  554. }
  555. if got, want, equal := internal.EqualJSONForTest(t, gotJSON, []byte(tt.wantJSON)); !equal {
  556. t.Errorf("JSON: got %s; want %s", got, want)
  557. }
  558. gotBack := &Snapshot{}
  559. if useJSONv2 {
  560. err = jsonv2.Unmarshal(gotJSON, &gotBack)
  561. } else {
  562. err = json.Unmarshal(gotJSON, &gotBack)
  563. }
  564. if err != nil {
  565. t.Fatal(err)
  566. }
  567. if wantBack := cmp.Or(tt.wantBack, tt.snapshot); !gotBack.Equal(wantBack) {
  568. t.Errorf("Snapshot: got %+v; want %+v", gotBack, wantBack)
  569. }
  570. }
  571. t.Run("json", func(t *testing.T) { doTest(t, false) })
  572. t.Run("jsonv2", func(t *testing.T) { doTest(t, true) })
  573. })
  574. }
  575. }