github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/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  }