github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/daemon/api_find_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2020 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 daemon_test
    21  
    22  import (
    23  	"fmt"
    24  	"net/http"
    25  
    26  	"gopkg.in/check.v1"
    27  
    28  	"github.com/snapcore/snapd/client"
    29  	"github.com/snapcore/snapd/daemon"
    30  	"github.com/snapcore/snapd/overlord/auth"
    31  	"github.com/snapcore/snapd/overlord/snapstate"
    32  	"github.com/snapcore/snapd/snap"
    33  	"github.com/snapcore/snapd/store"
    34  )
    35  
    36  var _ = check.Suite(&findSuite{})
    37  
    38  type findSuite struct {
    39  	apiBaseSuite
    40  }
    41  
    42  func (s *findSuite) TestFind(c *check.C) {
    43  	s.daemon(c)
    44  
    45  	s.suggestedCurrency = "EUR"
    46  
    47  	s.rsnaps = []*snap.Info{{
    48  		SideInfo: snap.SideInfo{
    49  			RealName: "store",
    50  		},
    51  		Publisher: snap.StoreAccount{
    52  			ID:          "foo-id",
    53  			Username:    "foo",
    54  			DisplayName: "Foo",
    55  			Validation:  "unproven",
    56  		},
    57  	}}
    58  
    59  	req, err := http.NewRequest("GET", "/v2/find?q=hi", nil)
    60  	c.Assert(err, check.IsNil)
    61  
    62  	rsp := s.req(c, req, nil).(*daemon.Resp)
    63  
    64  	snaps := snapList(rsp.Result)
    65  	c.Assert(snaps, check.HasLen, 1)
    66  	c.Assert(snaps[0]["name"], check.Equals, "store")
    67  	c.Check(snaps[0]["prices"], check.IsNil)
    68  	c.Check(snaps[0]["channels"], check.IsNil)
    69  
    70  	c.Check(rsp.SuggestedCurrency, check.Equals, "EUR")
    71  
    72  	c.Check(s.storeSearch, check.DeepEquals, store.Search{Query: "hi"})
    73  	c.Check(s.currentSnaps, check.HasLen, 0)
    74  	c.Check(s.actions, check.HasLen, 0)
    75  }
    76  
    77  func (s *findSuite) TestFindRefreshes(c *check.C) {
    78  	s.daemon(c)
    79  
    80  	s.rsnaps = []*snap.Info{{
    81  		SideInfo: snap.SideInfo{
    82  			RealName: "store",
    83  		},
    84  		Publisher: snap.StoreAccount{
    85  			ID:          "foo-id",
    86  			Username:    "foo",
    87  			DisplayName: "Foo",
    88  			Validation:  "unproven",
    89  		},
    90  	}}
    91  	s.mockSnap(c, "name: store\nversion: 1.0")
    92  
    93  	req, err := http.NewRequest("GET", "/v2/find?select=refresh", nil)
    94  	c.Assert(err, check.IsNil)
    95  
    96  	rsp := s.req(c, req, nil).(*daemon.Resp)
    97  
    98  	snaps := snapList(rsp.Result)
    99  	c.Assert(snaps, check.HasLen, 1)
   100  	c.Assert(snaps[0]["name"], check.Equals, "store")
   101  	c.Check(s.currentSnaps, check.HasLen, 1)
   102  	c.Check(s.actions, check.HasLen, 1)
   103  }
   104  
   105  func (s *findSuite) TestFindRefreshSideloaded(c *check.C) {
   106  	d := s.daemon(c)
   107  
   108  	s.rsnaps = []*snap.Info{{
   109  		SideInfo: snap.SideInfo{
   110  			RealName: "store",
   111  		},
   112  		Publisher: snap.StoreAccount{
   113  			ID:          "foo-id",
   114  			Username:    "foo",
   115  			DisplayName: "Foo",
   116  			Validation:  "unproven",
   117  		},
   118  	}}
   119  
   120  	s.mockSnap(c, "name: store\nversion: 1.0")
   121  
   122  	var snapst snapstate.SnapState
   123  	st := d.Overlord().State()
   124  	st.Lock()
   125  	err := snapstate.Get(st, "store", &snapst)
   126  	st.Unlock()
   127  	c.Assert(err, check.IsNil)
   128  	c.Assert(snapst.Sequence, check.HasLen, 1)
   129  
   130  	// clear the snapid
   131  	snapst.Sequence[0].SnapID = ""
   132  	st.Lock()
   133  	snapstate.Set(st, "store", &snapst)
   134  	st.Unlock()
   135  
   136  	req, err := http.NewRequest("GET", "/v2/find?select=refresh", nil)
   137  	c.Assert(err, check.IsNil)
   138  
   139  	rsp := s.req(c, req, nil).(*daemon.Resp)
   140  
   141  	snaps := snapList(rsp.Result)
   142  	c.Assert(snaps, check.HasLen, 0)
   143  	c.Check(s.currentSnaps, check.HasLen, 0)
   144  	c.Check(s.actions, check.HasLen, 0)
   145  }
   146  
   147  func (s *findSuite) TestFindPrivate(c *check.C) {
   148  	s.daemon(c)
   149  
   150  	s.rsnaps = []*snap.Info{}
   151  
   152  	req, err := http.NewRequest("GET", "/v2/find?q=foo&select=private", nil)
   153  	c.Assert(err, check.IsNil)
   154  
   155  	_ = s.req(c, req, nil).(*daemon.Resp)
   156  
   157  	c.Check(s.storeSearch, check.DeepEquals, store.Search{
   158  		Query:   "foo",
   159  		Private: true,
   160  	})
   161  }
   162  
   163  func (s *findSuite) TestFindUserAgentContextCreated(c *check.C) {
   164  	s.daemon(c)
   165  
   166  	req, err := http.NewRequest("GET", "/v2/find", nil)
   167  	c.Assert(err, check.IsNil)
   168  	req.Header.Add("User-Agent", "some-agent/1.0")
   169  
   170  	_ = s.req(c, req, nil).(*daemon.Resp)
   171  
   172  	c.Check(store.ClientUserAgent(s.ctx), check.Equals, "some-agent/1.0")
   173  }
   174  
   175  func (s *findSuite) TestFindOneUserAgentContextCreated(c *check.C) {
   176  	s.daemon(c)
   177  
   178  	s.rsnaps = []*snap.Info{{
   179  		SnapType: snap.TypeApp,
   180  		Version:  "v2",
   181  		SideInfo: snap.SideInfo{
   182  			RealName: "banana",
   183  		},
   184  		Publisher: snap.StoreAccount{
   185  			ID:          "foo-id",
   186  			Username:    "foo",
   187  			DisplayName: "Foo",
   188  			Validation:  "unproven",
   189  		},
   190  	}}
   191  	req, err := http.NewRequest("GET", "/v2/find?name=foo", nil)
   192  	c.Assert(err, check.IsNil)
   193  	req.Header.Add("User-Agent", "some-agent/1.0")
   194  
   195  	_ = s.req(c, req, nil).(*daemon.Resp)
   196  
   197  	c.Check(store.ClientUserAgent(s.ctx), check.Equals, "some-agent/1.0")
   198  }
   199  
   200  func (s *findSuite) TestFindPrefix(c *check.C) {
   201  	s.daemon(c)
   202  
   203  	s.rsnaps = []*snap.Info{}
   204  
   205  	req, err := http.NewRequest("GET", "/v2/find?name=foo*", nil)
   206  	c.Assert(err, check.IsNil)
   207  
   208  	_ = s.req(c, req, nil).(*daemon.Resp)
   209  
   210  	c.Check(s.storeSearch, check.DeepEquals, store.Search{Query: "foo", Prefix: true})
   211  }
   212  
   213  func (s *findSuite) TestFindSection(c *check.C) {
   214  	s.daemon(c)
   215  
   216  	s.rsnaps = []*snap.Info{}
   217  
   218  	req, err := http.NewRequest("GET", "/v2/find?q=foo&section=bar", nil)
   219  	c.Assert(err, check.IsNil)
   220  
   221  	_ = s.req(c, req, nil).(*daemon.Resp)
   222  
   223  	c.Check(s.storeSearch, check.DeepEquals, store.Search{
   224  		Query:    "foo",
   225  		Category: "bar",
   226  	})
   227  }
   228  
   229  func (s *findSuite) TestFindScope(c *check.C) {
   230  	s.daemon(c)
   231  
   232  	s.rsnaps = []*snap.Info{}
   233  
   234  	req, err := http.NewRequest("GET", "/v2/find?q=foo&scope=creep", nil)
   235  	c.Assert(err, check.IsNil)
   236  
   237  	_ = s.req(c, req, nil).(*daemon.Resp)
   238  
   239  	c.Check(s.storeSearch, check.DeepEquals, store.Search{
   240  		Query: "foo",
   241  		Scope: "creep",
   242  	})
   243  }
   244  
   245  func (s *findSuite) TestFindCommonID(c *check.C) {
   246  	s.daemon(c)
   247  
   248  	s.rsnaps = []*snap.Info{{
   249  		SideInfo: snap.SideInfo{
   250  			RealName: "store",
   251  		},
   252  		Publisher: snap.StoreAccount{
   253  			ID:          "foo-id",
   254  			Username:    "foo",
   255  			DisplayName: "Foo",
   256  			Validation:  "unproven",
   257  		},
   258  		CommonIDs: []string{"org.foo"},
   259  	}}
   260  	s.mockSnap(c, "name: store\nversion: 1.0")
   261  
   262  	req, err := http.NewRequest("GET", "/v2/find?name=foo", nil)
   263  	c.Assert(err, check.IsNil)
   264  
   265  	rsp := s.req(c, req, nil).(*daemon.Resp)
   266  
   267  	snaps := snapList(rsp.Result)
   268  	c.Assert(snaps, check.HasLen, 1)
   269  	c.Check(snaps[0]["common-ids"], check.DeepEquals, []interface{}{"org.foo"})
   270  }
   271  
   272  func (s *findSuite) TestFindByCommonID(c *check.C) {
   273  	s.daemon(c)
   274  
   275  	s.rsnaps = []*snap.Info{{
   276  		SideInfo: snap.SideInfo{
   277  			RealName: "store",
   278  		},
   279  		Publisher: snap.StoreAccount{
   280  			ID:          "foo-id",
   281  			Username:    "foo",
   282  			DisplayName: "Foo",
   283  			Validation:  "unproven",
   284  		},
   285  		CommonIDs: []string{"org.foo"},
   286  	}}
   287  	s.mockSnap(c, "name: store\nversion: 1.0")
   288  
   289  	req, err := http.NewRequest("GET", "/v2/find?common-id=org.foo", nil)
   290  	c.Assert(err, check.IsNil)
   291  
   292  	rsp := s.req(c, req, nil).(*daemon.Resp)
   293  
   294  	snaps := snapList(rsp.Result)
   295  	c.Assert(snaps, check.HasLen, 1)
   296  	c.Check(s.storeSearch, check.DeepEquals, store.Search{CommonID: "org.foo"})
   297  }
   298  
   299  func (s *findSuite) TestFindOne(c *check.C) {
   300  	s.daemon(c)
   301  
   302  	s.rsnaps = []*snap.Info{{
   303  		SideInfo: snap.SideInfo{
   304  			RealName: "store",
   305  		},
   306  		Base: "base0",
   307  		Publisher: snap.StoreAccount{
   308  			ID:          "foo-id",
   309  			Username:    "foo",
   310  			DisplayName: "Foo",
   311  			Validation:  "verified",
   312  		},
   313  		Channels: map[string]*snap.ChannelSnapInfo{
   314  			"stable": {
   315  				Revision: snap.R(42),
   316  			},
   317  		},
   318  	}}
   319  	s.mockSnap(c, "name: store\nversion: 1.0")
   320  
   321  	req, err := http.NewRequest("GET", "/v2/find?name=foo", nil)
   322  	c.Assert(err, check.IsNil)
   323  
   324  	rsp := s.req(c, req, nil).(*daemon.Resp)
   325  
   326  	c.Check(s.storeSearch, check.DeepEquals, store.Search{})
   327  
   328  	snaps := snapList(rsp.Result)
   329  	c.Assert(snaps, check.HasLen, 1)
   330  	c.Check(snaps[0]["name"], check.Equals, "store")
   331  	c.Check(snaps[0]["base"], check.Equals, "base0")
   332  	c.Check(snaps[0]["publisher"], check.DeepEquals, map[string]interface{}{
   333  		"id":           "foo-id",
   334  		"username":     "foo",
   335  		"display-name": "Foo",
   336  		"validation":   "verified",
   337  	})
   338  	m := snaps[0]["channels"].(map[string]interface{})["stable"].(map[string]interface{})
   339  
   340  	c.Check(m["revision"], check.Equals, "42")
   341  }
   342  
   343  func (s *findSuite) TestFindOneNotFound(c *check.C) {
   344  	s.daemon(c)
   345  
   346  	s.err = store.ErrSnapNotFound
   347  	s.mockSnap(c, "name: store\nversion: 1.0")
   348  
   349  	req, err := http.NewRequest("GET", "/v2/find?name=foo", nil)
   350  	c.Assert(err, check.IsNil)
   351  
   352  	rsp := s.req(c, req, nil).(*daemon.Resp)
   353  
   354  	c.Check(s.storeSearch, check.DeepEquals, store.Search{})
   355  	c.Check(rsp.Status, check.Equals, 404)
   356  }
   357  
   358  func (s *findSuite) TestFindOneWithAuth(c *check.C) {
   359  	d := s.daemon(c)
   360  
   361  	state := d.Overlord().State()
   362  	state.Lock()
   363  	user, err := auth.NewUser(state, "username", "email@test.com", "macaroon", []string{"discharge"})
   364  	state.Unlock()
   365  	c.Check(err, check.IsNil)
   366  
   367  	req, err := http.NewRequest("GET", "/v2/find?q=name:gfoo", nil)
   368  	c.Assert(err, check.IsNil)
   369  
   370  	c.Assert(s.user, check.IsNil)
   371  
   372  	_, ok := s.req(c, req, user).(*daemon.Resp)
   373  	c.Assert(ok, check.Equals, true)
   374  	// ensure user was set
   375  	c.Assert(s.user, check.DeepEquals, user)
   376  }
   377  
   378  func (s *findSuite) TestFindRefreshNotOther(c *check.C) {
   379  	s.daemon(c)
   380  
   381  	for _, other := range []string{"name", "q", "common-id"} {
   382  		req, err := http.NewRequest("GET", "/v2/find?select=refresh&"+other+"=foo*", nil)
   383  		c.Assert(err, check.IsNil)
   384  
   385  		rsp := s.req(c, req, nil).(*daemon.Resp)
   386  		c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError)
   387  		c.Check(rsp.Status, check.Equals, 400)
   388  		c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Equals, "cannot use '"+other+"' with 'select=refresh'")
   389  	}
   390  }
   391  
   392  func (s *findSuite) TestFindNotTogether(c *check.C) {
   393  	s.daemon(c)
   394  
   395  	queries := map[string]string{"q": "foo", "name": "foo*", "common-id": "foo"}
   396  	for ki, vi := range queries {
   397  		for kj, vj := range queries {
   398  			if ki == kj {
   399  				continue
   400  			}
   401  
   402  			req, err := http.NewRequest("GET", fmt.Sprintf("/v2/find?%s=%s&%s=%s", ki, vi, kj, vj), nil)
   403  			c.Assert(err, check.IsNil)
   404  
   405  			rsp := s.req(c, req, nil).(*daemon.Resp)
   406  			c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError)
   407  			c.Check(rsp.Status, check.Equals, 400)
   408  			exp1 := "cannot use '" + ki + "' and '" + kj + "' together"
   409  			exp2 := "cannot use '" + kj + "' and '" + ki + "' together"
   410  			c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, exp1+"|"+exp2)
   411  		}
   412  	}
   413  }
   414  
   415  func (s *findSuite) TestFindBadQueryReturnsCorrectErrorKind(c *check.C) {
   416  	s.daemon(c)
   417  
   418  	s.err = store.ErrBadQuery
   419  	req, err := http.NewRequest("GET", "/v2/find?q=return-bad-query-please", nil)
   420  	c.Assert(err, check.IsNil)
   421  
   422  	rsp := s.req(c, req, nil).(*daemon.Resp)
   423  	c.Check(rsp.Type, check.Equals, daemon.ResponseTypeError)
   424  	c.Check(rsp.Status, check.Equals, 400)
   425  	c.Check(rsp.Result.(*daemon.ErrorResult).Message, check.Matches, "bad query")
   426  	c.Check(rsp.Result.(*daemon.ErrorResult).Kind, check.Equals, client.ErrorKindBadQuery)
   427  }
   428  
   429  func (s *findSuite) TestFindPriced(c *check.C) {
   430  	s.daemon(c)
   431  
   432  	s.suggestedCurrency = "GBP"
   433  
   434  	s.rsnaps = []*snap.Info{{
   435  		SnapType: snap.TypeApp,
   436  		Version:  "v2",
   437  		Prices: map[string]float64{
   438  			"GBP": 1.23,
   439  			"EUR": 2.34,
   440  		},
   441  		MustBuy: true,
   442  		SideInfo: snap.SideInfo{
   443  			RealName: "banana",
   444  		},
   445  		Publisher: snap.StoreAccount{
   446  			ID:          "foo-id",
   447  			Username:    "foo",
   448  			DisplayName: "Foo",
   449  			Validation:  "unproven",
   450  		},
   451  	}}
   452  
   453  	req, err := http.NewRequest("GET", "/v2/find?q=banana&channel=stable", nil)
   454  	c.Assert(err, check.IsNil)
   455  	rsp, ok := s.req(c, req, nil).(*daemon.Resp)
   456  	c.Assert(ok, check.Equals, true)
   457  
   458  	snaps := snapList(rsp.Result)
   459  	c.Assert(snaps, check.HasLen, 1)
   460  
   461  	snap := snaps[0]
   462  	c.Check(snap["name"], check.Equals, "banana")
   463  	c.Check(snap["prices"], check.DeepEquals, map[string]interface{}{
   464  		"EUR": 2.34,
   465  		"GBP": 1.23,
   466  	})
   467  	c.Check(snap["status"], check.Equals, "priced")
   468  
   469  	c.Check(rsp.SuggestedCurrency, check.Equals, "GBP")
   470  }
   471  
   472  func (s *findSuite) TestFindScreenshotted(c *check.C) {
   473  	s.daemon(c)
   474  
   475  	s.rsnaps = []*snap.Info{{
   476  		SnapType: snap.TypeApp,
   477  		Version:  "v2",
   478  		Media: []snap.MediaInfo{
   479  			{
   480  				Type:   "screenshot",
   481  				URL:    "http://example.com/screenshot.png",
   482  				Width:  800,
   483  				Height: 1280,
   484  			},
   485  			{
   486  				Type: "screenshot",
   487  				URL:  "http://example.com/screenshot2.png",
   488  			},
   489  		},
   490  		MustBuy: true,
   491  		SideInfo: snap.SideInfo{
   492  			RealName: "test-screenshot",
   493  		},
   494  		Publisher: snap.StoreAccount{
   495  			ID:          "foo-id",
   496  			Username:    "foo",
   497  			DisplayName: "Foo",
   498  			Validation:  "unproven",
   499  		},
   500  	}}
   501  
   502  	req, err := http.NewRequest("GET", "/v2/find?q=test-screenshot", nil)
   503  	c.Assert(err, check.IsNil)
   504  	rsp, ok := s.req(c, req, nil).(*daemon.Resp)
   505  	c.Assert(ok, check.Equals, true)
   506  
   507  	snaps := snapList(rsp.Result)
   508  	c.Assert(snaps, check.HasLen, 1)
   509  
   510  	c.Check(snaps[0]["name"], check.Equals, "test-screenshot")
   511  	c.Check(snaps[0]["media"], check.DeepEquals, []interface{}{
   512  		map[string]interface{}{
   513  			"type":   "screenshot",
   514  			"url":    "http://example.com/screenshot.png",
   515  			"width":  float64(800),
   516  			"height": float64(1280),
   517  		},
   518  		map[string]interface{}{
   519  			"type": "screenshot",
   520  			"url":  "http://example.com/screenshot2.png",
   521  		},
   522  	})
   523  }
   524  
   525  func (s *findSuite) TestSnapsStoreConfinement(c *check.C) {
   526  	s.daemon(c)
   527  
   528  	s.rsnaps = []*snap.Info{
   529  		{
   530  			// no explicit confinement in this one
   531  			SideInfo: snap.SideInfo{
   532  				RealName: "foo",
   533  			},
   534  		},
   535  		{
   536  			Confinement: snap.StrictConfinement,
   537  			SideInfo: snap.SideInfo{
   538  				RealName: "bar",
   539  			},
   540  		},
   541  		{
   542  			Confinement: snap.DevModeConfinement,
   543  			SideInfo: snap.SideInfo{
   544  				RealName: "baz",
   545  			},
   546  		},
   547  	}
   548  
   549  	req, err := http.NewRequest("GET", "/v2/find", nil)
   550  	c.Assert(err, check.IsNil)
   551  
   552  	rsp := s.req(c, req, nil).(*daemon.Resp)
   553  
   554  	snaps := snapList(rsp.Result)
   555  	c.Assert(snaps, check.HasLen, 3)
   556  
   557  	for i, ss := range [][2]string{
   558  		{"foo", string(snap.StrictConfinement)},
   559  		{"bar", string(snap.StrictConfinement)},
   560  		{"baz", string(snap.DevModeConfinement)},
   561  	} {
   562  		name, mode := ss[0], ss[1]
   563  		c.Check(snaps[i]["name"], check.Equals, name, check.Commentf(name))
   564  		c.Check(snaps[i]["confinement"], check.Equals, mode, check.Commentf(name))
   565  	}
   566  }