github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/packages_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2016 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 client_test 21 22 import ( 23 "encoding/json" 24 "errors" 25 "fmt" 26 "io/ioutil" 27 "net/url" 28 "os" 29 "time" 30 31 "golang.org/x/xerrors" 32 "gopkg.in/check.v1" 33 34 "github.com/snapcore/snapd/client" 35 "github.com/snapcore/snapd/snap" 36 ) 37 38 func (cs *clientSuite) TestClientSnapsCallsEndpoint(c *check.C) { 39 _, _ = cs.cli.List(nil, nil) 40 c.Check(cs.req.Method, check.Equals, "GET") 41 c.Check(cs.req.URL.Path, check.Equals, "/v2/snaps") 42 c.Check(cs.req.URL.Query(), check.DeepEquals, url.Values{}) 43 } 44 45 func (cs *clientSuite) TestClientFindRefreshSetsQuery(c *check.C) { 46 _, _, _ = cs.cli.Find(&client.FindOptions{ 47 Refresh: true, 48 }) 49 c.Check(cs.req.Method, check.Equals, "GET") 50 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 51 c.Check(cs.req.URL.Query(), check.DeepEquals, url.Values{ 52 "select": []string{"refresh"}, 53 }) 54 } 55 56 func (cs *clientSuite) TestClientFindRefreshSetsQueryWithSec(c *check.C) { 57 _, _, _ = cs.cli.Find(&client.FindOptions{ 58 Refresh: true, 59 Section: "mysection", 60 }) 61 c.Check(cs.req.Method, check.Equals, "GET") 62 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 63 c.Check(cs.req.URL.Query(), check.DeepEquals, url.Values{ 64 "section": []string{"mysection"}, "select": []string{"refresh"}, 65 }) 66 } 67 68 func (cs *clientSuite) TestClientFindWithSectionSetsQuery(c *check.C) { 69 _, _, _ = cs.cli.Find(&client.FindOptions{ 70 Section: "mysection", 71 }) 72 c.Check(cs.req.Method, check.Equals, "GET") 73 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 74 c.Check(cs.req.URL.Query(), check.DeepEquals, url.Values{ 75 "section": []string{"mysection"}, 76 }) 77 } 78 79 func (cs *clientSuite) TestClientFindPrivateSetsQuery(c *check.C) { 80 _, _, _ = cs.cli.Find(&client.FindOptions{ 81 Private: true, 82 }) 83 c.Check(cs.req.Method, check.Equals, "GET") 84 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 85 86 c.Check(cs.req.URL.Query().Get("select"), check.Equals, "private") 87 } 88 89 func (cs *clientSuite) TestClientFindWithScopeSetsQuery(c *check.C) { 90 _, _, _ = cs.cli.Find(&client.FindOptions{ 91 Scope: "mouthwash", 92 }) 93 c.Check(cs.req.Method, check.Equals, "GET") 94 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 95 c.Check(cs.req.URL.Query(), check.DeepEquals, url.Values{ 96 "scope": []string{"mouthwash"}, 97 }) 98 } 99 100 func (cs *clientSuite) TestClientSnapsInvalidSnapsJSON(c *check.C) { 101 cs.rsp = `{ 102 "type": "sync", 103 "result": "not a list of snaps" 104 }` 105 _, err := cs.cli.List(nil, nil) 106 c.Check(err, check.ErrorMatches, `.*cannot unmarshal.*`) 107 } 108 109 func (cs *clientSuite) TestClientNoSnaps(c *check.C) { 110 cs.rsp = `{ 111 "type": "sync", 112 "result": [], 113 "suggested-currency": "GBP" 114 }` 115 _, err := cs.cli.List(nil, nil) 116 c.Check(err, check.Equals, client.ErrNoSnapsInstalled) 117 _, err = cs.cli.List([]string{"foo"}, nil) 118 c.Check(err, check.Equals, client.ErrNoSnapsInstalled) 119 } 120 121 func (cs *clientSuite) TestClientSnaps(c *check.C) { 122 healthTimestamp, err := time.Parse(time.RFC3339Nano, "2019-05-13T16:27:01.475851677+01:00") 123 c.Assert(err, check.IsNil) 124 125 // TODO: update this JSON as it's ancient 126 cs.rsp = `{ 127 "type": "sync", 128 "result": [{ 129 "id": "funky-snap-id", 130 "title": "Title", 131 "summary": "salutation snap", 132 "description": "hello-world", 133 "download-size": 22212, 134 "health": { 135 "revision": "29", 136 "timestamp": "2019-05-13T16:27:01.475851677+01:00", 137 "status": "okay" 138 }, 139 "icon": "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/hello.svg_NZLfWbh.png", 140 "installed-size": -1, 141 "license": "GPL-3.0", 142 "name": "hello-world", 143 "developer": "canonical", 144 "publisher": { 145 "id": "canonical", 146 "username": "canonical", 147 "display-name": "Canonical", 148 "validation": "verified" 149 }, 150 "resource": "/v2/snaps/hello-world.canonical", 151 "status": "available", 152 "type": "app", 153 "version": "1.0.18", 154 "confinement": "strict", 155 "private": true, 156 "common-ids": ["org.funky.snap"] 157 }], 158 "suggested-currency": "GBP" 159 }` 160 applications, err := cs.cli.List(nil, nil) 161 c.Check(err, check.IsNil) 162 c.Check(applications, check.DeepEquals, []*client.Snap{{ 163 ID: "funky-snap-id", 164 Title: "Title", 165 Summary: "salutation snap", 166 Description: "hello-world", 167 DownloadSize: 22212, 168 Icon: "https://myapps.developer.ubuntu.com/site_media/appmedia/2015/03/hello.svg_NZLfWbh.png", 169 Health: &client.SnapHealth{ 170 Revision: snap.R(29), 171 Timestamp: healthTimestamp, 172 Status: "okay", 173 }, 174 InstalledSize: -1, 175 License: "GPL-3.0", 176 Name: "hello-world", 177 Developer: "canonical", 178 Publisher: &snap.StoreAccount{ 179 ID: "canonical", 180 Username: "canonical", 181 DisplayName: "Canonical", 182 Validation: "verified", 183 }, 184 Status: client.StatusAvailable, 185 Type: client.TypeApp, 186 Version: "1.0.18", 187 Confinement: client.StrictConfinement, 188 Private: true, 189 DevMode: false, 190 CommonIDs: []string{"org.funky.snap"}, 191 }}) 192 } 193 194 func (cs *clientSuite) TestClientFilterSnaps(c *check.C) { 195 _, _, _ = cs.cli.Find(&client.FindOptions{Query: "foo"}) 196 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 197 c.Check(cs.req.URL.RawQuery, check.Equals, "q=foo") 198 } 199 200 func (cs *clientSuite) TestClientFindPrefix(c *check.C) { 201 _, _, _ = cs.cli.Find(&client.FindOptions{Query: "foo", Prefix: true}) 202 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 203 c.Check(cs.req.URL.RawQuery, check.Equals, "name=foo%2A") // 2A is `*` 204 } 205 206 func (cs *clientSuite) TestClientFindCommonID(c *check.C) { 207 _, _, _ = cs.cli.Find(&client.FindOptions{CommonID: "org.kde.ktuberling.desktop"}) 208 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 209 c.Check(cs.req.URL.RawQuery, check.Equals, "common-id=org.kde.ktuberling.desktop") 210 } 211 212 func (cs *clientSuite) TestClientFindOne(c *check.C) { 213 _, _, _ = cs.cli.FindOne("foo") 214 c.Check(cs.req.URL.Path, check.Equals, "/v2/find") 215 c.Check(cs.req.URL.RawQuery, check.Equals, "name=foo") 216 } 217 218 const ( 219 pkgName = "chatroom" 220 ) 221 222 func (cs *clientSuite) TestClientSnap(c *check.C) { 223 // example data obtained via 224 // printf "GET /v2/find?name=test-snapd-tools HTTP/1.0\r\n\r\n" | nc -U -q 1 /run/snapd.socket|grep '{'|python3 -m json.tool 225 // XXX: update / sync with what daemon is actually putting out 226 cs.rsp = `{ 227 "type": "sync", 228 "result": { 229 "id": "funky-snap-id", 230 "title": "Title", 231 "summary": "bla bla", 232 "description": "WebRTC Video chat server for Snappy", 233 "download-size": 6930947, 234 "icon": "/v2/icons/chatroom.ogra/icon", 235 "installed-size": 18976651, 236 "install-date": "2016-01-02T15:04:05Z", 237 "license": "GPL-3.0", 238 "name": "chatroom", 239 "developer": "ogra", 240 "publisher": { 241 "id": "ogra-id", 242 "username": "ogra", 243 "display-name": "Ogra", 244 "validation": "unproven" 245 }, 246 "resource": "/v2/snaps/chatroom.ogra", 247 "status": "active", 248 "type": "app", 249 "version": "0.1-8", 250 "revision": 42, 251 "confinement": "strict", 252 "private": true, 253 "devmode": true, 254 "trymode": true, 255 "screenshots": [ 256 {"url":"http://example.com/shot1.png", "width":640, "height":480}, 257 {"url":"http://example.com/shot2.png"} 258 ], 259 "media": [ 260 {"type": "icon", "url":"http://example.com/icon.png"}, 261 {"type": "screenshot", "url":"http://example.com/shot1.png", "width":640, "height":480}, 262 {"type": "screenshot", "url":"http://example.com/shot2.png"} 263 ], 264 "cohort-key": "some-long-cohort-key", 265 "website": "http://example.com/funky", 266 "common-ids": ["org.funky.snap"], 267 "store-url": "https://snapcraft.io/chatroom" 268 } 269 }` 270 pkg, _, err := cs.cli.Snap(pkgName) 271 c.Assert(cs.req.Method, check.Equals, "GET") 272 c.Assert(cs.req.URL.Path, check.Equals, fmt.Sprintf("/v2/snaps/%s", pkgName)) 273 c.Assert(err, check.IsNil) 274 c.Assert(pkg, check.DeepEquals, &client.Snap{ 275 ID: "funky-snap-id", 276 Summary: "bla bla", 277 Title: "Title", 278 Description: "WebRTC Video chat server for Snappy", 279 DownloadSize: 6930947, 280 Icon: "/v2/icons/chatroom.ogra/icon", 281 InstalledSize: 18976651, 282 InstallDate: time.Date(2016, 1, 2, 15, 4, 5, 0, time.UTC), 283 License: "GPL-3.0", 284 Name: "chatroom", 285 Developer: "ogra", 286 Publisher: &snap.StoreAccount{ 287 ID: "ogra-id", 288 Username: "ogra", 289 DisplayName: "Ogra", 290 Validation: "unproven", 291 }, 292 Status: client.StatusActive, 293 Type: client.TypeApp, 294 Version: "0.1-8", 295 Revision: snap.R(42), 296 Confinement: client.StrictConfinement, 297 Private: true, 298 DevMode: true, 299 TryMode: true, 300 Screenshots: []snap.ScreenshotInfo{ 301 {URL: "http://example.com/shot1.png", Width: 640, Height: 480}, 302 {URL: "http://example.com/shot2.png"}, 303 }, 304 Media: []snap.MediaInfo{ 305 {Type: "icon", URL: "http://example.com/icon.png"}, 306 {Type: "screenshot", URL: "http://example.com/shot1.png", Width: 640, Height: 480}, 307 {Type: "screenshot", URL: "http://example.com/shot2.png"}, 308 }, 309 CommonIDs: []string{"org.funky.snap"}, 310 CohortKey: "some-long-cohort-key", 311 Website: "http://example.com/funky", 312 StoreURL: "https://snapcraft.io/chatroom", 313 }) 314 } 315 316 func (cs *clientSuite) TestAppInfoNoServiceNoDaemon(c *check.C) { 317 buf, err := json.MarshalIndent(client.AppInfo{Name: "hello"}, "\t", "\t") 318 c.Assert(err, check.IsNil) 319 c.Check(string(buf), check.Equals, `{ 320 "name": "hello" 321 }`) 322 } 323 324 func (cs *clientSuite) TestAppInfoServiceDaemon(c *check.C) { 325 buf, err := json.MarshalIndent(client.AppInfo{ 326 Snap: "foo", 327 Name: "hello", 328 Daemon: "daemon", 329 Enabled: true, 330 Active: false, 331 }, "\t", "\t") 332 c.Assert(err, check.IsNil) 333 c.Check(string(buf), check.Equals, `{ 334 "snap": "foo", 335 "name": "hello", 336 "daemon": "daemon", 337 "enabled": true 338 }`) 339 } 340 341 func (cs *clientSuite) TestAppInfoNilNotService(c *check.C) { 342 var app *client.AppInfo 343 c.Check(app.IsService(), check.Equals, false) 344 } 345 346 func (cs *clientSuite) TestAppInfoNoDaemonNotService(c *check.C) { 347 var app *client.AppInfo 348 c.Assert(json.Unmarshal([]byte(`{"name": "hello"}`), &app), check.IsNil) 349 c.Check(app.Name, check.Equals, "hello") 350 c.Check(app.IsService(), check.Equals, false) 351 } 352 353 func (cs *clientSuite) TestAppInfoEmptyDaemonNotService(c *check.C) { 354 var app *client.AppInfo 355 c.Assert(json.Unmarshal([]byte(`{"name": "hello", "daemon": ""}`), &app), check.IsNil) 356 c.Check(app.Name, check.Equals, "hello") 357 c.Check(app.IsService(), check.Equals, false) 358 } 359 360 func (cs *clientSuite) TestAppInfoDaemonIsService(c *check.C) { 361 var app *client.AppInfo 362 363 c.Assert(json.Unmarshal([]byte(`{"name": "hello", "daemon": "x"}`), &app), check.IsNil) 364 c.Check(app.Name, check.Equals, "hello") 365 c.Check(app.IsService(), check.Equals, true) 366 } 367 368 func (cs *clientSuite) TestClientSectionsErrIsWrapped(c *check.C) { 369 cs.err = errors.New("boom") 370 _, err := cs.cli.Sections() 371 var e xerrors.Wrapper 372 c.Assert(err, check.Implements, &e) 373 } 374 375 func (cs *clientSuite) TestClientFindOneErrIsWrapped(c *check.C) { 376 cs.err = errors.New("boom") 377 _, _, err := cs.cli.FindOne("snap") 378 var e xerrors.Wrapper 379 c.Assert(err, check.Implements, &e) 380 } 381 382 func (cs *clientSuite) TestClientSnapErrIsWrapped(c *check.C) { 383 // setting cs.err will trigger a "client.ClientError" 384 cs.err = errors.New("boom") 385 _, _, err := cs.cli.Snap("snap") 386 var e xerrors.Wrapper 387 c.Assert(err, check.Implements, &e) 388 } 389 390 func (cs *clientSuite) TestClientFindFromPathErrIsWrapped(c *check.C) { 391 var e client.AuthorizationError 392 393 // this will trigger a "client.AuthorizationError" 394 err := ioutil.WriteFile(client.TestStoreAuthFilename(os.Getenv("HOME")), []byte("rubbish"), 0644) 395 c.Assert(err, check.IsNil) 396 397 // check that all the functions that use snapsFromPath() get a 398 // wrapped error 399 _, _, err = cs.cli.FindOne("snap") 400 c.Assert(xerrors.As(err, &e), check.Equals, true) 401 402 _, _, err = cs.cli.Find(nil) 403 c.Assert(xerrors.As(err, &e), check.Equals, true) 404 405 _, err = cs.cli.List([]string{"snap"}, nil) 406 c.Assert(xerrors.As(err, &e), check.Equals, true) 407 }