github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/interfaces_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 client_test 21 22 import ( 23 "encoding/json" 24 25 "gopkg.in/check.v1" 26 27 "github.com/snapcore/snapd/client" 28 ) 29 30 func (cs *clientSuite) TestClientInterfacesOptionEncoding(c *check.C) { 31 // Choose some options 32 _, _ = cs.cli.Interfaces(&client.InterfaceOptions{ 33 Names: []string{"a", "b"}, 34 Doc: true, 35 Plugs: true, 36 Slots: true, 37 Connected: true, 38 }) 39 c.Check(cs.req.Method, check.Equals, "GET") 40 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 41 c.Check(cs.req.URL.RawQuery, check.Equals, 42 "doc=true&names=a%2Cb&plugs=true&select=connected&slots=true") 43 } 44 45 func (cs *clientSuite) TestClientInterfacesAll(c *check.C) { 46 // Ask for a summary of all interfaces. 47 cs.rsp = `{ 48 "type": "sync", 49 "result": [ 50 {"name": "iface-a", "summary": "the A iface"}, 51 {"name": "iface-b", "summary": "the B iface"}, 52 {"name": "iface-c", "summary": "the C iface"} 53 ] 54 }` 55 ifaces, err := cs.cli.Interfaces(nil) 56 c.Check(cs.req.Method, check.Equals, "GET") 57 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 58 // This uses the select=all query option to indicate that new response 59 // format should be used. The same API endpoint is used by the Interfaces 60 // and by the Connections functions an the absence or presence of the 61 // select query option decides what kind of result should be returned 62 // (legacy or modern). 63 c.Check(cs.req.URL.RawQuery, check.Equals, "select=all") 64 c.Assert(err, check.IsNil) 65 c.Check(ifaces, check.DeepEquals, []*client.Interface{ 66 {Name: "iface-a", Summary: "the A iface"}, 67 {Name: "iface-b", Summary: "the B iface"}, 68 {Name: "iface-c", Summary: "the C iface"}, 69 }) 70 } 71 72 func (cs *clientSuite) TestClientInterfacesConnected(c *check.C) { 73 // Ask for for a summary of connected interfaces. 74 cs.rsp = `{ 75 "type": "sync", 76 "result": [ 77 {"name": "iface-a", "summary": "the A iface"}, 78 {"name": "iface-c", "summary": "the C iface"} 79 ] 80 }` 81 ifaces, err := cs.cli.Interfaces(&client.InterfaceOptions{ 82 Connected: true, 83 }) 84 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 85 // This uses select=connected to ignore interfaces that just sit on some 86 // snap but are not connected to anything. 87 c.Check(cs.req.URL.RawQuery, check.Equals, "select=connected") 88 c.Assert(err, check.IsNil) 89 c.Check(ifaces, check.DeepEquals, []*client.Interface{ 90 {Name: "iface-a", Summary: "the A iface"}, 91 // interface b was not connected so it doesn't get listed. 92 {Name: "iface-c", Summary: "the C iface"}, 93 }) 94 } 95 96 func (cs *clientSuite) TestClientInterfacesSelectedDetails(c *check.C) { 97 // Ask for single element and request docs, plugs and slots. 98 cs.rsp = `{ 99 "type": "sync", 100 "result": [ 101 { 102 "name": "iface-a", 103 "summary": "the A iface", 104 "doc-url": "http://example.org/ifaces/a", 105 "plugs": [{ 106 "snap": "consumer", 107 "plug": "plug", 108 "interface": "iface-a" 109 }], 110 "slots": [{ 111 "snap": "producer", 112 "slot": "slot", 113 "interface": "iface-a" 114 }] 115 } 116 ] 117 }` 118 opts := &client.InterfaceOptions{Names: []string{"iface-a"}, Doc: true, Plugs: true, Slots: true} 119 ifaces, err := cs.cli.Interfaces(opts) 120 c.Check(cs.req.Method, check.Equals, "GET") 121 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 122 // This enables documentation, plugs, slots, chooses a specific interface 123 // (iface-a), and uses select=all to indicate that new response is desired. 124 c.Check(cs.req.URL.RawQuery, check.Equals, 125 "doc=true&names=iface-a&plugs=true&select=all&slots=true") 126 c.Assert(err, check.IsNil) 127 c.Check(ifaces, check.DeepEquals, []*client.Interface{ 128 { 129 Name: "iface-a", 130 Summary: "the A iface", 131 DocURL: "http://example.org/ifaces/a", 132 Plugs: []client.Plug{{Snap: "consumer", Name: "plug", Interface: "iface-a"}}, 133 Slots: []client.Slot{{Snap: "producer", Name: "slot", Interface: "iface-a"}}, 134 }, 135 }) 136 } 137 138 func (cs *clientSuite) TestClientInterfacesMultiple(c *check.C) { 139 // Ask for multiple interfaces. 140 cs.rsp = `{ 141 "type": "sync", 142 "result": [ 143 {"name": "iface-a", "summary": "the A iface"}, 144 {"name": "iface-b", "summary": "the B iface"} 145 ] 146 }` 147 ifaces, err := cs.cli.Interfaces(&client.InterfaceOptions{Names: []string{"iface-a", "iface-b"}}) 148 c.Check(cs.req.Method, check.Equals, "GET") 149 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 150 // This chooses a specific interfaces (iface-a, iface-b) 151 c.Check(cs.req.URL.RawQuery, check.Equals, "names=iface-a%2Ciface-b&select=all") 152 c.Assert(err, check.IsNil) 153 c.Check(ifaces, check.DeepEquals, []*client.Interface{ 154 {Name: "iface-a", Summary: "the A iface"}, 155 {Name: "iface-b", Summary: "the B iface"}, 156 }) 157 } 158 159 func (cs *clientSuite) TestClientConnectCallsEndpoint(c *check.C) { 160 cs.cli.Connect("producer", "plug", "consumer", "slot") 161 c.Check(cs.req.Method, check.Equals, "POST") 162 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 163 } 164 165 func (cs *clientSuite) TestClientConnect(c *check.C) { 166 cs.status = 202 167 cs.rsp = `{ 168 "type": "async", 169 "status-code": 202, 170 "result": { }, 171 "change": "foo" 172 }` 173 id, err := cs.cli.Connect("producer", "plug", "consumer", "slot") 174 c.Assert(err, check.IsNil) 175 c.Check(id, check.Equals, "foo") 176 var body map[string]interface{} 177 decoder := json.NewDecoder(cs.req.Body) 178 err = decoder.Decode(&body) 179 c.Check(err, check.IsNil) 180 c.Check(body, check.DeepEquals, map[string]interface{}{ 181 "action": "connect", 182 "plugs": []interface{}{ 183 map[string]interface{}{ 184 "snap": "producer", 185 "plug": "plug", 186 }, 187 }, 188 "slots": []interface{}{ 189 map[string]interface{}{ 190 "snap": "consumer", 191 "slot": "slot", 192 }, 193 }, 194 }) 195 } 196 197 func (cs *clientSuite) TestClientDisconnectCallsEndpoint(c *check.C) { 198 cs.cli.Disconnect("producer", "plug", "consumer", "slot", nil) 199 c.Check(cs.req.Method, check.Equals, "POST") 200 c.Check(cs.req.URL.Path, check.Equals, "/v2/interfaces") 201 } 202 203 func (cs *clientSuite) TestClientDisconnect(c *check.C) { 204 cs.status = 202 205 cs.rsp = `{ 206 "type": "async", 207 "status-code": 202, 208 "result": { }, 209 "change": "42" 210 }` 211 opts := &client.DisconnectOptions{Forget: false} 212 id, err := cs.cli.Disconnect("producer", "plug", "consumer", "slot", opts) 213 c.Assert(err, check.IsNil) 214 c.Check(id, check.Equals, "42") 215 var body map[string]interface{} 216 decoder := json.NewDecoder(cs.req.Body) 217 err = decoder.Decode(&body) 218 c.Check(err, check.IsNil) 219 c.Check(body, check.DeepEquals, map[string]interface{}{ 220 "action": "disconnect", 221 "plugs": []interface{}{ 222 map[string]interface{}{ 223 "snap": "producer", 224 "plug": "plug", 225 }, 226 }, 227 "slots": []interface{}{ 228 map[string]interface{}{ 229 "snap": "consumer", 230 "slot": "slot", 231 }, 232 }, 233 }) 234 } 235 236 func (cs *clientSuite) TestClientDisconnectForget(c *check.C) { 237 cs.status = 202 238 cs.rsp = `{ 239 "type": "async", 240 "status-code": 202, 241 "result": { }, 242 "change": "42" 243 }` 244 opts := &client.DisconnectOptions{Forget: true} 245 id, err := cs.cli.Disconnect("producer", "plug", "consumer", "slot", opts) 246 c.Assert(err, check.IsNil) 247 c.Check(id, check.Equals, "42") 248 var body map[string]interface{} 249 decoder := json.NewDecoder(cs.req.Body) 250 err = decoder.Decode(&body) 251 c.Check(err, check.IsNil) 252 c.Check(body, check.DeepEquals, map[string]interface{}{ 253 "action": "disconnect", 254 "forget": true, 255 "plugs": []interface{}{ 256 map[string]interface{}{ 257 "snap": "producer", 258 "plug": "plug", 259 }, 260 }, 261 "slots": []interface{}{ 262 map[string]interface{}{ 263 "snap": "consumer", 264 "slot": "slot", 265 }, 266 }, 267 }) 268 }