gitee.com/mysnapcore/mysnapd@v0.1.0/asserts/snapasserts/validation_sets_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package snapasserts_test 21 22 import ( 23 "fmt" 24 "math/rand" 25 "sort" 26 "strconv" 27 28 . "gopkg.in/check.v1" 29 30 "gitee.com/mysnapcore/mysnapd/asserts" 31 "gitee.com/mysnapcore/mysnapd/asserts/assertstest" 32 "gitee.com/mysnapcore/mysnapd/asserts/snapasserts" 33 "gitee.com/mysnapcore/mysnapd/snap" 34 "gitee.com/mysnapcore/mysnapd/snap/naming" 35 ) 36 37 type validationSetsSuite struct{} 38 39 var _ = Suite(&validationSetsSuite{}) 40 41 func (s *validationSetsSuite) TestAddFromSameSequence(c *C) { 42 mySnapAt7Valset := assertstest.FakeAssertion(map[string]interface{}{ 43 "type": "validation-set", 44 "authority-id": "account-id", 45 "series": "16", 46 "account-id": "account-id", 47 "name": "my-snap-ctl", 48 "sequence": "1", 49 "snaps": []interface{}{ 50 map[string]interface{}{ 51 "name": "my-snap", 52 "id": "mysnapididididididididididididid", 53 "presence": "required", 54 "revision": "7", 55 }, 56 }, 57 }).(*asserts.ValidationSet) 58 59 mySnapAt8Valset := assertstest.FakeAssertion(map[string]interface{}{ 60 "type": "validation-set", 61 "authority-id": "account-id", 62 "series": "16", 63 "account-id": "account-id", 64 "name": "my-snap-ctl", 65 "sequence": "2", 66 "snaps": []interface{}{ 67 map[string]interface{}{ 68 "name": "my-snap", 69 "id": "mysnapididididididididididididid", 70 "presence": "required", 71 "revision": "8", 72 }, 73 }, 74 }).(*asserts.ValidationSet) 75 76 valsets := snapasserts.NewValidationSets() 77 err := valsets.Add(mySnapAt7Valset) 78 c.Assert(err, IsNil) 79 err = valsets.Add(mySnapAt8Valset) 80 c.Check(err, ErrorMatches, `cannot add a second validation-set under "account-id/my-snap-ctl"`) 81 } 82 83 func (s *validationSetsSuite) TestIntersections(c *C) { 84 mySnapAt7Valset := assertstest.FakeAssertion(map[string]interface{}{ 85 "type": "validation-set", 86 "authority-id": "account-id", 87 "series": "16", 88 "account-id": "account-id", 89 "name": "my-snap-ctl", 90 "sequence": "1", 91 "snaps": []interface{}{ 92 map[string]interface{}{ 93 "name": "my-snap", 94 "id": "mysnapididididididididididididid", 95 "presence": "required", 96 "revision": "7", 97 }, 98 }, 99 }).(*asserts.ValidationSet) 100 101 mySnapAt7Valset2 := assertstest.FakeAssertion(map[string]interface{}{ 102 "type": "validation-set", 103 "authority-id": "account-id", 104 "series": "16", 105 "account-id": "account-id", 106 "name": "my-snap-ctl2", 107 "sequence": "2", 108 "snaps": []interface{}{ 109 map[string]interface{}{ 110 "name": "my-snap", 111 "id": "mysnapididididididididididididid", 112 "presence": "required", 113 "revision": "7", 114 }, 115 }, 116 }).(*asserts.ValidationSet) 117 118 mySnapAt8Valset := assertstest.FakeAssertion(map[string]interface{}{ 119 "type": "validation-set", 120 "authority-id": "account-id", 121 "series": "16", 122 "account-id": "account-id", 123 "name": "my-snap-ctl-other", 124 "sequence": "1", 125 "snaps": []interface{}{ 126 map[string]interface{}{ 127 "name": "my-snap", 128 "id": "mysnapididididididididididididid", 129 "presence": "required", 130 "revision": "8", 131 }, 132 }, 133 }).(*asserts.ValidationSet) 134 135 mySnapAt8OptValset := assertstest.FakeAssertion(map[string]interface{}{ 136 "type": "validation-set", 137 "authority-id": "account-id", 138 "series": "16", 139 "account-id": "account-id", 140 "name": "my-snap-ctl-opt", 141 "sequence": "1", 142 "snaps": []interface{}{ 143 map[string]interface{}{ 144 "name": "my-snap", 145 "id": "mysnapididididididididididididid", 146 "presence": "optional", 147 "revision": "8", 148 }, 149 }, 150 }).(*asserts.ValidationSet) 151 152 mySnapInvalidValset := assertstest.FakeAssertion(map[string]interface{}{ 153 "type": "validation-set", 154 "authority-id": "account-id", 155 "series": "16", 156 "account-id": "account-id", 157 "name": "my-snap-ctl-inv", 158 "sequence": "1", 159 "snaps": []interface{}{ 160 map[string]interface{}{ 161 "name": "my-snap", 162 "id": "mysnapididididididididididididid", 163 "presence": "invalid", 164 }, 165 }, 166 }).(*asserts.ValidationSet) 167 168 mySnapAt7OptValset := assertstest.FakeAssertion(map[string]interface{}{ 169 "type": "validation-set", 170 "authority-id": "account-id", 171 "series": "16", 172 "account-id": "account-id", 173 "name": "my-snap-ctl-opt2", 174 "sequence": "1", 175 "snaps": []interface{}{ 176 map[string]interface{}{ 177 "name": "my-snap", 178 "id": "mysnapididididididididididididid", 179 "presence": "optional", 180 "revision": "7", 181 }, 182 }, 183 }).(*asserts.ValidationSet) 184 185 mySnapReqValset := assertstest.FakeAssertion(map[string]interface{}{ 186 "type": "validation-set", 187 "authority-id": "account-id", 188 "series": "16", 189 "account-id": "account-id", 190 "name": "my-snap-ctl-req-only", 191 "sequence": "1", 192 "snaps": []interface{}{ 193 map[string]interface{}{ 194 "name": "my-snap", 195 "id": "mysnapididididididididididididid", 196 "presence": "required", 197 }, 198 }, 199 }).(*asserts.ValidationSet) 200 201 mySnapOptValset := assertstest.FakeAssertion(map[string]interface{}{ 202 "type": "validation-set", 203 "authority-id": "account-id", 204 "series": "16", 205 "account-id": "account-id", 206 "name": "my-snap-ctl-opt-only", 207 "sequence": "1", 208 "snaps": []interface{}{ 209 map[string]interface{}{ 210 "name": "my-snap", 211 "id": "mysnapididididididididididididid", 212 "presence": "optional", 213 }, 214 }, 215 }).(*asserts.ValidationSet) 216 217 tests := []struct { 218 sets []*asserts.ValidationSet 219 conflictErr string 220 }{ 221 {[]*asserts.ValidationSet{mySnapAt7Valset}, ""}, 222 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt7Valset2}, ""}, 223 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\)`}, 224 {[]*asserts.ValidationSet{mySnapAt8Valset, mySnapAt8OptValset}, ""}, 225 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8OptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-opt\)`}, 226 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at revision 7 \(account-id/my-snap-ctl\)`}, 227 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at revision 7 \(account-id/my-snap-ctl\)`}, 228 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapInvalidValset}, ""}, 229 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapAt8OptValset}, ""}, 230 {[]*asserts.ValidationSet{mySnapAt7OptValset, mySnapAt8OptValset}, ""}, // no conflict but interpreted as invalid 231 {[]*asserts.ValidationSet{mySnapAt7OptValset, mySnapAt8OptValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl,account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\)`}, 232 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapInvalidValset, mySnapAt7Valset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-opt\)`}, 233 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapReqValset}, ""}, 234 {[]*asserts.ValidationSet{mySnapReqValset, mySnapAt7Valset}, ""}, 235 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapReqValset}, ""}, 236 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapReqValset, mySnapAt7OptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\) or required at any revision \(account-id/my-snap-ctl-req-only\)`}, 237 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapAt7OptValset, mySnapReqValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl-opt2\), 8 \(account-id/my-snap-ctl-opt\) or required at any revision \(account-id/my-snap-ctl-req-only\)`}, 238 {[]*asserts.ValidationSet{mySnapReqValset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at any revision \(account-id/my-snap-ctl-req-only\)`}, 239 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapReqValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at any revision \(account-id/my-snap-ctl-req-only\)`}, 240 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset, mySnapOptValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\)`}, 241 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapOptValset}, ""}, 242 {[]*asserts.ValidationSet{mySnapOptValset, mySnapAt7Valset}, ""}, 243 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapOptValset}, ""}, 244 {[]*asserts.ValidationSet{mySnapAt8OptValset, mySnapOptValset, mySnapAt7OptValset}, ""}, // no conflict but interpreted as invalid 245 {[]*asserts.ValidationSet{mySnapInvalidValset, mySnapOptValset}, ""}, 246 {[]*asserts.ValidationSet{mySnapOptValset, mySnapInvalidValset}, ""}, 247 {[]*asserts.ValidationSet{mySnapAt7Valset, mySnapAt8Valset, mySnapReqValset, mySnapInvalidValset}, `(?ms)validation sets are in conflict:.*cannot constrain snap "my-snap" as both invalid \(account-id/my-snap-ctl-inv\) and required at different revisions 7 \(account-id/my-snap-ctl\), 8 \(account-id/my-snap-ctl-other\) or at any revision \(account-id/my-snap-ctl-req-only\)`}, 248 } 249 250 for _, t := range tests { 251 valsets := snapasserts.NewValidationSets() 252 cSets := make(map[string]*asserts.ValidationSet) 253 for _, valset := range t.sets { 254 err := valsets.Add(valset) 255 c.Assert(err, IsNil) 256 // mySnapOptValset never influcens an outcome 257 if valset != mySnapOptValset { 258 cSets[fmt.Sprintf("%s/%s", valset.AccountID(), valset.Name())] = valset 259 } 260 } 261 err := valsets.Conflict() 262 if t.conflictErr == "" { 263 c.Check(err, IsNil) 264 } else { 265 c.Check(err, ErrorMatches, t.conflictErr) 266 ce := err.(*snapasserts.ValidationSetsConflictError) 267 c.Check(ce.Sets, DeepEquals, cSets) 268 } 269 } 270 } 271 272 func (s *validationSetsSuite) TestCheckInstalledSnapsNoValidationSets(c *C) { 273 valsets := snapasserts.NewValidationSets() 274 snaps := []*snapasserts.InstalledSnap{ 275 snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)), 276 } 277 err := valsets.CheckInstalledSnaps(snaps, nil) 278 c.Assert(err, IsNil) 279 } 280 281 func (s *validationSetsSuite) TestCheckInstalledSnaps(c *C) { 282 // require: snapB rev 3, snapC rev 2. 283 // invalid: snapA 284 vs1 := assertstest.FakeAssertion(map[string]interface{}{ 285 "type": "validation-set", 286 "authority-id": "acme", 287 "series": "16", 288 "account-id": "acme", 289 "name": "fooname", 290 "sequence": "1", 291 "snaps": []interface{}{ 292 map[string]interface{}{ 293 "name": "snap-a", 294 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 295 "presence": "invalid", 296 }, 297 map[string]interface{}{ 298 "name": "snap-b", 299 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 300 "revision": "3", 301 "presence": "required", 302 }, 303 map[string]interface{}{ 304 "name": "snap-c", 305 "id": "mysnapcccccccccccccccccccccccccc", 306 "revision": "2", 307 "presence": "optional", 308 }, 309 }, 310 }).(*asserts.ValidationSet) 311 312 // require: snapD any rev 313 // optional: snapE any rev 314 vs2 := assertstest.FakeAssertion(map[string]interface{}{ 315 "type": "validation-set", 316 "authority-id": "acme", 317 "series": "16", 318 "account-id": "acme", 319 "name": "barname", 320 "sequence": "3", 321 "snaps": []interface{}{ 322 map[string]interface{}{ 323 "name": "snap-d", 324 "id": "mysnapdddddddddddddddddddddddddd", 325 "presence": "required", 326 }, 327 map[string]interface{}{ 328 "name": "snap-e", 329 "id": "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", 330 "presence": "optional", 331 }, 332 }, 333 }).(*asserts.ValidationSet) 334 335 // optional: snapE any rev 336 // note: since it only has an optional snap, acme/bazname is not expected 337 // not be invalid by any of the checks. 338 vs3 := assertstest.FakeAssertion(map[string]interface{}{ 339 "type": "validation-set", 340 "authority-id": "acme", 341 "series": "16", 342 "account-id": "acme", 343 "name": "bazname", 344 "sequence": "2", 345 "snaps": []interface{}{ 346 map[string]interface{}{ 347 "name": "snap-e", 348 "id": "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", 349 "presence": "optional", 350 }, 351 }, 352 }).(*asserts.ValidationSet) 353 354 // invalid: snapA 355 vs4 := assertstest.FakeAssertion(map[string]interface{}{ 356 "type": "validation-set", 357 "authority-id": "acme", 358 "series": "16", 359 "account-id": "acme", 360 "name": "booname", 361 "sequence": "1", 362 "snaps": []interface{}{ 363 map[string]interface{}{ 364 "name": "snap-a", 365 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 366 "presence": "invalid", 367 }, 368 }, 369 }).(*asserts.ValidationSet) 370 371 vs5 := assertstest.FakeAssertion(map[string]interface{}{ 372 "type": "validation-set", 373 "authority-id": "acme", 374 "series": "16", 375 "account-id": "acme", 376 "name": "huhname", 377 "sequence": "1", 378 "snaps": []interface{}{ 379 map[string]interface{}{ 380 "name": "snap-f", 381 "id": "mysnapffffffffffffffffffffffffff", 382 "revision": "4", 383 "presence": "required", 384 }, 385 }, 386 }).(*asserts.ValidationSet) 387 388 vs6 := assertstest.FakeAssertion(map[string]interface{}{ 389 "type": "validation-set", 390 "authority-id": "acme", 391 "series": "16", 392 "account-id": "acme", 393 "name": "duhname", 394 "sequence": "1", 395 "snaps": []interface{}{ 396 map[string]interface{}{ 397 "name": "snap-f", 398 "id": "mysnapffffffffffffffffffffffffff", 399 "revision": "4", 400 "presence": "required", 401 }, 402 }, 403 }).(*asserts.ValidationSet) 404 405 vs7 := assertstest.FakeAssertion(map[string]interface{}{ 406 "type": "validation-set", 407 "authority-id": "acme", 408 "series": "16", 409 "account-id": "acme", 410 "name": "bahname", 411 "sequence": "1", 412 "snaps": []interface{}{ 413 map[string]interface{}{ 414 "name": "snap-f", 415 "id": "mysnapffffffffffffffffffffffffff", 416 "presence": "required", 417 }, 418 }, 419 }).(*asserts.ValidationSet) 420 421 valsets := snapasserts.NewValidationSets() 422 c.Assert(valsets.Add(vs1), IsNil) 423 c.Assert(valsets.Add(vs2), IsNil) 424 c.Assert(valsets.Add(vs3), IsNil) 425 c.Assert(valsets.Add(vs4), IsNil) 426 c.Assert(valsets.Add(vs5), IsNil) 427 c.Assert(valsets.Add(vs6), IsNil) 428 c.Assert(valsets.Add(vs7), IsNil) 429 430 snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)) 431 snapAlocal := snapasserts.NewInstalledSnap("snap-a", "", snap.R("x2")) 432 snapB := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(3)) 433 snapBinvRev := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(8)) 434 snapBlocal := snapasserts.NewInstalledSnap("snap-b", "", snap.R("x3")) 435 snapC := snapasserts.NewInstalledSnap("snap-c", "mysnapcccccccccccccccccccccccccc", snap.R(2)) 436 snapCinvRev := snapasserts.NewInstalledSnap("snap-c", "mysnapcccccccccccccccccccccccccc", snap.R(99)) 437 snapD := snapasserts.NewInstalledSnap("snap-d", "mysnapdddddddddddddddddddddddddd", snap.R(2)) 438 snapDrev99 := snapasserts.NewInstalledSnap("snap-d", "mysnapdddddddddddddddddddddddddd", snap.R(99)) 439 snapDlocal := snapasserts.NewInstalledSnap("snap-d", "", snap.R("x3")) 440 snapE := snapasserts.NewInstalledSnap("snap-e", "mysnapeeeeeeeeeeeeeeeeeeeeeeeeee", snap.R(2)) 441 snapF := snapasserts.NewInstalledSnap("snap-f", "mysnapffffffffffffffffffffffffff", snap.R(4)) 442 // extra snap, not referenced by any validation set 443 snapZ := snapasserts.NewInstalledSnap("snap-z", "mysnapzzzzzzzzzzzzzzzzzzzzzzzzzz", snap.R(1)) 444 445 tests := []struct { 446 snaps []*snapasserts.InstalledSnap 447 expectedInvalid map[string][]string 448 expectedMissing map[string]map[snap.Revision][]string 449 expectedWrongRev map[string]map[snap.Revision][]string 450 }{ 451 { 452 // required snaps not installed 453 snaps: nil, 454 expectedMissing: map[string]map[snap.Revision][]string{ 455 "snap-b": { 456 snap.R(3): {"acme/fooname"}, 457 }, 458 "snap-d": { 459 snap.R(0): {"acme/barname"}, 460 }, 461 "snap-f": { 462 snap.R(0): {"acme/bahname"}, 463 snap.R(4): {"acme/duhname", "acme/huhname"}, 464 }, 465 }, 466 }, 467 { 468 // required snaps not installed 469 snaps: []*snapasserts.InstalledSnap{ 470 snapZ, 471 }, 472 expectedMissing: map[string]map[snap.Revision][]string{ 473 "snap-b": { 474 snap.R(3): {"acme/fooname"}, 475 }, 476 "snap-d": { 477 snap.R(0): {"acme/barname"}, 478 }, 479 "snap-f": { 480 snap.R(0): {"acme/bahname"}, 481 snap.R(4): {"acme/duhname", "acme/huhname"}, 482 }, 483 }, 484 }, 485 { 486 snaps: []*snapasserts.InstalledSnap{ 487 // covered by acme/fooname validation-set 488 snapB, 489 // covered by acme/barname validation-set. snap-e not installed but optional 490 snapDrev99, 491 // covered by acme/duhname and acme/huhname 492 snapF, 493 }, 494 // ale fine 495 }, 496 { 497 snaps: []*snapasserts.InstalledSnap{ 498 // covered by acme/fooname validation-set and acme/booname, snap-a presence is invalid 499 snapA, 500 snapB, 501 // covered by acme/barname validation-set. snap-e not installed but optional 502 snapDrev99, 503 // covered by acme/duhname and acme/huhname 504 snapF, 505 }, 506 expectedInvalid: map[string][]string{ 507 "snap-a": {"acme/booname", "acme/fooname"}, 508 }, 509 }, 510 { 511 snaps: []*snapasserts.InstalledSnap{ 512 // covered by acme/fooname and acme/booname validation-sets, snapB missing, snap-a presence is invalid 513 snapA, 514 // covered by acme/barname validation-set. snap-e not installed but optional 515 snapDrev99, 516 snapF, 517 }, 518 expectedInvalid: map[string][]string{ 519 "snap-a": {"acme/booname", "acme/fooname"}, 520 }, 521 expectedMissing: map[string]map[snap.Revision][]string{ 522 "snap-b": { 523 snap.R(3): {"acme/fooname"}, 524 }, 525 }, 526 }, 527 { 528 snaps: []*snapasserts.InstalledSnap{ 529 // covered by acme/fooname validation-set 530 snapB, 531 snapC, 532 // covered by acme/barname validation-set. snap-e not installed but optional 533 snapD, 534 // covered by acme/duhname and acme/huhname 535 snapF, 536 }, 537 // ale fine 538 }, 539 { 540 snaps: []*snapasserts.InstalledSnap{ 541 // covered by acme/fooname validation-set, snap-c optional but wrong revision 542 snapB, 543 snapCinvRev, 544 // covered by acme/barname validation-set. snap-e not installed but optional 545 snapD, 546 // covered by acme/duhname and acme/huhname 547 snapF, 548 }, 549 expectedWrongRev: map[string]map[snap.Revision][]string{ 550 "snap-c": { 551 snap.R(2): {"acme/fooname"}, 552 }, 553 }, 554 }, 555 { 556 snaps: []*snapasserts.InstalledSnap{ 557 // covered by acme/fooname validation-set but wrong revision 558 snapBinvRev, 559 // covered by acme/barname validation-set. 560 snapD, 561 // covered by acme/duhname and acme/huhname 562 snapF, 563 }, 564 expectedWrongRev: map[string]map[snap.Revision][]string{ 565 "snap-b": { 566 snap.R(3): {"acme/fooname"}, 567 }, 568 }, 569 }, 570 { 571 snaps: []*snapasserts.InstalledSnap{ 572 // covered by acme/fooname validation-set 573 snapB, 574 // covered by acme/barname validation-set. snap-d not installed. 575 snapE, 576 // covered by acme/duhname and acme/huhname 577 snapF, 578 }, 579 expectedMissing: map[string]map[snap.Revision][]string{ 580 "snap-d": { 581 snap.R(0): {"acme/barname"}, 582 }, 583 }, 584 }, 585 { 586 snaps: []*snapasserts.InstalledSnap{ 587 // required snaps from acme/fooname are not installed. 588 // covered by acme/barname validation-set 589 snapDrev99, 590 snapE, 591 // covered by acme/duhname and acme/huhname 592 snapF, 593 }, 594 expectedMissing: map[string]map[snap.Revision][]string{ 595 "snap-b": { 596 snap.R(3): {"acme/fooname"}, 597 }, 598 }, 599 }, 600 { 601 snaps: []*snapasserts.InstalledSnap{ 602 // covered by acme/fooname validation-set, required missing. 603 snapC, 604 // covered by acme/barname validation-set, required missing. 605 snapE, 606 // covered by acme/duhname and acme/huhname 607 snapF, 608 }, 609 expectedMissing: map[string]map[snap.Revision][]string{ 610 "snap-b": { 611 snap.R(3): {"acme/fooname"}, 612 }, 613 "snap-d": { 614 snap.R(0): {"acme/barname"}, 615 }, 616 }, 617 }, 618 // local snaps 619 { 620 snaps: []*snapasserts.InstalledSnap{ 621 // covered by acme/fooname validation-set. 622 snapB, 623 // covered by acme/barname validation-set, local snap-d. 624 snapDlocal, 625 // covered by acme/duhname and acme/huhname 626 snapF, 627 }, 628 // all fine 629 }, 630 { 631 snaps: []*snapasserts.InstalledSnap{ 632 // covered by acme/fooname validation-set, snap-a is invalid. 633 snapAlocal, 634 snapB, 635 // covered by acme/barname validation-set. 636 snapD, 637 snapF, 638 }, 639 expectedInvalid: map[string][]string{ 640 "snap-a": {"acme/booname", "acme/fooname"}, 641 }, 642 }, 643 { 644 snaps: []*snapasserts.InstalledSnap{ 645 // covered by acme/fooname validation-set, snap-b is wrong rev (local). 646 snapBlocal, 647 // covered by acme/barname validation-set. 648 snapD, 649 // covered by acme/duhname and acme/huhname 650 snapF, 651 }, 652 expectedWrongRev: map[string]map[snap.Revision][]string{ 653 "snap-b": { 654 snap.R(3): {"acme/fooname"}, 655 }, 656 }, 657 }, 658 } 659 660 checkSets := func(snapsToValidationSets map[string][]string, vs map[string]*asserts.ValidationSet) { 661 for _, vsetKeys := range snapsToValidationSets { 662 for _, key := range vsetKeys { 663 vset, ok := vs[key] 664 c.Assert(ok, Equals, true) 665 c.Assert(vset.AccountID()+"/"+vset.Name(), Equals, key) 666 } 667 } 668 } 669 670 expectedSets := make(map[string]*asserts.ValidationSet, 7) 671 for _, vs := range []*asserts.ValidationSet{vs1, vs2, vs3, vs4, vs5, vs6, vs7} { 672 expectedSets[fmt.Sprintf("%s/%s", vs.AccountID(), vs.Name())] = vs 673 } 674 675 for i, tc := range tests { 676 err := valsets.CheckInstalledSnaps(tc.snaps, nil) 677 if err == nil { 678 c.Assert(tc.expectedInvalid, IsNil) 679 c.Assert(tc.expectedMissing, IsNil) 680 c.Assert(tc.expectedWrongRev, IsNil) 681 continue 682 } 683 verr, ok := err.(*snapasserts.ValidationSetsValidationError) 684 c.Assert(ok, Equals, true, Commentf("#%d", i)) 685 c.Assert(verr.InvalidSnaps, DeepEquals, tc.expectedInvalid, Commentf("#%d", i)) 686 c.Assert(verr.MissingSnaps, DeepEquals, tc.expectedMissing, Commentf("#%d", i)) 687 c.Assert(verr.WrongRevisionSnaps, DeepEquals, tc.expectedWrongRev, Commentf("#%d", i)) 688 c.Assert(verr.Sets, DeepEquals, expectedSets) 689 checkSets(verr.InvalidSnaps, verr.Sets) 690 } 691 } 692 693 func (s *validationSetsSuite) TestCheckInstalledSnapsIgnoreValidation(c *C) { 694 // require: snapB rev 3, snapC rev 2. 695 // invalid: snapA 696 vs := assertstest.FakeAssertion(map[string]interface{}{ 697 "type": "validation-set", 698 "authority-id": "acme", 699 "series": "16", 700 "account-id": "acme", 701 "name": "fooname", 702 "sequence": "1", 703 "snaps": []interface{}{ 704 map[string]interface{}{ 705 "name": "snap-a", 706 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 707 "presence": "invalid", 708 }, 709 map[string]interface{}{ 710 "name": "snap-b", 711 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 712 "revision": "3", 713 "presence": "required", 714 }, 715 map[string]interface{}{ 716 "name": "snap-c", 717 "id": "mysnapcccccccccccccccccccccccccc", 718 "revision": "2", 719 "presence": "optional", 720 }, 721 }, 722 }).(*asserts.ValidationSet) 723 724 valsets := snapasserts.NewValidationSets() 725 c.Assert(valsets.Add(vs), IsNil) 726 727 snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)) 728 snapB := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(3)) 729 snapBinvRev := snapasserts.NewInstalledSnap("snap-b", "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", snap.R(8)) 730 731 // validity check 732 c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapA, snapB}, nil), ErrorMatches, "validation sets assertions are not met:\n"+ 733 "- invalid snaps:\n"+ 734 " - snap-a \\(invalid for sets acme/fooname\\)") 735 // snapA is invalid but ignore-validation is set so it's ok 736 c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapA, snapB}, map[string]bool{"snap-a": true}), IsNil) 737 738 // validity check 739 c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapBinvRev}, nil), ErrorMatches, "validation sets assertions are not met:\n"+ 740 "- snaps at wrong revisions:\n"+ 741 " - snap-b \\(required at revision 3 by sets acme/fooname\\)") 742 // snapB is at the wrong revision, but ignore-validation is set so it's ok 743 c.Check(valsets.CheckInstalledSnaps([]*snapasserts.InstalledSnap{snapBinvRev}, map[string]bool{"snap-b": true}), IsNil) 744 } 745 746 func (s *validationSetsSuite) TestCheckInstalledSnapsErrorFormat(c *C) { 747 vs1 := assertstest.FakeAssertion(map[string]interface{}{ 748 "type": "validation-set", 749 "authority-id": "acme", 750 "series": "16", 751 "account-id": "acme", 752 "name": "fooname", 753 "sequence": "1", 754 "snaps": []interface{}{ 755 map[string]interface{}{ 756 "name": "snap-a", 757 "id": "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", 758 "presence": "invalid", 759 }, 760 map[string]interface{}{ 761 "name": "snap-b", 762 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 763 "revision": "3", 764 "presence": "required", 765 }, 766 }, 767 }).(*asserts.ValidationSet) 768 vs2 := assertstest.FakeAssertion(map[string]interface{}{ 769 "type": "validation-set", 770 "authority-id": "acme", 771 "series": "16", 772 "account-id": "acme", 773 "name": "barname", 774 "sequence": "2", 775 "snaps": []interface{}{ 776 map[string]interface{}{ 777 "name": "snap-b", 778 "id": "mysnapbbbbbbbbbbbbbbbbbbbbbbbbbb", 779 "presence": "required", 780 }, 781 }, 782 }).(*asserts.ValidationSet) 783 784 valsets := snapasserts.NewValidationSets() 785 c.Assert(valsets.Add(vs1), IsNil) 786 c.Assert(valsets.Add(vs2), IsNil) 787 788 // not strictly important, but ensures test data makes sense and avoids confusing results 789 c.Assert(valsets.Conflict(), IsNil) 790 791 snapA := snapasserts.NewInstalledSnap("snap-a", "mysnapaaaaaaaaaaaaaaaaaaaaaaaaaa", snap.R(1)) 792 snapBlocal := snapasserts.NewInstalledSnap("snap-b", "", snap.R("x3")) 793 794 tests := []struct { 795 snaps []*snapasserts.InstalledSnap 796 errorMsg string 797 }{ 798 { 799 nil, 800 "validation sets assertions are not met:\n" + 801 "- missing required snaps:\n" + 802 " - snap-b \\(required at any revision by sets acme/barname, at revision 3 by sets acme/fooname\\)", 803 }, 804 { 805 []*snapasserts.InstalledSnap{snapA}, 806 "validation sets assertions are not met:\n" + 807 "- missing required snaps:\n" + 808 " - snap-b \\(required at any revision by sets acme/barname, at revision 3 by sets acme/fooname\\)\n" + 809 "- invalid snaps:\n" + 810 " - snap-a \\(invalid for sets acme/fooname\\)", 811 }, 812 { 813 []*snapasserts.InstalledSnap{snapBlocal}, 814 "validation sets assertions are not met:\n" + 815 "- snaps at wrong revisions:\n" + 816 " - snap-b \\(required at revision 3 by sets acme/fooname\\)", 817 }, 818 } 819 820 for i, tc := range tests { 821 err := valsets.CheckInstalledSnaps(tc.snaps, nil) 822 c.Assert(err, NotNil, Commentf("#%d", i)) 823 c.Assert(err, ErrorMatches, tc.errorMsg, Commentf("#%d: ", i)) 824 } 825 } 826 827 func (s *validationSetsSuite) TestSortByRevision(c *C) { 828 revs := []snap.Revision{snap.R(10), snap.R(4), snap.R(5), snap.R(-1)} 829 830 sort.Sort(snapasserts.ByRevision(revs)) 831 c.Assert(revs, DeepEquals, []snap.Revision{snap.R(-1), snap.R(4), snap.R(5), snap.R(10)}) 832 } 833 834 func (s *validationSetsSuite) TestCheckPresenceRequired(c *C) { 835 valset1 := assertstest.FakeAssertion(map[string]interface{}{ 836 "type": "validation-set", 837 "authority-id": "account-id", 838 "series": "16", 839 "account-id": "account-id", 840 "name": "my-snap-ctl", 841 "sequence": "1", 842 "snaps": []interface{}{ 843 map[string]interface{}{ 844 "name": "my-snap", 845 "id": "mysnapididididididididididididid", 846 "presence": "required", 847 "revision": "7", 848 }, 849 map[string]interface{}{ 850 "name": "other-snap", 851 "id": "123456ididididididididididididid", 852 "presence": "optional", 853 }, 854 }, 855 }).(*asserts.ValidationSet) 856 857 valset2 := assertstest.FakeAssertion(map[string]interface{}{ 858 "type": "validation-set", 859 "authority-id": "account-id", 860 "series": "16", 861 "account-id": "account-id", 862 "name": "my-snap-ctl2", 863 "sequence": "2", 864 "snaps": []interface{}{ 865 map[string]interface{}{ 866 "name": "my-snap", 867 "id": "mysnapididididididididididididid", 868 "presence": "required", 869 "revision": "7", 870 }, 871 map[string]interface{}{ 872 "name": "other-snap", 873 "id": "123456ididididididididididididid", 874 "presence": "invalid", 875 }, 876 }, 877 }).(*asserts.ValidationSet) 878 879 // my-snap required but no specific revision set. 880 valset3 := assertstest.FakeAssertion(map[string]interface{}{ 881 "type": "validation-set", 882 "authority-id": "account-id", 883 "series": "16", 884 "account-id": "account-id", 885 "name": "my-snap-ctl3", 886 "sequence": "1", 887 "snaps": []interface{}{ 888 map[string]interface{}{ 889 "name": "my-snap", 890 "id": "mysnapididididididididididididid", 891 "presence": "required", 892 }, 893 }, 894 }).(*asserts.ValidationSet) 895 896 valsets := snapasserts.NewValidationSets() 897 898 // no validation sets 899 vsKeys, _, err := valsets.CheckPresenceRequired(naming.Snap("my-snap")) 900 c.Assert(err, IsNil) 901 c.Check(vsKeys, HasLen, 0) 902 903 c.Assert(valsets.Add(valset1), IsNil) 904 c.Assert(valsets.Add(valset2), IsNil) 905 c.Assert(valsets.Add(valset3), IsNil) 906 907 // validity 908 c.Assert(valsets.Conflict(), IsNil) 909 910 vsKeys, rev, err := valsets.CheckPresenceRequired(naming.Snap("my-snap")) 911 c.Assert(err, IsNil) 912 c.Check(rev, DeepEquals, snap.Revision{N: 7}) 913 c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2", "16/account-id/my-snap-ctl3/1"}) 914 915 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.NewSnapRef("my-snap", "mysnapididididididididididididid")) 916 c.Assert(err, IsNil) 917 c.Check(rev, DeepEquals, snap.Revision{N: 7}) 918 c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2", "16/account-id/my-snap-ctl3/1"}) 919 920 // other-snap is not required 921 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.Snap("other-snap")) 922 c.Assert(err, ErrorMatches, `unexpected presence "invalid" for snap "other-snap"`) 923 pr, ok := err.(*snapasserts.PresenceConstraintError) 924 c.Assert(ok, Equals, true) 925 c.Check(pr.SnapName, Equals, "other-snap") 926 c.Check(pr.Presence, Equals, asserts.PresenceInvalid) 927 c.Check(rev, DeepEquals, snap.Revision{N: 0}) 928 c.Check(vsKeys, HasLen, 0) 929 930 // unknown snap is not required 931 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.NewSnapRef("unknown-snap", "00000000idididididididididididid")) 932 c.Assert(err, IsNil) 933 c.Check(rev, DeepEquals, snap.Revision{N: 0}) 934 c.Check(vsKeys, HasLen, 0) 935 936 // just one set, required but no revision specified 937 valsets = snapasserts.NewValidationSets() 938 c.Assert(valsets.Add(valset3), IsNil) 939 vsKeys, rev, err = valsets.CheckPresenceRequired(naming.Snap("my-snap")) 940 c.Assert(err, IsNil) 941 c.Check(rev, DeepEquals, snap.Revision{N: 0}) 942 c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl3/1"}) 943 } 944 945 func (s *validationSetsSuite) TestIsPresenceInvalid(c *C) { 946 valset1 := assertstest.FakeAssertion(map[string]interface{}{ 947 "type": "validation-set", 948 "authority-id": "account-id", 949 "series": "16", 950 "account-id": "account-id", 951 "name": "my-snap-ctl", 952 "sequence": "1", 953 "snaps": []interface{}{ 954 map[string]interface{}{ 955 "name": "my-snap", 956 "id": "mysnapididididididididididididid", 957 "presence": "invalid", 958 }, 959 map[string]interface{}{ 960 "name": "other-snap", 961 "id": "123456ididididididididididididid", 962 "presence": "optional", 963 }, 964 }, 965 }).(*asserts.ValidationSet) 966 967 valset2 := assertstest.FakeAssertion(map[string]interface{}{ 968 "type": "validation-set", 969 "authority-id": "account-id", 970 "series": "16", 971 "account-id": "account-id", 972 "name": "my-snap-ctl2", 973 "sequence": "2", 974 "snaps": []interface{}{ 975 map[string]interface{}{ 976 "name": "my-snap", 977 "id": "mysnapididididididididididididid", 978 "presence": "invalid", 979 }, 980 }, 981 }).(*asserts.ValidationSet) 982 983 valsets := snapasserts.NewValidationSets() 984 985 // no validation sets 986 vsKeys, err := valsets.CheckPresenceInvalid(naming.Snap("my-snap")) 987 c.Assert(err, IsNil) 988 c.Check(vsKeys, HasLen, 0) 989 990 c.Assert(valsets.Add(valset1), IsNil) 991 c.Assert(valsets.Add(valset2), IsNil) 992 993 // validity 994 c.Assert(valsets.Conflict(), IsNil) 995 996 // invalid in two sets 997 vsKeys, err = valsets.CheckPresenceInvalid(naming.Snap("my-snap")) 998 c.Assert(err, IsNil) 999 c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2"}) 1000 1001 vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("my-snap", "mysnapididididididididididididid")) 1002 c.Assert(err, IsNil) 1003 c.Check(vsKeys, DeepEquals, []snapasserts.ValidationSetKey{"16/account-id/my-snap-ctl/1", "16/account-id/my-snap-ctl2/2"}) 1004 1005 // other-snap isn't invalid 1006 vsKeys, err = valsets.CheckPresenceInvalid(naming.Snap("other-snap")) 1007 c.Assert(err, ErrorMatches, `unexpected presence "optional" for snap "other-snap"`) 1008 pr, ok := err.(*snapasserts.PresenceConstraintError) 1009 c.Assert(ok, Equals, true) 1010 c.Check(pr.SnapName, Equals, "other-snap") 1011 c.Check(pr.Presence, Equals, asserts.PresenceOptional) 1012 c.Check(vsKeys, HasLen, 0) 1013 1014 vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("other-snap", "123456ididididididididididididid")) 1015 c.Assert(err, ErrorMatches, `unexpected presence "optional" for snap "other-snap"`) 1016 c.Check(vsKeys, HasLen, 0) 1017 1018 // unknown snap isn't invalid 1019 vsKeys, err = valsets.CheckPresenceInvalid(naming.NewSnapRef("unknown-snap", "00000000idididididididididididid")) 1020 c.Assert(err, IsNil) 1021 c.Check(vsKeys, HasLen, 0) 1022 } 1023 1024 func (s *validationSetsSuite) TestParseValidationSet(c *C) { 1025 for _, tc := range []struct { 1026 input string 1027 errMsg string 1028 account string 1029 name string 1030 sequence int 1031 }{ 1032 { 1033 input: "foo/bar", 1034 account: "foo", 1035 name: "bar", 1036 }, 1037 { 1038 input: "foo/bar=9", 1039 account: "foo", 1040 name: "bar", 1041 sequence: 9, 1042 }, 1043 { 1044 input: "foo", 1045 errMsg: `cannot parse validation set "foo": expected a single account/name`, 1046 }, 1047 { 1048 input: "foo/bar/baz", 1049 errMsg: `cannot parse validation set "foo/bar/baz": expected a single account/name`, 1050 }, 1051 { 1052 input: "", 1053 errMsg: `cannot parse validation set "": expected a single account/name`, 1054 }, 1055 { 1056 input: "foo=1", 1057 errMsg: `cannot parse validation set "foo=1": expected a single account/name`, 1058 }, 1059 { 1060 input: "foo/bar=x", 1061 errMsg: `cannot parse validation set "foo/bar=x": invalid sequence: strconv.Atoi: parsing "x": invalid syntax`, 1062 }, 1063 { 1064 input: "foo=bar=", 1065 errMsg: `cannot parse validation set "foo=bar=": expected account/name=seq`, 1066 }, 1067 { 1068 input: "$foo/bar", 1069 errMsg: `cannot parse validation set "\$foo/bar": invalid account ID "\$foo"`, 1070 }, 1071 { 1072 input: "foo/$bar", 1073 errMsg: `cannot parse validation set "foo/\$bar": invalid validation set name "\$bar"`, 1074 }, 1075 } { 1076 account, name, seq, err := snapasserts.ParseValidationSet(tc.input) 1077 if tc.errMsg != "" { 1078 c.Assert(err, ErrorMatches, tc.errMsg) 1079 } else { 1080 c.Assert(err, IsNil) 1081 } 1082 c.Check(account, Equals, tc.account) 1083 c.Check(name, Equals, tc.name) 1084 c.Check(seq, Equals, tc.sequence) 1085 } 1086 } 1087 1088 func (s *validationSetsSuite) TestValidationSetKeyFormat(c *C) { 1089 series, acc, name := "a", "b", "c" 1090 sequence := 1 1091 1092 valSet := assertstest.FakeAssertion(map[string]interface{}{ 1093 "type": "validation-set", 1094 "authority-id": acc, 1095 "series": series, 1096 "account-id": acc, 1097 "name": name, 1098 "sequence": strconv.Itoa(sequence), 1099 "snaps": []interface{}{ 1100 map[string]interface{}{ 1101 "name": "my-snap", 1102 "id": "mysnapididididididididididididid", 1103 "presence": "required", 1104 }, 1105 }, 1106 }).(*asserts.ValidationSet) 1107 1108 valSetKey := snapasserts.NewValidationSetKey(valSet) 1109 c.Assert(valSetKey.String(), Equals, fmt.Sprintf("%s/%s/%s/%d", series, acc, name, sequence)) 1110 } 1111 1112 func (s *validationSetsSuite) TestValidationSetKeySliceSort(c *C) { 1113 valSets := snapasserts.ValidationSetKeySlice([]snapasserts.ValidationSetKey{"1/a/a/1", "1/a/b/1", "1/a/b/2", "2/a/a/1", "2/a/a/2", "a/a/a/1"}) 1114 rand.Shuffle(len(valSets), func(x, y int) { 1115 valSets[x], valSets[y] = valSets[y], valSets[x] 1116 }) 1117 1118 sort.Sort(valSets) 1119 c.Assert(valSets, DeepEquals, snapasserts.ValidationSetKeySlice([]snapasserts.ValidationSetKey{"1/a/a/1", "1/a/b/1", "1/a/b/2", "2/a/a/1", "2/a/a/2", "a/a/a/1"})) 1120 } 1121 1122 func (s *validationSetsSuite) TestValidationSetKeySliceCommaSeparated(c *C) { 1123 valSets := snapasserts.ValidationSetKeySlice([]snapasserts.ValidationSetKey{"1/a/a/1", "1/a/b/1", "1/a/b/2", "2/a/a/1"}) 1124 c.Assert(valSets.CommaSeparated(), Equals, "1/a/a/1,1/a/b/1,1/a/b/2,2/a/a/1") 1125 } 1126 1127 func (s *validationSetsSuite) TestValidationSetKeyComponents(c *C) { 1128 valsetKey := snapasserts.NewValidationSetKey(assertstest.FakeAssertion(map[string]interface{}{ 1129 "type": "validation-set", 1130 "series": "a", 1131 "authority-id": "b", 1132 "account-id": "b", 1133 "name": "c", 1134 "sequence": "13", 1135 "snaps": []interface{}{ 1136 map[string]interface{}{ 1137 "name": "my-snap", 1138 "id": "mysnapididididididididididididid", 1139 "presence": "required", 1140 }, 1141 }, 1142 }).(*asserts.ValidationSet)) 1143 c.Assert(valsetKey.Components(), DeepEquals, []string{"a", "b", "c", "13"}) 1144 }