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  }