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  }