github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/store/store_action_fetch_assertions_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-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 store_test 21 22 import ( 23 "encoding/json" 24 "fmt" 25 "io/ioutil" 26 "net/http" 27 "net/http/httptest" 28 "net/url" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/asserts" 33 "github.com/snapcore/snapd/release" 34 "github.com/snapcore/snapd/store" 35 ) 36 37 type storeActionFetchAssertionsSuite struct { 38 baseStoreSuite 39 } 40 41 var _ = Suite(&storeActionFetchAssertionsSuite{}) 42 43 type testAssertQuery struct { 44 toResolve map[asserts.Grouping][]*asserts.AtRevision 45 toResolveSeq map[asserts.Grouping][]*asserts.AtSequence 46 errors map[string]error 47 } 48 49 func (q *testAssertQuery) ToResolve() (map[asserts.Grouping][]*asserts.AtRevision, map[asserts.Grouping][]*asserts.AtSequence, error) { 50 return q.toResolve, q.toResolveSeq, nil 51 } 52 53 func (q *testAssertQuery) addError(e error, u string) { 54 if q.errors == nil { 55 q.errors = make(map[string]error) 56 } 57 q.errors[u] = e 58 } 59 60 func (q *testAssertQuery) AddError(e error, ref *asserts.Ref) error { 61 q.addError(e, ref.Unique()) 62 return nil 63 } 64 65 func (q *testAssertQuery) AddSequenceError(e error, atSeq *asserts.AtSequence) error { 66 q.addError(e, atSeq.Unique()) 67 return nil 68 } 69 70 func (q *testAssertQuery) AddGroupingError(e error, grouping asserts.Grouping) error { 71 q.addError(e, fmt.Sprintf("{%s}", grouping)) 72 return nil 73 } 74 75 func (s *storeActionFetchAssertionsSuite) TestFetch(c *C) { 76 restore := release.MockOnClassic(false) 77 defer restore() 78 79 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 80 assertRequest(c, r, "POST", snapActionPath) 81 // check device authorization is set, implicitly checking doRequest was used 82 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 83 84 jsonReq, err := ioutil.ReadAll(r.Body) 85 c.Assert(err, IsNil) 86 var req struct { 87 Context []map[string]interface{} `json:"context"` 88 Actions []map[string]interface{} `json:"actions"` 89 90 AssertionMaxFormats map[string]int `json:"assertion-max-formats"` 91 } 92 93 err = json.Unmarshal(jsonReq, &req) 94 c.Assert(err, IsNil) 95 96 c.Assert(req.Context, HasLen, 0) 97 c.Assert(req.Actions, HasLen, 1) 98 expectedAction := map[string]interface{}{ 99 "action": "fetch-assertions", 100 "key": "g1", 101 "assertions": []interface{}{ 102 map[string]interface{}{ 103 "type": "snap-declaration", 104 "primary-key": []interface{}{ 105 "16", 106 "iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 107 }, 108 }, 109 }, 110 } 111 c.Assert(req.Actions[0], DeepEquals, expectedAction) 112 113 c.Assert(req.AssertionMaxFormats, DeepEquals, asserts.MaxSupportedFormats(1)) 114 115 fmt.Fprintf(w, `{ 116 "results": [{ 117 "result": "fetch-assertions", 118 "key": "g1", 119 "assertion-stream-urls": [ 120 "https://api.snapcraft.io/v2/assertions/snap-declaration/16/iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5" 121 ] 122 } 123 ] 124 }`) 125 })) 126 127 c.Assert(mockServer, NotNil) 128 defer mockServer.Close() 129 130 mockServerURL, _ := url.Parse(mockServer.URL) 131 cfg := store.Config{ 132 StoreBaseURL: mockServerURL, 133 } 134 dauthCtx := &testDauthContext{c: c, device: s.device} 135 sto := store.New(&cfg, dauthCtx) 136 137 assertq := &testAssertQuery{ 138 toResolve: map[asserts.Grouping][]*asserts.AtRevision{ 139 asserts.Grouping("g1"): {{ 140 Ref: asserts.Ref{ 141 Type: asserts.SnapDeclarationType, 142 PrimaryKey: []string{ 143 "16", 144 "iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 145 }, 146 }, 147 Revision: asserts.RevisionNotKnown, 148 }}, 149 }, 150 } 151 152 results, aresults, err := sto.SnapAction(s.ctx, nil, 153 nil, assertq, nil, nil) 154 c.Assert(err, IsNil) 155 c.Check(results, HasLen, 0) 156 c.Check(aresults, HasLen, 1) 157 c.Check(aresults[0].Grouping, Equals, asserts.Grouping("g1")) 158 c.Check(aresults[0].StreamURLs, DeepEquals, []string{ 159 "https://api.snapcraft.io/v2/assertions/snap-declaration/16/iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 160 }) 161 } 162 163 func (s *storeActionFetchAssertionsSuite) TestUpdateIfNewerThan(c *C) { 164 restore := release.MockOnClassic(false) 165 defer restore() 166 167 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 168 assertRequest(c, r, "POST", snapActionPath) 169 // check device authorization is set, implicitly checking doRequest was used 170 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 171 172 jsonReq, err := ioutil.ReadAll(r.Body) 173 c.Assert(err, IsNil) 174 var req struct { 175 Context []map[string]interface{} `json:"context"` 176 Actions []map[string]interface{} `json:"actions"` 177 178 AssertionMaxFormats map[string]int `json:"assertion-max-formats"` 179 } 180 181 err = json.Unmarshal(jsonReq, &req) 182 c.Assert(err, IsNil) 183 184 c.Assert(req.Context, HasLen, 0) 185 c.Assert(req.Actions, HasLen, 2) 186 expectedAction1 := map[string]interface{}{ 187 "action": "fetch-assertions", 188 "key": "g1", 189 "assertions": []interface{}{ 190 map[string]interface{}{ 191 "type": "snap-declaration", 192 "primary-key": []interface{}{ 193 "16", 194 "iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 195 }, 196 "if-newer-than": float64(0), 197 }, 198 }, 199 } 200 expectedAction2 := map[string]interface{}{ 201 "action": "fetch-assertions", 202 "key": "g2", 203 "assertions": []interface{}{ 204 map[string]interface{}{ 205 "type": "snap-declaration", 206 "primary-key": []interface{}{ 207 "16", 208 "CSO04Jhav2yK0uz97cr0ipQRyqg0qQL6", 209 }, 210 "if-newer-than": float64(1), 211 }, 212 }, 213 } 214 expectedActions := []map[string]interface{}{expectedAction1, expectedAction2} 215 if req.Actions[0]["key"] != "g1" { 216 expectedActions = []map[string]interface{}{expectedAction2, expectedAction1} 217 } 218 c.Assert(req.Actions, DeepEquals, expectedActions) 219 220 c.Assert(req.AssertionMaxFormats, DeepEquals, asserts.MaxSupportedFormats(1)) 221 222 fmt.Fprintf(w, `{ 223 "results": [{ 224 "result": "fetch-assertions", 225 "key": "g1", 226 "assertion-stream-urls": [ 227 "https://api.snapcraft.io/v2/assertions/snap-declaration/16/iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5" 228 ] 229 }, { 230 "result": "fetch-assertions", 231 "key": "g2", 232 "assertion-stream-urls": [] 233 } 234 ] 235 }`) 236 })) 237 238 c.Assert(mockServer, NotNil) 239 defer mockServer.Close() 240 241 mockServerURL, _ := url.Parse(mockServer.URL) 242 cfg := store.Config{ 243 StoreBaseURL: mockServerURL, 244 } 245 dauthCtx := &testDauthContext{c: c, device: s.device} 246 sto := store.New(&cfg, dauthCtx) 247 248 assertq := &testAssertQuery{ 249 toResolve: map[asserts.Grouping][]*asserts.AtRevision{ 250 asserts.Grouping("g1"): {{ 251 Ref: asserts.Ref{ 252 Type: asserts.SnapDeclarationType, 253 PrimaryKey: []string{ 254 "16", 255 "iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 256 }, 257 }, 258 Revision: 0, 259 }}, 260 asserts.Grouping("g2"): {{ 261 Ref: asserts.Ref{ 262 Type: asserts.SnapDeclarationType, 263 PrimaryKey: []string{ 264 "16", 265 "CSO04Jhav2yK0uz97cr0ipQRyqg0qQL6", 266 }, 267 }, 268 Revision: 1, 269 }}, 270 }, 271 } 272 273 results, aresults, err := sto.SnapAction(s.ctx, nil, 274 nil, assertq, nil, nil) 275 c.Assert(err, IsNil) 276 c.Check(results, HasLen, 0) 277 c.Check(aresults, HasLen, 2) 278 seen := 0 279 for _, aresult := range aresults { 280 if aresult.Grouping == asserts.Grouping("g1") { 281 seen++ 282 c.Check(aresult.StreamURLs, DeepEquals, []string{ 283 "https://api.snapcraft.io/v2/assertions/snap-declaration/16/iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 284 }) 285 } else { 286 seen++ 287 c.Check(aresult.Grouping, Equals, asserts.Grouping("g2")) 288 c.Check(aresult.StreamURLs, HasLen, 0) 289 } 290 } 291 c.Check(seen, Equals, 2) 292 } 293 294 func (s *storeActionFetchAssertionsSuite) TestFetchNotFound(c *C) { 295 restore := release.MockOnClassic(false) 296 defer restore() 297 298 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 299 assertRequest(c, r, "POST", snapActionPath) 300 // check device authorization is set, implicitly checking doRequest was used 301 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 302 303 jsonReq, err := ioutil.ReadAll(r.Body) 304 c.Assert(err, IsNil) 305 var req struct { 306 Context []map[string]interface{} `json:"context"` 307 Actions []map[string]interface{} `json:"actions"` 308 } 309 310 err = json.Unmarshal(jsonReq, &req) 311 c.Assert(err, IsNil) 312 313 c.Assert(req.Context, HasLen, 0) 314 c.Assert(req.Actions, HasLen, 1) 315 expectedAction := map[string]interface{}{ 316 "action": "fetch-assertions", 317 "key": "g1", 318 "assertions": []interface{}{ 319 map[string]interface{}{ 320 "type": "snap-declaration", 321 "primary-key": []interface{}{ 322 "16", 323 "xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 324 }, 325 }, 326 }, 327 } 328 c.Assert(req.Actions[0], DeepEquals, expectedAction) 329 330 fmt.Fprintf(w, `{ 331 "results": [{ 332 "result": "fetch-assertions", 333 "key": "g1", 334 "assertion-stream-urls": [], 335 "error-list": [ 336 { 337 "code": "not-found", 338 "message": "not found: no assertion with type \"snap-declaration\" and key {\"series\":\"16\",\"snap-id\":\"xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5\"}", 339 "primary-key": [ 340 "16", 341 "xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5" 342 ], 343 "type": "snap-declaration" 344 } 345 ] 346 } 347 ] 348 }`) 349 })) 350 351 c.Assert(mockServer, NotNil) 352 defer mockServer.Close() 353 354 mockServerURL, _ := url.Parse(mockServer.URL) 355 cfg := store.Config{ 356 StoreBaseURL: mockServerURL, 357 } 358 dauthCtx := &testDauthContext{c: c, device: s.device} 359 sto := store.New(&cfg, dauthCtx) 360 361 assertq := &testAssertQuery{ 362 toResolve: map[asserts.Grouping][]*asserts.AtRevision{ 363 asserts.Grouping("g1"): {{ 364 Ref: asserts.Ref{ 365 Type: asserts.SnapDeclarationType, 366 PrimaryKey: []string{ 367 "16", 368 "xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 369 }, 370 }, 371 Revision: asserts.RevisionNotKnown, 372 }}, 373 }, 374 } 375 376 results, aresults, err := sto.SnapAction(s.ctx, nil, 377 nil, assertq, nil, nil) 378 c.Assert(err, IsNil) 379 c.Check(results, HasLen, 0) 380 c.Check(aresults, HasLen, 0) 381 382 c.Check(assertq.errors, DeepEquals, map[string]error{ 383 "snap-declaration/16/xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5": &asserts.NotFoundError{ 384 Type: asserts.SnapDeclarationType, 385 Headers: map[string]string{"series": "16", "snap-id": "xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5"}}, 386 }) 387 } 388 389 func (s *storeActionFetchAssertionsSuite) TestFetchValidationSetNotFound(c *C) { 390 restore := release.MockOnClassic(false) 391 defer restore() 392 393 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 394 assertRequest(c, r, "POST", snapActionPath) 395 // check device authorization is set, implicitly checking doRequest was used 396 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 397 398 jsonReq, err := ioutil.ReadAll(r.Body) 399 c.Assert(err, IsNil) 400 var req struct { 401 Context []map[string]interface{} `json:"context"` 402 Actions []map[string]interface{} `json:"actions"` 403 } 404 405 err = json.Unmarshal(jsonReq, &req) 406 c.Assert(err, IsNil) 407 408 c.Assert(req.Context, HasLen, 0) 409 c.Assert(req.Actions, HasLen, 1) 410 expectedAction := map[string]interface{}{ 411 "action": "fetch-assertions", 412 "key": "g1", 413 "assertions": []interface{}{ 414 map[string]interface{}{ 415 "type": "validation-set", 416 "sequence-key": []interface{}{ 417 "16", 418 "foo", 419 "bar", 420 }, 421 }, 422 }, 423 } 424 c.Assert(req.Actions[0], DeepEquals, expectedAction) 425 426 fmt.Fprintf(w, `{ 427 "results": [{ 428 "result": "fetch-assertions", 429 "key": "g1", 430 "assertion-stream-urls": [], 431 "error-list": [ 432 { 433 "code": "not-found", 434 "message": "not found: no assertion with type \"validation-set\" and sequence key {\"account-id\":\"foo\",\"name\":\"bar\",\"series\":\"16\"}", 435 "sequence-key": [ 436 "16", 437 "foo", 438 "bar" 439 ], 440 "type": "validation-set" 441 } 442 ] 443 } 444 ] 445 }`) 446 })) 447 448 c.Assert(mockServer, NotNil) 449 defer mockServer.Close() 450 451 mockServerURL, _ := url.Parse(mockServer.URL) 452 cfg := store.Config{ 453 StoreBaseURL: mockServerURL, 454 } 455 dauthCtx := &testDauthContext{c: c, device: s.device} 456 sto := store.New(&cfg, dauthCtx) 457 458 assertq := &testAssertQuery{ 459 toResolveSeq: map[asserts.Grouping][]*asserts.AtSequence{ 460 asserts.Grouping("g1"): { 461 &asserts.AtSequence{ 462 Type: asserts.ValidationSetType, 463 SequenceKey: []string{ 464 "16", 465 "foo", 466 "bar", 467 }, 468 Revision: asserts.RevisionNotKnown, 469 }, 470 }, 471 }, 472 } 473 474 results, aresults, err := sto.SnapAction(s.ctx, nil, 475 nil, assertq, nil, nil) 476 c.Assert(err, IsNil) 477 c.Check(results, HasLen, 0) 478 c.Check(aresults, HasLen, 0) 479 480 c.Check(assertq.errors, DeepEquals, map[string]error{ 481 "validation-set/16/foo/bar": &asserts.NotFoundError{ 482 Type: asserts.ValidationSetType, 483 Headers: map[string]string{"series": "16", "account-id": "foo", "name": "bar"}}, 484 }) 485 } 486 487 func (s *storeActionFetchAssertionsSuite) TestReportFetchAssertionsError(c *C) { 488 notFound := store.ErrorListEntryJSON{ 489 Code: "not-found", 490 Type: "snap-declaration", 491 PrimaryKey: []string{ 492 "16", 493 "xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 494 }, 495 Message: `not found: no assertion with type "snap-declaration" and key {"series":"16","snap-id":"xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5"}`, 496 } 497 invalidRequest := store.ErrorListEntryJSON{ 498 Code: "invalid-request", 499 Type: "snap-declaration", 500 PrimaryKey: []string{}, 501 Message: `invalid request: invalid key, should be "{series}/{snap-id}"`, 502 } 503 // not a realistic error, but for completeness 504 otherRefError := store.ErrorListEntryJSON{ 505 Code: "other-ref-error", 506 Type: "snap-declaration", 507 PrimaryKey: []string{ 508 "16", 509 "xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 510 }, 511 Message: "other ref error", 512 } 513 otherSeqKeyError := store.ErrorListEntryJSON{ 514 Code: "other-seq-error", 515 Type: "validation-set", 516 SequenceKey: []string{ 517 "16", 518 "foo", 519 "bar", 520 }, 521 Message: "other sequence key error", 522 } 523 524 tests := []struct { 525 errorList []store.ErrorListEntryJSON 526 errkey string 527 err string 528 }{ 529 {[]store.ErrorListEntryJSON{notFound}, "snap-declaration/16/xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", "snap-declaration.*not found"}, 530 {[]store.ErrorListEntryJSON{otherRefError}, "snap-declaration/16/xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", "other ref error"}, 531 {[]store.ErrorListEntryJSON{otherRefError, notFound}, "snap-declaration/16/xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", "other ref error"}, 532 {[]store.ErrorListEntryJSON{notFound, otherRefError}, "snap-declaration/16/xEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", "other ref error"}, 533 {[]store.ErrorListEntryJSON{otherSeqKeyError}, "validation-set/16/foo/bar", "other sequence key error"}, 534 {[]store.ErrorListEntryJSON{notFound, otherSeqKeyError}, "validation-set/16/foo/bar", "other sequence key error"}, 535 {[]store.ErrorListEntryJSON{invalidRequest}, "{g1}", "invalid request: invalid key.*"}, 536 {[]store.ErrorListEntryJSON{invalidRequest, otherRefError}, "{g1}", "invalid request: invalid key.*"}, 537 {[]store.ErrorListEntryJSON{invalidRequest, notFound}, "{g1}", "invalid request: invalid key.*"}, 538 {[]store.ErrorListEntryJSON{notFound, invalidRequest, otherRefError}, "{g1}", "invalid request: invalid key.*"}, 539 } 540 541 for _, t := range tests { 542 assertq := &testAssertQuery{} 543 544 res := &store.SnapActionResultJSON{ 545 Key: "g1", 546 ErrorList: t.errorList, 547 } 548 549 err := store.ReportFetchAssertionsError(res, assertq) 550 c.Assert(err, IsNil) 551 552 c.Check(assertq.errors, HasLen, 1) 553 for k, e := range assertq.errors { 554 c.Check(k, Equals, t.errkey) 555 c.Check(e, ErrorMatches, t.err) 556 } 557 } 558 } 559 560 func (s *storeActionFetchAssertionsSuite) TestUpdateSequenceForming(c *C) { 561 restore := release.MockOnClassic(false) 562 defer restore() 563 564 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 565 assertRequest(c, r, "POST", snapActionPath) 566 // check device authorization is set, implicitly checking doRequest was used 567 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 568 569 jsonReq, err := ioutil.ReadAll(r.Body) 570 c.Assert(err, IsNil) 571 var req struct { 572 Context []map[string]interface{} `json:"context"` 573 Actions []map[string]interface{} `json:"actions"` 574 575 AssertionMaxFormats map[string]int `json:"assertion-max-formats"` 576 } 577 578 err = json.Unmarshal(jsonReq, &req) 579 c.Assert(err, IsNil) 580 581 c.Assert(req.Context, HasLen, 0) 582 c.Assert(req.Actions, HasLen, 2) 583 expectedAction1 := map[string]interface{}{ 584 "action": "fetch-assertions", 585 "key": "g1", 586 "assertions": []interface{}{ 587 map[string]interface{}{ 588 "type": "validation-set", 589 "sequence-key": []interface{}{ 590 "16", 591 "account-1/name-1", 592 }, 593 "sequence": float64(3), 594 }, 595 map[string]interface{}{ 596 "type": "validation-set", 597 "sequence-key": []interface{}{ 598 "16", 599 "account-1/name-2", 600 }, 601 "if-sequence-equal-or-newer-than": float64(5), 602 "if-newer-than": float64(10), 603 }, 604 }, 605 } 606 expectedAction2 := map[string]interface{}{ 607 "action": "fetch-assertions", 608 "key": "g2", 609 "assertions": []interface{}{ 610 map[string]interface{}{ 611 "type": "validation-set", 612 "sequence-key": []interface{}{ 613 "16", 614 "account-2/name", 615 }, 616 }, 617 }, 618 } 619 expectedActions := []map[string]interface{}{expectedAction1, expectedAction2} 620 if req.Actions[0]["key"] != "g1" { 621 expectedActions = []map[string]interface{}{expectedAction2, expectedAction1} 622 } 623 c.Assert(req.Actions, DeepEquals, expectedActions) 624 625 c.Assert(req.AssertionMaxFormats, DeepEquals, asserts.MaxSupportedFormats(1)) 626 627 fmt.Fprintf(w, `{ 628 "results": [{ 629 "result": "fetch-assertions", 630 "key": "g1", 631 "assertion-stream-urls": [ 632 "https://api.snapcraft.io/v2/assertions/snap-declaration/16/iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5" 633 ] 634 }, { 635 "result": "fetch-assertions", 636 "key": "g2", 637 "assertion-stream-urls": [] 638 } 639 ] 640 }`) 641 })) 642 643 c.Assert(mockServer, NotNil) 644 defer mockServer.Close() 645 646 mockServerURL, _ := url.Parse(mockServer.URL) 647 cfg := store.Config{ 648 StoreBaseURL: mockServerURL, 649 } 650 dauthCtx := &testDauthContext{c: c, device: s.device} 651 sto := store.New(&cfg, dauthCtx) 652 653 assertq := &testAssertQuery{ 654 toResolveSeq: map[asserts.Grouping][]*asserts.AtSequence{ 655 asserts.Grouping("g1"): { 656 &asserts.AtSequence{ 657 Type: asserts.ValidationSetType, 658 SequenceKey: []string{ 659 "16", 660 "account-1/name-1", 661 }, 662 Sequence: 3, 663 Pinned: true, 664 Revision: asserts.RevisionNotKnown, 665 }, 666 &asserts.AtSequence{ 667 Type: asserts.ValidationSetType, 668 SequenceKey: []string{ 669 "16", 670 "account-1/name-2", 671 }, 672 Sequence: 5, 673 Revision: 10, 674 }, 675 }, 676 asserts.Grouping("g2"): { 677 &asserts.AtSequence{ 678 Type: asserts.ValidationSetType, 679 SequenceKey: []string{ 680 "16", 681 "account-2/name", 682 }, 683 Revision: asserts.RevisionNotKnown, 684 }, 685 }, 686 }, 687 } 688 689 results, aresults, err := sto.SnapAction(s.ctx, nil, 690 nil, assertq, nil, nil) 691 c.Assert(err, IsNil) 692 c.Check(results, HasLen, 0) 693 c.Check(aresults, HasLen, 2) 694 seen := 0 695 for _, aresult := range aresults { 696 if aresult.Grouping == asserts.Grouping("g1") { 697 seen++ 698 c.Check(aresult.StreamURLs, DeepEquals, []string{ 699 "https://api.snapcraft.io/v2/assertions/snap-declaration/16/iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 700 }) 701 } else { 702 seen++ 703 c.Check(aresult.Grouping, Equals, asserts.Grouping("g2")) 704 c.Check(aresult.StreamURLs, HasLen, 0) 705 } 706 } 707 c.Check(seen, Equals, 2) 708 } 709 710 func (s *storeActionFetchAssertionsSuite) TestUpdateSequenceFormingCommonGroupings(c *C) { 711 restore := release.MockOnClassic(false) 712 defer restore() 713 714 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 715 assertRequest(c, r, "POST", snapActionPath) 716 // check device authorization is set, implicitly checking doRequest was used 717 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 718 719 jsonReq, err := ioutil.ReadAll(r.Body) 720 c.Assert(err, IsNil) 721 var req struct { 722 Context []map[string]interface{} `json:"context"` 723 Actions []map[string]interface{} `json:"actions"` 724 725 AssertionMaxFormats map[string]int `json:"assertion-max-formats"` 726 } 727 728 err = json.Unmarshal(jsonReq, &req) 729 c.Assert(err, IsNil) 730 731 c.Assert(req.Context, HasLen, 0) 732 c.Assert(req.Actions, HasLen, 2) 733 734 expectedAction1 := map[string]interface{}{ 735 "action": "fetch-assertions", 736 "key": "g1", 737 "assertions": []interface{}{ 738 map[string]interface{}{ 739 "type": "snap-declaration", 740 "primary-key": []interface{}{ 741 "16", 742 "iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 743 }, 744 "if-newer-than": float64(0), 745 }, 746 map[string]interface{}{ 747 "type": "validation-set", 748 "sequence-key": []interface{}{ 749 "16", 750 "account-1/name-1", 751 }, 752 "sequence": float64(3), 753 }, 754 map[string]interface{}{ 755 "type": "validation-set", 756 "sequence-key": []interface{}{ 757 "16", 758 "account-1/name-2", 759 }, 760 "if-sequence-equal-or-newer-than": float64(5), 761 "if-newer-than": float64(10), 762 }, 763 }, 764 } 765 expectedAction2 := map[string]interface{}{ 766 "action": "fetch-assertions", 767 "key": "g2", 768 "assertions": []interface{}{ 769 map[string]interface{}{ 770 "type": "snap-declaration", 771 "primary-key": []interface{}{ 772 "16", 773 "CSO04Jhav2yK0uz97cr0ipQRyqg0qQL6", 774 }, 775 "if-newer-than": float64(1), 776 }, 777 map[string]interface{}{ 778 "type": "validation-set", 779 "sequence-key": []interface{}{ 780 "16", 781 "account-2/name", 782 }, 783 }, 784 }, 785 } 786 787 expectedActions := []map[string]interface{}{expectedAction1, expectedAction2} 788 if req.Actions[0]["key"] != "g1" { 789 expectedActions = []map[string]interface{}{expectedAction2, expectedAction1} 790 } 791 c.Assert(req.Actions, DeepEquals, expectedActions) 792 c.Assert(req.AssertionMaxFormats, DeepEquals, asserts.MaxSupportedFormats(1)) 793 794 fmt.Fprintf(w, `{ 795 "results": [{ 796 "result": "fetch-assertions", "key": "g1", "assertion-stream-urls": [] 797 }, { 798 "result": "fetch-assertions", "key": "g2", "assertion-stream-urls": [] 799 }] 800 }`) 801 })) 802 803 c.Assert(mockServer, NotNil) 804 defer mockServer.Close() 805 806 mockServerURL, _ := url.Parse(mockServer.URL) 807 cfg := store.Config{ 808 StoreBaseURL: mockServerURL, 809 } 810 dauthCtx := &testDauthContext{c: c, device: s.device} 811 sto := store.New(&cfg, dauthCtx) 812 813 assertq := &testAssertQuery{ 814 toResolve: map[asserts.Grouping][]*asserts.AtRevision{ 815 asserts.Grouping("g1"): {{ 816 Ref: asserts.Ref{ 817 Type: asserts.SnapDeclarationType, 818 PrimaryKey: []string{ 819 "16", 820 "iEr2EpvaIaqrXxoM2JyHOmuXQYvSzUt5", 821 }, 822 }, 823 Revision: 0, 824 }}, 825 asserts.Grouping("g2"): {{ 826 Ref: asserts.Ref{ 827 Type: asserts.SnapDeclarationType, 828 PrimaryKey: []string{ 829 "16", 830 "CSO04Jhav2yK0uz97cr0ipQRyqg0qQL6", 831 }, 832 }, 833 Revision: 1, 834 }}, 835 }, 836 toResolveSeq: map[asserts.Grouping][]*asserts.AtSequence{ 837 asserts.Grouping("g1"): { 838 &asserts.AtSequence{ 839 Type: asserts.ValidationSetType, 840 SequenceKey: []string{ 841 "16", 842 "account-1/name-1", 843 }, 844 Sequence: 3, 845 Pinned: true, 846 Revision: asserts.RevisionNotKnown, 847 }, 848 &asserts.AtSequence{ 849 Type: asserts.ValidationSetType, 850 SequenceKey: []string{ 851 "16", 852 "account-1/name-2", 853 }, 854 Sequence: 5, 855 Revision: 10, 856 }, 857 }, 858 asserts.Grouping("g2"): { 859 &asserts.AtSequence{ 860 Type: asserts.ValidationSetType, 861 SequenceKey: []string{ 862 "16", 863 "account-2/name", 864 }, 865 Revision: asserts.RevisionNotKnown, 866 }, 867 }, 868 }, 869 } 870 871 _, _, err := sto.SnapAction(s.ctx, nil, nil, assertq, nil, nil) 872 c.Assert(err, IsNil) 873 }