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