github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/store/store_asserts_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 "bytes" 24 "io" 25 "net/http" 26 "net/http/httptest" 27 "net/url" 28 "time" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/asserts" 33 "github.com/snapcore/snapd/asserts/assertstest" 34 "github.com/snapcore/snapd/store" 35 ) 36 37 type storeAssertsSuite struct { 38 baseStoreSuite 39 40 storeSigning *assertstest.StoreStack 41 dev1Acct *asserts.Account 42 decl1 *asserts.SnapDeclaration 43 44 db *asserts.Database 45 } 46 47 var _ = Suite(&storeAssertsSuite{}) 48 49 func (s *storeAssertsSuite) SetUpTest(c *C) { 50 s.baseStoreSuite.SetUpTest(c) 51 52 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 53 s.dev1Acct = assertstest.NewAccount(s.storeSigning, "developer1", map[string]interface{}{ 54 "account-id": "developer1", 55 }, "") 56 57 a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 58 "series": "16", 59 "snap-id": "asnapid", 60 "snap-name": "asnap", 61 "publisher-id": "developer1", 62 "timestamp": time.Now().UTC().Format(time.RFC3339), 63 }, nil, "") 64 c.Assert(err, IsNil) 65 s.decl1 = a.(*asserts.SnapDeclaration) 66 67 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 68 Backstore: asserts.NewMemoryBackstore(), 69 Trusted: s.storeSigning.Trusted, 70 }) 71 c.Assert(err, IsNil) 72 s.db = db 73 } 74 75 var testAssertion = `type: snap-declaration 76 authority-id: super 77 series: 16 78 snap-id: snapidfoo 79 publisher-id: devidbaz 80 snap-name: mysnap 81 timestamp: 2016-03-30T12:22:16Z 82 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 83 84 openpgp wsBcBAABCAAQBQJW+8VBCRDWhXkqAWcrfgAAQ9gIABZFgMPByJZeUE835FkX3/y2hORn 85 AzE3R1ktDkQEVe/nfVDMACAuaw1fKmUS4zQ7LIrx/AZYw5i0vKVmJszL42LBWVsqR0+p9Cxebzv9 86 U2VUSIajEsUUKkBwzD8wxFzagepFlScif1NvCGZx0vcGUOu0Ent0v+gqgAv21of4efKqEW7crlI1 87 T/A8LqZYmIzKRHGwCVucCyAUD8xnwt9nyWLgLB+LLPOVFNK8SR6YyNsX05Yz1BUSndBfaTN8j/k8 88 8isKGZE6P0O9ozBbNIAE8v8NMWQegJ4uWuil7D3psLkzQIrxSypk9TrQ2GlIG2hJdUovc5zBuroe 89 xS4u9rVT6UY=` 90 91 func (s *storeAssertsSuite) TestAssertion(c *C) { 92 restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 88) 93 defer restore() 94 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 95 assertRequest(c, r, "GET", "/v2/assertions/.*") 96 // check device authorization is set, implicitly checking doRequest was used 97 c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 98 99 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 100 c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/snapidfoo") 101 c.Check(r.URL.Query().Get("max-format"), Equals, "88") 102 io.WriteString(w, testAssertion) 103 })) 104 105 c.Assert(mockServer, NotNil) 106 defer mockServer.Close() 107 108 mockServerURL, _ := url.Parse(mockServer.URL) 109 cfg := store.Config{ 110 StoreBaseURL: mockServerURL, 111 } 112 dauthCtx := &testDauthContext{c: c, device: s.device} 113 sto := store.New(&cfg, dauthCtx) 114 115 a, err := sto.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) 116 c.Assert(err, IsNil) 117 c.Check(a, NotNil) 118 c.Check(a.Type(), Equals, asserts.SnapDeclarationType) 119 } 120 121 func (s *storeAssertsSuite) TestAssertionProxyStoreFromAuthContext(c *C) { 122 restore := asserts.MockMaxSupportedFormat(asserts.SnapDeclarationType, 88) 123 defer restore() 124 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 125 assertRequest(c, r, "GET", "/v2/assertions/.*") 126 // check device authorization is set, implicitly checking doRequest was used 127 c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 128 129 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 130 c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/snapidfoo") 131 c.Check(r.URL.Query().Get("max-format"), Equals, "88") 132 io.WriteString(w, testAssertion) 133 })) 134 135 c.Assert(mockServer, NotNil) 136 defer mockServer.Close() 137 138 mockServerURL, _ := url.Parse(mockServer.URL) 139 nowhereURL, err := url.Parse("http://nowhere.invalid") 140 c.Assert(err, IsNil) 141 cfg := store.Config{ 142 AssertionsBaseURL: nowhereURL, 143 } 144 dauthCtx := &testDauthContext{ 145 c: c, 146 device: s.device, 147 proxyStoreID: "foo", 148 proxyStoreURL: mockServerURL, 149 } 150 sto := store.New(&cfg, dauthCtx) 151 152 a, err := sto.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) 153 c.Assert(err, IsNil) 154 c.Check(a, NotNil) 155 c.Check(a.Type(), Equals, asserts.SnapDeclarationType) 156 } 157 158 func (s *storeAssertsSuite) TestAssertionNotFoundV1(c *C) { 159 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 160 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 161 c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/snapidfoo") 162 w.Header().Set("Content-Type", "application/problem+json") 163 w.WriteHeader(404) 164 io.WriteString(w, `{"status": 404,"title": "not found"}`) 165 })) 166 167 c.Assert(mockServer, NotNil) 168 defer mockServer.Close() 169 170 mockServerURL, _ := url.Parse(mockServer.URL) 171 cfg := store.Config{ 172 AssertionsBaseURL: mockServerURL, 173 } 174 sto := store.New(&cfg, nil) 175 176 _, err := sto.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) 177 c.Check(asserts.IsNotFound(err), Equals, true) 178 c.Check(err, DeepEquals, &asserts.NotFoundError{ 179 Type: asserts.SnapDeclarationType, 180 Headers: map[string]string{ 181 "series": "16", 182 "snap-id": "snapidfoo", 183 }, 184 }) 185 } 186 187 func (s *storeAssertsSuite) TestAssertionNotFoundV2(c *C) { 188 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 189 assertRequest(c, r, "GET", "/v2/assertions/.*") 190 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 191 c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/snapidfoo") 192 w.Header().Set("Content-Type", "application/json") 193 w.WriteHeader(404) 194 io.WriteString(w, `{"error-list":[{"code":"not-found","message":"not found: no ..."}]}`) 195 })) 196 197 c.Assert(mockServer, NotNil) 198 defer mockServer.Close() 199 200 mockServerURL, _ := url.Parse(mockServer.URL) 201 cfg := store.Config{ 202 AssertionsBaseURL: mockServerURL, 203 } 204 sto := store.New(&cfg, nil) 205 206 _, err := sto.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) 207 c.Check(asserts.IsNotFound(err), Equals, true) 208 c.Check(err, DeepEquals, &asserts.NotFoundError{ 209 Type: asserts.SnapDeclarationType, 210 Headers: map[string]string{ 211 "series": "16", 212 "snap-id": "snapidfoo", 213 }, 214 }) 215 } 216 217 func (s *storeAssertsSuite) TestAssertion500(c *C) { 218 var n = 0 219 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 220 assertRequest(c, r, "GET", "/v2/assertions/.*") 221 n++ 222 w.WriteHeader(500) 223 })) 224 225 c.Assert(mockServer, NotNil) 226 defer mockServer.Close() 227 228 mockServerURL, _ := url.Parse(mockServer.URL) 229 cfg := store.Config{ 230 AssertionsBaseURL: mockServerURL, 231 } 232 sto := store.New(&cfg, nil) 233 234 _, err := sto.Assertion(asserts.SnapDeclarationType, []string{"16", "snapidfoo"}, nil) 235 c.Assert(err, ErrorMatches, `cannot fetch assertion: got unexpected HTTP status code 500 via .+`) 236 c.Assert(n, Equals, 5) 237 } 238 239 func (s *storeAssertsSuite) TestDownloadAssertionsSimple(c *C) { 240 assertstest.AddMany(s.db, s.storeSigning.StoreAccountKey(""), s.dev1Acct) 241 242 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 243 assertRequest(c, r, "GET", "/assertions/.*") 244 // check device authorization is set, implicitly checking doRequest was used 245 c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 246 247 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 248 c.Check(r.URL.Path, Matches, ".*/snap-declaration/16/asnapid") 249 c.Check(r.URL.Query().Get("max-format"), Equals, "88") 250 w.Write(asserts.Encode(s.decl1)) 251 })) 252 253 c.Assert(mockServer, NotNil) 254 defer mockServer.Close() 255 256 mockServerURL, _ := url.Parse(mockServer.URL) 257 cfg := store.Config{ 258 StoreBaseURL: mockServerURL, 259 } 260 261 dauthCtx := &testDauthContext{c: c, device: s.device} 262 sto := store.New(&cfg, dauthCtx) 263 264 streamURL, err := mockServerURL.Parse("/assertions/snap-declaration/16/asnapid") 265 c.Assert(err, IsNil) 266 urls := []string{streamURL.String() + "?max-format=88"} 267 268 b := asserts.NewBatch(nil) 269 err = sto.DownloadAssertions(urls, b, nil) 270 c.Assert(err, IsNil) 271 272 c.Assert(b.CommitTo(s.db, nil), IsNil) 273 274 // added 275 _, err = s.decl1.Ref().Resolve(s.db.Find) 276 c.Check(err, IsNil) 277 } 278 279 func (s *storeAssertsSuite) TestDownloadAssertionsWithStreams(c *C) { 280 stream1 := append(asserts.Encode(s.decl1), "\n"...) 281 stream1 = append(stream1, asserts.Encode(s.dev1Acct)...) 282 stream2 := asserts.Encode(s.storeSigning.StoreAccountKey("")) 283 284 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 285 assertRequest(c, r, "GET", "/assertions/.*") 286 287 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 288 var stream []byte 289 switch r.URL.Path { 290 case "/assertions/stream1": 291 stream = stream1 292 case "/assertions/stream2": 293 stream = stream2 294 default: 295 c.Fatal("unexpected stream url") 296 } 297 298 w.Write(stream) 299 })) 300 301 c.Assert(mockServer, NotNil) 302 defer mockServer.Close() 303 304 mockServerURL, _ := url.Parse(mockServer.URL) 305 cfg := store.Config{ 306 StoreBaseURL: mockServerURL, 307 } 308 309 dauthCtx := &testDauthContext{c: c, device: s.device} 310 sto := store.New(&cfg, dauthCtx) 311 312 stream1URL, err := mockServerURL.Parse("/assertions/stream1") 313 c.Assert(err, IsNil) 314 stream2URL, err := mockServerURL.Parse("/assertions/stream2") 315 c.Assert(err, IsNil) 316 317 urls := []string{stream1URL.String(), stream2URL.String()} 318 319 b := asserts.NewBatch(nil) 320 err = sto.DownloadAssertions(urls, b, nil) 321 c.Assert(err, IsNil) 322 323 c.Assert(b.CommitTo(s.db, nil), IsNil) 324 325 // added 326 _, err = s.decl1.Ref().Resolve(s.db.Find) 327 c.Check(err, IsNil) 328 } 329 330 func (s *storeAssertsSuite) TestDownloadAssertionsBrokenStream(c *C) { 331 stream1 := append(asserts.Encode(s.decl1), "\n"...) 332 stream1 = append(stream1, asserts.Encode(s.dev1Acct)...) 333 334 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 335 assertRequest(c, r, "GET", "/assertions/stream1") 336 337 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 338 339 breakAt := bytes.Index(stream1, []byte("account-id")) 340 w.Write(stream1[:breakAt]) 341 })) 342 343 c.Assert(mockServer, NotNil) 344 defer mockServer.Close() 345 346 mockServerURL, _ := url.Parse(mockServer.URL) 347 cfg := store.Config{ 348 StoreBaseURL: mockServerURL, 349 } 350 351 dauthCtx := &testDauthContext{c: c, device: s.device} 352 sto := store.New(&cfg, dauthCtx) 353 354 stream1URL, err := mockServerURL.Parse("/assertions/stream1") 355 c.Assert(err, IsNil) 356 357 urls := []string{stream1URL.String()} 358 359 b := asserts.NewBatch(nil) 360 err = sto.DownloadAssertions(urls, b, nil) 361 c.Assert(err, Equals, io.ErrUnexpectedEOF) 362 } 363 364 func (s *storeAssertsSuite) TestDownloadAssertions500(c *C) { 365 n := 0 366 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 367 assertRequest(c, r, "GET", "/assertions/stream1") 368 369 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 370 371 n++ 372 w.WriteHeader(500) 373 })) 374 375 c.Assert(mockServer, NotNil) 376 defer mockServer.Close() 377 378 mockServerURL, _ := url.Parse(mockServer.URL) 379 cfg := store.Config{ 380 StoreBaseURL: mockServerURL, 381 } 382 383 dauthCtx := &testDauthContext{c: c, device: s.device} 384 sto := store.New(&cfg, dauthCtx) 385 386 stream1URL, err := mockServerURL.Parse("/assertions/stream1") 387 c.Assert(err, IsNil) 388 389 urls := []string{stream1URL.String()} 390 391 b := asserts.NewBatch(nil) 392 err = sto.DownloadAssertions(urls, b, nil) 393 c.Assert(err, ErrorMatches, `cannot download assertion stream: got unexpected HTTP status code 500 via .+`) 394 c.Check(n, Equals, 5) 395 } 396 397 func (s *storeAssertsSuite) TestDownloadAssertionsStreamNotFound(c *C) { 398 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 399 assertRequest(c, r, "GET", "/assertions/stream1") 400 401 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 402 w.Header().Set("Content-Type", "application/problem+json") 403 w.WriteHeader(404) 404 io.WriteString(w, `{"error-list":[{"code":"not-found","message":"not found: no ..."}]}`) 405 })) 406 407 c.Assert(mockServer, NotNil) 408 defer mockServer.Close() 409 410 mockServerURL, _ := url.Parse(mockServer.URL) 411 cfg := store.Config{ 412 StoreBaseURL: mockServerURL, 413 } 414 415 dauthCtx := &testDauthContext{c: c, device: s.device} 416 sto := store.New(&cfg, dauthCtx) 417 418 stream1URL, err := mockServerURL.Parse("/assertions/stream1") 419 c.Assert(err, IsNil) 420 421 urls := []string{stream1URL.String()} 422 423 b := asserts.NewBatch(nil) 424 err = sto.DownloadAssertions(urls, b, nil) 425 c.Assert(err, ErrorMatches, `assertion service error: \"not found.*`) 426 } 427 428 var testValidationSetAssertion = `type: validation-set 429 authority-id: K9scFPuK62alndiUfSxJte9WSeZihEcD 430 series: 16 431 account-id: K9scFPuK62alndiUfSxJte9WSeZihEcD 432 name: set-1 433 sequence: 2 434 snaps: 435 - 436 id: yOqKhntON3vR7kwEbVPsILm7bUViPDzz 437 name: lxd 438 presence: optional 439 revision: 1 440 - 441 id: andRelFqGSFNzJRfWD6SEL34YzEfEEiR 442 name: jq 443 presence: optional 444 - 445 id: UP3QB9yet9QvNhXcCZVdgL1VleVaqz8V 446 name: suligap-python3-qrcode 447 revision: 4 448 timestamp: 2020-11-06T09:16:26Z 449 sign-key-sha3-384: 4sq0NF2nUf53bg-G3AXBs0Paj73IYg4g1kWpBEVaAnzh1eNQEI2-UVeFz4e1MEUW 450 451 AcLBcwQAAQoAHRYhBA519PIIp64v4+mi5pz1bjeveYQwBQJfpRRqAAoJEJz1bjeveYQwIHUP/A6z 452 51knc4y/hYF/aAbrea1VFBxddu7BW18w4J97QDWJOah+TT7HMbvduEneeTEPNl9fO8CUtqUSV5JH 453 GO5WmcS8gHMELMRz7deMKkwzHU1tL7G3xAqIP5ctkNDhobJyCQmU8yyJdp2e6dw5RVFBE9WcAlpO 454 bRhYIFIUUO0Fn6XvKZuDvCFC3rzRmQV/taAR0jYTbHgeOirr8loEfTKKQZQOaE2GyA5cl0vzx3UT 455 5uct/giBHDNXFocHEpw/1wwUkqZgOGkT3/tuyiYd0HQ5jdTDldHs9EPRIcwTEjjFtseBUr9W5m/a 456 kFkWBWPe5FkLvC74H8WXUQbQHgii6RxDnJ1bBVzCOH65pgtRWNCTcoYr5sEB2tPEFEh50bha+37Q 457 1c3lvGGQWyQRz5uxE5aZNiTaLdnQxPEF+nFd1yTwh7yR8Gqv/SuQMxS/AMQz/3sltfssOjayOtV1 458 N2R8HGUVKutoRGWMp+YmGO68wHjk5Ff9cIQvXfDviSl4KezrDIIFRqx0ZJaYh1FDmOTfAK68yEFu 459 P8aWCC2W3HIrdx2mnikT3oVf6yN1KSY5qCE2xdhyyKtt+4y5ZJdQK6JxzTanzh4PZVdiPIUhDv4r 460 AeDBddPc+mqQtb8bpZ7hMD+dA/B4dA3cRl44Nb/5KcfKjdvl7qpmJQl88OA3DOMpXuxmrrVA 461 ` 462 463 func (s *storeAssertsSuite) TestSeqFormingAssertion(c *C) { 464 restore := asserts.MockMaxSupportedFormat(asserts.ValidationSetType, 88) 465 defer restore() 466 467 // overwritten by test loop for each test case 468 expectedSeqArg := "dummy" 469 470 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 471 assertRequest(c, r, "GET", "/v2/assertions/.*") 472 // check device authorization is set, implicitly checking doRequest was used 473 c.Check(r.Header.Get("X-Device-Authorization"), Equals, `Macaroon root="device-macaroon"`) 474 475 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 476 c.Check(r.URL.Path, Matches, ".*/validation-set/16/account-foo/set-bar") 477 q := r.URL.Query() 478 c.Check(q.Get("sequence"), Equals, expectedSeqArg) 479 c.Check(q.Get("max-format"), Equals, "88") 480 io.WriteString(w, testValidationSetAssertion) 481 })) 482 483 c.Assert(mockServer, NotNil) 484 defer mockServer.Close() 485 486 for _, tc := range []struct { 487 sequenceKey []string 488 sequence int 489 expectedSeqArg string 490 }{ 491 {[]string{"16", "account-foo", "set-bar"}, 2, "2"}, 492 {[]string{"16", "account-foo", "set-bar"}, 0, "latest"}, 493 } { 494 expectedSeqArg = tc.expectedSeqArg 495 496 mockServerURL, _ := url.Parse(mockServer.URL) 497 cfg := store.Config{ 498 StoreBaseURL: mockServerURL, 499 } 500 dauthCtx := &testDauthContext{c: c, device: s.device} 501 sto := store.New(&cfg, dauthCtx) 502 503 a, err := sto.SeqFormingAssertion(asserts.ValidationSetType, tc.sequenceKey, tc.sequence, nil) 504 c.Assert(err, IsNil) 505 c.Check(a, NotNil) 506 c.Check(a.Type(), Equals, asserts.ValidationSetType) 507 } 508 } 509 510 func (s *storeAssertsSuite) TestSeqFormingAssertionNotFound(c *C) { 511 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 512 assertRequest(c, r, "GET", "/v2/assertions/.*") 513 c.Check(r.Header.Get("Accept"), Equals, "application/x.ubuntu.assertion") 514 c.Check(r.URL.Path, Matches, ".*/validation-set/16/account-foo/set-bar") 515 w.Header().Set("Content-Type", "application/problem+json") 516 w.WriteHeader(404) 517 io.WriteString(w, `{"error-list":[{"code":"not-found","message":"not found: no ..."}]}`) 518 })) 519 520 c.Assert(mockServer, NotNil) 521 defer mockServer.Close() 522 523 mockServerURL, _ := url.Parse(mockServer.URL) 524 cfg := store.Config{ 525 AssertionsBaseURL: mockServerURL, 526 } 527 sto := store.New(&cfg, nil) 528 529 _, err := sto.SeqFormingAssertion(asserts.ValidationSetType, []string{"16", "account-foo", "set-bar"}, 1, nil) 530 c.Check(asserts.IsNotFound(err), Equals, true) 531 c.Check(err, DeepEquals, &asserts.NotFoundError{ 532 Type: asserts.ValidationSetType, 533 Headers: map[string]string{ 534 "series": "16", 535 "account-id": "account-foo", 536 "name": "set-bar", 537 "sequence": "1", 538 }, 539 }) 540 541 // latest requested 542 _, err = sto.SeqFormingAssertion(asserts.ValidationSetType, []string{"16", "account-foo", "set-bar"}, 0, nil) 543 c.Check(asserts.IsNotFound(err), Equals, true) 544 c.Check(err, DeepEquals, &asserts.NotFoundError{ 545 Type: asserts.ValidationSetType, 546 Headers: map[string]string{ 547 "series": "16", 548 "account-id": "account-foo", 549 "name": "set-bar", 550 }, 551 }) 552 }