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