github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/asserts/asserts_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-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 asserts_test 21 22 import ( 23 "bytes" 24 "io" 25 "strings" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/asserts" 30 ) 31 32 type assertsSuite struct{} 33 34 var _ = Suite(&assertsSuite{}) 35 36 func (as *assertsSuite) TestType(c *C) { 37 c.Check(asserts.Type("test-only"), Equals, asserts.TestOnlyType) 38 } 39 40 func (as *assertsSuite) TestUnknown(c *C) { 41 c.Check(asserts.Type(""), IsNil) 42 c.Check(asserts.Type("unknown"), IsNil) 43 } 44 45 func (as *assertsSuite) TestTypeMaxSupportedFormat(c *C) { 46 c.Check(asserts.Type("test-only").MaxSupportedFormat(), Equals, 1) 47 } 48 49 func (as *assertsSuite) TestTypeNames(c *C) { 50 c.Check(asserts.TypeNames(), DeepEquals, []string{ 51 "account", 52 "account-key", 53 "account-key-request", 54 "base-declaration", 55 "device-session-request", 56 "model", 57 "repair", 58 "serial", 59 "serial-request", 60 "snap-build", 61 "snap-declaration", 62 "snap-developer", 63 "snap-revision", 64 "store", 65 "system-user", 66 "test-only", 67 "test-only-2", 68 "test-only-decl", 69 "test-only-no-authority", 70 "test-only-no-authority-pk", 71 "test-only-rev", 72 "test-only-seq", 73 "validation", 74 "validation-set", 75 }) 76 } 77 78 func (as *assertsSuite) TestMaxSupportedFormats(c *C) { 79 snapDeclMaxFormat := asserts.SnapDeclarationType.MaxSupportedFormat() 80 systemUserMaxFormat := asserts.SystemUserType.MaxSupportedFormat() 81 // sanity 82 c.Check(snapDeclMaxFormat >= 4, Equals, true) 83 c.Check(systemUserMaxFormat >= 1, Equals, true) 84 c.Check(asserts.MaxSupportedFormats(1), DeepEquals, map[string]int{ 85 "snap-declaration": snapDeclMaxFormat, 86 "system-user": systemUserMaxFormat, 87 "test-only": 1, 88 "test-only-seq": 2, 89 }) 90 91 // all 92 maxFormats := asserts.MaxSupportedFormats(0) 93 c.Assert(maxFormats, HasLen, len(asserts.TypeNames())) 94 c.Check(maxFormats["test-only"], Equals, 1) 95 c.Check(maxFormats["test-only-2"], Equals, 0) 96 c.Check(maxFormats["snap-declaration"], Equals, snapDeclMaxFormat) 97 } 98 99 func (as *assertsSuite) TestSuggestFormat(c *C) { 100 fmtnum, err := asserts.SuggestFormat(asserts.Type("test-only-2"), nil, nil) 101 c.Assert(err, IsNil) 102 c.Check(fmtnum, Equals, 0) 103 } 104 105 func (as *assertsSuite) TestPrimaryKeyHelpers(c *C) { 106 headers, err := asserts.HeadersFromPrimaryKey(asserts.TestOnlyType, []string{"one"}) 107 c.Assert(err, IsNil) 108 c.Check(headers, DeepEquals, map[string]string{ 109 "primary-key": "one", 110 }) 111 112 headers, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"bar", "baz"}) 113 c.Assert(err, IsNil) 114 c.Check(headers, DeepEquals, map[string]string{ 115 "pk1": "bar", 116 "pk2": "baz", 117 }) 118 119 _, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"bar"}) 120 c.Check(err, ErrorMatches, `primary key has wrong length for "test-only-2" assertion`) 121 122 _, err = asserts.HeadersFromPrimaryKey(asserts.TestOnly2Type, []string{"", "baz"}) 123 c.Check(err, ErrorMatches, `primary key "pk1" header cannot be empty`) 124 125 pk, err := asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers) 126 c.Assert(err, IsNil) 127 c.Check(pk, DeepEquals, []string{"bar", "baz"}) 128 129 headers["other"] = "foo" 130 pk1, err := asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers) 131 c.Assert(err, IsNil) 132 c.Check(pk1, DeepEquals, pk) 133 134 delete(headers, "pk2") 135 _, err = asserts.PrimaryKeyFromHeaders(asserts.TestOnly2Type, headers) 136 c.Check(err, ErrorMatches, `must provide primary key: pk2`) 137 } 138 139 func (as *assertsSuite) TestRef(c *C) { 140 ref := &asserts.Ref{ 141 Type: asserts.TestOnly2Type, 142 PrimaryKey: []string{"abc", "xyz"}, 143 } 144 c.Check(ref.Unique(), Equals, "test-only-2/abc/xyz") 145 } 146 147 func (as *assertsSuite) TestRefString(c *C) { 148 ref := &asserts.Ref{ 149 Type: asserts.AccountType, 150 PrimaryKey: []string{"canonical"}, 151 } 152 153 c.Check(ref.String(), Equals, "account (canonical)") 154 155 ref = &asserts.Ref{ 156 Type: asserts.SnapDeclarationType, 157 PrimaryKey: []string{"18", "SNAPID"}, 158 } 159 160 c.Check(ref.String(), Equals, "snap-declaration (SNAPID; series:18)") 161 162 ref = &asserts.Ref{ 163 Type: asserts.ModelType, 164 PrimaryKey: []string{"18", "BRAND", "baz-3000"}, 165 } 166 167 c.Check(ref.String(), Equals, "model (baz-3000; series:18 brand-id:BRAND)") 168 169 // broken primary key 170 ref = &asserts.Ref{ 171 Type: asserts.ModelType, 172 PrimaryKey: []string{"18"}, 173 } 174 c.Check(ref.String(), Equals, "model (???)") 175 176 ref = &asserts.Ref{ 177 Type: asserts.TestOnlyNoAuthorityType, 178 } 179 c.Check(ref.String(), Equals, "test-only-no-authority (-)") 180 } 181 182 func (as *assertsSuite) TestRefResolveError(c *C) { 183 ref := &asserts.Ref{ 184 Type: asserts.TestOnly2Type, 185 PrimaryKey: []string{"abc"}, 186 } 187 _, err := ref.Resolve(nil) 188 c.Check(err, ErrorMatches, `"test-only-2" assertion reference primary key has the wrong length \(expected \[pk1 pk2\]\): \[abc\]`) 189 } 190 191 func (as *assertsSuite) TestAtRevisionString(c *C) { 192 ref := asserts.Ref{ 193 Type: asserts.AccountType, 194 PrimaryKey: []string{"canonical"}, 195 } 196 197 at := &asserts.AtRevision{ 198 Ref: ref, 199 } 200 c.Check(at.String(), Equals, "account (canonical) at revision 0") 201 202 at = &asserts.AtRevision{ 203 Ref: ref, 204 Revision: asserts.RevisionNotKnown, 205 } 206 c.Check(at.String(), Equals, "account (canonical)") 207 } 208 209 const exKeyID = "Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" 210 211 const exampleEmptyBodyAllDefaults = "type: test-only\n" + 212 "authority-id: auth-id1\n" + 213 "primary-key: abc\n" + 214 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 215 "\n\n" + 216 "AXNpZw==" 217 218 func (as *assertsSuite) TestDecodeEmptyBodyAllDefaults(c *C) { 219 a, err := asserts.Decode([]byte(exampleEmptyBodyAllDefaults)) 220 c.Assert(err, IsNil) 221 c.Check(a.Type(), Equals, asserts.TestOnlyType) 222 _, ok := a.(*asserts.TestOnly) 223 c.Check(ok, Equals, true) 224 c.Check(a.Revision(), Equals, 0) 225 c.Check(a.Format(), Equals, 0) 226 c.Check(a.Body(), IsNil) 227 c.Check(a.Header("header1"), IsNil) 228 c.Check(a.HeaderString("header1"), Equals, "") 229 c.Check(a.AuthorityID(), Equals, "auth-id1") 230 c.Check(a.SignKeyID(), Equals, exKeyID) 231 } 232 233 const exampleEmptyBody2NlNl = "type: test-only\n" + 234 "authority-id: auth-id1\n" + 235 "primary-key: xyz\n" + 236 "revision: 0\n" + 237 "body-length: 0\n" + 238 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 239 "\n\n" + 240 "\n\n" + 241 "AXNpZw==\n" 242 243 func (as *assertsSuite) TestDecodeEmptyBodyNormalize2NlNl(c *C) { 244 a, err := asserts.Decode([]byte(exampleEmptyBody2NlNl)) 245 c.Assert(err, IsNil) 246 c.Check(a.Type(), Equals, asserts.TestOnlyType) 247 c.Check(a.Revision(), Equals, 0) 248 c.Check(a.Format(), Equals, 0) 249 c.Check(a.Body(), IsNil) 250 } 251 252 const exampleBodyAndExtraHeaders = "type: test-only\n" + 253 "format: 1\n" + 254 "authority-id: auth-id2\n" + 255 "primary-key: abc\n" + 256 "revision: 5\n" + 257 "header1: value1\n" + 258 "header2: value2\n" + 259 "body-length: 8\n" + 260 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 261 "THE-BODY" + 262 "\n\n" + 263 "AXNpZw==\n" 264 265 func (as *assertsSuite) TestDecodeWithABodyAndExtraHeaders(c *C) { 266 a, err := asserts.Decode([]byte(exampleBodyAndExtraHeaders)) 267 c.Assert(err, IsNil) 268 c.Check(a.Type(), Equals, asserts.TestOnlyType) 269 c.Check(a.AuthorityID(), Equals, "auth-id2") 270 c.Check(a.SignKeyID(), Equals, exKeyID) 271 c.Check(a.Header("primary-key"), Equals, "abc") 272 c.Check(a.Revision(), Equals, 5) 273 c.Check(a.Format(), Equals, 1) 274 c.Check(a.SupportedFormat(), Equals, true) 275 c.Check(a.Header("header1"), Equals, "value1") 276 c.Check(a.Header("header2"), Equals, "value2") 277 c.Check(a.Body(), DeepEquals, []byte("THE-BODY")) 278 279 } 280 281 const exampleUnsupportedFormat = "type: test-only\n" + 282 "format: 77\n" + 283 "authority-id: auth-id2\n" + 284 "primary-key: abc\n" + 285 "revision: 5\n" + 286 "header1: value1\n" + 287 "header2: value2\n" + 288 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 289 "AXNpZw==\n" 290 291 func (as *assertsSuite) TestDecodeUnsupportedFormat(c *C) { 292 a, err := asserts.Decode([]byte(exampleUnsupportedFormat)) 293 c.Assert(err, IsNil) 294 c.Check(a.Type(), Equals, asserts.TestOnlyType) 295 c.Check(a.AuthorityID(), Equals, "auth-id2") 296 c.Check(a.SignKeyID(), Equals, exKeyID) 297 c.Check(a.Header("primary-key"), Equals, "abc") 298 c.Check(a.Revision(), Equals, 5) 299 c.Check(a.Format(), Equals, 77) 300 c.Check(a.SupportedFormat(), Equals, false) 301 } 302 303 func (as *assertsSuite) TestDecodeGetSignatureBits(c *C) { 304 content := "type: test-only\n" + 305 "authority-id: auth-id1\n" + 306 "primary-key: xyz\n" + 307 "revision: 5\n" + 308 "header1: value1\n" + 309 "body-length: 8\n" + 310 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 311 "THE-BODY" 312 encoded := content + 313 "\n\n" + 314 "AXNpZw==" 315 a, err := asserts.Decode([]byte(encoded)) 316 c.Assert(err, IsNil) 317 c.Check(a.Type(), Equals, asserts.TestOnlyType) 318 c.Check(a.AuthorityID(), Equals, "auth-id1") 319 c.Check(a.SignKeyID(), Equals, exKeyID) 320 cont, signature := a.Signature() 321 c.Check(signature, DeepEquals, []byte("AXNpZw==")) 322 c.Check(cont, DeepEquals, []byte(content)) 323 } 324 325 func (as *assertsSuite) TestDecodeNoSignatureSplit(c *C) { 326 for _, encoded := range []string{"", "foo"} { 327 _, err := asserts.Decode([]byte(encoded)) 328 c.Check(err, ErrorMatches, "assertion content/signature separator not found") 329 } 330 } 331 332 func (as *assertsSuite) TestDecodeHeaderParsingErrors(c *C) { 333 headerParsingErrorsTests := []struct{ encoded, expectedErr string }{ 334 {string([]byte{255, '\n', '\n'}), "header is not utf8"}, 335 {"foo: a\nbar\n\n", `header entry missing ':' separator: "bar"`}, 336 {"TYPE: foo\n\n", `invalid header name: "TYPE"`}, 337 {"foo: a\nbar:>\n\n", `header entry should have a space or newline \(for multiline\) before value: "bar:>"`}, 338 {"foo: a\nbar:\n\n", `expected 4 chars nesting prefix after multiline introduction "bar:": EOF`}, 339 {"foo: a\nbar:\nbaz: x\n\n", `expected 4 chars nesting prefix after multiline introduction "bar:": "baz: x"`}, 340 {"foo: a:\nbar: b\nfoo: x\n\n", `repeated header: "foo"`}, 341 } 342 343 for _, test := range headerParsingErrorsTests { 344 _, err := asserts.Decode([]byte(test.encoded)) 345 c.Check(err, ErrorMatches, "parsing assertion headers: "+test.expectedErr) 346 } 347 } 348 349 func (as *assertsSuite) TestDecodeInvalid(c *C) { 350 keyIDHdr := "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n" 351 encoded := "type: test-only\n" + 352 "format: 0\n" + 353 "authority-id: auth-id\n" + 354 "primary-key: abc\n" + 355 "revision: 0\n" + 356 "body-length: 5\n" + 357 keyIDHdr + 358 "\n" + 359 "abcde" + 360 "\n\n" + 361 "AXNpZw==" 362 363 invalidAssertTests := []struct{ original, invalid, expectedErr string }{ 364 {"body-length: 5", "body-length: z", `assertion: "body-length" header is not an integer: z`}, 365 {"body-length: 5", "body-length: 3", "assertion body length and declared body-length don't match: 5 != 3"}, 366 {"authority-id: auth-id\n", "", `assertion: "authority-id" header is mandatory`}, 367 {"authority-id: auth-id\n", "authority-id: \n", `assertion: "authority-id" header should not be empty`}, 368 {keyIDHdr, "", `assertion: "sign-key-sha3-384" header is mandatory`}, 369 {keyIDHdr, "sign-key-sha3-384: \n", `assertion: "sign-key-sha3-384" header should not be empty`}, 370 {keyIDHdr, "sign-key-sha3-384: $\n", `assertion: "sign-key-sha3-384" header cannot be decoded: .*`}, 371 {keyIDHdr, "sign-key-sha3-384: eHl6\n", `assertion: "sign-key-sha3-384" header does not have the expected bit length: 24`}, 372 {"AXNpZw==", "", "empty assertion signature"}, 373 {"type: test-only\n", "", `assertion: "type" header is mandatory`}, 374 {"type: test-only\n", "type: unknown\n", `unknown assertion type: "unknown"`}, 375 {"revision: 0\n", "revision: Z\n", `assertion: "revision" header is not an integer: Z`}, 376 {"revision: 0\n", "revision:\n - 1\n", `assertion: "revision" header is not an integer: \[1\]`}, 377 {"revision: 0\n", "revision: 00\n", `assertion: "revision" header has invalid prefix zeros: 00`}, 378 {"revision: 0\n", "revision: -10\n", "assertion: revision should be positive: -10"}, 379 {"revision: 0\n", "revision: 99999999999999999999\n", `assertion: "revision" header is out of range: 99999999999999999999`}, 380 {"format: 0\n", "format: Z\n", `assertion: "format" header is not an integer: Z`}, 381 {"format: 0\n", "format: -10\n", "assertion: format should be positive: -10"}, 382 {"primary-key: abc\n", "", `assertion test-only: "primary-key" header is mandatory`}, 383 {"primary-key: abc\n", "primary-key:\n - abc\n", `assertion test-only: "primary-key" header must be a string`}, 384 {"primary-key: abc\n", "primary-key: a/c\n", `assertion test-only: "primary-key" primary key header cannot contain '/'`}, 385 {"abcde", "ab\xffde", "body is not utf8"}, 386 } 387 388 for _, test := range invalidAssertTests { 389 invalid := strings.Replace(encoded, test.original, test.invalid, 1) 390 _, err := asserts.Decode([]byte(invalid)) 391 c.Check(err, ErrorMatches, test.expectedErr) 392 } 393 } 394 395 func (as *assertsSuite) TestDecodeNoAuthorityInvalid(c *C) { 396 invalid := "type: test-only-no-authority\n" + 397 "authority-id: auth-id1\n" + 398 "hdr: FOO\n" + 399 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij" + 400 "\n\n" + 401 "openpgp c2ln" 402 403 _, err := asserts.Decode([]byte(invalid)) 404 c.Check(err, ErrorMatches, `"test-only-no-authority" assertion cannot have authority-id set`) 405 } 406 407 func checkContent(c *C, a asserts.Assertion, encoded string) { 408 expected, err := asserts.Decode([]byte(encoded)) 409 c.Assert(err, IsNil) 410 expectedCont, _ := expected.Signature() 411 412 cont, _ := a.Signature() 413 c.Check(cont, DeepEquals, expectedCont) 414 } 415 416 func (as *assertsSuite) TestEncoderDecoderHappy(c *C) { 417 stream := new(bytes.Buffer) 418 enc := asserts.NewEncoder(stream) 419 enc.WriteEncoded([]byte(exampleEmptyBody2NlNl)) 420 enc.WriteEncoded([]byte(exampleBodyAndExtraHeaders)) 421 enc.WriteEncoded([]byte(exampleEmptyBodyAllDefaults)) 422 423 decoder := asserts.NewDecoder(stream) 424 a, err := decoder.Decode() 425 c.Assert(err, IsNil) 426 c.Check(a.Type(), Equals, asserts.TestOnlyType) 427 _, ok := a.(*asserts.TestOnly) 428 c.Check(ok, Equals, true) 429 checkContent(c, a, exampleEmptyBody2NlNl) 430 431 a, err = decoder.Decode() 432 c.Assert(err, IsNil) 433 checkContent(c, a, exampleBodyAndExtraHeaders) 434 435 a, err = decoder.Decode() 436 c.Assert(err, IsNil) 437 checkContent(c, a, exampleEmptyBodyAllDefaults) 438 439 a, err = decoder.Decode() 440 c.Assert(err, Equals, io.EOF) 441 } 442 443 func (as *assertsSuite) TestDecodeEmptyStream(c *C) { 444 stream := new(bytes.Buffer) 445 decoder := asserts.NewDecoder(stream) 446 _, err := decoder.Decode() 447 c.Check(err, Equals, io.EOF) 448 } 449 450 func (as *assertsSuite) TestDecoderHappyWithSeparatorsVariations(c *C) { 451 streams := []string{ 452 exampleBodyAndExtraHeaders, 453 exampleEmptyBody2NlNl, 454 exampleEmptyBodyAllDefaults, 455 } 456 457 for _, streamData := range streams { 458 stream := bytes.NewBufferString(streamData) 459 decoder := asserts.NewDecoderStressed(stream, 16, 1024, 1024, 1024) 460 a, err := decoder.Decode() 461 c.Assert(err, IsNil, Commentf("stream: %q", streamData)) 462 463 checkContent(c, a, streamData) 464 465 a, err = decoder.Decode() 466 c.Check(a, IsNil) 467 c.Check(err, Equals, io.EOF, Commentf("stream: %q", streamData)) 468 } 469 } 470 471 func (as *assertsSuite) TestDecoderHappyWithTrailerDoubleNewlines(c *C) { 472 streams := []string{ 473 exampleBodyAndExtraHeaders, 474 exampleEmptyBody2NlNl, 475 exampleEmptyBodyAllDefaults, 476 } 477 478 for _, streamData := range streams { 479 stream := bytes.NewBufferString(streamData) 480 if strings.HasSuffix(streamData, "\n") { 481 stream.WriteString("\n") 482 } else { 483 stream.WriteString("\n\n") 484 } 485 486 decoder := asserts.NewDecoderStressed(stream, 16, 1024, 1024, 1024) 487 a, err := decoder.Decode() 488 c.Assert(err, IsNil, Commentf("stream: %q", streamData)) 489 490 checkContent(c, a, streamData) 491 492 a, err = decoder.Decode() 493 c.Check(a, IsNil) 494 c.Check(err, Equals, io.EOF, Commentf("stream: %q", streamData)) 495 } 496 } 497 498 func (as *assertsSuite) TestDecoderUnexpectedEOF(c *C) { 499 streamData := exampleBodyAndExtraHeaders + "\n" + exampleEmptyBodyAllDefaults 500 fstHeadEnd := strings.Index(exampleBodyAndExtraHeaders, "\n\n") 501 sndHeadEnd := len(exampleBodyAndExtraHeaders) + 1 + strings.Index(exampleEmptyBodyAllDefaults, "\n\n") 502 503 for _, brk := range []int{1, fstHeadEnd / 2, fstHeadEnd, fstHeadEnd + 1, fstHeadEnd + 2, fstHeadEnd + 6} { 504 stream := bytes.NewBufferString(streamData[:brk]) 505 decoder := asserts.NewDecoderStressed(stream, 16, 1024, 1024, 1024) 506 _, err := decoder.Decode() 507 c.Check(err, Equals, io.ErrUnexpectedEOF, Commentf("brk: %d", brk)) 508 } 509 510 for _, brk := range []int{sndHeadEnd, sndHeadEnd + 1} { 511 stream := bytes.NewBufferString(streamData[:brk]) 512 decoder := asserts.NewDecoder(stream) 513 _, err := decoder.Decode() 514 c.Assert(err, IsNil) 515 516 _, err = decoder.Decode() 517 c.Check(err, Equals, io.ErrUnexpectedEOF, Commentf("brk: %d", brk)) 518 } 519 } 520 521 func (as *assertsSuite) TestDecoderBrokenBodySeparation(c *C) { 522 streamData := strings.Replace(exampleBodyAndExtraHeaders, "THE-BODY\n\n", "THE-BODY", 1) 523 decoder := asserts.NewDecoder(bytes.NewBufferString(streamData)) 524 _, err := decoder.Decode() 525 c.Assert(err, ErrorMatches, "missing content/signature separator") 526 527 streamData = strings.Replace(exampleBodyAndExtraHeaders, "THE-BODY\n\n", "THE-BODY\n", 1) 528 decoder = asserts.NewDecoder(bytes.NewBufferString(streamData)) 529 _, err = decoder.Decode() 530 c.Assert(err, ErrorMatches, "missing content/signature separator") 531 } 532 533 func (as *assertsSuite) TestDecoderHeadTooBig(c *C) { 534 decoder := asserts.NewDecoderStressed(bytes.NewBufferString(exampleBodyAndExtraHeaders), 4, 4, 1024, 1024) 535 _, err := decoder.Decode() 536 c.Assert(err, ErrorMatches, `error reading assertion headers: maximum size exceeded while looking for delimiter "\\n\\n"`) 537 } 538 539 func (as *assertsSuite) TestDecoderBodyTooBig(c *C) { 540 decoder := asserts.NewDecoderStressed(bytes.NewBufferString(exampleBodyAndExtraHeaders), 1024, 1024, 5, 1024) 541 _, err := decoder.Decode() 542 c.Assert(err, ErrorMatches, "assertion body length 8 exceeds maximum body size") 543 } 544 545 func (as *assertsSuite) TestDecoderSignatureTooBig(c *C) { 546 decoder := asserts.NewDecoderStressed(bytes.NewBufferString(exampleBodyAndExtraHeaders), 4, 1024, 1024, 7) 547 _, err := decoder.Decode() 548 c.Assert(err, ErrorMatches, `error reading assertion signature: maximum size exceeded while looking for delimiter "\\n\\n"`) 549 } 550 551 func (as *assertsSuite) TestDecoderDefaultMaxBodySize(c *C) { 552 enc := strings.Replace(exampleBodyAndExtraHeaders, "body-length: 8", "body-length: 2097153", 1) 553 decoder := asserts.NewDecoder(bytes.NewBufferString(enc)) 554 _, err := decoder.Decode() 555 c.Assert(err, ErrorMatches, "assertion body length 2097153 exceeds maximum body size") 556 } 557 558 func (as *assertsSuite) TestDecoderWithTypeMaxBodySize(c *C) { 559 ex1 := strings.Replace(exampleBodyAndExtraHeaders, "body-length: 8", "body-length: 2097152", 1) 560 ex1 = strings.Replace(ex1, "THE-BODY", strings.Repeat("B", 2*1024*1024), 1) 561 ex1toobig := strings.Replace(exampleBodyAndExtraHeaders, "body-length: 8", "body-length: 2097153", 1) 562 ex1toobig = strings.Replace(ex1toobig, "THE-BODY", strings.Repeat("B", 2*1024*1024+1), 1) 563 const ex2 = `type: test-only-2 564 authority-id: auth-id1 565 pk1: foo 566 pk2: bar 567 body-length: 3 568 sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij 569 570 XYZ 571 572 AXNpZw==` 573 574 decoder := asserts.NewDecoderWithTypeMaxBodySize(bytes.NewBufferString(ex1+"\n"+ex2), map[*asserts.AssertionType]int{ 575 asserts.TestOnly2Type: 3, 576 }) 577 a1, err := decoder.Decode() 578 c.Assert(err, IsNil) 579 c.Check(a1.Body(), HasLen, 2*1024*1024) 580 a2, err := decoder.Decode() 581 c.Assert(err, IsNil) 582 c.Check(a2.Body(), DeepEquals, []byte("XYZ")) 583 584 decoder = asserts.NewDecoderWithTypeMaxBodySize(bytes.NewBufferString(ex1+"\n"+ex2), map[*asserts.AssertionType]int{ 585 asserts.TestOnly2Type: 2, 586 }) 587 a1, err = decoder.Decode() 588 c.Assert(err, IsNil) 589 c.Check(a1.Body(), HasLen, 2*1024*1024) 590 _, err = decoder.Decode() 591 c.Assert(err, ErrorMatches, `assertion body length 3 exceeds maximum body size 2 for "test-only-2" assertions`) 592 593 decoder = asserts.NewDecoderWithTypeMaxBodySize(bytes.NewBufferString(ex2+"\n\n"+ex1toobig), map[*asserts.AssertionType]int{ 594 asserts.TestOnly2Type: 3, 595 }) 596 a2, err = decoder.Decode() 597 c.Assert(err, IsNil) 598 c.Check(a2.Body(), DeepEquals, []byte("XYZ")) 599 _, err = decoder.Decode() 600 c.Assert(err, ErrorMatches, "assertion body length 2097153 exceeds maximum body size") 601 } 602 603 func (as *assertsSuite) TestEncode(c *C) { 604 encoded := []byte("type: test-only\n" + 605 "authority-id: auth-id2\n" + 606 "primary-key: xyz\n" + 607 "revision: 5\n" + 608 "header1: value1\n" + 609 "header2: value2\n" + 610 "body-length: 8\n" + 611 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 612 "THE-BODY" + 613 "\n\n" + 614 "AXNpZw==") 615 a, err := asserts.Decode(encoded) 616 c.Assert(err, IsNil) 617 encodeRes := asserts.Encode(a) 618 c.Check(encodeRes, DeepEquals, encoded) 619 } 620 621 func (as *assertsSuite) TestEncoderOK(c *C) { 622 encoded := []byte("type: test-only\n" + 623 "authority-id: auth-id2\n" + 624 "primary-key: xyzyz\n" + 625 "revision: 5\n" + 626 "header1: value1\n" + 627 "header2: value2\n" + 628 "body-length: 8\n" + 629 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 630 "THE-BODY" + 631 "\n\n" + 632 "AXNpZw==") 633 a0, err := asserts.Decode(encoded) 634 c.Assert(err, IsNil) 635 cont0, _ := a0.Signature() 636 637 stream := new(bytes.Buffer) 638 enc := asserts.NewEncoder(stream) 639 enc.Encode(a0) 640 641 c.Check(bytes.HasSuffix(stream.Bytes(), []byte{'\n'}), Equals, true) 642 643 dec := asserts.NewDecoder(stream) 644 a1, err := dec.Decode() 645 c.Assert(err, IsNil) 646 647 cont1, _ := a1.Signature() 648 c.Check(cont1, DeepEquals, cont0) 649 } 650 651 func (as *assertsSuite) TestEncoderSingleDecodeOK(c *C) { 652 encoded := []byte("type: test-only\n" + 653 "authority-id: auth-id2\n" + 654 "primary-key: abc\n" + 655 "revision: 5\n" + 656 "header1: value1\n" + 657 "header2: value2\n" + 658 "body-length: 8\n" + 659 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 660 "THE-BODY" + 661 "\n\n" + 662 "AXNpZw==") 663 a0, err := asserts.Decode(encoded) 664 c.Assert(err, IsNil) 665 cont0, _ := a0.Signature() 666 667 stream := new(bytes.Buffer) 668 enc := asserts.NewEncoder(stream) 669 enc.Encode(a0) 670 671 a1, err := asserts.Decode(stream.Bytes()) 672 c.Assert(err, IsNil) 673 674 cont1, _ := a1.Signature() 675 c.Check(cont1, DeepEquals, cont0) 676 } 677 678 func (as *assertsSuite) TestSignFormatSanityEmptyBody(c *C) { 679 headers := map[string]interface{}{ 680 "authority-id": "auth-id1", 681 "primary-key": "0", 682 } 683 a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1) 684 c.Assert(err, IsNil) 685 686 _, err = asserts.Decode(asserts.Encode(a)) 687 c.Check(err, IsNil) 688 } 689 690 func (as *assertsSuite) TestSignFormatSanityNonEmptyBody(c *C) { 691 headers := map[string]interface{}{ 692 "authority-id": "auth-id1", 693 "primary-key": "0", 694 } 695 body := []byte("THE-BODY") 696 a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, body, testPrivKey1) 697 c.Assert(err, IsNil) 698 c.Check(a.Body(), DeepEquals, body) 699 700 decoded, err := asserts.Decode(asserts.Encode(a)) 701 c.Assert(err, IsNil) 702 c.Check(decoded.Body(), DeepEquals, body) 703 } 704 705 func (as *assertsSuite) TestSignFormatSanitySupportMultilineHeaderValues(c *C) { 706 headers := map[string]interface{}{ 707 "authority-id": "auth-id1", 708 "primary-key": "0", 709 } 710 711 multilineVals := []string{ 712 "a\n", 713 "\na", 714 "a\n\b\nc", 715 "a\n\b\nc\n", 716 "\na\n", 717 "\n\na\n\nb\n\nc", 718 } 719 720 for _, multilineVal := range multilineVals { 721 headers["multiline"] = multilineVal 722 if len(multilineVal)%2 == 1 { 723 headers["odd"] = "true" 724 } 725 726 a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1) 727 c.Assert(err, IsNil) 728 729 decoded, err := asserts.Decode(asserts.Encode(a)) 730 c.Assert(err, IsNil) 731 732 c.Check(decoded.Header("multiline"), Equals, multilineVal) 733 } 734 } 735 736 func (as *assertsSuite) TestSignFormatAndRevision(c *C) { 737 headers := map[string]interface{}{ 738 "authority-id": "auth-id1", 739 "primary-key": "0", 740 "format": "1", 741 "revision": "11", 742 } 743 744 a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1) 745 c.Assert(err, IsNil) 746 747 c.Check(a.Revision(), Equals, 11) 748 c.Check(a.Format(), Equals, 1) 749 c.Check(a.SupportedFormat(), Equals, true) 750 751 a1, err := asserts.Decode(asserts.Encode(a)) 752 c.Assert(err, IsNil) 753 754 c.Check(a1.Revision(), Equals, 11) 755 c.Check(a1.Format(), Equals, 1) 756 c.Check(a1.SupportedFormat(), Equals, true) 757 } 758 759 func (as *assertsSuite) TestSignBodyIsUTF8Text(c *C) { 760 headers := map[string]interface{}{ 761 "authority-id": "auth-id1", 762 "primary-key": "0", 763 } 764 _, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, []byte{'\xff'}, testPrivKey1) 765 c.Assert(err, ErrorMatches, "assertion body is not utf8") 766 } 767 768 func (as *assertsSuite) TestHeaders(c *C) { 769 encoded := []byte("type: test-only\n" + 770 "authority-id: auth-id2\n" + 771 "primary-key: abc\n" + 772 "revision: 5\n" + 773 "header1: value1\n" + 774 "header2: value2\n" + 775 "body-length: 8\n" + 776 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 777 "THE-BODY" + 778 "\n\n" + 779 "AXNpZw==") 780 a, err := asserts.Decode(encoded) 781 c.Assert(err, IsNil) 782 783 hs := a.Headers() 784 c.Check(hs, DeepEquals, map[string]interface{}{ 785 "type": "test-only", 786 "authority-id": "auth-id2", 787 "primary-key": "abc", 788 "revision": "5", 789 "header1": "value1", 790 "header2": "value2", 791 "body-length": "8", 792 "sign-key-sha3-384": exKeyID, 793 }) 794 } 795 796 func (as *assertsSuite) TestHeadersReturnsCopy(c *C) { 797 encoded := []byte("type: test-only\n" + 798 "authority-id: auth-id2\n" + 799 "primary-key: xyz\n" + 800 "revision: 5\n" + 801 "header1: value1\n" + 802 "header2: value2\n" + 803 "body-length: 8\n" + 804 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 805 "THE-BODY" + 806 "\n\n" + 807 "AXNpZw==") 808 a, err := asserts.Decode(encoded) 809 c.Assert(err, IsNil) 810 811 hs := a.Headers() 812 // casual later result mutation doesn't trip us 813 delete(hs, "primary-key") 814 c.Check(a.Header("primary-key"), Equals, "xyz") 815 } 816 817 func (as *assertsSuite) TestAssembleRoundtrip(c *C) { 818 encoded := []byte("type: test-only\n" + 819 "format: 1\n" + 820 "authority-id: auth-id2\n" + 821 "primary-key: abc\n" + 822 "revision: 5\n" + 823 "header1: value1\n" + 824 "header2: value2\n" + 825 "body-length: 8\n" + 826 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 827 "THE-BODY" + 828 "\n\n" + 829 "AXNpZw==") 830 a, err := asserts.Decode(encoded) 831 c.Assert(err, IsNil) 832 833 cont, sig := a.Signature() 834 reassembled, err := asserts.Assemble(a.Headers(), a.Body(), cont, sig) 835 c.Assert(err, IsNil) 836 837 c.Check(reassembled.Headers(), DeepEquals, a.Headers()) 838 c.Check(reassembled.Body(), DeepEquals, a.Body()) 839 840 reassembledEncoded := asserts.Encode(reassembled) 841 c.Check(reassembledEncoded, DeepEquals, encoded) 842 } 843 844 func (as *assertsSuite) TestSignKeyID(c *C) { 845 headers := map[string]interface{}{ 846 "authority-id": "auth-id1", 847 "primary-key": "0", 848 } 849 a, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1) 850 c.Assert(err, IsNil) 851 852 keyID := a.SignKeyID() 853 c.Check(keyID, Equals, testPrivKey1.PublicKey().ID()) 854 } 855 856 func (as *assertsSuite) TestSelfRef(c *C) { 857 headers := map[string]interface{}{ 858 "authority-id": "auth-id1", 859 "primary-key": "0", 860 } 861 a1, err := asserts.AssembleAndSignInTest(asserts.TestOnlyType, headers, nil, testPrivKey1) 862 c.Assert(err, IsNil) 863 864 c.Check(a1.Ref(), DeepEquals, &asserts.Ref{ 865 Type: asserts.TestOnlyType, 866 PrimaryKey: []string{"0"}, 867 }) 868 869 c.Check(a1.At(), DeepEquals, &asserts.AtRevision{ 870 Ref: asserts.Ref{ 871 Type: asserts.TestOnlyType, 872 PrimaryKey: []string{"0"}, 873 }, 874 Revision: 0, 875 }) 876 877 headers = map[string]interface{}{ 878 "authority-id": "auth-id1", 879 "pk1": "a", 880 "pk2": "b", 881 "revision": "1", 882 } 883 a2, err := asserts.AssembleAndSignInTest(asserts.TestOnly2Type, headers, nil, testPrivKey1) 884 c.Assert(err, IsNil) 885 886 c.Check(a2.Ref(), DeepEquals, &asserts.Ref{ 887 Type: asserts.TestOnly2Type, 888 PrimaryKey: []string{"a", "b"}, 889 }) 890 891 c.Check(a2.At(), DeepEquals, &asserts.AtRevision{ 892 Ref: asserts.Ref{ 893 Type: asserts.TestOnly2Type, 894 PrimaryKey: []string{"a", "b"}, 895 }, 896 Revision: 1, 897 }) 898 } 899 900 func (as *assertsSuite) TestAssembleHeadersCheck(c *C) { 901 cont := []byte("type: test-only\n" + 902 "authority-id: auth-id2\n" + 903 "primary-key: abc\n" + 904 "revision: 5") 905 headers := map[string]interface{}{ 906 "type": "test-only", 907 "authority-id": "auth-id2", 908 "primary-key": "abc", 909 "revision": 5, // must be a string actually! 910 } 911 912 _, err := asserts.Assemble(headers, nil, cont, nil) 913 c.Check(err, ErrorMatches, `header "revision": header values must be strings or nested lists or maps with strings as the only scalars: 5`) 914 } 915 916 func (as *assertsSuite) TestSignWithoutAuthorityMisuse(c *C) { 917 _, err := asserts.SignWithoutAuthority(asserts.TestOnlyType, nil, nil, testPrivKey1) 918 c.Check(err, ErrorMatches, `cannot sign assertions needing a definite authority with SignWithoutAuthority`) 919 920 _, err = asserts.SignWithoutAuthority(asserts.TestOnlyNoAuthorityType, 921 map[string]interface{}{ 922 "authority-id": "auth-id1", 923 "hdr": "FOO", 924 }, nil, testPrivKey1) 925 c.Check(err, ErrorMatches, `"test-only-no-authority" assertion cannot have authority-id set`) 926 } 927 928 func (ss *serialSuite) TestSignatureCheckError(c *C) { 929 sreq, err := asserts.SignWithoutAuthority(asserts.TestOnlyNoAuthorityType, 930 map[string]interface{}{ 931 "hdr": "FOO", 932 }, nil, testPrivKey1) 933 c.Assert(err, IsNil) 934 935 err = asserts.SignatureCheck(sreq, testPrivKey2.PublicKey()) 936 c.Check(err, ErrorMatches, `failed signature verification:.*`) 937 } 938 939 func (as *assertsSuite) TestWithAuthority(c *C) { 940 withAuthority := []string{ 941 "account", 942 "account-key", 943 "base-declaration", 944 "store", 945 "snap-declaration", 946 "snap-build", 947 "snap-revision", 948 "snap-developer", 949 "model", 950 "serial", 951 "system-user", 952 "validation", 953 "validation-set", 954 "repair", 955 } 956 c.Check(withAuthority, HasLen, asserts.NumAssertionType-3) // excluding device-session-request, serial-request, account-key-request 957 for _, name := range withAuthority { 958 typ := asserts.Type(name) 959 _, err := asserts.AssembleAndSignInTest(typ, nil, nil, testPrivKey1) 960 c.Check(err, ErrorMatches, `"authority-id" header is mandatory`) 961 } 962 } 963 964 func (as *assertsSuite) TestSequenceForming(c *C) { 965 sequenceForming := []string{ 966 "repair", 967 "validation-set", 968 } 969 for _, name := range sequenceForming { 970 typ := asserts.Type(name) 971 c.Check(typ.SequenceForming(), Equals, true) 972 } 973 974 c.Check(asserts.SnapDeclarationType.SequenceForming(), Equals, false) 975 } 976 977 func (as *assertsSuite) TestHeadersFromSequenceKey(c *C) { 978 headers, err := asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{"one"}) 979 c.Assert(err, IsNil) 980 c.Check(headers, DeepEquals, map[string]string{"n": "one"}) 981 982 _, err = asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{"one", "two"}) 983 c.Check(err, ErrorMatches, `sequence key has wrong length for "test-only-seq" assertion`) 984 985 _, err = asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{}) 986 c.Check(err, ErrorMatches, `sequence key has wrong length for "test-only-seq" assertion`) 987 988 _, err = asserts.HeadersFromSequenceKey(asserts.TestOnlySeqType, []string{""}) 989 c.Check(err, ErrorMatches, `sequence key "n" header cannot be empty`) 990 } 991 992 func (as *assertsSuite) TestAtSequenceString(c *C) { 993 atSeq := asserts.AtSequence{ 994 Type: asserts.ValidationSetType, 995 SequenceKey: []string{"16", "canonical", "foo"}, 996 Sequence: 8, 997 Revision: 2, 998 } 999 c.Check(atSeq.String(), Equals, "validation-set canonical/foo/8 at revision 2") 1000 1001 // Sequence number not set 1002 atSeq = asserts.AtSequence{ 1003 Type: asserts.ValidationSetType, 1004 SequenceKey: []string{"16", "canonical", "foo"}, 1005 Revision: asserts.RevisionNotKnown, 1006 } 1007 c.Check(atSeq.String(), Equals, "validation-set canonical/foo") 1008 1009 atSeq = asserts.AtSequence{ 1010 Type: asserts.ValidationSetType, 1011 SequenceKey: []string{"16", "canonical", "foo"}, 1012 Sequence: 8, 1013 Pinned: true, 1014 Revision: 2, 1015 } 1016 c.Check(atSeq.String(), Equals, "validation-set canonical/foo=8 at revision 2") 1017 1018 atSeq = asserts.AtSequence{ 1019 Type: asserts.ValidationSetType, 1020 SequenceKey: []string{"16", "canonical"}, 1021 Revision: 2, 1022 } 1023 c.Check(atSeq.String(), Equals, "validation-set ??? at revision 2") 1024 } 1025 1026 func (as *assertsSuite) TestAtSequenceUnique(c *C) { 1027 atSeq := asserts.AtSequence{ 1028 Type: asserts.ValidationSetType, 1029 SequenceKey: []string{"16", "canonical", "foo"}, 1030 Sequence: 8, 1031 Revision: 2, 1032 } 1033 c.Check(atSeq.Unique(), Equals, "validation-set/16/canonical/foo") 1034 1035 // not a valid sequence-key (but Unique() doesn't care). 1036 atSeq = asserts.AtSequence{ 1037 Type: asserts.ValidationSetType, 1038 SequenceKey: []string{"16", "canonical"}, 1039 } 1040 c.Check(atSeq.Unique(), Equals, "validation-set/16/canonical") 1041 } 1042 1043 func (as *assertsSuite) TestAtSequenceResolveError(c *C) { 1044 atSeq := asserts.AtSequence{ 1045 Type: asserts.ValidationSetType, 1046 SequenceKey: []string{"abc"}, 1047 Sequence: 1, 1048 } 1049 _, err := atSeq.Resolve(nil) 1050 c.Check(err, ErrorMatches, `"validation-set" assertion reference primary key has the wrong length \(expected \[series account-id name sequence\]\): \[abc 1\]`) 1051 1052 atSeq = asserts.AtSequence{ 1053 Type: asserts.ValidationSetType, 1054 SequenceKey: []string{"16", "canonical", "foo"}, 1055 } 1056 _, err = atSeq.Resolve(nil) 1057 c.Assert(err, DeepEquals, &asserts.NotFoundError{ 1058 Type: asserts.ValidationSetType, 1059 Headers: map[string]string{ 1060 "series": "16", 1061 "account-id": "canonical", 1062 "name": "foo", 1063 }, 1064 }) 1065 } 1066 1067 func (as *assertsSuite) TestAtSequenceResolve(c *C) { 1068 atSeq := asserts.AtSequence{ 1069 Type: asserts.TestOnlySeqType, 1070 SequenceKey: []string{"foo"}, 1071 Sequence: 3, 1072 } 1073 a, err := atSeq.Resolve(func(atype *asserts.AssertionType, hdrs map[string]string) (asserts.Assertion, error) { 1074 c.Assert(atype, Equals, asserts.TestOnlySeqType) 1075 c.Assert(hdrs, DeepEquals, map[string]string{ 1076 "n": "foo", 1077 "sequence": "3", 1078 }) 1079 encoded := []byte("type: test-only-seq\n" + 1080 "format: 1\n" + 1081 "authority-id: auth-id2\n" + 1082 "n: abc\n" + 1083 "revision: 5\n" + 1084 "sequence: 3\n" + 1085 "sign-key-sha3-384: Jv8_JiHiIzJVcO9M55pPdqSDWUvuhfDIBJUS-3VW7F_idjix7Ffn5qMxB21ZQuij\n\n" + 1086 "\n\n" + 1087 "AXNpZw==") 1088 a, err := asserts.Decode(encoded) 1089 return a, err 1090 }) 1091 c.Assert(err, IsNil) 1092 c.Assert(a, NotNil) 1093 c.Check(a.Type().Name, Equals, "test-only-seq") 1094 }