github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/cmd/snap/cmd_connect_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 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 main_test 21 22 import ( 23 "fmt" 24 "net/http" 25 "os" 26 27 "github.com/jessevdk/go-flags" 28 . "gopkg.in/check.v1" 29 30 "github.com/snapcore/snapd/client" 31 . "github.com/snapcore/snapd/cmd/snap" 32 ) 33 34 func (s *SnapSuite) TestConnectHelp(c *C) { 35 msg := `Usage: 36 snap.test connect [connect-OPTIONS] [<snap>:<plug>] [<snap>:<slot>] 37 38 The connect command connects a plug to a slot. 39 It may be called in the following ways: 40 41 $ snap connect <snap>:<plug> <snap>:<slot> 42 43 Connects the provided plug to the given slot. 44 45 $ snap connect <snap>:<plug> <snap> 46 47 Connects the specific plug to the only slot in the provided snap that matches 48 the connected interface. If more than one potential slot exists, the command 49 fails. 50 51 $ snap connect <snap>:<plug> 52 53 Connects the provided plug to the slot in the core snap with a name matching 54 the plug name. 55 56 [connect command options] 57 --no-wait Do not wait for the operation to finish but just print 58 the change id. 59 ` 60 s.testSubCommandHelp(c, "connect", msg) 61 } 62 63 func (s *SnapSuite) TestConnectExplicitEverything(c *C) { 64 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 65 switch r.URL.Path { 66 case "/v2/interfaces": 67 c.Check(r.Method, Equals, "POST") 68 c.Check(DecodedRequestBody(c, r), DeepEquals, map[string]interface{}{ 69 "action": "connect", 70 "plugs": []interface{}{ 71 map[string]interface{}{ 72 "snap": "producer", 73 "plug": "plug", 74 }, 75 }, 76 "slots": []interface{}{ 77 map[string]interface{}{ 78 "snap": "consumer", 79 "slot": "slot", 80 }, 81 }, 82 }) 83 w.WriteHeader(202) 84 fmt.Fprintln(w, `{"type":"async", "status-code": 202, "change": "zzz"}`) 85 case "/v2/changes/zzz": 86 c.Check(r.Method, Equals, "GET") 87 fmt.Fprintln(w, `{"type":"sync", "result":{"ready": true, "status": "Done"}}`) 88 default: 89 c.Fatalf("unexpected path %q", r.URL.Path) 90 } 91 }) 92 rest, err := Parser(Client()).ParseArgs([]string{"connect", "producer:plug", "consumer:slot"}) 93 c.Assert(err, IsNil) 94 c.Assert(rest, DeepEquals, []string{}) 95 } 96 97 func (s *SnapSuite) TestConnectExplicitPlugImplicitSlot(c *C) { 98 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 99 switch r.URL.Path { 100 case "/v2/interfaces": 101 c.Check(r.Method, Equals, "POST") 102 c.Check(DecodedRequestBody(c, r), DeepEquals, map[string]interface{}{ 103 "action": "connect", 104 "plugs": []interface{}{ 105 map[string]interface{}{ 106 "snap": "producer", 107 "plug": "plug", 108 }, 109 }, 110 "slots": []interface{}{ 111 map[string]interface{}{ 112 "snap": "consumer", 113 "slot": "", 114 }, 115 }, 116 }) 117 w.WriteHeader(202) 118 fmt.Fprintln(w, `{"type":"async", "status-code": 202, "change": "zzz"}`) 119 case "/v2/changes/zzz": 120 c.Check(r.Method, Equals, "GET") 121 fmt.Fprintln(w, `{"type":"sync", "result":{"ready": true, "status": "Done"}}`) 122 default: 123 c.Fatalf("unexpected path %q", r.URL.Path) 124 } 125 }) 126 rest, err := Parser(Client()).ParseArgs([]string{"connect", "producer:plug", "consumer"}) 127 c.Assert(err, IsNil) 128 c.Assert(rest, DeepEquals, []string{}) 129 } 130 131 func (s *SnapSuite) TestConnectImplicitPlugExplicitSlot(c *C) { 132 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 133 switch r.URL.Path { 134 case "/v2/interfaces": 135 c.Check(r.Method, Equals, "POST") 136 c.Check(DecodedRequestBody(c, r), DeepEquals, map[string]interface{}{ 137 "action": "connect", 138 "plugs": []interface{}{ 139 map[string]interface{}{ 140 "snap": "", 141 "plug": "plug", 142 }, 143 }, 144 "slots": []interface{}{ 145 map[string]interface{}{ 146 "snap": "consumer", 147 "slot": "slot", 148 }, 149 }, 150 }) 151 w.WriteHeader(202) 152 fmt.Fprintln(w, `{"type":"async", "status-code": 202, "change": "zzz"}`) 153 case "/v2/changes/zzz": 154 c.Check(r.Method, Equals, "GET") 155 fmt.Fprintln(w, `{"type":"sync", "result":{"ready": true, "status": "Done"}}`) 156 default: 157 c.Fatalf("unexpected path %q", r.URL.Path) 158 } 159 }) 160 rest, err := Parser(Client()).ParseArgs([]string{"connect", "plug", "consumer:slot"}) 161 c.Assert(err, IsNil) 162 c.Assert(rest, DeepEquals, []string{}) 163 } 164 165 func (s *SnapSuite) TestConnectImplicitPlugImplicitSlot(c *C) { 166 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 167 switch r.URL.Path { 168 case "/v2/interfaces": 169 c.Check(r.Method, Equals, "POST") 170 c.Check(DecodedRequestBody(c, r), DeepEquals, map[string]interface{}{ 171 "action": "connect", 172 "plugs": []interface{}{ 173 map[string]interface{}{ 174 "snap": "", 175 "plug": "plug", 176 }, 177 }, 178 "slots": []interface{}{ 179 map[string]interface{}{ 180 "snap": "consumer", 181 "slot": "", 182 }, 183 }, 184 }) 185 w.WriteHeader(202) 186 fmt.Fprintln(w, `{"type":"async", "status-code": 202, "change": "zzz"}`) 187 case "/v2/changes/zzz": 188 c.Check(r.Method, Equals, "GET") 189 fmt.Fprintln(w, `{"type":"sync", "result":{"ready": true, "status": "Done"}}`) 190 default: 191 c.Fatalf("unexpected path %q", r.URL.Path) 192 } 193 }) 194 rest, err := Parser(Client()).ParseArgs([]string{"connect", "plug", "consumer"}) 195 c.Assert(err, IsNil) 196 c.Assert(rest, DeepEquals, []string{}) 197 } 198 199 var fortestingConnectionList = client.Connections{ 200 Slots: []client.Slot{ 201 { 202 Snap: "core", 203 Name: "x11", 204 Interface: "x11", 205 }, 206 { 207 Snap: "core", 208 Name: "core-support", 209 Interface: "core-support", 210 Connections: []client.PlugRef{ 211 { 212 Snap: "core", 213 Name: "core-support-plug", 214 }, 215 }, 216 }, 217 { 218 Snap: "wake-up-alarm", 219 Name: "toggle", 220 Interface: "bool-file", 221 Label: "Alarm toggle", 222 }, 223 { 224 Snap: "canonical-pi2", 225 Name: "pin-13", 226 Interface: "bool-file", 227 Label: "Pin 13", 228 Connections: []client.PlugRef{ 229 { 230 Snap: "keyboard-lights", 231 Name: "capslock-led", 232 }, 233 }, 234 }, 235 }, 236 Plugs: []client.Plug{ 237 { 238 Snap: "core", 239 Name: "core-support-plug", 240 Interface: "core-support", 241 Connections: []client.SlotRef{ 242 { 243 Snap: "core", 244 Name: "core-support", 245 }, 246 }, 247 }, 248 { 249 Snap: "core", 250 Name: "network-bind-plug", 251 Interface: "network-bind", 252 }, 253 { 254 Snap: "paste-daemon", 255 Name: "network-listening", 256 Interface: "network-listening", 257 Label: "Ability to be a network service", 258 }, 259 { 260 Snap: "potato", 261 Name: "frying", 262 Interface: "frying", 263 Label: "Ability to fry a network service", 264 }, 265 { 266 Snap: "keyboard-lights", 267 Name: "capslock-led", 268 Interface: "bool-file", 269 Label: "Capslock indicator LED", 270 Connections: []client.SlotRef{ 271 { 272 Snap: "canonical-pi2", 273 Name: "pin-13", 274 }, 275 }, 276 }, 277 }, 278 } 279 280 func (s *SnapSuite) TestConnectCompletion(c *C) { 281 s.RedirectClientToTestServer(func(w http.ResponseWriter, r *http.Request) { 282 switch r.URL.Path { 283 case "/v2/connections": 284 c.Assert(r.Method, Equals, "GET") 285 EncodeResponseBody(c, w, map[string]interface{}{ 286 "type": "sync", 287 "result": fortestingConnectionList, 288 }) 289 default: 290 c.Fatalf("unexpected path %q", r.URL.Path) 291 } 292 }) 293 os.Setenv("GO_FLAGS_COMPLETION", "verbose") 294 defer os.Unsetenv("GO_FLAGS_COMPLETION") 295 296 expected := []flags.Completion{} 297 parser := Parser(Client()) 298 parser.CompletionHandler = func(obtained []flags.Completion) { 299 c.Check(obtained, DeepEquals, expected) 300 } 301 302 expected = []flags.Completion{{Item: "core:"}, {Item: "paste-daemon:"}, {Item: "potato:"}} 303 _, err := parser.ParseArgs([]string{"connect", ""}) 304 c.Assert(err, IsNil) 305 306 // connect's first argument can't start with : (only for the 2nd arg, the slot) 307 expected = nil 308 _, err = parser.ParseArgs([]string{"connect", ":"}) 309 c.Assert(err, IsNil) 310 311 expected = []flags.Completion{{Item: "paste-daemon:network-listening", Description: "plug"}} 312 _, err = parser.ParseArgs([]string{"connect", "pa"}) 313 c.Assert(err, IsNil) 314 315 expected = []flags.Completion{{Item: "core:"}, {Item: "wake-up-alarm:"}} 316 _, err = parser.ParseArgs([]string{"connect", "paste-daemon:network-listening", ""}) 317 c.Assert(err, IsNil) 318 319 expected = []flags.Completion{{Item: "wake-up-alarm:toggle", Description: "slot"}} 320 _, err = parser.ParseArgs([]string{"connect", "paste-daemon:network-listening", "w"}) 321 c.Assert(err, IsNil) 322 323 expected = []flags.Completion{{Item: ":x11", Description: "slot"}} 324 _, err = parser.ParseArgs([]string{"connect", "paste-daemon:network-listening", ":"}) 325 c.Assert(err, IsNil) 326 327 c.Assert(s.Stdout(), Equals, "") 328 c.Assert(s.Stderr(), Equals, "") 329 }