github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap/cmd_model_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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 main_test
    21  
    22  import (
    23  	"fmt"
    24  	"net/http"
    25  
    26  	"gopkg.in/check.v1"
    27  
    28  	snap "github.com/snapcore/snapd/cmd/snap"
    29  )
    30  
    31  const happyModelAssertionResponse = `type: model
    32  authority-id: mememe
    33  series: 16
    34  brand-id: mememe
    35  model: test-model
    36  architecture: amd64
    37  base: core18
    38  gadget: pc=18
    39  kernel: pc-kernel=18
    40  required-snaps:
    41    - core
    42    - hello-world
    43  timestamp: 2017-07-27T00:00:00.0Z
    44  sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t
    45  
    46  AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j
    47  jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS
    48  U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2
    49  luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N
    50  6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll
    51  zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq
    52  p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ
    53  iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa
    54  ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag
    55  85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv
    56  `
    57  
    58  const happyModelWithDisplayNameAssertionResponse = `type: model
    59  authority-id: mememe
    60  series: 16
    61  brand-id: mememe
    62  model: test-model
    63  architecture: amd64
    64  display-name: Model Name
    65  base: core18
    66  gadget: pc=18
    67  kernel: pc-kernel=18
    68  required-snaps:
    69    - core
    70    - hello-world
    71  timestamp: 2017-07-27T00:00:00.0Z
    72  sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t
    73  
    74  AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j
    75  jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS
    76  U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2
    77  luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N
    78  6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll
    79  zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq
    80  p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ
    81  iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa
    82  ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag
    83  85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv
    84  `
    85  
    86  const happyAccountAssertionResponse = `type: account
    87  authority-id: canonical
    88  account-id: mememe
    89  display-name: MeMeMe
    90  timestamp: 2016-04-01T00:00:00.0Z
    91  username: meuser
    92  validation: certified
    93  sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk
    94  
    95  AcLDXAQAAQoABgUCV7UYzwAKCRDUpVvql9g3IK7uH/4udqNOurx5WYVknzXdwekp0ovHCQJ0iBPw
    96  TSFxEVr9faZSzb7eqJ1WicHsShf97PYS3ClRYAiluFsjRA8Y03kkSVJHjC+sIwGFubsnkmgflt6D
    97  WEmYIl0UBmeaEDS8uY4Xvp9NsLTzNEj2kvzy/52gKaTc1ZSl5RDL9ppMav+0V9iBYpiDPBWH2rJ+
    98  aDSD8Rkyygm0UscfAKyDKH4lrvZ0WkYyi1YVNPrjQ/AtBySh6Q4iJ3LifzKa9woIyAuJET/4/FPY
    99  oirqHAfuvNod36yNQIyNqEc20AvTvZNH0PSsg4rq3DLjIPzv5KbJO9lhsasNJK1OdL6x8Yqrdsbk
   100  ldZp4qkzfjV7VOMQKaadfcZPRaVVeJWOBnBiaukzkhoNlQi1sdCdkBB/AJHZF8QXw6c7vPDcfnCV
   101  1lW7ddQ2p8IsJbT6LzpJu3GW/P4xhNgCjtCJ1AJm9a9RqLwQYgdLZwwDa9iCRtqTbRXBlfy3apps
   102  1VjbQ3h5iCd0hNfwDBnGVm1rhLKHCD1DUdNE43oN2ZlE7XGyh0HFV6vKlpqoW3eoXCIxWu+HBY96
   103  +LSl/jQgCkb0nxYyzEYK4Reb31D0mYw1Nji5W+MIF5E09+DYZoOT0UvR05YMwMEOeSdI/hLWg/5P
   104  k+GDK+/KopMmpd4D1+jjtF7ZvqDpmAV98jJGB2F88RyVb4gcjmFFyTi4Kv6vzz/oLpbm0qrizC0W
   105  HLGDN/ymGA5sHzEgEx7U540vz/q9VX60FKqL2YZr/DcyY9GKX5kCG4sNqIIHbcJneZ4frM99oVDu
   106  7Jv+DIx/Di6D1ULXol2XjxbbJLKHFtHksR97ceaFvcZwTogC61IYUBJCvvMoqdXAWMhEXCr0QfQ5
   107  Xbi31XW2d4/lF/zWlAkRnGTzufIXFni7+nEuOK0SQEzO3/WaRedK1SGOOtTDjB8/3OJeW96AUYK5
   108  oTIynkYkEyHWMNCXALg+WQW6L4/YO7aUjZ97zOWIugd7Xy63aT3r/EHafqaY2nacOhLfkeKZ830b
   109  o/ezjoZQAxbh6ce7JnXRgE9ELxjdAhBTpGjmmmN2sYrJ7zP9bOgly0BnEPXGSQfFA+NNNw1FADx1
   110  MUY8q9DBjmVtgqY+1KGTV5X8KvQCBMODZIf/XJPHdCRAHxMd8COypcwgL2vDIIXpOFbi1J/B0GF+
   111  eklxk9wzBA8AecBMCwCzIRHDNpD1oa2we38bVFrOug6e/VId1k1jYFJjiLyLCDmV8IMYwEllHSXp
   112  LQAdm3xZ7t4WnxYC8YSCk9mXf3CZg59SpmnV5Q5Z6A5Pl7Nc3sj7hcsMBZEsOMPzNC9dPsBnZvjs
   113  WpPUffJzEdhHBFhvYMuD4Vqj6ejUv9l3oTrjQWVC`
   114  
   115  // note: this serial assertion was generated by adding print statements to the
   116  // test in api_model_test.go that generate a fake serial assertion
   117  const happySerialAssertionResponse = `type: serial
   118  authority-id: my-brand
   119  brand-id: my-brand
   120  model: my-old-model
   121  serial: serialserial
   122  device-key:
   123      AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2go
   124      mTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE=
   125  device-key-sha3-384: iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO
   126  timestamp: 2019-08-26T16:34:21-05:00
   127  sign-key-sha3-384: anCEGC2NYq7DzDEi6y7OafQCVeVLS90XlLt9PNjrRl9sim5rmRHDDNFNO7ODcWQW
   128  
   129  AcJwBAABCgAGBQJdZFBdAADCLALwR6Sy24wm9PffwbvUhOEXneyY3BnxKC0+NgdHu1gU8go9vEP1
   130  i+Flh5uoS70+MBIO+nmF8T+9JWIx2QWFDDxvcuFosnIhvUajCEQohauys5FMz/H/WvB0vrbTBpvK
   131  eg==
   132  `
   133  
   134  const noModelAssertionYetResponse = `
   135  {
   136  	"type": "error",
   137  	"status-code": 404,
   138  	"status": "Not Found",
   139  	"result": {
   140  	  "message": "no model assertion yet",
   141  	  "kind": "assertion-not-found",
   142  	  "value": "model"
   143  	}
   144  }`
   145  
   146  const noSerialAssertionYetResponse = `
   147  {
   148  	"type": "error",
   149  	"status-code": 404,
   150  	"status": "Not Found",
   151  	"result": {
   152  	  "message": "no serial assertion yet",
   153  	  "kind": "assertion-not-found",
   154  	  "value": "serial"
   155  	}
   156  }`
   157  
   158  // helper for constructing different types of responses to the client
   159  type checkResponder func(c *check.C, w http.ResponseWriter, r *http.Request)
   160  
   161  func simpleHappyResponder(body string) checkResponder {
   162  	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
   163  		c.Check(r.Method, check.Equals, "GET")
   164  		c.Check(r.URL.RawQuery, check.Equals, "")
   165  		fmt.Fprintln(w, body)
   166  	}
   167  }
   168  
   169  func simpleUnhappyResponder(errBody string) checkResponder {
   170  	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
   171  		c.Check(r.Method, check.Equals, "GET")
   172  		c.Check(r.URL.RawQuery, check.Equals, "")
   173  		w.Header().Set("Content-Type", "application/json")
   174  		w.WriteHeader(404)
   175  		fmt.Fprintln(w, errBody)
   176  	}
   177  }
   178  
   179  func simpleAssertionAccountResponder(body string) checkResponder {
   180  	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
   181  		c.Check(r.Method, check.Equals, "GET")
   182  		w.Header().Set("X-Ubuntu-Assertions-Count", "1")
   183  		fmt.Fprintln(w, body)
   184  	}
   185  }
   186  
   187  func makeHappyTestServerHandler(c *check.C, modelResp, serialResp, accountResp checkResponder) func(w http.ResponseWriter, r *http.Request) {
   188  	var nModelSerial, nModel, nKnown int
   189  	return func(w http.ResponseWriter, r *http.Request) {
   190  		switch r.URL.Path {
   191  		case "/v2/model":
   192  			switch nModel {
   193  			case 0:
   194  				modelResp(c, w, r)
   195  			default:
   196  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1)
   197  			}
   198  			nModel++
   199  		case "/v2/model/serial":
   200  			switch nModelSerial {
   201  			case 0:
   202  				serialResp(c, w, r)
   203  			default:
   204  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModelSerial+1)
   205  			}
   206  			nModelSerial++
   207  		case "/v2/assertions/account":
   208  			switch nKnown {
   209  			case 0:
   210  				accountResp(c, w, r)
   211  			default:
   212  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nKnown+1)
   213  			}
   214  			nKnown++
   215  		default:
   216  			c.Fatalf("unexpected request to %s", r.URL.Path)
   217  		}
   218  	}
   219  }
   220  
   221  func (s *SnapSuite) TestNoModelYet(c *check.C) {
   222  	s.RedirectClientToTestServer(
   223  		makeHappyTestServerHandler(
   224  			c,
   225  			simpleUnhappyResponder(noModelAssertionYetResponse),
   226  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   227  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   228  		))
   229  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"})
   230  	c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`)
   231  }
   232  
   233  func (s *SnapSuite) TestNoSerialYet(c *check.C) {
   234  	s.RedirectClientToTestServer(
   235  		makeHappyTestServerHandler(
   236  			c,
   237  			simpleHappyResponder(happyModelAssertionResponse),
   238  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   239  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   240  		))
   241  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"})
   242  	c.Assert(err, check.ErrorMatches, `device not registered yet \(no serial assertion found\)`)
   243  	c.Check(s.Stderr(), check.Equals, "")
   244  	c.Check(s.Stdout(), check.Equals, `
   245  brand-id:  mememe
   246  model:     test-model
   247  `[1:])
   248  }
   249  
   250  func (s *SnapSuite) TestModel(c *check.C) {
   251  
   252  	for _, tt := range []struct {
   253  		comment string
   254  		modelF  checkResponder
   255  		serialF checkResponder
   256  		outText string
   257  	}{
   258  		{
   259  			comment: "normal serial and model asserts",
   260  			modelF:  simpleHappyResponder(happyModelAssertionResponse),
   261  			serialF: simpleHappyResponder(happySerialAssertionResponse),
   262  			outText: `
   263  brand   MeMeMe (meuser*)
   264  model   test-model
   265  serial  serialserial
   266  `[1:],
   267  		},
   268  		{
   269  			comment: "model assert has display-name",
   270  			modelF:  simpleHappyResponder(happyModelWithDisplayNameAssertionResponse),
   271  			serialF: simpleHappyResponder(happySerialAssertionResponse),
   272  			outText: `
   273  brand   MeMeMe (meuser*)
   274  model   Model Name (test-model)
   275  serial  serialserial
   276  `[1:],
   277  		},
   278  		{
   279  			comment: "missing serial assert",
   280  			modelF:  simpleHappyResponder(happyModelAssertionResponse),
   281  			serialF: simpleUnhappyResponder(noSerialAssertionYetResponse),
   282  			outText: `
   283  brand   MeMeMe (meuser*)
   284  model   test-model
   285  serial  - (device not registered yet)
   286  `[1:],
   287  		},
   288  	} {
   289  		s.RedirectClientToTestServer(
   290  			makeHappyTestServerHandler(
   291  				c,
   292  				tt.modelF,
   293  				tt.serialF,
   294  				simpleAssertionAccountResponder(happyAccountAssertionResponse),
   295  			))
   296  		rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"})
   297  		c.Assert(err, check.IsNil)
   298  		c.Assert(rest, check.DeepEquals, []string{})
   299  		c.Check(s.Stdout(), check.Equals, tt.outText, check.Commentf("\n%s\n", tt.outText))
   300  		c.Check(s.Stderr(), check.Equals, "")
   301  		s.ResetStdStreams()
   302  	}
   303  }
   304  
   305  func (s *SnapSuite) TestModelVerbose(c *check.C) {
   306  	s.RedirectClientToTestServer(
   307  		makeHappyTestServerHandler(
   308  			c,
   309  			simpleHappyResponder(happyModelAssertionResponse),
   310  			simpleHappyResponder(happySerialAssertionResponse),
   311  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   312  		))
   313  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
   314  	c.Assert(err, check.IsNil)
   315  	c.Assert(rest, check.DeepEquals, []string{})
   316  	c.Check(s.Stdout(), check.Equals, `
   317  brand-id:        mememe
   318  model:           test-model
   319  serial:          serialserial
   320  architecture:    amd64
   321  base:            core18
   322  gadget:          pc=18
   323  kernel:          pc-kernel=18
   324  timestamp:       2017-07-27T00:00:00Z
   325  required-snaps:  
   326    - core
   327    - hello-world
   328  `[1:])
   329  	c.Check(s.Stderr(), check.Equals, "")
   330  }
   331  
   332  func (s *SnapSuite) TestModelVerboseNoSerialYet(c *check.C) {
   333  	s.RedirectClientToTestServer(
   334  		makeHappyTestServerHandler(
   335  			c,
   336  			simpleHappyResponder(happyModelAssertionResponse),
   337  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   338  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   339  		))
   340  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
   341  	c.Assert(err, check.IsNil)
   342  	c.Assert(rest, check.DeepEquals, []string{})
   343  	c.Check(s.Stdout(), check.Equals, `
   344  brand-id:        mememe
   345  model:           test-model
   346  serial:          -- (device not registered yet)
   347  architecture:    amd64
   348  base:            core18
   349  gadget:          pc=18
   350  kernel:          pc-kernel=18
   351  timestamp:       2017-07-27T00:00:00Z
   352  required-snaps:  
   353    - core
   354    - hello-world
   355  `[1:])
   356  	c.Check(s.Stderr(), check.Equals, "")
   357  }
   358  
   359  func (s *SnapSuite) TestModelAssertion(c *check.C) {
   360  	s.RedirectClientToTestServer(
   361  		makeHappyTestServerHandler(
   362  			c,
   363  			simpleHappyResponder(happyModelAssertionResponse),
   364  			simpleHappyResponder(happySerialAssertionResponse),
   365  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   366  		))
   367  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion"})
   368  	c.Assert(err, check.IsNil)
   369  	c.Assert(rest, check.DeepEquals, []string{})
   370  	c.Check(s.Stdout(), check.Equals, happyModelAssertionResponse)
   371  	c.Check(s.Stderr(), check.Equals, "")
   372  }
   373  
   374  func (s *SnapSuite) TestModelAssertionVerbose(c *check.C) {
   375  	// check that no calls to the server happen
   376  	s.RedirectClientToTestServer(
   377  		func(w http.ResponseWriter, r *http.Request) {
   378  			c.Fatalf("unexpected request to %s", r.URL.Path)
   379  		},
   380  	)
   381  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion", "--verbose"})
   382  	c.Assert(err, check.ErrorMatches, "cannot use --verbose with --assertion")
   383  	c.Check(s.Stdout(), check.Equals, "")
   384  	c.Check(s.Stderr(), check.Equals, "")
   385  }
   386  
   387  func (s *SnapSuite) TestSerial(c *check.C) {
   388  	s.RedirectClientToTestServer(
   389  		makeHappyTestServerHandler(
   390  			c,
   391  			simpleHappyResponder(happyModelAssertionResponse),
   392  			simpleHappyResponder(happySerialAssertionResponse),
   393  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   394  		))
   395  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"})
   396  	c.Assert(err, check.IsNil)
   397  	c.Assert(rest, check.DeepEquals, []string{})
   398  	c.Check(s.Stdout(), check.Equals, `
   399  brand-id:  my-brand
   400  model:     my-old-model
   401  serial:    serialserial
   402  `[1:])
   403  	c.Check(s.Stderr(), check.Equals, "")
   404  }
   405  
   406  func (s *SnapSuite) TestSerialVerbose(c *check.C) {
   407  	s.RedirectClientToTestServer(
   408  		makeHappyTestServerHandler(
   409  			c,
   410  			simpleHappyResponder(happyModelAssertionResponse),
   411  			simpleHappyResponder(happySerialAssertionResponse),
   412  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   413  		))
   414  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--verbose", "--abs-time"})
   415  	c.Assert(err, check.IsNil)
   416  	c.Assert(rest, check.DeepEquals, []string{})
   417  	c.Check(s.Stdout(), check.Equals, `
   418  brand-id:   my-brand
   419  model:      my-old-model
   420  serial:     serialserial
   421  timestamp:  2019-08-26T16:34:21-05:00
   422  device-key-sha3-384: |
   423    iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO
   424  device-key: |
   425    AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2g
   426    omTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE=
   427  `[1:])
   428  	c.Check(s.Stderr(), check.Equals, "")
   429  }
   430  
   431  func (s *SnapSuite) TestSerialAssertion(c *check.C) {
   432  	s.RedirectClientToTestServer(
   433  		makeHappyTestServerHandler(
   434  			c,
   435  			simpleHappyResponder(happyModelAssertionResponse),
   436  			simpleHappyResponder(happySerialAssertionResponse),
   437  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   438  		))
   439  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"})
   440  	c.Assert(err, check.IsNil)
   441  	c.Assert(rest, check.DeepEquals, []string{})
   442  	c.Check(s.Stdout(), check.Equals, happySerialAssertionResponse)
   443  	c.Check(s.Stderr(), check.Equals, "")
   444  }
   445  
   446  func (s *SnapSuite) TestSerialAssertionSerialAssertionMissing(c *check.C) {
   447  	s.RedirectClientToTestServer(
   448  		makeHappyTestServerHandler(
   449  			c,
   450  			simpleHappyResponder(happyModelAssertionResponse),
   451  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   452  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   453  		))
   454  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"})
   455  	c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`)
   456  	c.Assert(s.Stdout(), check.Equals, "")
   457  	c.Assert(s.Stderr(), check.Equals, "")
   458  }