github.com/rigado/snapd@v2.42.5-go-mod+incompatible/cmd/snap/cmd_advise_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 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  	"bufio"
    24  	"bytes"
    25  	"fmt"
    26  	"net"
    27  	"os"
    28  	"strconv"
    29  	"strings"
    30  	"syscall"
    31  
    32  	. "gopkg.in/check.v1"
    33  
    34  	"github.com/snapcore/snapd/advisor"
    35  	snap "github.com/snapcore/snapd/cmd/snap"
    36  	"github.com/snapcore/snapd/dirs"
    37  )
    38  
    39  type sillyFinder struct{}
    40  
    41  func mkSillyFinder() (advisor.Finder, error) {
    42  	return &sillyFinder{}, nil
    43  }
    44  
    45  func (sf *sillyFinder) FindCommand(command string) ([]advisor.Command, error) {
    46  	switch command {
    47  	case "hello":
    48  		return []advisor.Command{
    49  			{Snap: "hello", Command: "hello"},
    50  			{Snap: "hello-wcm", Command: "hello"},
    51  		}, nil
    52  	case "error-please":
    53  		return nil, fmt.Errorf("get failed")
    54  	default:
    55  		return nil, nil
    56  	}
    57  }
    58  
    59  func (sf *sillyFinder) FindPackage(pkgName string) (*advisor.Package, error) {
    60  	switch pkgName {
    61  	case "hello":
    62  		return &advisor.Package{Snap: "hello", Summary: "summary for hello"}, nil
    63  	case "error-please":
    64  		return nil, fmt.Errorf("find-pkg failed")
    65  	default:
    66  		return nil, nil
    67  	}
    68  }
    69  
    70  func (*sillyFinder) Close() error { return nil }
    71  
    72  func (s *SnapSuite) TestAdviseCommandHappyText(c *C) {
    73  	restore := advisor.ReplaceCommandsFinder(mkSillyFinder)
    74  	defer restore()
    75  
    76  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"advise-snap", "--command", "hello"})
    77  	c.Assert(err, IsNil)
    78  	c.Assert(rest, DeepEquals, []string{})
    79  	c.Assert(s.Stdout(), Equals, `
    80  Command "hello" not found, but can be installed with:
    81  
    82  sudo snap install hello
    83  sudo snap install hello-wcm
    84  
    85  See 'snap info <snap name>' for additional versions.
    86  
    87  `)
    88  	c.Assert(s.Stderr(), Equals, "")
    89  }
    90  
    91  func (s *SnapSuite) TestAdviseCommandHappyJSON(c *C) {
    92  	restore := advisor.ReplaceCommandsFinder(mkSillyFinder)
    93  	defer restore()
    94  
    95  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"advise-snap", "--command", "--format=json", "hello"})
    96  	c.Assert(err, IsNil)
    97  	c.Assert(rest, DeepEquals, []string{})
    98  	c.Assert(s.Stdout(), Equals, `[{"Snap":"hello","Command":"hello"},{"Snap":"hello-wcm","Command":"hello"}]`+"\n")
    99  	c.Assert(s.Stderr(), Equals, "")
   100  }
   101  
   102  func (s *SnapSuite) TestAdviseCommandDumpDb(c *C) {
   103  	dirs.SetRootDir(c.MkDir())
   104  	c.Assert(os.MkdirAll(dirs.SnapCacheDir, 0755), IsNil)
   105  	defer dirs.SetRootDir("")
   106  
   107  	db, err := advisor.Create()
   108  	c.Assert(err, IsNil)
   109  	c.Assert(db.AddSnap("foo", "1.0", "foo summary", []string{"foo", "bar"}), IsNil)
   110  	c.Assert(db.Commit(), IsNil)
   111  
   112  	rest, err := snap.Parser(snap.Client()).ParseArgs([]string{"advise-snap", "--dump-db"})
   113  	c.Assert(err, IsNil)
   114  	c.Assert(rest, DeepEquals, []string{})
   115  	c.Assert(s.Stderr(), Equals, "")
   116  	c.Assert(s.Stdout(), Matches, `bar foo 1.0\nfoo foo 1.0\n`)
   117  }
   118  
   119  func (s *SnapSuite) TestAdviseCommandMisspellText(c *C) {
   120  	restore := advisor.ReplaceCommandsFinder(mkSillyFinder)
   121  	defer restore()
   122  
   123  	for _, misspelling := range []string{"helo", "0hello", "hell0", "hello0"} {
   124  		err := snap.AdviseCommand(misspelling, "pretty")
   125  		c.Assert(err, IsNil)
   126  		c.Assert(s.Stdout(), Equals, fmt.Sprintf(`
   127  Command "%s" not found, did you mean:
   128  
   129   command "hello" from snap "hello"
   130   command "hello" from snap "hello-wcm"
   131  
   132  See 'snap info <snap name>' for additional versions.
   133  
   134  `, misspelling))
   135  		c.Assert(s.Stderr(), Equals, "")
   136  
   137  		s.stdout.Reset()
   138  		s.stderr.Reset()
   139  	}
   140  }
   141  
   142  func (s *SnapSuite) TestAdviseFromAptIntegrationNoAptPackage(c *C) {
   143  	restore := advisor.ReplaceCommandsFinder(mkSillyFinder)
   144  	defer restore()
   145  
   146  	fds, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
   147  	c.Assert(err, IsNil)
   148  
   149  	os.Setenv("APT_HOOK_SOCKET", strconv.Itoa(int(fds[1])))
   150  	// note we don't close fds[1] ourselves; adviseViaAptHook might, or we might leak it
   151  	// (we don't close it here to avoid accidentally closing an arbitrary file descriptor that reused the number)
   152  
   153  	done := make(chan bool, 1)
   154  	go func() {
   155  		f := os.NewFile(uintptr(fds[0]), "advise-sock")
   156  		conn, err := net.FileConn(f)
   157  		c.Assert(err, IsNil)
   158  		defer conn.Close()
   159  		defer f.Close()
   160  
   161  		// handshake
   162  		_, err = conn.Write([]byte(`{"jsonrpc":"2.0","method":"org.debian.apt.hooks.hello","id":0,"params":{"versions":["0.1"]}}` + "\n\n"))
   163  		c.Assert(err, IsNil)
   164  
   165  		// reply from snap
   166  		r := bufio.NewReader(conn)
   167  		buf, _, err := r.ReadLine()
   168  		c.Assert(err, IsNil)
   169  		c.Assert(string(buf), Equals, `{"jsonrpc":"2.0","id":0,"result":{"version":"0.1"}}`)
   170  		// plus empty line
   171  		buf, _, err = r.ReadLine()
   172  		c.Assert(err, IsNil)
   173  		c.Assert(string(buf), Equals, ``)
   174  
   175  		// payload
   176  		_, err = conn.Write([]byte(`{"jsonrpc":"2.0","method":"org.debian.apt.hooks.install.fail","params":{"command":"install","search-terms":["aws-cli"],"unknown-packages":["hello"],"packages":[]}}` + "\n\n"))
   177  		c.Assert(err, IsNil)
   178  
   179  		// bye
   180  		_, err = conn.Write([]byte(`{"jsonrpc":"2.0","method":"org.debian.apt.hooks.bye","params":{}}` + "\n\n"))
   181  		c.Assert(err, IsNil)
   182  
   183  		done <- true
   184  	}()
   185  
   186  	cmd := snap.CmdAdviseSnap()
   187  	cmd.FromApt = true
   188  	err = cmd.Execute(nil)
   189  	c.Assert(err, IsNil)
   190  	c.Assert(s.Stdout(), Equals, `
   191  No apt package "hello", but there is a snap with that name.
   192  Try "snap install hello"
   193  
   194  `)
   195  	c.Assert(s.Stderr(), Equals, "")
   196  	c.Assert(<-done, Equals, true)
   197  }
   198  
   199  func (s *SnapSuite) TestReadRpc(c *C) {
   200  	rpc := strings.Replace(`
   201  {
   202      "jsonrpc": "2.0",
   203      "method": "org.debian.apt.hooks.install.pre-prompt",
   204      "params": {
   205          "command": "install",
   206          "packages": [
   207              {
   208                  "architecture": "amd64",
   209                  "automatic": false,
   210                  "id": 38033,
   211                  "mode": "install",
   212                  "name": "hello",
   213                  "versions": {
   214                      "candidate": {
   215                          "architecture": "amd64",
   216                          "id": 22712,
   217                          "pin": 500,
   218                          "version": "4:17.12.3-1ubuntu1"
   219                      },
   220                      "install": {
   221                          "architecture": "amd64",
   222                          "id": 22712,
   223                          "pin": 500,
   224                          "version": "4:17.12.3-1ubuntu1"
   225                      }
   226                  }
   227              },
   228              {
   229                  "architecture": "amd64",
   230                  "automatic": true,
   231                  "id": 38202,
   232                  "mode": "install",
   233                  "name": "hello-kpart",
   234                  "versions": {
   235                      "candidate": {
   236                          "architecture": "amd64",
   237                          "id": 22713,
   238                          "pin": 500,
   239                          "version": "4:17.12.3-1ubuntu1"
   240                      },
   241                      "install": {
   242                          "architecture": "amd64",
   243                          "id": 22713,
   244                          "pin": 500,
   245                          "version": "4:17.12.3-1ubuntu1"
   246                      }
   247                  }
   248              }
   249          ],
   250          "search-terms": [
   251              "hello"
   252          ],
   253          "unknown-packages": []
   254      }
   255  }`, "\n", "", -1)
   256  	// all apt rpc ends with \n\n
   257  	rpc = rpc + "\n\n"
   258  	// this can be parsed without errors
   259  	_, err := snap.ReadRpc(bufio.NewReader(bytes.NewBufferString(rpc)))
   260  	c.Assert(err, IsNil)
   261  }