github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/cmd/snap/cmd_snap_op_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2018 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 "io/ioutil" 25 "mime" 26 "mime/multipart" 27 "net/http" 28 "net/http/httptest" 29 "os" 30 "path/filepath" 31 "regexp" 32 "strings" 33 "time" 34 35 "gopkg.in/check.v1" 36 37 "github.com/snapcore/snapd/client" 38 snap "github.com/snapcore/snapd/cmd/snap" 39 "github.com/snapcore/snapd/progress" 40 "github.com/snapcore/snapd/progress/progresstest" 41 "github.com/snapcore/snapd/release" 42 "github.com/snapcore/snapd/testutil" 43 ) 44 45 type snapOpTestServer struct { 46 c *check.C 47 48 checker func(r *http.Request) 49 n int 50 total int 51 channel string 52 trackingChannel string 53 confinement string 54 rebooting bool 55 snap string 56 } 57 58 var _ = check.Suite(&SnapOpSuite{}) 59 60 func (t *snapOpTestServer) handle(w http.ResponseWriter, r *http.Request) { 61 switch t.n { 62 case 0: 63 t.checker(r) 64 method := "POST" 65 if strings.HasSuffix(r.URL.Path, "/conf") { 66 method = "PUT" 67 } 68 t.c.Check(r.Method, check.Equals, method) 69 w.WriteHeader(202) 70 fmt.Fprintln(w, `{"type":"async", "change": "42", "status-code": 202}`) 71 case 1: 72 t.c.Check(r.Method, check.Equals, "GET") 73 t.c.Check(r.URL.Path, check.Equals, "/v2/changes/42") 74 if !t.rebooting { 75 fmt.Fprintln(w, `{"type": "sync", "result": {"status": "Doing"}}`) 76 } else { 77 fmt.Fprintln(w, `{"type": "sync", "result": {"status": "Doing"}, "maintenance": {"kind": "system-restart", "message": "system is restarting"}}}`) 78 } 79 case 2: 80 t.c.Check(r.Method, check.Equals, "GET") 81 t.c.Check(r.URL.Path, check.Equals, "/v2/changes/42") 82 fmt.Fprintf(w, `{"type": "sync", "result": {"ready": true, "status": "Done", "data": {"snap-name": "%s"}}}\n`, t.snap) 83 case 3: 84 t.c.Check(r.Method, check.Equals, "GET") 85 t.c.Check(r.URL.Path, check.Equals, "/v2/snaps") 86 fmt.Fprintf(w, `{"type": "sync", "result": [{"name": "%s", "status": "active", "version": "1.0", "developer": "bar", "publisher": {"id": "bar-id", "username": "bar", "display-name": "Bar", "validation": "unproven"}, "revision":42, "channel":"%s", "tracking-channel": "%s", "confinement": "%s"}]}\n`, t.snap, t.channel, t.trackingChannel, t.confinement) 87 default: 88 t.c.Fatalf("expected to get %d requests, now on %d", t.total, t.n+1) 89 } 90 91 t.n++ 92 } 93 94 type SnapOpSuite struct { 95 BaseSnapSuite 96 97 restoreAll func() 98 srv snapOpTestServer 99 } 100 101 func (s *SnapOpSuite) SetUpTest(c *check.C) { 102 s.BaseSnapSuite.SetUpTest(c) 103 104 restoreClientRetry := client.MockDoTimings(time.Millisecond, 100*time.Millisecond) 105 restorePollTime := snap.MockPollTime(time.Millisecond) 106 s.restoreAll = func() { 107 restoreClientRetry() 108 restorePollTime() 109 } 110 111 s.srv = snapOpTestServer{ 112 c: c, 113 total: 4, 114 snap: "foo", 115 } 116 } 117 118 func (s *SnapOpSuite) TearDownTest(c *check.C) { 119 s.restoreAll() 120 s.BaseSnapSuite.TearDownTest(c) 121 } 122 123 func (s *SnapOpSuite) TestWait(c *check.C) { 124 meter := &progresstest.Meter{} 125 defer progress.MockMeter(meter)() 126 restore := snap.MockMaxGoneTime(time.Millisecond) 127 defer restore() 128 129 // lazy way of getting a URL that won't work nor break stuff 130 server := httptest.NewServer(nil) 131 snap.ClientConfig.BaseURL = server.URL 132 server.Close() 133 134 cli := snap.Client() 135 chg, err := snap.Wait(cli, "x") 136 c.Assert(chg, check.IsNil) 137 c.Assert(err, check.NotNil) 138 c.Check(meter.Labels, testutil.Contains, "Waiting for server to restart") 139 } 140 141 func (s *SnapOpSuite) TestWaitRecovers(c *check.C) { 142 meter := &progresstest.Meter{} 143 defer progress.MockMeter(meter)() 144 145 nah := true 146 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 147 if nah { 148 nah = false 149 return 150 } 151 fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done"}}`) 152 }) 153 154 cli := snap.Client() 155 chg, err := snap.Wait(cli, "x") 156 // we got the change 157 c.Check(chg, check.NotNil) 158 c.Assert(err, check.IsNil) 159 160 // but only after recovering 161 c.Check(meter.Labels, testutil.Contains, "Waiting for server to restart") 162 } 163 164 func (s *SnapOpSuite) TestWaitRebooting(c *check.C) { 165 meter := &progresstest.Meter{} 166 defer progress.MockMeter(meter)() 167 restore := snap.MockMaxGoneTime(time.Millisecond) 168 defer restore() 169 170 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 171 fmt.Fprintln(w, `{"type": "sync", 172 "result": { 173 "ready": false, 174 "status": "Doing", 175 "tasks": [{"kind": "bar", "summary": "...", "status": "Doing", "progress": {"done": 1, "total": 1}, "log": ["INFO: info"]}] 176 }, 177 "maintenance": {"kind": "system-restart", "message": "system is restarting"}}`) 178 }) 179 180 cli := snap.Client() 181 chg, err := snap.Wait(cli, "x") 182 c.Assert(chg, check.IsNil) 183 c.Assert(err, check.DeepEquals, &client.Error{Kind: client.ErrorKindSystemRestart, Message: "system is restarting"}) 184 185 // last available info is still displayed 186 c.Check(meter.Notices, testutil.Contains, "INFO: info") 187 } 188 189 func (s *SnapOpSuite) TestInstall(c *check.C) { 190 s.srv.checker = func(r *http.Request) { 191 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 192 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 193 "action": "install", 194 "channel": "candidate", 195 "cohort-key": "what", 196 }) 197 s.srv.channel = "candidate" 198 } 199 200 s.RedirectClientToTestServer(s.srv.handle) 201 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "candidate", "--cohort", "what", "foo"}) 202 c.Assert(err, check.IsNil) 203 c.Assert(rest, check.DeepEquals, []string{}) 204 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(candidate\) 1.0 from Bar installed`) 205 c.Check(s.Stderr(), check.Equals, "") 206 // ensure that the fake server api was actually hit 207 c.Check(s.srv.n, check.Equals, s.srv.total) 208 } 209 210 func (s *SnapOpSuite) TestInstallIgnoreRunning(c *check.C) { 211 s.srv.checker = func(r *http.Request) { 212 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 213 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 214 "action": "install", 215 "ignore-running": true, 216 }) 217 } 218 219 s.RedirectClientToTestServer(s.srv.handle) 220 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--ignore-running", "foo"}) 221 c.Assert(err, check.IsNil) 222 c.Assert(rest, check.DeepEquals, []string{}) 223 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 224 c.Check(s.Stderr(), check.Equals, "") 225 // ensure that the fake server api was actually hit 226 c.Check(s.srv.n, check.Equals, s.srv.total) 227 } 228 229 func (s *SnapOpSuite) TestInstallNoPATH(c *check.C) { 230 // PATH restored by test tear down 231 os.Setenv("PATH", "/bin:/usr/bin:/sbin:/usr/sbin") 232 s.srv.checker = func(r *http.Request) { 233 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 234 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 235 "action": "install", 236 "channel": "candidate", 237 }) 238 s.srv.channel = "candidate" 239 } 240 241 s.RedirectClientToTestServer(s.srv.handle) 242 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "candidate", "foo"}) 243 c.Assert(err, check.IsNil) 244 c.Assert(rest, check.DeepEquals, []string{}) 245 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(candidate\) 1.0 from Bar installed`) 246 c.Check(s.Stderr(), testutil.MatchesWrapped, `Warning: \S+/bin was not found in your \$PATH.*`) 247 // ensure that the fake server api was actually hit 248 c.Check(s.srv.n, check.Equals, s.srv.total) 249 } 250 251 func (s *SnapOpSuite) TestInstallNoPATHMaybeResetBySudo(c *check.C) { 252 // PATH restored by test tear down 253 os.Setenv("PATH", "/bin:/usr/bin:/sbin:/usr/sbin") 254 if old, isset := os.LookupEnv("SUDO_UID"); isset { 255 defer os.Setenv("SUDO_UID", old) 256 } 257 os.Setenv("SUDO_UID", "1234") 258 restore := release.MockReleaseInfo(&release.OS{ID: "fedora"}) 259 defer restore() 260 s.srv.checker = func(r *http.Request) { 261 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 262 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 263 "action": "install", 264 "channel": "candidate", 265 }) 266 s.srv.channel = "candidate" 267 } 268 269 s.RedirectClientToTestServer(s.srv.handle) 270 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "candidate", "foo"}) 271 c.Assert(err, check.IsNil) 272 c.Assert(rest, check.DeepEquals, []string{}) 273 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(candidate\) 1.0 from Bar installed`) 274 c.Check(s.Stderr(), check.HasLen, 0) 275 // ensure that the fake server api was actually hit 276 c.Check(s.srv.n, check.Equals, s.srv.total) 277 } 278 279 func (s *SnapOpSuite) TestInstallFromTrack(c *check.C) { 280 s.srv.checker = func(r *http.Request) { 281 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 282 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 283 "action": "install", 284 "channel": "3.4", 285 }) 286 s.srv.channel = "3.4/stable" 287 } 288 289 s.RedirectClientToTestServer(s.srv.handle) 290 // snap install --channel=3.4 means 3.4/stable, this is what we test here 291 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "3.4", "foo"}) 292 c.Assert(err, check.IsNil) 293 c.Assert(rest, check.DeepEquals, []string{}) 294 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(3.4/stable\) 1.0 from Bar installed`) 295 c.Check(s.Stderr(), check.Equals, "") 296 // ensure that the fake server api was actually hit 297 c.Check(s.srv.n, check.Equals, s.srv.total) 298 } 299 300 func (s *SnapOpSuite) TestInstallFromBranch(c *check.C) { 301 s.srv.checker = func(r *http.Request) { 302 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 303 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 304 "action": "install", 305 "channel": "3.4/stable/hotfix-1", 306 }) 307 s.srv.channel = "3.4/stable/hotfix-1" 308 } 309 310 s.RedirectClientToTestServer(s.srv.handle) 311 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "3.4/stable/hotfix-1", "foo"}) 312 c.Assert(err, check.IsNil) 313 c.Assert(rest, check.DeepEquals, []string{}) 314 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(3.4/stable/hotfix-1\) 1.0 from Bar installed`) 315 c.Check(s.Stderr(), check.Equals, "") 316 // ensure that the fake server api was actually hit 317 c.Check(s.srv.n, check.Equals, s.srv.total) 318 } 319 320 func (s *SnapOpSuite) TestInstallSameRiskInTrack(c *check.C) { 321 s.srv.checker = func(r *http.Request) { 322 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 323 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 324 "action": "install", 325 "channel": "latest/stable", 326 }) 327 s.srv.channel = "stable" 328 s.srv.trackingChannel = "latest/stable" 329 } 330 331 s.RedirectClientToTestServer(s.srv.handle) 332 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "latest/stable", "foo"}) 333 c.Assert(err, check.IsNil) 334 c.Assert(rest, check.DeepEquals, []string{}) 335 c.Check(s.Stdout(), check.Equals, "foo 1.0 from Bar installed\n") 336 c.Check(s.Stderr(), check.Equals, "") 337 // ensure that the fake server api was actually hit 338 c.Check(s.srv.n, check.Equals, s.srv.total) 339 } 340 341 func (s *SnapOpSuite) TestInstallSameRiskInDefaultTrack(c *check.C) { 342 s.srv.checker = func(r *http.Request) { 343 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 344 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 345 "action": "install", 346 "channel": "stable", 347 }) 348 s.srv.channel = "18/stable" 349 s.srv.trackingChannel = "18/stable" 350 } 351 352 s.RedirectClientToTestServer(s.srv.handle) 353 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--stable", "foo"}) 354 c.Assert(err, check.IsNil) 355 c.Assert(rest, check.DeepEquals, []string{}) 356 c.Check(s.Stdout(), check.Equals, "foo (18/stable) 1.0 from Bar installed\n") 357 c.Check(s.Stderr(), check.Equals, "") 358 // ensure that the fake server api was actually hit 359 c.Check(s.srv.n, check.Equals, s.srv.total) 360 } 361 362 func (s *SnapOpSuite) TestInstallRiskChannelClosed(c *check.C) { 363 s.srv.checker = func(r *http.Request) { 364 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 365 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 366 "action": "install", 367 "channel": "edge", 368 }) 369 s.srv.channel = "stable" 370 s.srv.trackingChannel = "edge" 371 } 372 373 s.RedirectClientToTestServer(s.srv.handle) 374 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "edge", "foo"}) 375 c.Assert(err, check.IsNil) 376 c.Assert(rest, check.DeepEquals, []string{}) 377 c.Check(s.Stdout(), check.Equals, `foo 1.0 from Bar installed 378 Channel edge for foo is closed; temporarily forwarding to stable. 379 `) 380 c.Check(s.Stderr(), check.Equals, "") 381 // ensure that the fake server api was actually hit 382 c.Check(s.srv.n, check.Equals, s.srv.total) 383 } 384 385 func (s *SnapOpSuite) TestInstallDevMode(c *check.C) { 386 s.srv.checker = func(r *http.Request) { 387 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 388 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 389 "action": "install", 390 "devmode": true, 391 "channel": "beta", 392 }) 393 s.srv.channel = "beta" 394 } 395 396 s.RedirectClientToTestServer(s.srv.handle) 397 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel", "beta", "--devmode", "foo"}) 398 c.Assert(err, check.IsNil) 399 c.Assert(rest, check.DeepEquals, []string{}) 400 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(beta\) 1.0 from Bar installed`) 401 c.Check(s.Stderr(), check.Equals, "") 402 // ensure that the fake server api was actually hit 403 c.Check(s.srv.n, check.Equals, s.srv.total) 404 } 405 406 func (s *SnapOpSuite) TestInstallClassic(c *check.C) { 407 s.srv.checker = func(r *http.Request) { 408 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 409 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 410 "action": "install", 411 "classic": true, 412 }) 413 s.srv.confinement = "classic" 414 } 415 416 s.RedirectClientToTestServer(s.srv.handle) 417 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--classic", "foo"}) 418 c.Assert(err, check.IsNil) 419 c.Assert(rest, check.DeepEquals, []string{}) 420 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 421 c.Check(s.Stderr(), check.Equals, "") 422 // ensure that the fake server api was actually hit 423 c.Check(s.srv.n, check.Equals, s.srv.total) 424 } 425 426 func (s *SnapOpSuite) TestInstallStrictWithClassicFlag(c *check.C) { 427 s.srv.checker = func(r *http.Request) { 428 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 429 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 430 "action": "install", 431 "classic": true, 432 }) 433 s.srv.confinement = "strict" 434 } 435 436 s.RedirectClientToTestServer(s.srv.handle) 437 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--classic", "foo"}) 438 c.Assert(err, check.IsNil) 439 c.Assert(rest, check.DeepEquals, []string{}) 440 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 441 c.Check(s.Stderr(), testutil.MatchesWrapped, `Warning:\s+flag --classic ignored for strictly confined snap foo.*`) 442 // ensure that the fake server api was actually hit 443 c.Check(s.srv.n, check.Equals, s.srv.total) 444 } 445 446 func (s *SnapOpSuite) TestInstallUnaliased(c *check.C) { 447 s.srv.checker = func(r *http.Request) { 448 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 449 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 450 "action": "install", 451 "unaliased": true, 452 }) 453 } 454 455 s.RedirectClientToTestServer(s.srv.handle) 456 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--unaliased", "foo"}) 457 c.Assert(err, check.IsNil) 458 c.Assert(rest, check.DeepEquals, []string{}) 459 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 460 c.Check(s.Stderr(), check.Equals, "") 461 // ensure that the fake server api was actually hit 462 c.Check(s.srv.n, check.Equals, s.srv.total) 463 } 464 465 func (s *SnapOpSuite) TestInstallSnapNotFound(c *check.C) { 466 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 467 fmt.Fprintln(w, `{"type": "error", "result": {"message": "snap not found", "value": "foo", "kind": "snap-not-found"}, "status-code": 404}`) 468 }) 469 470 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "foo"}) 471 c.Assert(err, check.NotNil) 472 c.Check(fmt.Sprintf("error: %v\n", err), check.Equals, `error: snap "foo" not found 473 `) 474 475 c.Check(s.Stdout(), check.Equals, "") 476 c.Check(s.Stderr(), check.Equals, "") 477 } 478 479 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailable(c *check.C) { 480 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 481 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision available as specified", "value": "foo", "kind": "snap-revision-not-available"}, "status-code": 404}`) 482 }) 483 484 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "foo"}) 485 c.Assert(err, check.NotNil) 486 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 487 error: snap "foo" not available as specified (see 'snap info foo') 488 `) 489 490 c.Check(s.Stdout(), check.Equals, "") 491 c.Check(s.Stderr(), check.Equals, "") 492 } 493 494 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableOnChannel(c *check.C) { 495 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 496 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision available as specified", "value": "foo", "kind": "snap-revision-not-available"}, "status-code": 404}`) 497 }) 498 499 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=mytrack", "foo"}) 500 c.Check(err, check.ErrorMatches, `snap "foo" not available on channel "mytrack" \(see 'snap info foo'\)`) 501 502 c.Check(s.Stdout(), check.Equals, "") 503 c.Check(s.Stderr(), check.Equals, "") 504 } 505 506 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableAtRevision(c *check.C) { 507 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 508 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision available as specified", "value": "foo", "kind": "snap-revision-not-available"}, "status-code": 404}`) 509 }) 510 511 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--revision=2", "foo"}) 512 c.Assert(err, check.NotNil) 513 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 514 error: snap "foo" revision 2 not available (see 'snap info foo') 515 `) 516 517 c.Check(s.Stdout(), check.Equals, "") 518 c.Check(s.Stderr(), check.Equals, "") 519 } 520 521 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelTrackOK(c *check.C) { 522 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 523 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 524 "snap-name": "foo", 525 "action": "install", 526 "architecture": "amd64", 527 "channel": "stable", 528 "releases": [{"architecture": "amd64", "channel": "beta"}, 529 {"architecture": "amd64", "channel": "edge"}] 530 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 531 }) 532 533 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "foo"}) 534 c.Assert(err, check.NotNil) 535 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 536 error: snap "foo" is not available on stable but is available to install on the 537 following channels: 538 539 beta snap install --beta foo 540 edge snap install --edge foo 541 542 Please be mindful pre-release channels may include features not 543 completely tested or implemented. Get more information with 'snap info 544 foo'. 545 `) 546 547 c.Check(s.Stdout(), check.Equals, "") 548 c.Check(s.Stderr(), check.Equals, "") 549 } 550 551 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelTrackOKPrerelOK(c *check.C) { 552 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 553 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 554 "snap-name": "foo", 555 "action": "install", 556 "architecture": "amd64", 557 "channel": "candidate", 558 "releases": [{"architecture": "amd64", "channel": "beta"}, 559 {"architecture": "amd64", "channel": "edge"}] 560 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 561 }) 562 563 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--candidate", "foo"}) 564 c.Assert(err, check.NotNil) 565 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 566 error: snap "foo" is not available on candidate but is available to install on 567 the following channels: 568 569 beta snap install --beta foo 570 edge snap install --edge foo 571 572 Get more information with 'snap info foo'. 573 `) 574 575 c.Check(s.Stdout(), check.Equals, "") 576 c.Check(s.Stderr(), check.Equals, "") 577 } 578 579 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelTrackOther(c *check.C) { 580 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 581 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 582 "snap-name": "foo", 583 "action": "install", 584 "architecture": "amd64", 585 "channel": "stable", 586 "releases": [{"architecture": "amd64", "channel": "1.0/stable"}, 587 {"architecture": "amd64", "channel": "2.0/stable"}] 588 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 589 }) 590 591 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "foo"}) 592 c.Assert(err, check.NotNil) 593 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 594 error: snap "foo" is not available on latest/stable but is available to install 595 on the following tracks: 596 597 1.0/stable snap install --channel=1.0 foo 598 2.0/stable snap install --channel=2.0 foo 599 600 Please be mindful that different tracks may include different features. 601 Get more information with 'snap info foo'. 602 `) 603 604 c.Check(s.Stdout(), check.Equals, "") 605 c.Check(s.Stderr(), check.Equals, "") 606 } 607 608 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelTrackLatestStable(c *check.C) { 609 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 610 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 611 "snap-name": "foo", 612 "action": "install", 613 "architecture": "amd64", 614 "channel": "2.0/stable", 615 "releases": [{"architecture": "amd64", "channel": "stable"}] 616 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 617 }) 618 619 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=2.0/stable", "foo"}) 620 c.Assert(err, check.NotNil) 621 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 622 error: snap "foo" is not available on 2.0/stable but is available to install on 623 the following tracks: 624 625 latest/stable snap install --stable foo 626 627 Please be mindful that different tracks may include different features. 628 Get more information with 'snap info foo'. 629 `) 630 631 c.Check(s.Stdout(), check.Equals, "") 632 c.Check(s.Stderr(), check.Equals, "") 633 } 634 635 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelTrackAndRiskOther(c *check.C) { 636 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 637 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 638 "snap-name": "foo", 639 "action": "install", 640 "architecture": "amd64", 641 "channel": "2.0/stable", 642 "releases": [{"architecture": "amd64", "channel": "1.0/edge"}] 643 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 644 }) 645 646 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=2.0/stable", "foo"}) 647 c.Assert(err, check.NotNil) 648 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 649 error: snap "foo" is not available on 2.0/stable but other tracks exist. 650 651 Please be mindful that different tracks may include different features. 652 Get more information with 'snap info foo'. 653 `) 654 655 c.Check(s.Stdout(), check.Equals, "") 656 c.Check(s.Stderr(), check.Equals, "") 657 } 658 659 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForArchitectureTrackAndRiskOK(c *check.C) { 660 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 661 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified architecture", "value": { 662 "snap-name": "foo", 663 "action": "install", 664 "architecture": "arm64", 665 "channel": "stable", 666 "releases": [{"architecture": "amd64", "channel": "stable"}, 667 {"architecture": "s390x", "channel": "stable"}] 668 }, "kind": "snap-architecture-not-available"}, "status-code": 404}`) 669 }) 670 671 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "foo"}) 672 c.Assert(err, check.NotNil) 673 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 674 error: snap "foo" is not available on stable for this architecture (arm64) but 675 exists on other architectures (amd64, s390x). 676 `) 677 678 c.Check(s.Stdout(), check.Equals, "") 679 c.Check(s.Stderr(), check.Equals, "") 680 } 681 682 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForArchitectureTrackAndRiskOther(c *check.C) { 683 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 684 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified architecture", "value": { 685 "snap-name": "foo", 686 "action": "install", 687 "architecture": "arm64", 688 "channel": "1.0/stable", 689 "releases": [{"architecture": "amd64", "channel": "stable"}, 690 {"architecture": "s390x", "channel": "stable"}] 691 }, "kind": "snap-architecture-not-available"}, "status-code": 404}`) 692 }) 693 694 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=1.0/stable", "foo"}) 695 c.Assert(err, check.NotNil) 696 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 697 error: snap "foo" is not available on this architecture (arm64) but exists on 698 other architectures (amd64, s390x). 699 `) 700 701 c.Check(s.Stdout(), check.Equals, "") 702 c.Check(s.Stderr(), check.Equals, "") 703 } 704 705 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableInvalidChannel(c *check.C) { 706 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 707 c.Fatal("unexpected call to server") 708 }) 709 710 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=a/b/c/d", "foo"}) 711 c.Assert(err, check.ErrorMatches, "channel name has too many components: a/b/c/d") 712 713 c.Check(s.Stdout(), check.Equals, "") 714 c.Check(s.Stderr(), check.Equals, "") 715 } 716 717 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelNonExistingBranchOnMainChannel(c *check.C) { 718 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 719 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 720 "snap-name": "foo", 721 "action": "install", 722 "architecture": "amd64", 723 "channel": "stable/baz", 724 "releases": [{"architecture": "amd64", "channel": "stable"}] 725 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 726 }) 727 728 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=stable/baz", "foo"}) 729 c.Assert(err, check.NotNil) 730 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 731 error: requested a non-existing branch on latest/stable for snap "foo": baz 732 `) 733 734 c.Check(s.Stdout(), check.Equals, "") 735 c.Check(s.Stderr(), check.Equals, "") 736 } 737 738 func (s *SnapOpSuite) TestInstallSnapRevisionNotAvailableForChannelNonExistingBranch(c *check.C) { 739 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 740 fmt.Fprintln(w, `{"type": "error", "result": {"message": "no snap revision on specified channel", "value": { 741 "snap-name": "foo", 742 "action": "install", 743 "architecture": "amd64", 744 "channel": "stable/baz", 745 "releases": [{"architecture": "amd64", "channel": "edge"}] 746 }, "kind": "snap-channel-not-available"}, "status-code": 404}`) 747 }) 748 749 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--channel=stable/baz", "foo"}) 750 c.Assert(err, check.NotNil) 751 c.Check(fmt.Sprintf("\nerror: %v\n", err), check.Equals, ` 752 error: requested a non-existing branch for snap "foo": latest/stable/baz 753 `) 754 755 c.Check(s.Stdout(), check.Equals, "") 756 c.Check(s.Stderr(), check.Equals, "") 757 } 758 759 func testForm(r *http.Request, c *check.C) *multipart.Form { 760 contentType := r.Header.Get("Content-Type") 761 mediaType, params, err := mime.ParseMediaType(contentType) 762 c.Assert(err, check.IsNil) 763 c.Assert(params["boundary"], check.Matches, ".{10,}") 764 c.Check(mediaType, check.Equals, "multipart/form-data") 765 766 form, err := multipart.NewReader(r.Body, params["boundary"]).ReadForm(1 << 20) 767 c.Assert(err, check.IsNil) 768 769 return form 770 } 771 772 func formFile(form *multipart.Form, c *check.C) (name, filename string, content []byte) { 773 c.Assert(form.File, check.HasLen, 1) 774 775 for name, fheaders := range form.File { 776 c.Assert(fheaders, check.HasLen, 1) 777 body, err := fheaders[0].Open() 778 c.Assert(err, check.IsNil) 779 defer body.Close() 780 filename = fheaders[0].Filename 781 content, err = ioutil.ReadAll(body) 782 c.Assert(err, check.IsNil) 783 784 return name, filename, content 785 } 786 787 return "", "", nil 788 } 789 790 func (s *SnapOpSuite) TestInstallPath(c *check.C) { 791 s.srv.checker = func(r *http.Request) { 792 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 793 794 form := testForm(r, c) 795 defer form.RemoveAll() 796 797 c.Check(form.Value["action"], check.DeepEquals, []string{"install"}) 798 c.Check(form.Value["devmode"], check.IsNil) 799 c.Check(form.Value["snap-path"], check.NotNil) 800 c.Check(form.Value, check.HasLen, 2) 801 802 name, _, body := formFile(form, c) 803 c.Check(name, check.Equals, "snap") 804 c.Check(string(body), check.Equals, "snap-data") 805 } 806 807 snapBody := []byte("snap-data") 808 s.RedirectClientToTestServer(s.srv.handle) 809 snapPath := filepath.Join(c.MkDir(), "foo.snap") 810 err := ioutil.WriteFile(snapPath, snapBody, 0644) 811 c.Assert(err, check.IsNil) 812 813 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", snapPath}) 814 c.Assert(err, check.IsNil) 815 c.Assert(rest, check.DeepEquals, []string{}) 816 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 817 c.Check(s.Stderr(), check.Equals, "") 818 // ensure that the fake server api was actually hit 819 c.Check(s.srv.n, check.Equals, s.srv.total) 820 } 821 822 func (s *SnapOpSuite) TestInstallPathDevMode(c *check.C) { 823 s.srv.checker = func(r *http.Request) { 824 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 825 form := testForm(r, c) 826 defer form.RemoveAll() 827 828 c.Check(form.Value["action"], check.DeepEquals, []string{"install"}) 829 c.Check(form.Value["devmode"], check.DeepEquals, []string{"true"}) 830 c.Check(form.Value["snap-path"], check.NotNil) 831 c.Check(form.Value, check.HasLen, 3) 832 833 name, _, body := formFile(form, c) 834 c.Check(name, check.Equals, "snap") 835 c.Check(string(body), check.Equals, "snap-data") 836 } 837 838 snapBody := []byte("snap-data") 839 s.RedirectClientToTestServer(s.srv.handle) 840 snapPath := filepath.Join(c.MkDir(), "foo.snap") 841 err := ioutil.WriteFile(snapPath, snapBody, 0644) 842 c.Assert(err, check.IsNil) 843 844 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--devmode", snapPath}) 845 c.Assert(err, check.IsNil) 846 c.Assert(rest, check.DeepEquals, []string{}) 847 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 848 c.Check(s.Stderr(), check.Equals, "") 849 // ensure that the fake server api was actually hit 850 c.Check(s.srv.n, check.Equals, s.srv.total) 851 } 852 853 func (s *SnapOpSuite) TestInstallPathClassic(c *check.C) { 854 s.srv.checker = func(r *http.Request) { 855 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 856 form := testForm(r, c) 857 defer form.RemoveAll() 858 859 c.Check(form.Value["action"], check.DeepEquals, []string{"install"}) 860 c.Check(form.Value["classic"], check.DeepEquals, []string{"true"}) 861 c.Check(form.Value["snap-path"], check.NotNil) 862 c.Check(form.Value, check.HasLen, 3) 863 864 name, _, body := formFile(form, c) 865 c.Check(name, check.Equals, "snap") 866 c.Check(string(body), check.Equals, "snap-data") 867 868 s.srv.confinement = "classic" 869 } 870 871 snapBody := []byte("snap-data") 872 s.RedirectClientToTestServer(s.srv.handle) 873 snapPath := filepath.Join(c.MkDir(), "foo.snap") 874 err := ioutil.WriteFile(snapPath, snapBody, 0644) 875 c.Assert(err, check.IsNil) 876 877 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--classic", snapPath}) 878 c.Assert(err, check.IsNil) 879 c.Assert(rest, check.DeepEquals, []string{}) 880 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 881 c.Check(s.Stderr(), check.Equals, "") 882 // ensure that the fake server api was actually hit 883 c.Check(s.srv.n, check.Equals, s.srv.total) 884 } 885 886 func (s *SnapOpSuite) TestInstallPathDangerous(c *check.C) { 887 s.srv.checker = func(r *http.Request) { 888 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 889 form := testForm(r, c) 890 defer form.RemoveAll() 891 892 c.Check(form.Value["action"], check.DeepEquals, []string{"install"}) 893 c.Check(form.Value["dangerous"], check.DeepEquals, []string{"true"}) 894 c.Check(form.Value["snap-path"], check.NotNil) 895 c.Check(form.Value, check.HasLen, 3) 896 897 name, _, body := formFile(form, c) 898 c.Check(name, check.Equals, "snap") 899 c.Check(string(body), check.Equals, "snap-data") 900 } 901 902 snapBody := []byte("snap-data") 903 s.RedirectClientToTestServer(s.srv.handle) 904 snapPath := filepath.Join(c.MkDir(), "foo.snap") 905 err := ioutil.WriteFile(snapPath, snapBody, 0644) 906 c.Assert(err, check.IsNil) 907 908 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--dangerous", snapPath}) 909 c.Assert(err, check.IsNil) 910 c.Assert(rest, check.DeepEquals, []string{}) 911 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar installed`) 912 c.Check(s.Stderr(), check.Equals, "") 913 // ensure that the fake server api was actually hit 914 c.Check(s.srv.n, check.Equals, s.srv.total) 915 } 916 917 func (s *SnapOpSuite) TestInstallPathInstance(c *check.C) { 918 s.srv.checker = func(r *http.Request) { 919 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 920 921 form := testForm(r, c) 922 defer form.RemoveAll() 923 924 c.Check(form.Value["action"], check.DeepEquals, []string{"install"}) 925 c.Check(form.Value["name"], check.DeepEquals, []string{"foo_bar"}) 926 c.Check(form.Value["devmode"], check.IsNil) 927 c.Check(form.Value["snap-path"], check.NotNil) 928 c.Check(form.Value, check.HasLen, 3) 929 930 name, _, body := formFile(form, c) 931 c.Check(name, check.Equals, "snap") 932 c.Check(string(body), check.Equals, "snap-data") 933 } 934 935 snapBody := []byte("snap-data") 936 s.RedirectClientToTestServer(s.srv.handle) 937 // instance is named foo_bar 938 s.srv.snap = "foo_bar" 939 snapPath := filepath.Join(c.MkDir(), "foo.snap") 940 err := ioutil.WriteFile(snapPath, snapBody, 0644) 941 c.Assert(err, check.IsNil) 942 943 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", snapPath, "--name", "foo_bar"}) 944 c.Assert(rest, check.DeepEquals, []string{}) 945 c.Assert(err, check.IsNil) 946 c.Check(s.Stdout(), check.Matches, `(?sm).*foo_bar 1.0 from Bar installed`) 947 c.Check(s.Stderr(), check.Equals, "") 948 // ensure that the fake server api was actually hit 949 c.Check(s.srv.n, check.Equals, s.srv.total) 950 } 951 952 func (s *SnapSuite) TestInstallWithInstanceNoPath(c *check.C) { 953 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--name", "foo_bar", "some-snap"}) 954 c.Assert(err, check.ErrorMatches, "cannot use explicit name when installing from store") 955 } 956 957 func (s *SnapSuite) TestInstallManyWithInstance(c *check.C) { 958 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--name", "foo_bar", "some-snap-1", "some-snap-2"}) 959 c.Assert(err, check.ErrorMatches, "cannot use instance name when installing multiple snaps") 960 } 961 962 func (s *SnapOpSuite) TestRevertRunthrough(c *check.C) { 963 s.srv.total = 4 964 s.srv.channel = "potato" 965 s.srv.checker = func(r *http.Request) { 966 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 967 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 968 "action": "revert", 969 }) 970 } 971 972 s.RedirectClientToTestServer(s.srv.handle) 973 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"revert", "foo"}) 974 c.Assert(err, check.IsNil) 975 c.Assert(rest, check.DeepEquals, []string{}) 976 // tracking channel is "" in the test server 977 c.Check(s.Stdout(), check.Equals, `foo reverted to 1.0 978 `) 979 c.Check(s.Stderr(), check.Equals, "") 980 // ensure that the fake server api was actually hit 981 c.Check(s.srv.n, check.Equals, s.srv.total) 982 } 983 984 func (s *SnapOpSuite) runRevertTest(c *check.C, opts *client.SnapOptions) { 985 modes := []struct { 986 enabled bool 987 name string 988 }{ 989 {opts.DevMode, "devmode"}, 990 {opts.JailMode, "jailmode"}, 991 {opts.Classic, "classic"}, 992 } 993 994 s.srv.checker = func(r *http.Request) { 995 996 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 997 d := DecodedRequestBody(c, r) 998 999 n := 1 1000 c.Check(d["action"], check.Equals, "revert") 1001 1002 for _, mode := range modes { 1003 if mode.enabled { 1004 n++ 1005 c.Check(d[mode.name], check.Equals, true) 1006 } else { 1007 c.Check(d[mode.name], check.IsNil) 1008 } 1009 } 1010 c.Check(d, check.HasLen, n) 1011 } 1012 1013 s.RedirectClientToTestServer(s.srv.handle) 1014 1015 cmd := []string{"revert", "foo"} 1016 for _, mode := range modes { 1017 if mode.enabled { 1018 cmd = append(cmd, "--"+mode.name) 1019 } 1020 } 1021 1022 rest, err := snap.Parser(snap.Client()).ParseArgs(cmd) 1023 c.Assert(err, check.IsNil) 1024 c.Assert(rest, check.DeepEquals, []string{}) 1025 c.Check(s.Stdout(), check.Equals, "foo reverted to 1.0\n") 1026 c.Check(s.Stderr(), check.Equals, "") 1027 // ensure that the fake server api was actually hit 1028 c.Check(s.srv.n, check.Equals, s.srv.total) 1029 } 1030 1031 func (s *SnapOpSuite) TestRevertNoMode(c *check.C) { 1032 s.runRevertTest(c, &client.SnapOptions{}) 1033 } 1034 1035 func (s *SnapOpSuite) TestRevertDevMode(c *check.C) { 1036 s.runRevertTest(c, &client.SnapOptions{DevMode: true}) 1037 } 1038 1039 func (s *SnapOpSuite) TestRevertJailMode(c *check.C) { 1040 s.runRevertTest(c, &client.SnapOptions{JailMode: true}) 1041 } 1042 1043 func (s *SnapOpSuite) TestRevertClassic(c *check.C) { 1044 s.runRevertTest(c, &client.SnapOptions{Classic: true}) 1045 } 1046 1047 func (s *SnapOpSuite) TestRevertMissingName(c *check.C) { 1048 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"revert"}) 1049 c.Assert(err, check.NotNil) 1050 c.Assert(err, check.ErrorMatches, "the required argument `<snap>` was not provided") 1051 } 1052 1053 func (s *SnapSuite) TestRefreshListLessOptions(c *check.C) { 1054 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1055 c.Fatal("expected to get 0 requests") 1056 }) 1057 1058 for _, flag := range []string{"--beta", "--channel=potato", "--classic"} { 1059 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--list", flag}) 1060 c.Assert(err, check.ErrorMatches, "--list does not accept additional arguments") 1061 1062 _, err = snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--list", flag, "some-snap"}) 1063 c.Assert(err, check.ErrorMatches, "--list does not accept additional arguments") 1064 } 1065 } 1066 1067 func (s *SnapSuite) TestRefreshList(c *check.C) { 1068 n := 0 1069 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1070 switch n { 1071 case 0: 1072 c.Check(r.Method, check.Equals, "GET") 1073 c.Check(r.URL.Path, check.Equals, "/v2/find") 1074 c.Check(r.URL.Query().Get("select"), check.Equals, "refresh") 1075 fmt.Fprintln(w, `{"type": "sync", "result": [{"name": "foo", "status": "active", "version": "4.2update1", "developer": "bar", "publisher": {"id": "bar-id", "username": "bar", "display-name": "Bar", "validation": "unproven"}, "revision":17,"summary":"some summary"}]}`) 1076 default: 1077 c.Fatalf("expected to get 1 requests, now on %d", n+1) 1078 } 1079 1080 n++ 1081 }) 1082 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--list"}) 1083 c.Assert(err, check.IsNil) 1084 c.Assert(rest, check.DeepEquals, []string{}) 1085 c.Check(s.Stdout(), check.Matches, `Name +Version +Rev +Publisher +Notes 1086 foo +4.2update1 +17 +bar +-.* 1087 `) 1088 c.Check(s.Stderr(), check.Equals, "") 1089 // ensure that the fake server api was actually hit 1090 c.Check(n, check.Equals, 1) 1091 } 1092 1093 func (s *SnapSuite) TestRefreshLegacyTime(c *check.C) { 1094 n := 0 1095 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1096 switch n { 1097 case 0: 1098 c.Check(r.Method, check.Equals, "GET") 1099 c.Check(r.URL.Path, check.Equals, "/v2/system-info") 1100 fmt.Fprintln(w, `{"type": "sync", "status-code": 200, "result": {"refresh": {"schedule": "00:00-04:59/5:00-10:59/11:00-16:59/17:00-23:59", "last": "2017-04-25T17:35:00+02:00", "next": "2017-04-26T00:58:00+02:00"}}}`) 1101 default: 1102 c.Fatalf("expected to get 1 requests, now on %d", n+1) 1103 } 1104 1105 n++ 1106 }) 1107 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--time", "--abs-time"}) 1108 c.Assert(err, check.IsNil) 1109 c.Assert(rest, check.DeepEquals, []string{}) 1110 c.Check(s.Stdout(), check.Equals, `schedule: 00:00-04:59/5:00-10:59/11:00-16:59/17:00-23:59 1111 last: 2017-04-25T17:35:00+02:00 1112 next: 2017-04-26T00:58:00+02:00 1113 `) 1114 c.Check(s.Stderr(), check.Equals, "") 1115 // ensure that the fake server api was actually hit 1116 c.Check(n, check.Equals, 1) 1117 } 1118 1119 func (s *SnapSuite) TestRefreshTimer(c *check.C) { 1120 n := 0 1121 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1122 switch n { 1123 case 0: 1124 c.Check(r.Method, check.Equals, "GET") 1125 c.Check(r.URL.Path, check.Equals, "/v2/system-info") 1126 fmt.Fprintln(w, `{"type": "sync", "status-code": 200, "result": {"refresh": {"timer": "0:00-24:00/4", "last": "2017-04-25T17:35:00+02:00", "next": "2017-04-26T00:58:00+02:00"}}}`) 1127 default: 1128 c.Fatalf("expected to get 1 requests, now on %d", n+1) 1129 } 1130 1131 n++ 1132 }) 1133 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--time", "--abs-time"}) 1134 c.Assert(err, check.IsNil) 1135 c.Assert(rest, check.DeepEquals, []string{}) 1136 c.Check(s.Stdout(), check.Equals, `timer: 0:00-24:00/4 1137 last: 2017-04-25T17:35:00+02:00 1138 next: 2017-04-26T00:58:00+02:00 1139 `) 1140 c.Check(s.Stderr(), check.Equals, "") 1141 // ensure that the fake server api was actually hit 1142 c.Check(n, check.Equals, 1) 1143 } 1144 1145 func (s *SnapSuite) TestRefreshHold(c *check.C) { 1146 n := 0 1147 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1148 switch n { 1149 case 0: 1150 c.Check(r.Method, check.Equals, "GET") 1151 c.Check(r.URL.Path, check.Equals, "/v2/system-info") 1152 fmt.Fprintln(w, `{"type": "sync", "status-code": 200, "result": {"refresh": {"timer": "0:00-24:00/4", "last": "2017-04-25T17:35:00+02:00", "next": "2017-04-26T00:58:00+02:00", "hold": "2017-04-28T00:00:00+02:00"}}}`) 1153 default: 1154 c.Fatalf("expected to get 1 requests, now on %d", n+1) 1155 } 1156 1157 n++ 1158 }) 1159 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--time", "--abs-time"}) 1160 c.Assert(err, check.IsNil) 1161 c.Assert(rest, check.DeepEquals, []string{}) 1162 c.Check(s.Stdout(), check.Equals, `timer: 0:00-24:00/4 1163 last: 2017-04-25T17:35:00+02:00 1164 hold: 2017-04-28T00:00:00+02:00 1165 next: 2017-04-26T00:58:00+02:00 (but held) 1166 `) 1167 c.Check(s.Stderr(), check.Equals, "") 1168 // ensure that the fake server api was actually hit 1169 c.Check(n, check.Equals, 1) 1170 } 1171 1172 func (s *SnapSuite) TestRefreshNoTimerNoSchedule(c *check.C) { 1173 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1174 c.Check(r.Method, check.Equals, "GET") 1175 c.Check(r.URL.Path, check.Equals, "/v2/system-info") 1176 fmt.Fprintln(w, `{"type": "sync", "status-code": 200, "result": {"refresh": {"last": "2017-04-25T17:35:00+0200", "next": "2017-04-26T00:58:00+0200"}}}`) 1177 }) 1178 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--time"}) 1179 c.Assert(err, check.ErrorMatches, `internal error: both refresh.timer and refresh.schedule are empty`) 1180 } 1181 1182 func (s *SnapOpSuite) TestRefreshOne(c *check.C) { 1183 s.RedirectClientToTestServer(s.srv.handle) 1184 s.srv.checker = func(r *http.Request) { 1185 c.Check(r.Method, check.Equals, "POST") 1186 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1187 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1188 "action": "refresh", 1189 }) 1190 } 1191 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "foo"}) 1192 c.Assert(err, check.IsNil) 1193 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar refreshed`) 1194 1195 } 1196 1197 func (s *SnapOpSuite) TestRefreshOneSwitchChannel(c *check.C) { 1198 s.RedirectClientToTestServer(s.srv.handle) 1199 s.srv.checker = func(r *http.Request) { 1200 c.Check(r.Method, check.Equals, "POST") 1201 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1202 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1203 "action": "refresh", 1204 "channel": "beta", 1205 }) 1206 s.srv.channel = "beta" 1207 } 1208 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--beta", "foo"}) 1209 c.Assert(err, check.IsNil) 1210 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(beta\) 1.0 from Bar refreshed`) 1211 } 1212 1213 func (s *SnapOpSuite) TestRefreshOneSwitchCohort(c *check.C) { 1214 s.RedirectClientToTestServer(s.srv.handle) 1215 s.srv.checker = func(r *http.Request) { 1216 c.Check(r.Method, check.Equals, "POST") 1217 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1218 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1219 "action": "refresh", 1220 "cohort-key": "what", 1221 }) 1222 } 1223 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--cohort=what", "foo"}) 1224 c.Assert(err, check.IsNil) 1225 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar refreshed`) 1226 } 1227 1228 func (s *SnapOpSuite) TestRefreshOneLeaveCohort(c *check.C) { 1229 s.RedirectClientToTestServer(s.srv.handle) 1230 s.srv.checker = func(r *http.Request) { 1231 c.Check(r.Method, check.Equals, "POST") 1232 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1233 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1234 "action": "refresh", 1235 "leave-cohort": true, 1236 }) 1237 } 1238 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--leave-cohort", "foo"}) 1239 c.Assert(err, check.IsNil) 1240 c.Check(s.Stdout(), check.Matches, `(?sm).*foo 1.0 from Bar refreshed`) 1241 } 1242 1243 func (s *SnapOpSuite) TestRefreshOneWithPinnedTrack(c *check.C) { 1244 s.RedirectClientToTestServer(s.srv.handle) 1245 s.srv.checker = func(r *http.Request) { 1246 c.Check(r.Method, check.Equals, "POST") 1247 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1248 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1249 "action": "refresh", 1250 "channel": "stable", 1251 }) 1252 s.srv.channel = "18/stable" 1253 s.srv.trackingChannel = "18/stable" 1254 } 1255 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--stable", "foo"}) 1256 c.Assert(err, check.IsNil) 1257 c.Check(s.Stdout(), check.Equals, "foo (18/stable) 1.0 from Bar refreshed\n") 1258 } 1259 1260 func (s *SnapOpSuite) TestRefreshOneClassic(c *check.C) { 1261 s.RedirectClientToTestServer(s.srv.handle) 1262 s.srv.checker = func(r *http.Request) { 1263 c.Check(r.Method, check.Equals, "POST") 1264 c.Check(r.URL.Path, check.Equals, "/v2/snaps/one") 1265 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1266 "action": "refresh", 1267 "classic": true, 1268 }) 1269 } 1270 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--classic", "one"}) 1271 c.Assert(err, check.IsNil) 1272 } 1273 1274 func (s *SnapOpSuite) TestRefreshOneDevmode(c *check.C) { 1275 s.RedirectClientToTestServer(s.srv.handle) 1276 s.srv.checker = func(r *http.Request) { 1277 c.Check(r.Method, check.Equals, "POST") 1278 c.Check(r.URL.Path, check.Equals, "/v2/snaps/one") 1279 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1280 "action": "refresh", 1281 "devmode": true, 1282 }) 1283 } 1284 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--devmode", "one"}) 1285 c.Assert(err, check.IsNil) 1286 } 1287 1288 func (s *SnapOpSuite) TestRefreshOneJailmode(c *check.C) { 1289 s.RedirectClientToTestServer(s.srv.handle) 1290 s.srv.checker = func(r *http.Request) { 1291 c.Check(r.Method, check.Equals, "POST") 1292 c.Check(r.URL.Path, check.Equals, "/v2/snaps/one") 1293 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1294 "action": "refresh", 1295 "jailmode": true, 1296 }) 1297 } 1298 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--jailmode", "one"}) 1299 c.Assert(err, check.IsNil) 1300 } 1301 1302 func (s *SnapOpSuite) TestRefreshOneIgnoreValidation(c *check.C) { 1303 s.RedirectClientToTestServer(s.srv.handle) 1304 s.srv.checker = func(r *http.Request) { 1305 c.Check(r.Method, check.Equals, "POST") 1306 c.Check(r.URL.Path, check.Equals, "/v2/snaps/one") 1307 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1308 "action": "refresh", 1309 "ignore-validation": true, 1310 }) 1311 } 1312 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--ignore-validation", "one"}) 1313 c.Assert(err, check.IsNil) 1314 } 1315 1316 func (s *SnapOpSuite) TestRefreshOneRebooting(c *check.C) { 1317 s.RedirectClientToTestServer(s.srv.handle) 1318 s.srv.checker = func(r *http.Request) { 1319 c.Check(r.Method, check.Equals, "POST") 1320 c.Check(r.URL.Path, check.Equals, "/v2/snaps/core") 1321 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1322 "action": "refresh", 1323 }) 1324 } 1325 s.srv.rebooting = true 1326 1327 restore := mockArgs("snap", "refresh", "core") 1328 defer restore() 1329 1330 err := snap.RunMain() 1331 c.Check(err, check.IsNil) 1332 c.Check(s.Stderr(), check.Equals, "snapd is about to reboot the system\n") 1333 1334 } 1335 1336 func (s *SnapOpSuite) TestRefreshOneChanDeprecated(c *check.C) { 1337 var in, out string 1338 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1339 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{"action": "refresh", "channel": out}) 1340 fmt.Fprintln(w, `{"type": "error", "result": {"message": "snap not found", "value": "foo", "kind": "snap-not-found"}, "status-code": 404}`) 1341 }) 1342 1343 for in, out = range map[string]string{ 1344 "/foo": "foo/stable", 1345 "/stable": "latest/stable", 1346 "///foo/stable//": "foo/stable", 1347 } { 1348 s.stderr.Reset() 1349 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--channel=" + in, "one"}) 1350 c.Assert(err, check.ErrorMatches, "snap \"one\" not found") 1351 c.Check(s.Stderr(), testutil.EqualsWrapped, `Warning: Specifying a channel "`+in+`" is relying on undefined behaviour. Interpreting it as "`+out+`" for now, but this will be an error later.`) 1352 } 1353 } 1354 1355 func (s *SnapOpSuite) TestRefreshOneModeErr(c *check.C) { 1356 s.RedirectClientToTestServer(nil) 1357 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--jailmode", "--devmode", "one"}) 1358 c.Assert(err, check.ErrorMatches, `cannot use devmode and jailmode flags together`) 1359 } 1360 1361 func (s *SnapOpSuite) TestRefreshOneChanErr(c *check.C) { 1362 s.RedirectClientToTestServer(nil) 1363 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--beta", "--channel=foo", "one"}) 1364 c.Assert(err, check.ErrorMatches, `Please specify a single channel`) 1365 } 1366 1367 func (s *SnapOpSuite) TestRefreshAllChannel(c *check.C) { 1368 s.RedirectClientToTestServer(nil) 1369 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--beta"}) 1370 c.Assert(err, check.ErrorMatches, `a single snap name is needed to specify mode or channel flags`) 1371 } 1372 1373 func (s *SnapOpSuite) TestRefreshManyChannel(c *check.C) { 1374 s.RedirectClientToTestServer(nil) 1375 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--beta", "one", "two"}) 1376 c.Assert(err, check.ErrorMatches, `a single snap name is needed to specify mode or channel flags`) 1377 } 1378 1379 func (s *SnapOpSuite) TestRefreshManyIgnoreValidation(c *check.C) { 1380 s.RedirectClientToTestServer(nil) 1381 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--ignore-validation", "one", "two"}) 1382 c.Assert(err, check.ErrorMatches, `a single snap name must be specified when ignoring validation`) 1383 } 1384 1385 func (s *SnapOpSuite) TestRefreshAllModeFlags(c *check.C) { 1386 s.RedirectClientToTestServer(nil) 1387 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--devmode"}) 1388 c.Assert(err, check.ErrorMatches, `a single snap name is needed to specify mode or channel flags`) 1389 } 1390 1391 func (s *SnapOpSuite) TestRefreshOneAmend(c *check.C) { 1392 s.RedirectClientToTestServer(s.srv.handle) 1393 s.srv.checker = func(r *http.Request) { 1394 c.Check(r.Method, check.Equals, "POST") 1395 c.Check(r.URL.Path, check.Equals, "/v2/snaps/one") 1396 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1397 "action": "refresh", 1398 "amend": true, 1399 }) 1400 } 1401 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--amend", "one"}) 1402 c.Assert(err, check.IsNil) 1403 } 1404 1405 func (s *SnapOpSuite) runTryTest(c *check.C, opts *client.SnapOptions) { 1406 // pass relative path to cmd 1407 tryDir := "some-dir" 1408 1409 modes := []struct { 1410 enabled bool 1411 name string 1412 }{ 1413 {opts.DevMode, "devmode"}, 1414 {opts.JailMode, "jailmode"}, 1415 {opts.Classic, "classic"}, 1416 } 1417 1418 s.srv.checker = func(r *http.Request) { 1419 // ensure the client always sends the absolute path 1420 fullTryDir, err := filepath.Abs(tryDir) 1421 c.Assert(err, check.IsNil) 1422 1423 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 1424 form := testForm(r, c) 1425 defer form.RemoveAll() 1426 1427 c.Assert(form.Value["action"], check.HasLen, 1) 1428 c.Assert(form.Value["snap-path"], check.HasLen, 1) 1429 c.Check(form.File, check.HasLen, 0) 1430 c.Check(form.Value["action"][0], check.Equals, "try") 1431 c.Check(form.Value["snap-path"][0], check.Matches, regexp.QuoteMeta(fullTryDir)) 1432 1433 for _, mode := range modes { 1434 if mode.enabled { 1435 c.Assert(form.Value[mode.name], check.HasLen, 1) 1436 c.Check(form.Value[mode.name][0], check.Equals, "true") 1437 } else { 1438 c.Check(form.Value[mode.name], check.IsNil) 1439 } 1440 } 1441 } 1442 1443 s.RedirectClientToTestServer(s.srv.handle) 1444 1445 cmd := []string{"try", tryDir} 1446 for _, mode := range modes { 1447 if mode.enabled { 1448 cmd = append(cmd, "--"+mode.name) 1449 } 1450 } 1451 1452 rest, err := snap.Parser(snap.Client()).ParseArgs(cmd) 1453 c.Assert(err, check.IsNil) 1454 c.Assert(rest, check.DeepEquals, []string{}) 1455 c.Check(s.Stdout(), check.Matches, fmt.Sprintf(`(?sm).*foo 1.0 mounted from .*%s`, tryDir)) 1456 c.Check(s.Stderr(), check.Equals, "") 1457 // ensure that the fake server api was actually hit 1458 c.Check(s.srv.n, check.Equals, s.srv.total) 1459 } 1460 1461 func (s *SnapOpSuite) TestTryNoMode(c *check.C) { 1462 s.runTryTest(c, &client.SnapOptions{}) 1463 } 1464 1465 func (s *SnapOpSuite) TestTryDevMode(c *check.C) { 1466 s.runTryTest(c, &client.SnapOptions{DevMode: true}) 1467 } 1468 1469 func (s *SnapOpSuite) TestTryJailMode(c *check.C) { 1470 s.runTryTest(c, &client.SnapOptions{JailMode: true}) 1471 } 1472 1473 func (s *SnapOpSuite) TestTryClassic(c *check.C) { 1474 s.runTryTest(c, &client.SnapOptions{Classic: true}) 1475 } 1476 1477 func (s *SnapOpSuite) TestTryNoSnapDirErrors(c *check.C) { 1478 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1479 c.Check(r.Method, check.Equals, "POST") 1480 w.WriteHeader(202) 1481 fmt.Fprintln(w, ` 1482 { 1483 "type": "error", 1484 "result": { 1485 "message":"error from server", 1486 "kind":"snap-not-a-snap" 1487 }, 1488 "status-code": 400 1489 }`) 1490 }) 1491 1492 cmd := []string{"try", "/"} 1493 _, err := snap.Parser(snap.Client()).ParseArgs(cmd) 1494 c.Assert(err, testutil.EqualsWrapped, ` 1495 "/" does not contain an unpacked snap. 1496 1497 Try 'snapcraft prime' in your project directory, then 'snap try' again.`) 1498 } 1499 1500 func (s *SnapOpSuite) TestTryMissingOpt(c *check.C) { 1501 oldArgs := os.Args 1502 defer func() { 1503 os.Args = oldArgs 1504 }() 1505 os.Args = []string{"snap", "try", "./"} 1506 var kind string 1507 1508 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1509 c.Check(r.Method, check.Equals, "POST", check.Commentf("%q", kind)) 1510 w.WriteHeader(400) 1511 fmt.Fprintf(w, ` 1512 { 1513 "type": "error", 1514 "result": { 1515 "message":"error from server", 1516 "value": "some-snap", 1517 "kind": %q 1518 }, 1519 "status-code": 400 1520 }`, kind) 1521 }) 1522 1523 type table struct { 1524 kind, expected string 1525 } 1526 1527 tests := []table{ 1528 {"snap-needs-classic", "published using classic confinement"}, 1529 {"snap-needs-devmode", "only meant for development"}, 1530 } 1531 1532 for _, test := range tests { 1533 kind = test.kind 1534 c.Check(snap.RunMain(), testutil.ContainsWrapped, test.expected, check.Commentf("%q", kind)) 1535 } 1536 } 1537 1538 func (s *SnapOpSuite) TestInstallConfinedAsClassic(c *check.C) { 1539 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1540 c.Check(r.Method, check.Equals, "POST") 1541 w.WriteHeader(400) 1542 fmt.Fprintf(w, `{ 1543 "type": "error", 1544 "result": { 1545 "message":"error from server", 1546 "value": "some-snap", 1547 "kind": "snap-not-classic" 1548 }, 1549 "status-code": 400 1550 }`) 1551 }) 1552 1553 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--classic", "some-snap"}) 1554 c.Assert(err, check.ErrorMatches, `snap "some-snap" is not compatible with --classic`) 1555 } 1556 1557 func (s *SnapSuite) TestInstallChannelDuplicationError(c *check.C) { 1558 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--edge", "--beta", "some-snap"}) 1559 c.Assert(err, check.ErrorMatches, "Please specify a single channel") 1560 } 1561 1562 func (s *SnapSuite) TestRefreshChannelDuplicationError(c *check.C) { 1563 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "--edge", "--beta", "some-snap"}) 1564 c.Assert(err, check.ErrorMatches, "Please specify a single channel") 1565 } 1566 1567 func (s *SnapOpSuite) TestInstallFromChannel(c *check.C) { 1568 s.srv.checker = func(r *http.Request) { 1569 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1570 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1571 "action": "install", 1572 "channel": "edge", 1573 }) 1574 s.srv.channel = "edge" 1575 } 1576 1577 s.RedirectClientToTestServer(s.srv.handle) 1578 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--edge", "foo"}) 1579 c.Assert(err, check.IsNil) 1580 c.Assert(rest, check.DeepEquals, []string{}) 1581 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(edge\) 1.0 from Bar installed`) 1582 c.Check(s.Stderr(), check.Equals, "") 1583 // ensure that the fake server api was actually hit 1584 c.Check(s.srv.n, check.Equals, s.srv.total) 1585 } 1586 1587 func (s *SnapOpSuite) TestEnable(c *check.C) { 1588 s.srv.total = 3 1589 s.srv.checker = func(r *http.Request) { 1590 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1591 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1592 "action": "enable", 1593 }) 1594 } 1595 1596 s.RedirectClientToTestServer(s.srv.handle) 1597 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"enable", "foo"}) 1598 c.Assert(err, check.IsNil) 1599 c.Assert(rest, check.DeepEquals, []string{}) 1600 c.Check(s.Stdout(), check.Matches, `(?sm).*foo enabled`) 1601 c.Check(s.Stderr(), check.Equals, "") 1602 // ensure that the fake server api was actually hit 1603 c.Check(s.srv.n, check.Equals, s.srv.total) 1604 } 1605 1606 func (s *SnapOpSuite) TestDisable(c *check.C) { 1607 s.srv.total = 3 1608 s.srv.checker = func(r *http.Request) { 1609 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1610 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1611 "action": "disable", 1612 }) 1613 } 1614 1615 s.RedirectClientToTestServer(s.srv.handle) 1616 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"disable", "foo"}) 1617 c.Assert(err, check.IsNil) 1618 c.Assert(rest, check.DeepEquals, []string{}) 1619 c.Check(s.Stdout(), check.Matches, `(?sm).*foo disabled`) 1620 c.Check(s.Stderr(), check.Equals, "") 1621 // ensure that the fake server api was actually hit 1622 c.Check(s.srv.n, check.Equals, s.srv.total) 1623 } 1624 1625 func (s *SnapOpSuite) TestRemove(c *check.C) { 1626 s.srv.total = 3 1627 s.srv.checker = func(r *http.Request) { 1628 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1629 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1630 "action": "remove", 1631 }) 1632 } 1633 1634 s.RedirectClientToTestServer(s.srv.handle) 1635 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"remove", "foo"}) 1636 c.Assert(err, check.IsNil) 1637 c.Assert(rest, check.DeepEquals, []string{}) 1638 c.Check(s.Stdout(), check.Matches, `(?sm).*foo removed`) 1639 c.Check(s.Stderr(), check.Equals, "") 1640 // ensure that the fake server api was actually hit 1641 c.Check(s.srv.n, check.Equals, s.srv.total) 1642 } 1643 1644 func (s *SnapOpSuite) TestRemoveWithPurge(c *check.C) { 1645 s.srv.total = 3 1646 s.srv.checker = func(r *http.Request) { 1647 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1648 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1649 "action": "remove", 1650 "purge": true, 1651 }) 1652 } 1653 1654 s.RedirectClientToTestServer(s.srv.handle) 1655 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"remove", "--purge", "foo"}) 1656 c.Assert(err, check.IsNil) 1657 c.Assert(rest, check.DeepEquals, []string{}) 1658 c.Check(s.Stdout(), check.Matches, `(?sm).*foo removed`) 1659 c.Check(s.Stderr(), check.Equals, "") 1660 // ensure that the fake server api was actually hit 1661 c.Check(s.srv.n, check.Equals, s.srv.total) 1662 } 1663 1664 func (s *SnapOpSuite) TestRemoveInsufficientDiskSpace(c *check.C) { 1665 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1666 fmt.Fprintln(w, `{ 1667 "type": "error", 1668 "result": { 1669 "message": "disk space error", 1670 "kind": "insufficient-disk-space", 1671 "value": { 1672 "snap-names": ["foo", "bar"], 1673 "change-kind": "remove" 1674 }, 1675 "status-code": 507 1676 }}`) 1677 }) 1678 1679 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"remove", "foo"}) 1680 c.Check(err, check.ErrorMatches, `(?sm)cannot remove "foo", "bar" due to low disk space for automatic snapshot,.*use --purge to avoid creating a snapshot`) 1681 c.Check(s.Stdout(), check.Equals, "") 1682 c.Check(s.Stderr(), check.Equals, "") 1683 } 1684 1685 func (s *SnapOpSuite) TestInstallInsufficientDiskSpace(c *check.C) { 1686 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1687 fmt.Fprintln(w, `{ 1688 "type": "error", 1689 "result": { 1690 "message": "disk space error", 1691 "kind": "insufficient-disk-space", 1692 "value": { 1693 "snap-names": ["foo"], 1694 "change-kind": "install" 1695 }, 1696 "status-code": 507 1697 }}`) 1698 }) 1699 1700 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "foo"}) 1701 c.Check(err, check.ErrorMatches, `cannot install "foo" due to low disk space`) 1702 c.Check(s.Stdout(), check.Equals, "") 1703 c.Check(s.Stderr(), check.Equals, "") 1704 } 1705 1706 func (s *SnapOpSuite) TestRefreshInsufficientDiskSpace(c *check.C) { 1707 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1708 fmt.Fprintln(w, `{ 1709 "type": "error", 1710 "result": { 1711 "message": "disk space error", 1712 "kind": "insufficient-disk-space", 1713 "value": { 1714 "snap-names": ["foo"], 1715 "change-kind": "refresh" 1716 }, 1717 "status-code": 507 1718 }}`) 1719 }) 1720 1721 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"refresh", "foo"}) 1722 c.Check(err, check.ErrorMatches, `cannot refresh "foo" due to low disk space`) 1723 c.Check(s.Stdout(), check.Equals, "") 1724 c.Check(s.Stderr(), check.Equals, "") 1725 } 1726 1727 func (s *SnapOpSuite) TestRemoveRevision(c *check.C) { 1728 s.srv.total = 3 1729 s.srv.checker = func(r *http.Request) { 1730 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1731 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1732 "action": "remove", 1733 "revision": "17", 1734 }) 1735 } 1736 1737 s.RedirectClientToTestServer(s.srv.handle) 1738 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"remove", "--revision=17", "foo"}) 1739 c.Assert(err, check.IsNil) 1740 c.Assert(rest, check.DeepEquals, []string{}) 1741 c.Check(s.Stdout(), check.Matches, `(?sm).*foo \(revision 17\) removed`) 1742 c.Check(s.Stderr(), check.Equals, "") 1743 // ensure that the fake server api was actually hit 1744 c.Check(s.srv.n, check.Equals, s.srv.total) 1745 } 1746 1747 func (s *SnapOpSuite) TestRemoveManyOptions(c *check.C) { 1748 s.RedirectClientToTestServer(nil) 1749 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"remove", "--revision=17", "one", "two"}) 1750 c.Assert(err, check.ErrorMatches, `a single snap name is needed to specify options`) 1751 _, err = snap.Parser(snap.Client()).ParseArgs([]string{"remove", "--purge", "one", "two"}) 1752 c.Assert(err, check.ErrorMatches, `a single snap name is needed to specify options`) 1753 } 1754 1755 func (s *SnapOpSuite) TestRemoveMany(c *check.C) { 1756 total := 3 1757 n := 0 1758 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1759 switch n { 1760 case 0: 1761 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 1762 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1763 "action": "remove", 1764 "snaps": []interface{}{"one", "two"}, 1765 }) 1766 1767 c.Check(r.Method, check.Equals, "POST") 1768 w.WriteHeader(202) 1769 fmt.Fprintln(w, `{"type":"async", "change": "42", "status-code": 202}`) 1770 case 1: 1771 c.Check(r.Method, check.Equals, "GET") 1772 c.Check(r.URL.Path, check.Equals, "/v2/changes/42") 1773 fmt.Fprintln(w, `{"type": "sync", "result": {"status": "Doing"}}`) 1774 case 2: 1775 c.Check(r.Method, check.Equals, "GET") 1776 c.Check(r.URL.Path, check.Equals, "/v2/changes/42") 1777 fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done", "data": {"snap-names": ["one","two"]}}}`) 1778 default: 1779 c.Fatalf("expected to get %d requests, now on %d", total, n+1) 1780 } 1781 1782 n++ 1783 }) 1784 1785 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"remove", "one", "two"}) 1786 c.Assert(err, check.IsNil) 1787 c.Assert(rest, check.DeepEquals, []string{}) 1788 c.Check(s.Stdout(), check.Matches, `(?sm).*one removed`) 1789 c.Check(s.Stdout(), check.Matches, `(?sm).*two removed`) 1790 c.Check(s.Stderr(), check.Equals, "") 1791 // ensure that the fake server api was actually hit 1792 c.Check(n, check.Equals, total) 1793 } 1794 1795 func (s *SnapOpSuite) TestInstallManyChannel(c *check.C) { 1796 s.RedirectClientToTestServer(nil) 1797 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "--beta", "one", "two"}) 1798 c.Assert(err, check.ErrorMatches, `a single snap name is needed to specify mode or channel flags`) 1799 } 1800 1801 func (s *SnapOpSuite) TestInstallManyMixFileAndStore(c *check.C) { 1802 s.RedirectClientToTestServer(nil) 1803 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "store-snap", "./local.snap"}) 1804 c.Assert(err, check.ErrorMatches, `only one snap file can be installed at a time`) 1805 } 1806 1807 func (s *SnapOpSuite) TestInstallMany(c *check.C) { 1808 total := 4 1809 n := 0 1810 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1811 switch n { 1812 case 0: 1813 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 1814 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1815 "action": "install", 1816 "snaps": []interface{}{"one", "two"}, 1817 }) 1818 1819 c.Check(r.Method, check.Equals, "POST") 1820 w.WriteHeader(202) 1821 fmt.Fprintln(w, `{"type":"async", "change": "42", "status-code": 202}`) 1822 case 1: 1823 c.Check(r.Method, check.Equals, "GET") 1824 c.Check(r.URL.Path, check.Equals, "/v2/changes/42") 1825 fmt.Fprintln(w, `{"type": "sync", "result": {"status": "Doing"}}`) 1826 case 2: 1827 c.Check(r.Method, check.Equals, "GET") 1828 c.Check(r.URL.Path, check.Equals, "/v2/changes/42") 1829 fmt.Fprintln(w, `{"type": "sync", "result": {"ready": true, "status": "Done", "data": {"snap-names": ["one","two"]}}}`) 1830 case 3: 1831 c.Check(r.Method, check.Equals, "GET") 1832 c.Check(r.URL.Path, check.Equals, "/v2/snaps") 1833 fmt.Fprintf(w, `{"type": "sync", "result": [{"name": "one", "status": "active", "version": "1.0", "developer": "bar", "publisher": {"id": "bar-id", "username": "bar", "display-name": "Bar", "validation": "unproven"}, "revision":42, "channel":"stable"},{"name": "two", "status": "active", "version": "2.0", "developer": "baz", "publisher": {"id": "baz-id", "username": "baz", "display-name": "Baz", "validation": "unproven"}, "revision":42, "channel":"edge"}]}\n`) 1834 1835 default: 1836 c.Fatalf("expected to get %d requests, now on %d", total, n+1) 1837 } 1838 1839 n++ 1840 }) 1841 1842 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"install", "one", "two"}) 1843 c.Assert(err, check.IsNil) 1844 c.Assert(rest, check.DeepEquals, []string{}) 1845 // note that (stable) is omitted 1846 c.Check(s.Stdout(), check.Matches, `(?sm).*one 1.0 from Bar installed`) 1847 c.Check(s.Stdout(), check.Matches, `(?sm).*two \(edge\) 2.0 from Baz installed`) 1848 c.Check(s.Stderr(), check.Equals, "") 1849 // ensure that the fake server api was actually hit 1850 c.Check(n, check.Equals, total) 1851 } 1852 1853 func (s *SnapOpSuite) TestInstallZeroEmpty(c *check.C) { 1854 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"install"}) 1855 c.Assert(err, check.ErrorMatches, "cannot install zero snaps") 1856 _, err = snap.Parser(snap.Client()).ParseArgs([]string{"install", ""}) 1857 c.Assert(err, check.ErrorMatches, "cannot install snap with empty name") 1858 _, err = snap.Parser(snap.Client()).ParseArgs([]string{"install", "", "bar"}) 1859 c.Assert(err, check.ErrorMatches, "cannot install snap with empty name") 1860 } 1861 1862 func (s *SnapOpSuite) TestNoWait(c *check.C) { 1863 s.srv.checker = func(r *http.Request) {} 1864 1865 cmds := [][]string{ 1866 {"remove", "--no-wait", "foo"}, 1867 {"remove", "--no-wait", "foo", "bar"}, 1868 {"install", "--no-wait", "foo"}, 1869 {"install", "--no-wait", "foo", "bar"}, 1870 {"revert", "--no-wait", "foo"}, 1871 {"refresh", "--no-wait", "foo"}, 1872 {"refresh", "--no-wait", "foo", "bar"}, 1873 {"refresh", "--no-wait"}, 1874 {"enable", "--no-wait", "foo"}, 1875 {"disable", "--no-wait", "foo"}, 1876 {"try", "--no-wait", "."}, 1877 {"switch", "--no-wait", "--channel=foo", "bar"}, 1878 // commands that use waitMixin from elsewhere 1879 {"start", "--no-wait", "foo"}, 1880 {"stop", "--no-wait", "foo"}, 1881 {"restart", "--no-wait", "foo"}, 1882 {"alias", "--no-wait", "foo", "bar"}, 1883 {"unalias", "--no-wait", "foo"}, 1884 {"prefer", "--no-wait", "foo"}, 1885 {"set", "--no-wait", "foo", "bar=baz"}, 1886 {"disconnect", "--no-wait", "foo:bar"}, 1887 {"connect", "--no-wait", "foo:bar"}, 1888 } 1889 1890 s.RedirectClientToTestServer(s.srv.handle) 1891 for _, cmd := range cmds { 1892 rest, err := snap.Parser(snap.Client()).ParseArgs(cmd) 1893 c.Assert(err, check.IsNil, check.Commentf("%v", cmd)) 1894 c.Assert(rest, check.DeepEquals, []string{}) 1895 c.Check(s.Stdout(), check.Matches, "(?sm)42\n") 1896 c.Check(s.Stderr(), check.Equals, "") 1897 c.Check(s.srv.n, check.Equals, 1) 1898 // reset 1899 s.srv.n = 0 1900 s.stdout.Reset() 1901 } 1902 } 1903 1904 func (s *SnapOpSuite) TestNoWaitImmediateError(c *check.C) { 1905 1906 cmds := [][]string{ 1907 {"remove", "--no-wait", "foo"}, 1908 {"remove", "--no-wait", "foo", "bar"}, 1909 {"install", "--no-wait", "foo"}, 1910 {"install", "--no-wait", "foo", "bar"}, 1911 {"revert", "--no-wait", "foo"}, 1912 {"refresh", "--no-wait", "foo"}, 1913 {"refresh", "--no-wait", "foo", "bar"}, 1914 {"refresh", "--no-wait"}, 1915 {"enable", "--no-wait", "foo"}, 1916 {"disable", "--no-wait", "foo"}, 1917 {"try", "--no-wait", "."}, 1918 {"switch", "--no-wait", "--channel=foo", "bar"}, 1919 // commands that use waitMixin from elsewhere 1920 {"start", "--no-wait", "foo"}, 1921 {"stop", "--no-wait", "foo"}, 1922 {"restart", "--no-wait", "foo"}, 1923 {"alias", "--no-wait", "foo", "bar"}, 1924 {"unalias", "--no-wait", "foo"}, 1925 {"prefer", "--no-wait", "foo"}, 1926 {"set", "--no-wait", "foo", "bar=baz"}, 1927 {"disconnect", "--no-wait", "foo:bar"}, 1928 {"connect", "--no-wait", "foo:bar"}, 1929 } 1930 1931 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1932 fmt.Fprintln(w, `{"type": "error", "result": {"message": "failure"}}`) 1933 }) 1934 1935 for _, cmd := range cmds { 1936 _, err := snap.Parser(snap.Client()).ParseArgs(cmd) 1937 c.Assert(err, check.ErrorMatches, "failure", check.Commentf("%v", cmd)) 1938 } 1939 } 1940 1941 func (s *SnapOpSuite) TestWaitServerError(c *check.C) { 1942 r := snap.MockMaxGoneTime(0) 1943 defer r() 1944 1945 cmds := [][]string{ 1946 {"remove", "foo"}, 1947 {"remove", "foo", "bar"}, 1948 {"install", "foo"}, 1949 {"install", "foo", "bar"}, 1950 {"revert", "foo"}, 1951 {"refresh", "foo"}, 1952 {"refresh", "foo", "bar"}, 1953 {"refresh"}, 1954 {"enable", "foo"}, 1955 {"disable", "foo"}, 1956 {"try", "."}, 1957 {"switch", "--channel=foo", "bar"}, 1958 // commands that use waitMixin from elsewhere 1959 {"start", "foo"}, 1960 {"stop", "foo"}, 1961 {"restart", "foo"}, 1962 {"alias", "foo", "bar"}, 1963 {"unalias", "foo"}, 1964 {"prefer", "foo"}, 1965 {"set", "foo", "bar=baz"}, 1966 {"disconnect", "foo:bar"}, 1967 {"connect", "foo:bar"}, 1968 } 1969 1970 n := 0 1971 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 1972 n++ 1973 if n == 1 { 1974 w.WriteHeader(202) 1975 fmt.Fprintln(w, `{"type":"async", "change": "42", "status-code": 202}`) 1976 return 1977 } 1978 if n == 3 { 1979 fmt.Fprintln(w, `{"type": "error", "result": {"message": "unexpected request"}}`) 1980 return 1981 } 1982 fmt.Fprintln(w, `{"type": "error", "result": {"message": "server error"}}`) 1983 }) 1984 1985 for _, cmd := range cmds { 1986 _, err := snap.Parser(snap.Client()).ParseArgs(cmd) 1987 c.Assert(err, check.ErrorMatches, "server error", check.Commentf("%v", cmd)) 1988 // reset 1989 n = 0 1990 } 1991 } 1992 1993 func (s *SnapOpSuite) TestSwitchHappy(c *check.C) { 1994 s.srv.total = 4 1995 s.srv.checker = func(r *http.Request) { 1996 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 1997 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 1998 "action": "switch", 1999 "channel": "beta", 2000 }) 2001 s.srv.trackingChannel = "beta" 2002 } 2003 2004 s.RedirectClientToTestServer(s.srv.handle) 2005 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "--beta", "foo"}) 2006 c.Assert(err, check.IsNil) 2007 c.Assert(rest, check.DeepEquals, []string{}) 2008 c.Check(s.Stdout(), check.Equals, `"foo" switched to the "beta" channel 2009 2010 `) 2011 c.Check(s.Stderr(), check.Equals, "") 2012 // ensure that the fake server api was actually hit 2013 c.Check(s.srv.n, check.Equals, s.srv.total) 2014 } 2015 2016 func (s *SnapOpSuite) TestSwitchHappyCohort(c *check.C) { 2017 s.srv.total = 4 2018 s.srv.checker = func(r *http.Request) { 2019 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 2020 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 2021 "action": "switch", 2022 "cohort-key": "what", 2023 }) 2024 } 2025 2026 s.RedirectClientToTestServer(s.srv.handle) 2027 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "--cohort=what", "foo"}) 2028 c.Assert(err, check.IsNil) 2029 c.Assert(rest, check.DeepEquals, []string{}) 2030 c.Check(s.Stdout(), check.Matches, `(?sm).*"foo" switched to the "what" cohort`) 2031 c.Check(s.Stderr(), check.Equals, "") 2032 // ensure that the fake server api was actually hit 2033 c.Check(s.srv.n, check.Equals, s.srv.total) 2034 } 2035 2036 func (s *SnapOpSuite) TestSwitchHappyLeaveCohort(c *check.C) { 2037 s.srv.total = 4 2038 s.srv.checker = func(r *http.Request) { 2039 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 2040 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 2041 "action": "switch", 2042 "leave-cohort": true, 2043 }) 2044 } 2045 2046 s.RedirectClientToTestServer(s.srv.handle) 2047 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "--leave-cohort", "foo"}) 2048 c.Assert(err, check.IsNil) 2049 c.Assert(rest, check.DeepEquals, []string{}) 2050 c.Check(s.Stdout(), check.Matches, `(?sm).*"foo" left the cohort`) 2051 c.Check(s.Stderr(), check.Equals, "") 2052 // ensure that the fake server api was actually hit 2053 c.Check(s.srv.n, check.Equals, s.srv.total) 2054 } 2055 2056 func (s *SnapOpSuite) TestSwitchHappyChannelAndCohort(c *check.C) { 2057 s.srv.total = 4 2058 s.srv.checker = func(r *http.Request) { 2059 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 2060 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 2061 "action": "switch", 2062 "cohort-key": "what", 2063 "channel": "edge", 2064 }) 2065 s.srv.trackingChannel = "edge" 2066 } 2067 2068 s.RedirectClientToTestServer(s.srv.handle) 2069 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "--cohort=what", "--edge", "foo"}) 2070 c.Assert(err, check.IsNil) 2071 c.Assert(rest, check.DeepEquals, []string{}) 2072 c.Check(s.Stdout(), check.Matches, `(?sm).*"foo" switched to the "edge" channel and the "what" cohort`) 2073 c.Check(s.Stderr(), check.Equals, "") 2074 // ensure that the fake server api was actually hit 2075 c.Check(s.srv.n, check.Equals, s.srv.total) 2076 } 2077 2078 func (s *SnapOpSuite) TestSwitchHappyChannelAndLeaveCohort(c *check.C) { 2079 s.srv.total = 4 2080 s.srv.checker = func(r *http.Request) { 2081 c.Check(r.URL.Path, check.Equals, "/v2/snaps/foo") 2082 c.Check(DecodedRequestBody(c, r), check.DeepEquals, map[string]interface{}{ 2083 "action": "switch", 2084 "leave-cohort": true, 2085 "channel": "edge", 2086 }) 2087 s.srv.trackingChannel = "edge" 2088 } 2089 2090 s.RedirectClientToTestServer(s.srv.handle) 2091 rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "--leave-cohort", "--edge", "foo"}) 2092 c.Assert(err, check.IsNil) 2093 c.Assert(rest, check.DeepEquals, []string{}) 2094 c.Check(s.Stdout(), check.Matches, `(?sm).*"foo" left the cohort, and switched to the "edge" channel`) 2095 c.Check(s.Stderr(), check.Equals, "") 2096 // ensure that the fake server api was actually hit 2097 c.Check(s.srv.n, check.Equals, s.srv.total) 2098 } 2099 2100 func (s *SnapOpSuite) TestSwitchUnhappy(c *check.C) { 2101 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch"}) 2102 c.Assert(err, check.ErrorMatches, "the required argument `<snap>` was not provided") 2103 } 2104 2105 func (s *SnapOpSuite) TestSwitchAlsoUnhappy(c *check.C) { 2106 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "foo"}) 2107 c.Assert(err, check.ErrorMatches, `nothing to switch.*`) 2108 } 2109 2110 func (s *SnapOpSuite) TestSwitchMoreUnhappy(c *check.C) { 2111 _, err := snap.Parser(snap.Client()).ParseArgs([]string{"switch", "foo", "--cohort=what", "--leave-cohort"}) 2112 c.Assert(err, check.ErrorMatches, `cannot specify both --cohort and --leave-cohort`) 2113 } 2114 2115 func (s *SnapOpSuite) TestSnapOpNetworkTimeoutError(c *check.C) { 2116 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 2117 c.Check(r.Method, check.Equals, "POST") 2118 w.WriteHeader(202) 2119 w.Write([]byte(` 2120 { 2121 "type": "error", 2122 "result": { 2123 "message":"Get https://api.snapcraft.io/api/v1/snaps/details/hello?channel=stable&fields=anon_download_url%2Carchitecture%2Cchannel%2Cdownload_sha3_384%2Csummary%2Cdescription%2Cdeltas%2Cbinary_filesize%2Cdownload_url%2Cepoch%2Cicon_url%2Clast_updated%2Cpackage_name%2Cprices%2Cpublisher%2Cratings_average%2Crevision%2Cscreenshot_urls%2Csnap_id%2Clicense%2Cbase%2Csupport_url%2Ccontact%2Ctitle%2Ccontent%2Cversion%2Corigin%2Cdeveloper_id%2Cprivate%2Cconfinement%2Cchannel_maps_list: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)", 2124 "kind":"network-timeout" 2125 }, 2126 "status-code": 400 2127 } 2128 `)) 2129 2130 }) 2131 2132 cmd := []string{"install", "hello"} 2133 _, err := snap.Parser(snap.Client()).ParseArgs(cmd) 2134 c.Assert(err, check.ErrorMatches, `unable to contact snap store`) 2135 }