File: cmd_model_test.go

package info (click to toggle)
snapd 2.73-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 81,460 kB
  • sloc: sh: 16,736; ansic: 16,652; python: 11,215; makefile: 1,966; exp: 190; awk: 58; xml: 22
file content (647 lines) | stat: -rw-r--r-- 22,659 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 2019 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package main_test

import (
	"fmt"
	"net/http"

	"gopkg.in/check.v1"

	snap "github.com/snapcore/snapd/cmd/snap"
)

const happyModelAssertionResponse = `type: model
authority-id: mememe
series: 16
brand-id: mememe
model: test-model
architecture: amd64
base: core18
gadget: pc=18
kernel: pc-kernel=18
store: mememestore
system-user-authority:
  - youyouyou
  - mememe
required-snaps:
  - core
  - hello-world
timestamp: 2017-07-27T00:00:00.0Z
sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t

AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j
jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS
U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2
luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N
6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll
zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq
p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ
iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa
ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag
85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv
`

const happyUC20ModelAssertionResponse = `type: model
authority-id: testrootorg
series: 16
brand-id: testrootorg
model: test-snapd-core-20-amd64
architecture: amd64
base: core20
storage-safety: prefer-encrypted
grade: dangerous
snaps:
  -
    default-channel: 20/edge
    id: UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
    name: pc
    type: gadget
  -
    default-channel: 20/edge
    id: pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
    name: pc-kernel
    type: kernel
  -
    name: app-snap
    default-channel: foo
    presence: optional
    modes:
      - recover
      - run
  -
    default-channel: latest/stable
    id: DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q
    name: core20
    type: base
  -
    default-channel: latest/stable
    id: PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
    name: snapd
    type: snapd
timestamp: 2018-09-11T22:00:00+00:00
sign-key-sha3-384: hIedp1AvrWlcDI4uS_qjoFLzjKl5enu4G2FYJpgB3Pj-tUzGlTQBxMBsBmi-tnJR

AcLBUgQAAQoABgUCX06qogAAv10QAFaqQ0NDDvIB7LqM0xNIz+5Y6PB5wJaRk0HqVsg2LlNgS0PQ
uJf0uFMV4GjQMraL7ZYv9BGyUoA+cz8Nbiz85m1g2ADt0ugqR/x2bAojii9lbFLmWpDMJcZhrtB1
3k32lEUwqTMvzYTGiZ6TVug0KYbdmf2+5IGxsayAS3EwdrfbuGRHZOv6XGV7bmm1GEwCRAFvgHCk
BHKoLZ+rfbNclF4l6G+biWJTdyc5jCMpMQ6X/INnx2hXaMRf9Jfrpl6s2bGCfsxW6HVf7AWZ8qHK
jtWPQqJ6NFu2Kw1lYIA202ReK8DC3gfAlOeNzUG5dTPor3KwAoDJJI8ZaQypOazEhO9SHERIutbP
eqPxPmEoB2+E0/o0+g0o5jK4qww3Yd7b8FTDkqm2xfuuldWAiAA4x6ZOQb2So9OLT6ovqHnD3D2r
pLW/lhqwfKp3xzIVUrLi0sjGOVXu5xFDDRyFICZ6kwC7JynRGfHoa5E2y7rv8ehnOZQJ+esz9sgY
lCJcyJ8vhabDlVHg0msSeNKMVBwhQnOSakEwlcfVyaSnapArkF+OCAMl8cuGpMTKO7vJLIJo2c2P
jcVE0ftsTGs9eBi2HmdDhu3e3fmxHt9VcC4uRSOnYNVcJnMh0yVmG8RGS/Dqcz04II7llww6JJYG
KKjQ3RU/TduXa8VJsoWiRRUYAv3H
`

const happyModelWithDisplayNameAssertionResponse = `type: model
authority-id: mememe
series: 16
brand-id: mememe
model: test-model
architecture: amd64
display-name: Model Name
base: core18
gadget: pc=18
kernel: pc-kernel=18
store: mememestore
system-user-authority:
  - youyouyou
  - mememe
required-snaps:
  - core
  - hello-world
timestamp: 2017-07-27T00:00:00.0Z
sign-key-sha3-384: 8B3Wmemeu3H6i4dEV4Q85Q4gIUCHIBCNMHq49e085QeLGHi7v27l3Cqmemer4__t

AcLBcwQAAQoAHRYhBMbX+t6MbKGH5C3nnLZW7+q0g6ELBQJdTdwTAAoJELZW7+q0g6ELEvgQAI3j
jXTqR6kKOqvw94pArwdMDUaZ++tebASAZgso8ejrW2DQGWSc0Q7SQICIR8bvHxqS1GtupQswOzwS
U8hjDTv7WEchH1jylyTj/1W1GernmitTKycecRlEkSOE+EpuqBFgTtj6PdA1Fj3CiCRi1rLMhgF2
luCOitBLaP+E8P3fuATsLqqDLYzt1VY4Y14MU75hMn+CxAQdnOZTI+NzGMasPsldmOYCPNaN/b3N
6/fDLU47RtNlMJ3K0Tz8kj0bqRbegKlD0RdNbAgo9iZwNmrr5E9WCu9f/0rUor/NIxO77H2ExIll
zhmsZ7E6qlxvAgBmzKgAXrn68gGrBkIb0eXKiCaKy/i2ApvjVZ9HkOzA6Ldd+SwNJv/iA8rdiMsq
p2BfKV5f3ju5b6+WktHxAakJ8iqQmj9Yh7piHjsOAUf1PEJd2s2nqQ+pEEn1F0B23gVCY/Fa9YRQ
iKtWVeL3rBw4dSAaK9rpTMqlNcr+yrdXfTK5YzkCC6RU4yzc5MW0hKeseeSiEDSaRYxvftjFfVNa
ZaVXKg8Lu+cHtCJDeYXEkPIDQzXswdBO1M8Mb9D0mYxQwHxwvsWv1DByB+Otq08EYgPh4kyHo7ag
85yK2e/NQ/fxSwQJMhBF74jM1z9arq6RMiE/KOleFAOraKn2hcROKnEeinABW+sOn6vNuMVv
`

const happyAccountAssertionResponse = `type: account
authority-id: canonical
account-id: mememe
display-name: MeMeMe
timestamp: 2016-04-01T00:00:00.0Z
username: meuser
validation: certified
sign-key-sha3-384: -CvQKAwRQ5h3Ffn10FILJoEZUXOv6km9FwA80-Rcj-f-6jadQ89VRswHNiEB9Lxk

AcLDXAQAAQoABgUCV7UYzwAKCRDUpVvql9g3IK7uH/4udqNOurx5WYVknzXdwekp0ovHCQJ0iBPw
TSFxEVr9faZSzb7eqJ1WicHsShf97PYS3ClRYAiluFsjRA8Y03kkSVJHjC+sIwGFubsnkmgflt6D
WEmYIl0UBmeaEDS8uY4Xvp9NsLTzNEj2kvzy/52gKaTc1ZSl5RDL9ppMav+0V9iBYpiDPBWH2rJ+
aDSD8Rkyygm0UscfAKyDKH4lrvZ0WkYyi1YVNPrjQ/AtBySh6Q4iJ3LifzKa9woIyAuJET/4/FPY
oirqHAfuvNod36yNQIyNqEc20AvTvZNH0PSsg4rq3DLjIPzv5KbJO9lhsasNJK1OdL6x8Yqrdsbk
ldZp4qkzfjV7VOMQKaadfcZPRaVVeJWOBnBiaukzkhoNlQi1sdCdkBB/AJHZF8QXw6c7vPDcfnCV
1lW7ddQ2p8IsJbT6LzpJu3GW/P4xhNgCjtCJ1AJm9a9RqLwQYgdLZwwDa9iCRtqTbRXBlfy3apps
1VjbQ3h5iCd0hNfwDBnGVm1rhLKHCD1DUdNE43oN2ZlE7XGyh0HFV6vKlpqoW3eoXCIxWu+HBY96
+LSl/jQgCkb0nxYyzEYK4Reb31D0mYw1Nji5W+MIF5E09+DYZoOT0UvR05YMwMEOeSdI/hLWg/5P
k+GDK+/KopMmpd4D1+jjtF7ZvqDpmAV98jJGB2F88RyVb4gcjmFFyTi4Kv6vzz/oLpbm0qrizC0W
HLGDN/ymGA5sHzEgEx7U540vz/q9VX60FKqL2YZr/DcyY9GKX5kCG4sNqIIHbcJneZ4frM99oVDu
7Jv+DIx/Di6D1ULXol2XjxbbJLKHFtHksR97ceaFvcZwTogC61IYUBJCvvMoqdXAWMhEXCr0QfQ5
Xbi31XW2d4/lF/zWlAkRnGTzufIXFni7+nEuOK0SQEzO3/WaRedK1SGOOtTDjB8/3OJeW96AUYK5
oTIynkYkEyHWMNCXALg+WQW6L4/YO7aUjZ97zOWIugd7Xy63aT3r/EHafqaY2nacOhLfkeKZ830b
o/ezjoZQAxbh6ce7JnXRgE9ELxjdAhBTpGjmmmN2sYrJ7zP9bOgly0BnEPXGSQfFA+NNNw1FADx1
MUY8q9DBjmVtgqY+1KGTV5X8KvQCBMODZIf/XJPHdCRAHxMd8COypcwgL2vDIIXpOFbi1J/B0GF+
eklxk9wzBA8AecBMCwCzIRHDNpD1oa2we38bVFrOug6e/VId1k1jYFJjiLyLCDmV8IMYwEllHSXp
LQAdm3xZ7t4WnxYC8YSCk9mXf3CZg59SpmnV5Q5Z6A5Pl7Nc3sj7hcsMBZEsOMPzNC9dPsBnZvjs
WpPUffJzEdhHBFhvYMuD4Vqj6ejUv9l3oTrjQWVC`

// note: this serial assertion was generated by adding print statements to the
// test in api_model_test.go that generate a fake serial assertion
const happySerialAssertionResponse = `type: serial
authority-id: my-brand
brand-id: my-brand
model: my-old-model
serial: serialserial
device-key:
    AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2go
    mTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE=
device-key-sha3-384: iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO
timestamp: 2019-08-26T16:34:21-05:00
sign-key-sha3-384: anCEGC2NYq7DzDEi6y7OafQCVeVLS90XlLt9PNjrRl9sim5rmRHDDNFNO7ODcWQW

AcJwBAABCgAGBQJdZFBdAADCLALwR6Sy24wm9PffwbvUhOEXneyY3BnxKC0+NgdHu1gU8go9vEP1
i+Flh5uoS70+MBIO+nmF8T+9JWIx2QWFDDxvcuFosnIhvUajCEQohauys5FMz/H/WvB0vrbTBpvK
eg==
`

const happySerialUC20AssertionResponse = `type: serial
authority-id: testrootorg
brand-id: testrootorg
model: test-snapd-core-20-amd64
serial: 7777
device-key:
    AcbBTQRWhcGAARAAuKf9n7WvZDI7u3NzMkD8WN+dxCYrb0UE9XIaHcbrj0i2zJpxCtUtpzoEo7Uk
    Cvxuhr2uBpzAa8fScwzOd77MGHIZQDpS7sFSkhYsSSN0m4sy8vRevsj0roN31fugCjRnhtLTkgxo
    KSoAsK87vYnC+m5V5AHaRER7q1KgpUoVD7eLOJZyrd/tWecsLL9OK87yAQHdF/cVlQupOP6OU3fK
    DllER6V2TD4jADK2Gyj2lDhy3F0+rE0a+zsGpmQQBorvzbozUHgBE3z/XjTTMrHYP4m+4V5HeWdn
    rHt/x1LZ8wMTCMT1eeruclC82UPRgF0zWI+P7WgBqogJpCbfadhAj1zvKW+5vJ385n0BU7PoAZtA
    KddBbsmEnfK/gWIxgFemIrYcYGhIBxYY6iNcygTYRFo4R9xm3bELHLG+viHggih4Lrjnb4sLHOdC
    h3C4/45bY+6hSno8GQGlp4kYQQM8mrF9st51jIM6oyB84NtoySLYYE1wMeGNzDHSuI+1IiRmaTgy
    Q2ImXTuqOhclhNA1sOi3R4H+oOBxe6GmoM5ATBPBqJeqUEvK8GpSRCig0QH4qMNF/abNKwvKhGMZ
    LqtpFp5LNx7xYuAwoVkcq0nxQTsXctl3gJqY+lRx7mIeoXLZPKZyJees+5v96oa9lMdNX3f5UUpX
    zq0cNhdgHrXZfcsAEQEAAQ==
device-key-sha3-384: CZeO_5nJm_Rg0izosNfcQRoQj9nFtAmK2Y_tz4YjlKlvS93b_9gTDHuby5HHwi7d
timestamp: 2020-09-03T14:42:47-05:00
sign-key-sha3-384: hIedp1AvrWlcDI4uS_qjoFLzjKl5enu4G2FYJpgB3Pj-tUzGlTQBxMBsBmi-tnJR

AcLBUgQAAQoABgUCX1FHNwAAqFoQABFiyzipoTYAuYN0Wd7cXuPPD7z+z+E+LoZZ+j4vUKqvnGX8
tksb2nEEOQhjSvVof5pPOswWgq8Nj52dtYA20R5Zgfy0MZHHcCCfgxaRj6EiFyrG5h9l5wWMnzdb
pXo9SJ3hxw6lKdj3n9RAAY0mACvw6f/trcyLeSxQ7EBm6X9c4ohJSjlHkKj0TlKkNTrFflko5aQH
uJUk/YgsvMTZUHbgj6QKHlODUH8iRvOHxzn/Y9BlnzBsb/SyzvNTPeQyzFtd9QkESI2sWghviys2
fGeEZPeXU6xts6Ht+xhr3mj5npZwkkL/6YxSzm9owQ0zGrfaFTswN+xoDKZ5498qRtSY3mCK/5xx
kvWpOTHHhfvuS3GGyvRZOih7IAffDEwQsUNh8V9IjQNNTIkCYTPZz4WBM42mI8UgeDsnDImmcoc0
GlqBeCxUigszJlEdUAHQklwW7Sgp13mceR3zB7BHgp4Sk7n0RyPuTQUA94ys6SeesK5YphwmhVed
V02lkdeqRbGt3yZ/T5Zg8CIUIM0RKDSqoHgvoCMZh98dRGv6LPRj/P0RSWmjYWotjdK+lXK1fySM
RXMNJIInZoC0x8qEwGLXVl5V3z8motLG71ie7PQ677W0dE9XM5LRnZHEKXP41jfaOO9vu12TtBsh
pe/pnYDfIzU6OyOsdmkGWaWD+nbD
`

const noModelAssertionYetResponse = `
{
	"type": "error",
	"status-code": 404,
	"status": "Not Found",
	"result": {
	  "message": "no model assertion yet",
	  "kind": "assertion-not-found",
	  "value": "model"
	}
}`

const noSerialAssertionYetResponse = `
{
	"type": "error",
	"status-code": 404,
	"status": "Not Found",
	"result": {
	  "message": "no serial assertion yet",
	  "kind": "assertion-not-found",
	  "value": "serial"
	}
}`

// helper for constructing different types of responses to the client
type checkResponder func(c *check.C, w http.ResponseWriter, r *http.Request)

func simpleHappyResponder(body string) checkResponder {
	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
		c.Check(r.Method, check.Equals, "GET")
		c.Check(r.URL.RawQuery, check.Equals, "")
		fmt.Fprintln(w, body)
	}
}

func simpleUnhappyResponder(errBody string) checkResponder {
	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
		c.Check(r.Method, check.Equals, "GET")
		c.Check(r.URL.RawQuery, check.Equals, "")
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(404)
		fmt.Fprintln(w, errBody)
	}
}

func simpleAssertionAccountResponder(body string) checkResponder {
	return func(c *check.C, w http.ResponseWriter, r *http.Request) {
		c.Check(r.Method, check.Equals, "GET")
		w.Header().Set("X-Ubuntu-Assertions-Count", "1")
		fmt.Fprintln(w, body)
	}
}

func makeHappyTestServerHandler(c *check.C, modelResp, serialResp, accountResp checkResponder) func(w http.ResponseWriter, r *http.Request) {
	var nModelSerial, nModel, nKnown int
	return func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v2/model":
			switch nModel {
			case 0:
				modelResp(c, w, r)
			default:
				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModel+1)
			}
			nModel++
		case "/v2/model/serial":
			switch nModelSerial {
			case 0:
				serialResp(c, w, r)
			default:
				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nModelSerial+1)
			}
			nModelSerial++
		case "/v2/assertions/account":
			switch nKnown {
			case 0:
				accountResp(c, w, r)
			default:
				c.Fatalf("expected to get 1 request for /v2/model, now on %d", nKnown+1)
			}
			nKnown++
		default:
			c.Fatalf("unexpected request to %s", r.URL.Path)
		}
	}
}

func (s *SnapSuite) TestNoModelYet(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleUnhappyResponder(noModelAssertionYetResponse),
			simpleUnhappyResponder(noSerialAssertionYetResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"})
	c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`)
}

func (s *SnapSuite) TestNoSerialYet(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleUnhappyResponder(noSerialAssertionYetResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"})
	c.Assert(err, check.ErrorMatches, `device not registered yet \(no serial assertion found\)`)
	c.Check(s.Stderr(), check.Equals, "")
	c.Check(s.Stdout(), check.Equals, `
brand-id:  mememe
model:     test-model
`[1:])
}

func (s *SnapSuite) TestModel(c *check.C) {

	for _, tt := range []struct {
		comment string
		modelF  checkResponder
		serialF checkResponder
		outText string
	}{
		{
			comment: "normal serial and model asserts",
			modelF:  simpleHappyResponder(happyModelAssertionResponse),
			serialF: simpleHappyResponder(happySerialAssertionResponse),
			outText: `
brand   MeMeMe (meuser**)
model   test-model
serial  serialserial
`[1:],
		},
		{
			comment: "normal uc20 serial and model asserts",
			modelF:  simpleHappyResponder(happyUC20ModelAssertionResponse),
			serialF: simpleHappyResponder(happySerialUC20AssertionResponse),
			outText: `
brand           MeMeMe (meuser**)
model           test-snapd-core-20-amd64
grade           dangerous
storage-safety  prefer-encrypted
serial          7777
`[1:],
		},
		{
			comment: "model assert has display-name",
			modelF:  simpleHappyResponder(happyModelWithDisplayNameAssertionResponse),
			serialF: simpleHappyResponder(happySerialAssertionResponse),
			outText: `
brand   MeMeMe (meuser**)
model   Model Name (test-model)
serial  serialserial
`[1:],
		},
		{
			comment: "missing serial assert",
			modelF:  simpleHappyResponder(happyModelAssertionResponse),
			serialF: simpleUnhappyResponder(noSerialAssertionYetResponse),
			outText: `
brand   MeMeMe (meuser**)
model   test-model
serial  - (device not registered yet)
`[1:],
		},
	} {
		s.RedirectClientToTestServer(
			makeHappyTestServerHandler(
				c,
				tt.modelF,
				tt.serialF,
				simpleAssertionAccountResponder(happyAccountAssertionResponse),
			))
		rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model"})
		c.Assert(err, check.IsNil)
		c.Assert(rest, check.DeepEquals, []string{})
		c.Check(s.Stdout(), check.Equals, tt.outText, check.Commentf("\n%s\n", tt.outText))
		c.Check(s.Stderr(), check.Equals, "")
		s.ResetStdStreams()
	}
}

func (s *SnapSuite) TestModelVerbose(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, `
brand-id:               mememe
model:                  test-model
serial:                 serialserial
architecture:           amd64
base:                   core18
gadget:                 pc=18
kernel:                 pc-kernel=18
store:                  mememestore
system-user-authority:  
  - youyouyou
  - mememe
timestamp:       2017-07-27T00:00:00Z
required-snaps:  
  - core
  - hello-world
`[1:])
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestModelVerboseUC20(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyUC20ModelAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, `
brand-id:        testrootorg
model:           test-snapd-core-20-amd64
grade:           dangerous
storage-safety:  prefer-encrypted
serial:          serialserial
architecture:    amd64
base:            core20
timestamp:       2018-09-11T22:00:00Z
snaps:
  - name:             pc
    id:               UqFziVZDHLSyO3TqSWgNBoAdHbLI4dAH
    type:             gadget
    default-channel:  20/edge
  - name:             pc-kernel
    id:               pYVQrBcKmBa0mZ4CCN7ExT6jH8rY1hza
    type:             kernel
    default-channel:  20/edge
  - name:             app-snap
    default-channel:  foo
    presence:         optional
    modes:            [recover, run]
  - name:             core20
    id:               DLqre5XGLbDqg9jPtiAhRRjDuPVa5X1q
    type:             base
    default-channel:  latest/stable
  - name:             snapd
    id:               PMrrV4ml8uWuEUDBT8dSGnKUYbevVhc4
    type:             snapd
    default-channel:  latest/stable
`[1:])
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestModelVerboseDisplayName(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelWithDisplayNameAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, `
brand-id:               mememe
model:                  test-model
serial:                 serialserial
architecture:           amd64
base:                   core18
display-name:           Model Name
gadget:                 pc=18
kernel:                 pc-kernel=18
store:                  mememestore
system-user-authority:  
  - youyouyou
  - mememe
timestamp:       2017-07-27T00:00:00Z
required-snaps:  
  - core
  - hello-world
`[1:])
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestModelVerboseNoSerialYet(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleUnhappyResponder(noSerialAssertionYetResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--verbose", "--abs-time"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, `
brand-id:               mememe
model:                  test-model
serial:                 -- (device not registered yet)
architecture:           amd64
base:                   core18
gadget:                 pc=18
kernel:                 pc-kernel=18
store:                  mememestore
system-user-authority:  
  - youyouyou
  - mememe
timestamp:       2017-07-27T00:00:00Z
required-snaps:  
  - core
  - hello-world
`[1:])
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestModelAssertion(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, happyModelAssertionResponse)
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestModelAssertionVerbose(c *check.C) {
	// check that no calls to the server happen
	s.RedirectClientToTestServer(
		func(w http.ResponseWriter, r *http.Request) {
			c.Fatalf("unexpected request to %s", r.URL.Path)
		},
	)
	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--assertion", "--verbose"})
	c.Assert(err, check.ErrorMatches, "cannot use --verbose with --assertion")
	c.Check(s.Stdout(), check.Equals, "")
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestSerial(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, `
brand-id:  my-brand
model:     my-old-model
serial:    serialserial
`[1:])
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestSerialVerbose(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--verbose", "--abs-time"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, `
brand-id:   my-brand
model:      my-old-model
serial:     serialserial
timestamp:  2019-08-26T16:34:21-05:00
device-key-sha3-384: |
  iqLo9doLzK8De9925UrdUyuvPbBad72OTWVE9YJXqd6nz9dKvwJ_lHP5bVxrl3VO
device-key: |
  AcZrBFaFwYABAvCgEOrrLA6FKcreHxCcOoTgBUZ+IRG7Nb8tzmEAklaQPGpv7skapUjwD1luE2g
  omTcoTssVHrfLpBoSDV1aBs44rg3NK40ZKPJP7d2zkds1GxUo1Ea5vfet3SJ4h3aRABEBAAE=
`[1:])
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestSerialAssertion(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleHappyResponder(happySerialAssertionResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"})
	c.Assert(err, check.IsNil)
	c.Assert(rest, check.DeepEquals, []string{})
	c.Check(s.Stdout(), check.Equals, happySerialAssertionResponse)
	c.Check(s.Stderr(), check.Equals, "")
}

func (s *SnapSuite) TestSerialAssertionSerialAssertionMissing(c *check.C) {
	s.RedirectClientToTestServer(
		makeHappyTestServerHandler(
			c,
			simpleHappyResponder(happyModelAssertionResponse),
			simpleUnhappyResponder(noSerialAssertionYetResponse),
			simpleAssertionAccountResponder(happyAccountAssertionResponse),
		))
	_, err := snap.Parser(snap.Client()).ParseArgs([]string{"model", "--serial", "--assertion"})
	c.Assert(err, check.ErrorMatches, `device not ready yet \(no assertions found\)`)
	c.Assert(s.Stdout(), check.Equals, "")
	c.Assert(s.Stderr(), check.Equals, "")
}