github.com/rigado/snapd@v2.42.5-go-mod+incompatible/daemon/api_download_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2019 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  	"bytes"
    24  	"context"
    25  	"fmt"
    26  	"io"
    27  	"io/ioutil"
    28  	"net/http"
    29  	"net/http/httptest"
    30  	"strings"
    31  
    32  	"gopkg.in/check.v1"
    33  
    34  	"github.com/snapcore/snapd/daemon"
    35  	"github.com/snapcore/snapd/dirs"
    36  	"github.com/snapcore/snapd/overlord"
    37  	"github.com/snapcore/snapd/overlord/auth"
    38  	"github.com/snapcore/snapd/overlord/snapstate"
    39  	"github.com/snapcore/snapd/snap"
    40  	"github.com/snapcore/snapd/store"
    41  	"github.com/snapcore/snapd/store/storetest"
    42  )
    43  
    44  type fakeStore struct{}
    45  
    46  var _ = check.Suite(&snapDownloadSuite{})
    47  
    48  type snapDownloadSuite struct {
    49  	storetest.Store
    50  	d *daemon.Daemon
    51  
    52  	snaps []string
    53  }
    54  
    55  func (s *snapDownloadSuite) SetUpTest(c *check.C) {
    56  	s.snaps = nil
    57  
    58  	o := overlord.Mock()
    59  	s.d = daemon.NewWithOverlord(o)
    60  
    61  	st := o.State()
    62  	st.Lock()
    63  	defer st.Unlock()
    64  	snapstate.ReplaceStore(st, s)
    65  	dirs.SetRootDir(c.MkDir())
    66  }
    67  
    68  var content = "SNAP"
    69  
    70  func (s *snapDownloadSuite) SnapInfo(ctx context.Context, spec store.SnapSpec, user *auth.UserState) (*snap.Info, error) {
    71  	switch spec.Name {
    72  	case "bar":
    73  		return &snap.Info{
    74  			DownloadInfo: snap.DownloadInfo{
    75  				Size:            int64(len(content)),
    76  				AnonDownloadURL: "http://localhost/bar",
    77  			},
    78  		}, nil
    79  	case "download-error-trigger-snap":
    80  		return &snap.Info{
    81  			DownloadInfo: snap.DownloadInfo{
    82  				Size:            100,
    83  				AnonDownloadURL: "http://localhost/foo",
    84  			},
    85  		}, nil
    86  	default:
    87  		return nil, store.ErrSnapNotFound
    88  	}
    89  }
    90  
    91  func (s *snapDownloadSuite) DownloadStream(ctx context.Context, name string, downloadInfo *snap.DownloadInfo, user *auth.UserState) (io.ReadCloser, error) {
    92  	if name == "bar" {
    93  		return ioutil.NopCloser(bytes.NewReader([]byte(content))), nil
    94  	}
    95  	return nil, fmt.Errorf("unexpected error")
    96  }
    97  
    98  func (s *snapDownloadSuite) TestDownloadSnapErrors(c *check.C) {
    99  	type scenario struct {
   100  		dataJSON string
   101  		status   int
   102  		err      string
   103  	}
   104  
   105  	for _, scen := range []scenario{
   106  		{
   107  			dataJSON: `{"action": "download"}`,
   108  			status:   400,
   109  			err:      "download operation requires one snap name",
   110  		},
   111  		{
   112  			dataJSON: `{"action": "foo", "snaps": ["foo"]}`,
   113  			status:   400,
   114  			err:      `unknown download operation "foo"`,
   115  		},
   116  		{
   117  			dataJSON: `{"snaps": ["foo"]}`,
   118  			status:   400,
   119  			err:      `download operation requires action`,
   120  		},
   121  		{
   122  			dataJSON: `{"action": "foo", "snaps": ["foo", "bar"]}`,
   123  			status:   400,
   124  			err:      `download operation supports only one snap`,
   125  		},
   126  		{
   127  			dataJSON: `{"}`,
   128  			status:   400,
   129  			err:      `cannot decode request body into download operation: unexpected EOF`,
   130  		},
   131  	} {
   132  		var err error
   133  		data := []byte(scen.dataJSON)
   134  
   135  		req, err := http.NewRequest("POST", "/v2/download", bytes.NewBuffer(data))
   136  		c.Assert(err, check.IsNil)
   137  		rsp := daemon.PostSnapDownload(daemon.SnapDownloadCmd, req, nil)
   138  
   139  		c.Assert(rsp.(*daemon.Resp).Status, check.Equals, scen.status)
   140  		if scen.err == "" {
   141  			c.Errorf("error was expected")
   142  		}
   143  		result := rsp.(*daemon.Resp).Result
   144  		c.Check(result.(*daemon.ErrorResult).Message, check.Matches, scen.err)
   145  	}
   146  }
   147  
   148  func (s *snapDownloadSuite) TestStreamOneSnap(c *check.C) {
   149  
   150  	type scenario struct {
   151  		dataJSON string
   152  		status   int
   153  		err      string
   154  	}
   155  
   156  	for _, s := range []scenario{
   157  		{
   158  			dataJSON: `{"action": "download", "snaps": ["doom"]}`,
   159  			status:   404,
   160  			err:      "snap not found",
   161  		},
   162  		{
   163  			dataJSON: `{"action": "download", "snaps": ["download-error-trigger-snap"]}`,
   164  			status:   500,
   165  			err:      "unexpected error",
   166  		},
   167  		{
   168  			dataJSON: `{"action": "download", "snaps": ["bar"]}`,
   169  			status:   200,
   170  			err:      "",
   171  		},
   172  	} {
   173  		req, err := http.NewRequest("POST", "/v2/download", strings.NewReader(s.dataJSON))
   174  		c.Assert(err, check.IsNil)
   175  		rsp := daemon.SnapDownloadCmd.POST(daemon.SnapDownloadCmd, req, nil)
   176  
   177  		if s.err != "" {
   178  			c.Assert(rsp.(*daemon.Resp).Status, check.Equals, s.status)
   179  			result := rsp.(*daemon.Resp).Result
   180  			c.Check(result.(*daemon.ErrorResult).Message, check.Matches, s.err)
   181  		} else {
   182  			c.Assert(rsp.(daemon.FileStream).SnapName, check.Equals, "bar")
   183  			c.Assert(rsp.(daemon.FileStream).Info.Size, check.Equals, int64(len(content)))
   184  
   185  			w := httptest.NewRecorder()
   186  			rsp.(daemon.FileStream).ServeHTTP(w, nil)
   187  
   188  			expectedLength := fmt.Sprintf("%d", len(content))
   189  
   190  			c.Assert(w.Code, check.Equals, s.status)
   191  			c.Assert(w.Header().Get("Content-Length"), check.Equals, expectedLength)
   192  			c.Assert(w.Header().Get("Content-Type"), check.Equals, "application/octet-stream")
   193  			c.Assert(w.Header().Get("Content-Disposition"), check.Equals, "attachment; filename=bar")
   194  			c.Assert(w.Body.String(), check.Equals, "SNAP")
   195  		}
   196  	}
   197  }