github.com/ubuntu-core/snappy@v0.0.0-20210827154228-9e584df982bb/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  store: mememestore
    41  system-user-authority:
    42    - youyouyou
    43    - mememe
    44  required-snaps:
    45    - core
    46    - hello-world
    47  timestamp: 2017-07-27T00:00:00.0Z
    48  sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t
    49  
    50  AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j
    51  jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS
    52  U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2
    53  luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N
    54  6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll
    55  zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq
    56  p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ
    57  iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa
    58  ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag
    59  85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv
    60  `
    61  
    62  const happyUC20ModelAssertionResponse = `type: model
    63  authority-id: testrootorg
    64  series: 16
    65  brand-id: testrootorg
    66  model: test-snapd-core-20-amd64
    67  architecture: amd64
    68  base: core20
    69  storage-safety: prefer-encrypted
    70  grade: dangerous
    71  snaps:
    72    -
    73      default-channel: 20/edge
    74      id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
    75      name: pc
    76      type: gadget
    77    -
    78      default-channel: 20/edge
    79      id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
    80      name: pc-kernel
    81      type: kernel
    82    -
    83      name: app-snap
    84      default-channel: foo
    85      presence: optional
    86      modes:
    87        - recover
    88        - run
    89    -
    90      default-channel: latest/stable
    91      id: DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q
    92      name: core20
    93      type: base
    94    -
    95      default-channel: latest/stable
    96      id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
    97      name: snapd
    98      type: snapd
    99  timestamp: 2018-09-11T22:00:00+00:00
   100  sign-key-sha3-384: hIedp1AvrWlcDI4uS_qjoFLzjKl5enu4G2FYJpgB3Pj-tUzGlTQBxMBsBmi-tnJR
   101  
   102  AcLBUgQAAQoABgUCX06qogAAv10QAFaqQ0NDDvIB7LqM0xNIz+5Y6PB5wJaRk0HqVsg2LlNgS0PQ
   103  uJf0uFMV4GjQMraL7ZYv9BGyUoA+cz8Nbiz85m1g2ADt0ugqR/x2bAojii9lbFLmWpDMJcZhrtB1
   104  3k32lEUwqTMvzYTGiZ6TVug0KYbdmf2+5IGxsayAS3EwdrfbuGRHZOv6XGV7bmm1GEwCRAFvgHCk
   105  BHKoLZ+rfbNclF4l6G+biWJTdyc5jCMpMQ6X/INnx2hXaMRf9Jfrpl6s2bGCfsxW6HVf7AWZ8qHK
   106  jtWPQqJ6NFu2Kw1lYIA202ReK8DC3gfAlOeNzUG5dTPor3KwAoDJJI8ZaQypOazEhO9SHERIutbP
   107  eqPxPmEoB2+E0/o0+g0o5jK4qww3Yd7b8FTDkqm2xfuuldWAiAA4x6ZOQb2So9OLT6ovqHnD3D2r
   108  pLW/lhqwfKp3xzIVUrLi0sjGOVXu5xFDDRyFICZ6kwC7JynRGfHoa5E2y7rv8ehnOZQJ+esz9sgY
   109  lCJcyJ8vhabDlVHg0msSeNKMVBwhQnOSakEwlcfVyaSnapArkF+OCAMl8cuGpMTKO7vJLIJo2c2P
   110  jcVE0ftsTGs9eBi2HmdDhu3e3fmxHt9VcC4uRSOnYNVcJnMh0yVmG8RGS/Dqcz04II7llww6JJYG
   111  KKjQ3RU/TduXa8VJsoWiRRUYAv3H
   112  `
   113  
   114  const happyModelWithDisplayNameAssertionResponse = `type: model
   115  authority-id: mememe
   116  series: 16
   117  brand-id: mememe
   118  model: test-model
   119  architecture: amd64
   120  display-name: Model Name
   121  base: core18
   122  gadget: pc=18
   123  kernel: pc-kernel=18
   124  store: mememestore
   125  system-user-authority:
   126    - youyouyou
   127    - mememe
   128  required-snaps:
   129    - core
   130    - hello-world
   131  timestamp: 2017-07-27T00:00:00.0Z
   132  sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t
   133  
   134  AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j
   135  jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS
   136  U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2
   137  luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N
   138  6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll
   139  zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq
   140  p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ
   141  iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa
   142  ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag
   143  85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv
   144  `
   145  
   146  const happyAccountAssertionResponse = `type: account
   147  authority-id: canonical
   148  account-id: mememe
   149  display-name: MeMeMe
   150  timestamp: 2016-04-01T00:00:00.0Z
   151  username: meuser
   152  validation: certified
   153  sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk
   154  
   155  AcLDXAQAAQoABgUCV7UYzwAKCRDUpVvql9g3IK7uH/4udqNOurx5WYVknzXdwekp0ovHCQJ0iBPw
   156  TSFxEVr9faZSzb7eqJ1WicHsShf97PYS3ClRYAiluFsjRA8Y03kkSVJHjC+sIwGFubsnkmgflt6D
   157  WEmYIl0UBmeaEDS8uY4Xvp9NsLTzNEj2kvzy/52gKaTc1ZSl5RDL9ppMav+0V9iBYpiDPBWH2rJ+
   158  aDSD8Rkyygm0UscfAKyDKH4lrvZ0WkYyi1YVNPrjQ/AtBySh6Q4iJ3LifzKa9woIyAuJET/4/FPY
   159  oirqHAfuvNod36yNQIyNqEc20AvTvZNH0PSsg4rq3DLjIPzv5KbJO9lhsasNJK1OdL6x8Yqrdsbk
   160  ldZp4qkzfjV7VOMQKaadfcZPRaVVeJWOBnBiaukzkhoNlQi1sdCdkBB/AJHZF8QXw6c7vPDcfnCV
   161  1lW7ddQ2p8IsJbT6LzpJu3GW/P4xhNgCjtCJ1AJm9a9RqLwQYgdLZwwDa9iCRtqTbRXBlfy3apps
   162  1VjbQ3h5iCd0hNfwDBnGVm1rhLKHCD1DUdNE43oN2ZlE7XGyh0HFV6vKlpqoW3eoXCIxWu+HBY96
   163  +LSl/jQgCkb0nxYyzEYK4Reb31D0mYw1Nji5W+MIF5E09+DYZoOT0UvR05YMwMEOeSdI/hLWg/5P
   164  k+GDK+/KopMmpd4D1+jjtF7ZvqDpmAV98jJGB2F88RyVb4gcjmFFyTi4Kv6vzz/oLpbm0qrizC0W
   165  HLGDN/ymGA5sHzEgEx7U540vz/q9VX60FKqL2YZr/DcyY9GKX5kCG4sNqIIHbcJneZ4frM99oVDu
   166  7Jv+DIx/Di6D1ULXol2XjxbbJLKHFtHksR97ceaFvcZwTogC61IYUBJCvvMoqdXAWMhEXCr0QfQ5
   167  Xbi31XW2d4/lF/zWlAkRnGTzufIXFni7+nEuOK0SQEzO3/WaRedK1SGOOtTDjB8/3OJeW96AUYK5
   168  oTIynkYkEyHWMNCXALg+WQW6L4/YO7aUjZ97zOWIugd7Xy63aT3r/EHafqaY2nacOhLfkeKZ830b
   169  o/ezjoZQAxbh6ce7JnXRgE9ELxjdAhBTpGjmmmN2sYrJ7zP9bOgly0BnEPXGSQfFA+NNNw1FADx1
   170  MUY8q9DBjmVtgqY+1KGTV5X8KvQCBMODZIf/XJPHdCRAHxMd8COypcwgL2vDIIXpOFbi1J/B0GF+
   171  eklxk9wzBA8AecBMCwCzIRHDNpD1oa2we38bVFrOug6e/VId1k1jYFJjiLyLCDmV8IMYwEllHSXp
   172  LQAdm3xZ7t4WnxYC8YSCk9mXf3CZg59SpmnV5Q5Z6A5Pl7Nc3sj7hcsMBZEsOMPzNC9dPsBnZvjs
   173  WpPUffJzEdhHBFhvYMuD4Vqj6ejUv9l3oTrjQWVC`
   174  
   175  // note: this serial assertion was generated by adding print statements to the
   176  // test in api_model_test.go that generate a fake serial assertion
   177  const happySerialAssertionResponse = `type: serial
   178  authority-id: my-brand
   179  brand-id: my-brand
   180  model: my-old-model
   181  serial: serialserial
   182  device-key:
   183      AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2go
   184      mTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE=
   185  device-key-sha3-384: iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO
   186  timestamp: 2019-08-26T16:34:21-05:00
   187  sign-key-sha3-384: anCEGC2NYq7DzDEi6y7OafQCVeVLS90XlLt9PNjrRl9sim5rmRHDDNFNO7ODcWQW
   188  
   189  AcJwBAABCgAGBQJdZFBdAADCLALwR6Sy24wm9PffwbvUhOEXneyY3BnxKC0+NgdHu1gU8go9vEP1
   190  i+Flh5uoS70+MBIO+nmF8T+9JWIx2QWFDDxvcuFosnIhvUajCEQohauys5FMz/H/WvB0vrbTBpvK
   191  eg==
   192  `
   193  
   194  const happySerialUC20AssertionResponse = `type: serial
   195  authority-id: testrootorg
   196  brand-id: testrootorg
   197  model: test-snapd-core-20-amd64
   198  serial: 7777
   199  device-key:
   200      AcbBTQRWhcGAARAAuKf9n7WvZDI7u3NzMkD8WN+dxCYrb0UE9XIaHcbrj0i2zJpxCtUtpzoEo7Uk
   201      Cvxuhr2uBpzAa8fScwzOd77MGHIZQDpS7sFSkhYsSSN0m4sy8vRevsj0roN31fugCjRnhtLTkgxo
   202      KSoAsK87vYnC+m5V5AHaRER7q1KgpUoVD7eLOJZyrd/tWecsLL9OK87yAQHdF/cVlQupOP6OU3fK
   203      DllER6V2TD4jADK2Gyj2lDhy3F0+rE0a+zsGpmQQBorvzbozUHgBE3z/XjTTMrHYP4m+4V5HeWdn
   204      rHt/x1LZ8wMTCMT1eeruclC82UPRgF0zWI+P7WgBqogJpCbfadhAj1zvKW+5vJ385n0BU7PoAZtA
   205      KddBbsmEnfK/gWIxgFemIrYcYGhIBxYY6iNcygTYRFo4R9xm3bELHLG+viHggih4Lrjnb4sLHOdC
   206      h3C4/45bY+6hSno8GQGlp4kYQQM8mrF9st51jIM6oyB84NtoySLYYE1wMeGNzDHSuI+1IiRmaTgy
   207      Q2ImXTuqOhclhNA1sOi3R4H+oOBxe6GmoM5ATBPBqJeqUEvK8GpSRCig0QH4qMNF/abNKwvKhGMZ
   208      LqtpFp5LNx7xYuAwoVkcq0nxQTsXctl3gJqY+lRx7mIeoXLZPKZyJees+5v96oa9lMdNX3f5UUpX
   209      zq0cNhdgHrXZfcsAEQEAAQ==
   210  device-key-sha3-384: CZeO_5nJm_Rg0izosNfcQRoQj9nFtAmK2Y_tz4YjlKlvS93b_9gTDHuby5HHwi7d
   211  timestamp: 2020-09-03T14:42:47-05:00
   212  sign-key-sha3-384: hIedp1AvrWlcDI4uS_qjoFLzjKl5enu4G2FYJpgB3Pj-tUzGlTQBxMBsBmi-tnJR
   213  
   214  AcLBUgQAAQoABgUCX1FHNwAAqFoQABFiyzipoTYAuYN0Wd7cXuPPD7z+z+E+LoZZ+j4vUKqvnGX8
   215  tksb2nEEOQhjSvVof5pPOswWgq8Nj52dtYA20R5Zgfy0MZHHcCCfgxaRj6EiFyrG5h9l5wWMnzdb
   216  pXo9SJ3hxw6lKdj3n9RAAY0mACvw6f/trcyLeSxQ7EBm6X9c4ohJSjlHkKj0TlKkNTrFflko5aQH
   217  uJUk/YgsvMTZUHbgj6QKHlODUH8iRvOHxzn/Y9BlnzBsb/SyzvNTPeQyzFtd9QkESI2sWghviys2
   218  fGeEZPeXU6xts6Ht+xhr3mj5npZwkkL/6YxSzm9owQ0zGrfaFTswN+xoDKZ5498qRtSY3mCK/5xx
   219  kvWpOTHHhfvuS3GGyvRZOih7IAffDEwQsUNh8V9IjQNNTIkCYTPZz4WBM42mI8UgeDsnDImmcoc0
   220  GlqBeCxUigszJlEdUAHQklwW7Sgp13mceR3zB7BHgp4Sk7n0RyPuTQUA94ys6SeesK5YphwmhVed
   221  V02lkdeqRbGt3yZ/T5Zg8CIUIM0RKDSqoHgvoCMZh98dRGv6LPRj/P0RSWmjYWotjdK+lXK1fySM
   222  RXMNJIInZoC0x8qEwGLXVl5V3z8motLG71ie7PQ677W0dE9XM5LRnZHEKXP41jfaOO9vu12TtBsh
   223  pe/pnYDfIzU6OyOsdmkGWaWD+nbD
   224  `
   225  
   226  const noModelAssertionYetResponse = `
   227  {
   228  	"type": "error",
   229  	"status-code": 404,
   230  	"status": "Not Found",
   231  	"result": {
   232  	  "message": "no model assertion yet",
   233  	  "kind": "assertion-not-found",
   234  	  "value": "model"
   235  	}
   236  }`
   237  
   238  const noSerialAssertionYetResponse = `
   239  {
   240  	"type": "error",
   241  	"status-code": 404,
   242  	"status": "Not Found",
   243  	"result": {
   244  	  "message": "no serial assertion yet",
   245  	  "kind": "assertion-not-found",
   246  	  "value": "serial"
   247  	}
   248  }`
   249  
   250  // helper for constructing different types of responses to the client
   251  type checkResponder func(c *check.C, w http.ResponseWriter, r *http.Request)
   252  
   253  func simpleHappyResponder(body string) checkResponder {
   254  	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
   255  		c.Check(r.Method, check.Equals, "GET")
   256  		c.Check(r.URL.RawQuery, check.Equals, "")
   257  		fmt.Fprintln(w, body)
   258  	}
   259  }
   260  
   261  func simpleUnhappyResponder(errBody string) checkResponder {
   262  	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
   263  		c.Check(r.Method, check.Equals, "GET")
   264  		c.Check(r.URL.RawQuery, check.Equals, "")
   265  		w.Header().Set("Content-Type", "application/json")
   266  		w.WriteHeader(404)
   267  		fmt.Fprintln(w, errBody)
   268  	}
   269  }
   270  
   271  func simpleAssertionAccountResponder(body string) checkResponder {
   272  	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
   273  		c.Check(r.Method, check.Equals, "GET")
   274  		w.Header().Set("X-Ubuntu-Assertions-Count", "1")
   275  		fmt.Fprintln(w, body)
   276  	}
   277  }
   278  
   279  func makeHappyTestServerHandler(c *check.C, modelResp, serialResp, accountResp checkResponder) func(w http.ResponseWriter, r *http.Request) {
   280  	var nModelSerial, nModel, nKnown int
   281  	return func(w http.ResponseWriter, r *http.Request) {
   282  		switch r.URL.Path {
   283  		case "/v2/model":
   284  			switch nModel {
   285  			case 0:
   286  				modelResp(c, w, r)
   287  			default:
   288  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1)
   289  			}
   290  			nModel++
   291  		case "/v2/model/serial":
   292  			switch nModelSerial {
   293  			case 0:
   294  				serialResp(c, w, r)
   295  			default:
   296  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModelSerial+1)
   297  			}
   298  			nModelSerial++
   299  		case "/v2/assertions/account":
   300  			switch nKnown {
   301  			case 0:
   302  				accountResp(c, w, r)
   303  			default:
   304  				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nKnown+1)
   305  			}
   306  			nKnown++
   307  		default:
   308  			c.Fatalf("unexpected request to %s", r.URL.Path)
   309  		}
   310  	}
   311  }
   312  
   313  func (s *SnapSuite) TestNoModelYet(c *check.C) {
   314  	s.RedirectClientToTestServer(
   315  		makeHappyTestServerHandler(
   316  			c,
   317  			simpleUnhappyResponder(noModelAssertionYetResponse),
   318  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   319  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   320  		))
   321  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"})
   322  	c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`)
   323  }
   324  
   325  func (s *SnapSuite) TestNoSerialYet(c *check.C) {
   326  	s.RedirectClientToTestServer(
   327  		makeHappyTestServerHandler(
   328  			c,
   329  			simpleHappyResponder(happyModelAssertionResponse),
   330  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   331  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   332  		))
   333  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"})
   334  	c.Assert(err, check.ErrorMatches, `device not registered yet \(no serial assertion found\)`)
   335  	c.Check(s.Stderr(), check.Equals, "")
   336  	c.Check(s.Stdout(), check.Equals, `
   337  brand-id:  mememe
   338  model:     test-model
   339  `[1:])
   340  }
   341  
   342  func (s *SnapSuite) TestModel(c *check.C) {
   343  
   344  	for _, tt := range []struct {
   345  		comment string
   346  		modelF  checkResponder
   347  		serialF checkResponder
   348  		outText string
   349  	}{
   350  		{
   351  			comment: "normal serial and model asserts",
   352  			modelF:  simpleHappyResponder(happyModelAssertionResponse),
   353  			serialF: simpleHappyResponder(happySerialAssertionResponse),
   354  			outText: `
   355  brand   MeMeMe (meuser*)
   356  model   test-model
   357  serial  serialserial
   358  `[1:],
   359  		},
   360  		{
   361  			comment: "normal uc20 serial and model asserts",
   362  			modelF:  simpleHappyResponder(happyUC20ModelAssertionResponse),
   363  			serialF: simpleHappyResponder(happySerialUC20AssertionResponse),
   364  			outText: `
   365  brand           MeMeMe (meuser*)
   366  model           test-snapd-core-20-amd64
   367  grade           dangerous
   368  storage-safety  prefer-encrypted
   369  serial          7777
   370  `[1:],
   371  		},
   372  		{
   373  			comment: "model assert has display-name",
   374  			modelF:  simpleHappyResponder(happyModelWithDisplayNameAssertionResponse),
   375  			serialF: simpleHappyResponder(happySerialAssertionResponse),
   376  			outText: `
   377  brand   MeMeMe (meuser*)
   378  model   Model Name (test-model)
   379  serial  serialserial
   380  `[1:],
   381  		},
   382  		{
   383  			comment: "missing serial assert",
   384  			modelF:  simpleHappyResponder(happyModelAssertionResponse),
   385  			serialF: simpleUnhappyResponder(noSerialAssertionYetResponse),
   386  			outText: `
   387  brand   MeMeMe (meuser*)
   388  model   test-model
   389  serial  - (device not registered yet)
   390  `[1:],
   391  		},
   392  	} {
   393  		s.RedirectClientToTestServer(
   394  			makeHappyTestServerHandler(
   395  				c,
   396  				tt.modelF,
   397  				tt.serialF,
   398  				simpleAssertionAccountResponder(happyAccountAssertionResponse),
   399  			))
   400  		rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"})
   401  		c.Assert(err, check.IsNil)
   402  		c.Assert(rest, check.DeepEquals, []string{})
   403  		c.Check(s.Stdout(), check.Equals, tt.outText, check.Commentf("\n%s\n", tt.outText))
   404  		c.Check(s.Stderr(), check.Equals, "")
   405  		s.ResetStdStreams()
   406  	}
   407  }
   408  
   409  func (s *SnapSuite) TestModelVerbose(c *check.C) {
   410  	s.RedirectClientToTestServer(
   411  		makeHappyTestServerHandler(
   412  			c,
   413  			simpleHappyResponder(happyModelAssertionResponse),
   414  			simpleHappyResponder(happySerialAssertionResponse),
   415  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   416  		))
   417  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
   418  	c.Assert(err, check.IsNil)
   419  	c.Assert(rest, check.DeepEquals, []string{})
   420  	c.Check(s.Stdout(), check.Equals, `
   421  brand-id:               mememe
   422  model:                  test-model
   423  serial:                 serialserial
   424  architecture:           amd64
   425  base:                   core18
   426  gadget:                 pc=18
   427  kernel:                 pc-kernel=18
   428  store:                  mememestore
   429  system-user-authority:  
   430    - youyouyou
   431    - mememe
   432  timestamp:       2017-07-27T00:00:00Z
   433  required-snaps:  
   434    - core
   435    - hello-world
   436  `[1:])
   437  	c.Check(s.Stderr(), check.Equals, "")
   438  }
   439  
   440  func (s *SnapSuite) TestModelVerboseUC20(c *check.C) {
   441  	s.RedirectClientToTestServer(
   442  		makeHappyTestServerHandler(
   443  			c,
   444  			simpleHappyResponder(happyUC20ModelAssertionResponse),
   445  			simpleHappyResponder(happySerialAssertionResponse),
   446  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   447  		))
   448  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
   449  	c.Assert(err, check.IsNil)
   450  	c.Assert(rest, check.DeepEquals, []string{})
   451  	c.Check(s.Stdout(), check.Equals, `
   452  brand-id:        testrootorg
   453  model:           test-snapd-core-20-amd64
   454  grade:           dangerous
   455  storage-safety:  prefer-encrypted
   456  serial:          serialserial
   457  architecture:    amd64
   458  base:            core20
   459  timestamp:       2018-09-11T22:00:00Z
   460  snaps:
   461    - name:             pc
   462      id:               UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
   463      type:             gadget
   464      default-channel:  20/edge
   465    - name:             pc-kernel
   466      id:               pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
   467      type:             kernel
   468      default-channel:  20/edge
   469    - name:             app-snap
   470      default-channel:  foo
   471      presence:         optional
   472      modes:            [recover, run]
   473    - name:             core20
   474      id:               DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q
   475      type:             base
   476      default-channel:  latest/stable
   477    - name:             snapd
   478      id:               PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
   479      type:             snapd
   480      default-channel:  latest/stable
   481  `[1:])
   482  	c.Check(s.Stderr(), check.Equals, "")
   483  }
   484  
   485  func (s *SnapSuite) TestModelVerboseDisplayName(c *check.C) {
   486  	s.RedirectClientToTestServer(
   487  		makeHappyTestServerHandler(
   488  			c,
   489  			simpleHappyResponder(happyModelWithDisplayNameAssertionResponse),
   490  			simpleHappyResponder(happySerialAssertionResponse),
   491  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   492  		))
   493  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
   494  	c.Assert(err, check.IsNil)
   495  	c.Assert(rest, check.DeepEquals, []string{})
   496  	c.Check(s.Stdout(), check.Equals, `
   497  brand-id:               mememe
   498  model:                  test-model
   499  serial:                 serialserial
   500  architecture:           amd64
   501  base:                   core18
   502  display-name:           Model Name
   503  gadget:                 pc=18
   504  kernel:                 pc-kernel=18
   505  store:                  mememestore
   506  system-user-authority:  
   507    - youyouyou
   508    - mememe
   509  timestamp:       2017-07-27T00:00:00Z
   510  required-snaps:  
   511    - core
   512    - hello-world
   513  `[1:])
   514  	c.Check(s.Stderr(), check.Equals, "")
   515  }
   516  
   517  func (s *SnapSuite) TestModelVerboseNoSerialYet(c *check.C) {
   518  	s.RedirectClientToTestServer(
   519  		makeHappyTestServerHandler(
   520  			c,
   521  			simpleHappyResponder(happyModelAssertionResponse),
   522  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   523  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   524  		))
   525  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
   526  	c.Assert(err, check.IsNil)
   527  	c.Assert(rest, check.DeepEquals, []string{})
   528  	c.Check(s.Stdout(), check.Equals, `
   529  brand-id:               mememe
   530  model:                  test-model
   531  serial:                 -- (device not registered yet)
   532  architecture:           amd64
   533  base:                   core18
   534  gadget:                 pc=18
   535  kernel:                 pc-kernel=18
   536  store:                  mememestore
   537  system-user-authority:  
   538    - youyouyou
   539    - mememe
   540  timestamp:       2017-07-27T00:00:00Z
   541  required-snaps:  
   542    - core
   543    - hello-world
   544  `[1:])
   545  	c.Check(s.Stderr(), check.Equals, "")
   546  }
   547  
   548  func (s *SnapSuite) TestModelAssertion(c *check.C) {
   549  	s.RedirectClientToTestServer(
   550  		makeHappyTestServerHandler(
   551  			c,
   552  			simpleHappyResponder(happyModelAssertionResponse),
   553  			simpleHappyResponder(happySerialAssertionResponse),
   554  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   555  		))
   556  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion"})
   557  	c.Assert(err, check.IsNil)
   558  	c.Assert(rest, check.DeepEquals, []string{})
   559  	c.Check(s.Stdout(), check.Equals, happyModelAssertionResponse)
   560  	c.Check(s.Stderr(), check.Equals, "")
   561  }
   562  
   563  func (s *SnapSuite) TestModelAssertionVerbose(c *check.C) {
   564  	// check that no calls to the server happen
   565  	s.RedirectClientToTestServer(
   566  		func(w http.ResponseWriter, r *http.Request) {
   567  			c.Fatalf("unexpected request to %s", r.URL.Path)
   568  		},
   569  	)
   570  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion", "--verbose"})
   571  	c.Assert(err, check.ErrorMatches, "cannot use --verbose with --assertion")
   572  	c.Check(s.Stdout(), check.Equals, "")
   573  	c.Check(s.Stderr(), check.Equals, "")
   574  }
   575  
   576  func (s *SnapSuite) TestSerial(c *check.C) {
   577  	s.RedirectClientToTestServer(
   578  		makeHappyTestServerHandler(
   579  			c,
   580  			simpleHappyResponder(happyModelAssertionResponse),
   581  			simpleHappyResponder(happySerialAssertionResponse),
   582  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   583  		))
   584  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"})
   585  	c.Assert(err, check.IsNil)
   586  	c.Assert(rest, check.DeepEquals, []string{})
   587  	c.Check(s.Stdout(), check.Equals, `
   588  brand-id:  my-brand
   589  model:     my-old-model
   590  serial:    serialserial
   591  `[1:])
   592  	c.Check(s.Stderr(), check.Equals, "")
   593  }
   594  
   595  func (s *SnapSuite) TestSerialVerbose(c *check.C) {
   596  	s.RedirectClientToTestServer(
   597  		makeHappyTestServerHandler(
   598  			c,
   599  			simpleHappyResponder(happyModelAssertionResponse),
   600  			simpleHappyResponder(happySerialAssertionResponse),
   601  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   602  		))
   603  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--verbose", "--abs-time"})
   604  	c.Assert(err, check.IsNil)
   605  	c.Assert(rest, check.DeepEquals, []string{})
   606  	c.Check(s.Stdout(), check.Equals, `
   607  brand-id:   my-brand
   608  model:      my-old-model
   609  serial:     serialserial
   610  timestamp:  2019-08-26T16:34:21-05:00
   611  device-key-sha3-384: |
   612    iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO
   613  device-key: |
   614    AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2g
   615    omTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE=
   616  `[1:])
   617  	c.Check(s.Stderr(), check.Equals, "")
   618  }
   619  
   620  func (s *SnapSuite) TestSerialAssertion(c *check.C) {
   621  	s.RedirectClientToTestServer(
   622  		makeHappyTestServerHandler(
   623  			c,
   624  			simpleHappyResponder(happyModelAssertionResponse),
   625  			simpleHappyResponder(happySerialAssertionResponse),
   626  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   627  		))
   628  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"})
   629  	c.Assert(err, check.IsNil)
   630  	c.Assert(rest, check.DeepEquals, []string{})
   631  	c.Check(s.Stdout(), check.Equals, happySerialAssertionResponse)
   632  	c.Check(s.Stderr(), check.Equals, "")
   633  }
   634  
   635  func (s *SnapSuite) TestSerialAssertionSerialAssertionMissing(c *check.C) {
   636  	s.RedirectClientToTestServer(
   637  		makeHappyTestServerHandler(
   638  			c,
   639  			simpleHappyResponder(happyModelAssertionResponse),
   640  			simpleUnhappyResponder(noSerialAssertionYetResponse),
   641  			simpleAssertionAccountResponder(happyAccountAssertionResponse),
   642  		))
   643  	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"})
   644  	c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`)
   645  	c.Assert(s.Stdout(), check.Equals, "")
   646  	c.Assert(s.Stderr(), check.Equals, "")
   647  }