github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/plugin/v7/rpc/cli_rpc_server_test.go (about)

     1  // +build V7
     2  
     3  package rpc_test
     4  
     5  import (
     6  	"errors"
     7  	"net"
     8  	"net/rpc"
     9  	"os"
    10  	"time"
    11  
    12  	"code.cloudfoundry.org/cli/actor/v7action"
    13  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    14  	"code.cloudfoundry.org/cli/cf/terminal"
    15  	"code.cloudfoundry.org/cli/command/commandfakes"
    16  	plugin "code.cloudfoundry.org/cli/plugin/v7"
    17  	plugin_models "code.cloudfoundry.org/cli/plugin/v7/models"
    18  	cmdRunner "code.cloudfoundry.org/cli/plugin/v7/rpc"
    19  	"code.cloudfoundry.org/cli/plugin/v7/rpc/rpcfakes"
    20  	"code.cloudfoundry.org/cli/types"
    21  	"code.cloudfoundry.org/cli/util/configv3"
    22  	. "github.com/onsi/ginkgo"
    23  	. "github.com/onsi/gomega"
    24  )
    25  
    26  var _ = Describe("Server", func() {
    27  
    28  	var (
    29  		err        error
    30  		client     *rpc.Client
    31  		rpcService *cmdRunner.CliRpcService
    32  	)
    33  
    34  	AfterEach(func() {
    35  		if client != nil {
    36  			client.Close()
    37  		}
    38  	})
    39  
    40  	BeforeEach(func() {
    41  		rpc.DefaultServer = rpc.NewServer()
    42  	})
    43  
    44  	Describe(".NewRpcService", func() {
    45  		BeforeEach(func() {
    46  			rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil)
    47  			Expect(err).ToNot(HaveOccurred())
    48  		})
    49  
    50  		It("returns an err of another Rpc process is already registered", func() {
    51  			_, err := cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil)
    52  			Expect(err).To(HaveOccurred())
    53  		})
    54  	})
    55  
    56  	Describe(".Stop", func() {
    57  		BeforeEach(func() {
    58  			rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil)
    59  			Expect(err).ToNot(HaveOccurred())
    60  
    61  			err := rpcService.Start()
    62  			Expect(err).ToNot(HaveOccurred())
    63  
    64  			pingCli(rpcService.Port())
    65  		})
    66  
    67  		It("shuts down the rpc server", func() {
    68  			rpcService.Stop()
    69  
    70  			//give time for server to stop
    71  			time.Sleep(50 * time.Millisecond)
    72  
    73  			client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port())
    74  			Expect(err).To(HaveOccurred())
    75  		})
    76  	})
    77  
    78  	Describe(".Start", func() {
    79  		BeforeEach(func() {
    80  			rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil)
    81  			Expect(err).ToNot(HaveOccurred())
    82  
    83  			err := rpcService.Start()
    84  			Expect(err).ToNot(HaveOccurred())
    85  
    86  			pingCli(rpcService.Port())
    87  		})
    88  
    89  		AfterEach(func() {
    90  			rpcService.Stop()
    91  
    92  			//give time for server to stop
    93  			time.Sleep(50 * time.Millisecond)
    94  		})
    95  
    96  		It("Start an Rpc server for communication", func() {
    97  			client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port())
    98  			Expect(err).ToNot(HaveOccurred())
    99  		})
   100  	})
   101  
   102  	Describe(".SetPluginMetadata", func() {
   103  		var (
   104  			metadata *plugin.PluginMetadata
   105  		)
   106  
   107  		BeforeEach(func() {
   108  			rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, nil, nil)
   109  			Expect(err).ToNot(HaveOccurred())
   110  
   111  			err := rpcService.Start()
   112  			Expect(err).ToNot(HaveOccurred())
   113  
   114  			pingCli(rpcService.Port())
   115  
   116  			client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port())
   117  			Expect(err).ToNot(HaveOccurred())
   118  
   119  			metadata = &plugin.PluginMetadata{
   120  				Name: "foo",
   121  				Commands: []plugin.Command{
   122  					{Name: "cmd_1", HelpText: "cm 1 help text"},
   123  					{Name: "cmd_2", HelpText: "cmd 2 help text"},
   124  				},
   125  			}
   126  		})
   127  
   128  		AfterEach(func() {
   129  			rpcService.Stop()
   130  
   131  			//give time for server to stop
   132  			time.Sleep(50 * time.Millisecond)
   133  		})
   134  
   135  		It("set the rpc command's Return Data", func() {
   136  			var success bool
   137  			err = client.Call("CliRpcCmd.SetPluginMetadata", metadata, &success)
   138  
   139  			Expect(err).ToNot(HaveOccurred())
   140  			Expect(success).To(BeTrue())
   141  			Expect(rpcService.RpcCmd.PluginMetadata).To(Equal(metadata))
   142  		})
   143  	})
   144  
   145  	Describe("disabling terminal output", func() {
   146  		var terminalOutputSwitch *rpcfakes.FakeTerminalOutputSwitch
   147  
   148  		BeforeEach(func() {
   149  			terminalOutputSwitch = new(rpcfakes.FakeTerminalOutputSwitch)
   150  			rpcService, err = cmdRunner.NewRpcService(nil, terminalOutputSwitch, nil, rpc.DefaultServer, nil, nil)
   151  			Expect(err).ToNot(HaveOccurred())
   152  
   153  			err := rpcService.Start()
   154  			Expect(err).ToNot(HaveOccurred())
   155  
   156  			pingCli(rpcService.Port())
   157  		})
   158  
   159  		It("should disable the terminal output switch", func() {
   160  			client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port())
   161  			Expect(err).ToNot(HaveOccurred())
   162  
   163  			var success bool
   164  			err = client.Call("CliRpcCmd.DisableTerminalOutput", true, &success)
   165  
   166  			Expect(err).ToNot(HaveOccurred())
   167  			Expect(success).To(BeTrue())
   168  			Expect(terminalOutputSwitch.DisableTerminalOutputCallCount()).To(Equal(1))
   169  			Expect(terminalOutputSwitch.DisableTerminalOutputArgsForCall(0)).To(Equal(true))
   170  		})
   171  	})
   172  
   173  	Describe("Plugin API", func() {
   174  		var (
   175  			fakePluginActor *rpcfakes.FakePluginActor
   176  			fakeConfig      *commandfakes.FakeConfig
   177  		)
   178  
   179  		BeforeEach(func() {
   180  			fakePluginActor = new(rpcfakes.FakePluginActor)
   181  			fakeConfig = new(commandfakes.FakeConfig)
   182  			outputCapture := terminal.NewTeePrinter(os.Stdout)
   183  			terminalOutputSwitch := terminal.NewTeePrinter(os.Stdout)
   184  
   185  			rpcService, err = cmdRunner.NewRpcService(outputCapture, terminalOutputSwitch, nil, rpc.DefaultServer, fakeConfig, fakePluginActor)
   186  			Expect(err).ToNot(HaveOccurred())
   187  
   188  			err := rpcService.Start()
   189  			Expect(err).ToNot(HaveOccurred())
   190  
   191  			pingCli(rpcService.Port())
   192  			client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port())
   193  			Expect(err).ToNot(HaveOccurred())
   194  		})
   195  
   196  		AfterEach(func() {
   197  			rpcService.Stop()
   198  
   199  			//give time for server to stop
   200  			time.Sleep(50 * time.Millisecond)
   201  		})
   202  
   203  		Describe("GetApp", func() {
   204  			var (
   205  				summary v7action.DetailedApplicationSummary
   206  			)
   207  			BeforeEach(func() {
   208  				summary = v7action.DetailedApplicationSummary{
   209  					ApplicationSummary: v7action.ApplicationSummary{
   210  						Application: v7action.Application{
   211  							GUID:      "some-app-guid",
   212  							Name:      "some-app",
   213  							StackName: "some-stack",
   214  							State:     constant.ApplicationStarted,
   215  						},
   216  						ProcessSummaries: v7action.ProcessSummaries{
   217  							{
   218  								Process: v7action.Process{
   219  									Type:               constant.ProcessTypeWeb,
   220  									Command:            *types.NewFilteredString("some-command-1"),
   221  									MemoryInMB:         types.NullUint64{IsSet: true, Value: 512},
   222  									DiskInMB:           types.NullUint64{IsSet: true, Value: 64},
   223  									HealthCheckTimeout: 60,
   224  									Instances:          types.NullInt{IsSet: true, Value: 5},
   225  								},
   226  								InstanceDetails: []v7action.ProcessInstance{
   227  									{State: constant.ProcessInstanceRunning},
   228  									{State: constant.ProcessInstanceRunning},
   229  									{State: constant.ProcessInstanceCrashed},
   230  									{State: constant.ProcessInstanceRunning},
   231  									{State: constant.ProcessInstanceRunning},
   232  								},
   233  							},
   234  							{
   235  								Process: v7action.Process{
   236  									Type:               "console",
   237  									Command:            *types.NewFilteredString("some-command-2"),
   238  									MemoryInMB:         types.NullUint64{IsSet: true, Value: 256},
   239  									DiskInMB:           types.NullUint64{IsSet: true, Value: 16},
   240  									HealthCheckTimeout: 120,
   241  									Instances:          types.NullInt{IsSet: true, Value: 1},
   242  								},
   243  								InstanceDetails: []v7action.ProcessInstance{
   244  									{State: constant.ProcessInstanceRunning},
   245  								},
   246  							},
   247  						},
   248  					},
   249  					CurrentDroplet: v7action.Droplet{
   250  						Stack: "cflinuxfs2",
   251  						Buildpacks: []v7action.DropletBuildpack{
   252  							{
   253  								Name:         "ruby_buildpack",
   254  								DetectOutput: "some-detect-output",
   255  							},
   256  							{
   257  								Name:         "some-buildpack",
   258  								DetectOutput: "",
   259  							},
   260  						},
   261  					},
   262  				}
   263  				fakePluginActor.GetDetailedAppSummaryReturns(summary, v7action.Warnings{"warning-1", "warning-2"}, nil)
   264  
   265  				fakeConfig.TargetedSpaceReturns(configv3.Space{
   266  					Name: "some-space",
   267  					GUID: "some-space-guid",
   268  				})
   269  
   270  			})
   271  
   272  			It("retrieves the app summary", func() {
   273  				result := plugin_models.DetailedApplicationSummary{}
   274  				err := client.Call("CliRpcCmd.GetApp", "some-app", &result)
   275  				Expect(err).ToNot(HaveOccurred())
   276  
   277  				Expect(fakePluginActor.GetDetailedAppSummaryCallCount()).To(Equal(1))
   278  				appName, spaceGUID, withObfuscatedValues := fakePluginActor.GetDetailedAppSummaryArgsForCall(0)
   279  				Expect(appName).To(Equal("some-app"))
   280  				Expect(spaceGUID).To(Equal("some-space-guid"))
   281  				Expect(withObfuscatedValues).To(BeTrue())
   282  			})
   283  
   284  			It("populates the plugin model with the retrieved app", func() {
   285  				result := plugin_models.DetailedApplicationSummary{}
   286  				err := client.Call("CliRpcCmd.GetApp", "some-app", &result)
   287  				Expect(err).ToNot(HaveOccurred())
   288  
   289  				//fmt.Fprintf(os.Stdout, "%+v", result)
   290  				Expect(result).To(BeEquivalentTo(summary))
   291  			})
   292  
   293  			Context("when retrieving the app fails", func() {
   294  				BeforeEach(func() {
   295  					fakePluginActor.GetDetailedAppSummaryReturns(v7action.DetailedApplicationSummary{}, nil, errors.New("some-error"))
   296  				})
   297  				It("returns an error", func() {
   298  					result := plugin_models.DetailedApplicationSummary{}
   299  					err := client.Call("CliRpcCmd.GetApp", "some-app", &result)
   300  					Expect(err).To(MatchError("some-error"))
   301  				})
   302  			})
   303  		})
   304  
   305  		Describe("GetOrg", func() {
   306  			var (
   307  				labels   map[string]types.NullString
   308  				metadata v7action.Metadata
   309  				org      v7action.Organization
   310  				spaces   []v7action.Space
   311  				domains  []v7action.Domain
   312  			)
   313  
   314  			BeforeEach(func() {
   315  				labels = map[string]types.NullString{
   316  					"k1": types.NewNullString("v1"),
   317  					"k2": types.NewNullString("v2"),
   318  				}
   319  
   320  				metadata = v7action.Metadata{
   321  					Labels: labels,
   322  				}
   323  
   324  				org = v7action.Organization{
   325  					Name:     "org-name",
   326  					GUID:     "org-guid",
   327  					Metadata: &metadata,
   328  				}
   329  
   330  				spaces = []v7action.Space{
   331  					v7action.Space{
   332  						Name: "space-name-1",
   333  						GUID: "space-guid-1",
   334  					},
   335  					v7action.Space{
   336  						Name: "space-name-2",
   337  						GUID: "space-guid-2",
   338  					},
   339  				}
   340  
   341  				domains = []v7action.Domain{
   342  					v7action.Domain{
   343  						Name:             "yodie.com",
   344  						GUID:             "yoodie.com-guid",
   345  						OrganizationGUID: org.GUID,
   346  					},
   347  				}
   348  
   349  				fakePluginActor.GetOrganizationByNameReturns(org, nil, nil)
   350  				fakePluginActor.GetOrganizationSpacesReturns(spaces, nil, nil)
   351  				fakePluginActor.GetOrganizationDomainsReturns(domains, nil, nil)
   352  			})
   353  
   354  			It("retrives the organization", func() {
   355  				result := plugin_models.OrgSummary{}
   356  				err := client.Call("CliRpcCmd.GetOrg", "org-name", &result)
   357  				Expect(err).ToNot(HaveOccurred())
   358  
   359  				Expect(fakePluginActor.GetOrganizationByNameCallCount()).To(Equal(1))
   360  				orgName := fakePluginActor.GetOrganizationByNameArgsForCall(0)
   361  				Expect(orgName).To(Equal(org.Name))
   362  			})
   363  
   364  			It("retrives the spaces for the organization", func() {
   365  				result := plugin_models.OrgSummary{}
   366  				err := client.Call("CliRpcCmd.GetOrg", "org-name", &result)
   367  				Expect(err).ToNot(HaveOccurred())
   368  
   369  				Expect(fakePluginActor.GetOrganizationSpacesCallCount()).To(Equal(1))
   370  				orgGUID := fakePluginActor.GetOrganizationSpacesArgsForCall(0)
   371  				Expect(orgGUID).To(Equal(org.GUID))
   372  			})
   373  
   374  			It("retrives the domains for the organization", func() {
   375  				result := plugin_models.OrgSummary{}
   376  				err := client.Call("CliRpcCmd.GetOrg", "org-name", &result)
   377  				Expect(err).ToNot(HaveOccurred())
   378  
   379  				Expect(fakePluginActor.GetOrganizationDomainsCallCount()).To(Equal(1))
   380  				orgGUID, labelSelector := fakePluginActor.GetOrganizationDomainsArgsForCall(0)
   381  				Expect(orgGUID).To(Equal(org.GUID))
   382  				Expect(labelSelector).To(Equal(""))
   383  			})
   384  
   385  			It("populates the plugin model with the retrieved org, space, and domain information", func() {
   386  				result := plugin_models.OrgSummary{}
   387  				err := client.Call("CliRpcCmd.GetOrg", "org-name", &result)
   388  				Expect(err).ToNot(HaveOccurred())
   389  
   390  				Expect(result.Name).To(Equal(org.Name))
   391  				Expect(result.GUID).To(Equal(org.GUID))
   392  
   393  				Expect(len(result.Spaces)).To(Equal(2))
   394  				Expect(result.Spaces[1].Name).To(Equal(spaces[1].Name))
   395  
   396  				Expect(len(result.Domains)).To(Equal(1))
   397  				Expect(result.Domains[0].Name).To(Equal(domains[0].Name))
   398  			})
   399  
   400  			It("populates the plugin model with Metadata", func() {
   401  				result := plugin_models.OrgSummary{}
   402  				err := client.Call("CliRpcCmd.GetOrg", "org-name", &result)
   403  				Expect(err).ToNot(HaveOccurred())
   404  
   405  				Expect(result.Metadata).ToNot(BeNil())
   406  				Expect(result.Metadata.Labels).To(BeEquivalentTo(labels))
   407  			})
   408  
   409  			Context("when retrieving the org fails", func() {
   410  				BeforeEach(func() {
   411  					fakePluginActor.GetOrganizationByNameReturns(v7action.Organization{}, nil, errors.New("org-error"))
   412  				})
   413  
   414  				It("returns an error", func() {
   415  					result := plugin_models.OrgSummary{}
   416  					err := client.Call("CliRpcCmd.GetOrg", "some-org", &result)
   417  					Expect(err).To(MatchError("org-error"))
   418  				})
   419  			})
   420  
   421  			Context("when retrieving the space fails", func() {
   422  				BeforeEach(func() {
   423  					fakePluginActor.GetOrganizationSpacesReturns([]v7action.Space{}, nil, errors.New("space-error"))
   424  				})
   425  
   426  				It("returns an error", func() {
   427  					result := plugin_models.OrgSummary{}
   428  					err := client.Call("CliRpcCmd.GetOrg", "some-org", &result)
   429  					Expect(err).To(MatchError("space-error"))
   430  				})
   431  			})
   432  		})
   433  
   434  		Describe("GetCurrentSpace", func() {
   435  			BeforeEach(func() {
   436  				fakeConfig.TargetedSpaceReturns(configv3.Space{
   437  					Name: "the-charlatans",
   438  					GUID: "united-travel-service",
   439  				})
   440  				fakeConfig.TargetedOrganizationReturns(configv3.Organization{
   441  					Name: "the-actress",
   442  					GUID: "family",
   443  				})
   444  				expectedSpace := v7action.Space{
   445  					GUID: "united-travel-service",
   446  					Name: "the-charlatans",
   447  				}
   448  				fakePluginActor.GetSpaceByNameAndOrganizationReturns(expectedSpace, v7action.Warnings{}, nil)
   449  			})
   450  
   451  			It("populates the plugin Space object with the current space settings in config", func() {
   452  				var space plugin_models.Space
   453  				err = client.Call("CliRpcCmd.GetCurrentSpace", "", &space)
   454  
   455  				Expect(err).ToNot(HaveOccurred())
   456  
   457  				result := plugin_models.DetailedApplicationSummary{}
   458  				err := client.Call("CliRpcCmd.GetApp", "some-app", &result)
   459  				Expect(err).ToNot(HaveOccurred())
   460  
   461  				Expect(fakePluginActor.GetSpaceByNameAndOrganizationCallCount()).To(Equal(1))
   462  				spaceName, orgGUID := fakePluginActor.GetSpaceByNameAndOrganizationArgsForCall(0)
   463  				Expect(spaceName).To(Equal("the-charlatans"))
   464  				Expect(orgGUID).To(Equal("family"))
   465  
   466  				Expect(space.Name).To(Equal("the-charlatans"))
   467  				Expect(space.GUID).To(Equal("united-travel-service"))
   468  			})
   469  
   470  			Context("when retrieving the current space fails", func() {
   471  				BeforeEach(func() {
   472  					fakePluginActor.GetSpaceByNameAndOrganizationReturns(v7action.Space{}, nil, errors.New("some-error"))
   473  				})
   474  
   475  				It("returns an error", func() {
   476  					result := plugin_models.DetailedApplicationSummary{}
   477  					err := client.Call("CliRpcCmd.GetCurrentSpace", "", &result)
   478  					Expect(err).To(MatchError("some-error"))
   479  				})
   480  			})
   481  		})
   482  
   483  		Describe("Username", func() {
   484  			When("logged in", func() {
   485  				BeforeEach(func() {
   486  					fakeConfig.CurrentUserNameReturns("Yodie", nil)
   487  				})
   488  				It("returns the logged in username", func() {
   489  					result := ""
   490  					err := client.Call("CliRpcCmd.Username", "", &result)
   491  					Expect(err).To(BeNil())
   492  					Expect(result).To(Equal("Yodie"))
   493  				})
   494  			})
   495  
   496  			When("not logged in", func() {
   497  				BeforeEach(func() {
   498  					fakeConfig.CurrentUserNameReturns("", nil)
   499  				})
   500  				It("returns the logged in username", func() {
   501  					result := ""
   502  					err := client.Call("CliRpcCmd.Username", "", &result)
   503  					Expect(result).To(Equal(""))
   504  					Expect(err).To(MatchError("not logged in"))
   505  				})
   506  			})
   507  			When("config errors", func() {
   508  				BeforeEach(func() {
   509  					fakeConfig.CurrentUserNameReturns("", errors.New("config failed.."))
   510  				})
   511  				It("returns error", func() {
   512  					result := ""
   513  					err := client.Call("CliRpcCmd.Username", "", &result)
   514  					Expect(result).To(Equal(""))
   515  					Expect(err).To(MatchError("error processing config: config failed.."))
   516  				})
   517  			})
   518  		})
   519  		Describe("AccessToken", func() {
   520  
   521  			BeforeEach(func() {
   522  				fakePluginActor.RefreshAccessTokenReturns("token example", nil)
   523  			})
   524  			It("retrieves the access token", func() {
   525  
   526  				result := ""
   527  				err := client.Call("CliRpcCmd.AccessToken", "", &result)
   528  				Expect(err).ToNot(HaveOccurred())
   529  				Expect(fakePluginActor.RefreshAccessTokenCallCount()).To(Equal(1))
   530  				Expect(result).To(Equal("token example"))
   531  
   532  			})
   533  		})
   534  
   535  		Describe("IsSkipSSLValidation", func() {
   536  			When("skip ssl validation is false", func() {
   537  				BeforeEach(func() {
   538  					fakeConfig.SkipSSLValidationReturns(false)
   539  				})
   540  
   541  				It("returns false", func() {
   542  					var result bool
   543  					err = client.Call("CliRpcCmd.IsSkipSSLValidation", "", &result)
   544  
   545  					Expect(err).ToNot(HaveOccurred())
   546  					Expect(result).To(BeFalse())
   547  				})
   548  			})
   549  			When("skip ssl validation is true", func() {
   550  				BeforeEach(func() {
   551  					fakeConfig.SkipSSLValidationReturns(true)
   552  				})
   553  
   554  				It("returns false", func() {
   555  					var result bool
   556  					err = client.Call("CliRpcCmd.IsSkipSSLValidation", "", &result)
   557  
   558  					Expect(err).ToNot(HaveOccurred())
   559  					Expect(result).To(BeTrue())
   560  				})
   561  			})
   562  		})
   563  	})
   564  
   565  	Describe(".CallCoreCommand", func() {
   566  
   567  		Describe("CLI Config object methods", func() {
   568  			var (
   569  				fakeConfig *commandfakes.FakeConfig
   570  			)
   571  
   572  			BeforeEach(func() {
   573  				fakeConfig = new(commandfakes.FakeConfig)
   574  			})
   575  
   576  			AfterEach(func() {
   577  				//give time for server to stop
   578  				time.Sleep(50 * time.Millisecond)
   579  			})
   580  
   581  			Context(".ApiEndpoint", func() {
   582  				BeforeEach(func() {
   583  					rpcService, err = cmdRunner.NewRpcService(nil, nil, nil, rpc.DefaultServer, fakeConfig, nil)
   584  					err := rpcService.Start()
   585  					Expect(err).ToNot(HaveOccurred())
   586  
   587  					pingCli(rpcService.Port())
   588  					fakeConfig.TargetReturns("www.fake-domain.com")
   589  				})
   590  				AfterEach(func() {
   591  					rpcService.Stop()
   592  
   593  					//give time for server to stop
   594  					time.Sleep(50 * time.Millisecond)
   595  				})
   596  
   597  				It("returns the ApiEndpoint() setting in config", func() {
   598  					client, err = rpc.Dial("tcp", "127.0.0.1:"+rpcService.Port())
   599  					Expect(err).ToNot(HaveOccurred())
   600  
   601  					var result string
   602  					err = client.Call("CliRpcCmd.ApiEndpoint", "", &result)
   603  					Expect(err).ToNot(HaveOccurred())
   604  					Expect(result).To(Equal("www.fake-domain.com"))
   605  					Expect(fakeConfig.TargetCallCount()).To(Equal(1))
   606  				})
   607  			})
   608  		})
   609  	})
   610  })
   611  
   612  func pingCli(port string) {
   613  	var connErr error
   614  	var conn net.Conn
   615  	for i := 0; i < 5; i++ {
   616  		conn, connErr = net.Dial("tcp", "127.0.0.1:"+port)
   617  		if connErr != nil {
   618  			time.Sleep(200 * time.Millisecond)
   619  		} else {
   620  			conn.Close()
   621  			break
   622  		}
   623  	}
   624  	Expect(connErr).ToNot(HaveOccurred())
   625  }