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