github.com/LukasHeimann/cloudfoundrycli/v8@v8.4.4/command/v7/service_command_test.go (about)

     1  package v7_test
     2  
     3  import (
     4  	"errors"
     5  	"strings"
     6  
     7  	"github.com/LukasHeimann/cloudfoundrycli/v8/actor/v7action"
     8  	"github.com/LukasHeimann/cloudfoundrycli/v8/command/commandfakes"
     9  	. "github.com/LukasHeimann/cloudfoundrycli/v8/command/v7"
    10  	"github.com/LukasHeimann/cloudfoundrycli/v8/command/v7/v7fakes"
    11  	"github.com/LukasHeimann/cloudfoundrycli/v8/resources"
    12  	"github.com/LukasHeimann/cloudfoundrycli/v8/types"
    13  	"github.com/LukasHeimann/cloudfoundrycli/v8/util/configv3"
    14  	"github.com/LukasHeimann/cloudfoundrycli/v8/util/ui"
    15  	. "github.com/onsi/ginkgo"
    16  	. "github.com/onsi/gomega"
    17  	. "github.com/onsi/gomega/gbytes"
    18  )
    19  
    20  var _ = Describe("service command", func() {
    21  	const (
    22  		serviceInstanceName = "fake-service-instance-name"
    23  		serviceInstanceGUID = "fake-service-instance-guid"
    24  		spaceName           = "fake-space-name"
    25  		spaceGUID           = "fake-space-guid"
    26  		orgName             = "fake-org-name"
    27  		username            = "fake-user-name"
    28  	)
    29  	var (
    30  		cmd             ServiceCommand
    31  		testUI          *ui.UI
    32  		fakeConfig      *commandfakes.FakeConfig
    33  		fakeSharedActor *commandfakes.FakeSharedActor
    34  		fakeActor       *v7fakes.FakeActor
    35  		executeErr      error
    36  	)
    37  
    38  	JustBeforeEach(func() {
    39  		executeErr = cmd.Execute(nil)
    40  	})
    41  
    42  	BeforeEach(func() {
    43  		testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
    44  		fakeConfig = new(commandfakes.FakeConfig)
    45  		fakeSharedActor = new(commandfakes.FakeSharedActor)
    46  		fakeActor = new(v7fakes.FakeActor)
    47  
    48  		cmd = ServiceCommand{
    49  			BaseCommand: BaseCommand{
    50  				UI:          testUI,
    51  				Config:      fakeConfig,
    52  				SharedActor: fakeSharedActor,
    53  				Actor:       fakeActor,
    54  			},
    55  		}
    56  
    57  		fakeActor.GetCurrentUserReturns(configv3.User{Name: username}, nil)
    58  
    59  		fakeConfig.TargetedSpaceReturns(configv3.Space{
    60  			GUID: spaceGUID,
    61  			Name: spaceName,
    62  		})
    63  
    64  		fakeConfig.TargetedOrganizationReturns(configv3.Organization{
    65  			Name: orgName,
    66  		})
    67  
    68  		fakeActor.GetServiceInstanceDetailsReturns(
    69  			v7action.ServiceInstanceDetails{
    70  				ServiceInstance: resources.ServiceInstance{
    71  					GUID: serviceInstanceGUID,
    72  					Name: serviceInstanceName,
    73  				},
    74  				SharedStatus: v7action.SharedStatus{
    75  					IsSharedToOtherSpaces: false,
    76  				},
    77  			},
    78  			v7action.Warnings{"warning one", "warning two"},
    79  			nil,
    80  		)
    81  
    82  		setPositionalFlags(&cmd, serviceInstanceName)
    83  	})
    84  
    85  	It("checks the user is logged in, and targeting an org and space", func() {
    86  		Expect(fakeSharedActor.CheckTargetCallCount()).To(Equal(1))
    87  		orgChecked, spaceChecked := fakeSharedActor.CheckTargetArgsForCall(0)
    88  		Expect(orgChecked).To(BeTrue())
    89  		Expect(spaceChecked).To(BeTrue())
    90  	})
    91  
    92  	When("the --guid flag is specified", func() {
    93  		BeforeEach(func() {
    94  			fakeActor.GetServiceInstanceByNameAndSpaceReturns(
    95  				resources.ServiceInstance{
    96  					GUID: serviceInstanceGUID,
    97  					Name: serviceInstanceName,
    98  				},
    99  				v7action.Warnings{"warning one", "warning two"},
   100  				nil,
   101  			)
   102  
   103  			setFlag(&cmd, "--guid")
   104  		})
   105  
   106  		It("looks up the service instance and prints the GUID and no warnings", func() {
   107  			Expect(executeErr).NotTo(HaveOccurred())
   108  
   109  			Expect(fakeActor.GetServiceInstanceByNameAndSpaceCallCount()).To(Equal(1))
   110  			actualName, actualSpaceGUID := fakeActor.GetServiceInstanceByNameAndSpaceArgsForCall(0)
   111  			Expect(actualName).To(Equal(serviceInstanceName))
   112  			Expect(actualSpaceGUID).To(Equal(spaceGUID))
   113  
   114  			Expect(testUI.Out).To(Say(`^%s\n$`, serviceInstanceGUID))
   115  			Expect(testUI.Err).NotTo(Say("warning"))
   116  		})
   117  
   118  		When("getting the service instance fails", func() {
   119  			BeforeEach(func() {
   120  				fakeActor.GetServiceInstanceByNameAndSpaceReturns(
   121  					resources.ServiceInstance{
   122  						GUID: serviceInstanceGUID,
   123  						Name: serviceInstanceName,
   124  					},
   125  					v7action.Warnings{"warning one", "warning two"},
   126  					errors.New("yuck"),
   127  				)
   128  			})
   129  
   130  			It("returns the error", func() {
   131  				Expect(executeErr).To(MatchError("yuck"))
   132  			})
   133  		})
   134  	})
   135  
   136  	When("it is a user-provided service instance", func() {
   137  		const (
   138  			routeServiceURL          = "https://route.com"
   139  			syslogURL                = "https://syslog.com"
   140  			tags                     = "foo, bar"
   141  			lastOperationType        = "update"
   142  			lastOperationState       = "succeeded"
   143  			lastOperationDescription = "doing amazing work"
   144  			lastOperationStartTime   = "a second ago"
   145  			lastOperationUpdatedTime = "just now"
   146  		)
   147  
   148  		BeforeEach(func() {
   149  			fakeActor.GetServiceInstanceDetailsReturns(
   150  				v7action.ServiceInstanceDetails{
   151  					ServiceInstance: resources.ServiceInstance{
   152  						GUID:            serviceInstanceGUID,
   153  						Name:            serviceInstanceName,
   154  						Type:            resources.UserProvidedServiceInstance,
   155  						SyslogDrainURL:  types.NewOptionalString(syslogURL),
   156  						RouteServiceURL: types.NewOptionalString(routeServiceURL),
   157  						Tags:            types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   158  						LastOperation: resources.LastOperation{
   159  							Type:        lastOperationType,
   160  							State:       lastOperationState,
   161  							Description: lastOperationDescription,
   162  							CreatedAt:   lastOperationStartTime,
   163  							UpdatedAt:   lastOperationUpdatedTime,
   164  						},
   165  					},
   166  				},
   167  				v7action.Warnings{"warning one", "warning two"},
   168  				nil,
   169  			)
   170  		})
   171  
   172  		It("looks up the service instance and prints the details and warnings", func() {
   173  			Expect(executeErr).NotTo(HaveOccurred())
   174  
   175  			Expect(fakeActor.GetServiceInstanceDetailsCallCount()).To(Equal(1))
   176  			actualName, actualSpaceGUID, actualOmitApps := fakeActor.GetServiceInstanceDetailsArgsForCall(0)
   177  			Expect(actualName).To(Equal(serviceInstanceName))
   178  			Expect(actualSpaceGUID).To(Equal(spaceGUID))
   179  			Expect(actualOmitApps).To(BeFalse())
   180  
   181  			Expect(testUI.Out).To(SatisfyAll(
   182  				Say(`Showing info of service %s in org %s / space %s as %s...\n`, serviceInstanceName, orgName, spaceName, username),
   183  				Say(`\n`),
   184  				Say(`name:\s+%s\n`, serviceInstanceName),
   185  				Say(`guid:\s+\S+\n`),
   186  				Say(`type:\s+user-provided`),
   187  				Say(`tags:\s+%s\n`, tags),
   188  				Say(`route service url:\s+%s\n`, routeServiceURL),
   189  				Say(`syslog drain url:\s+%s\n`, syslogURL),
   190  				Say(`\n`),
   191  				Say(`Showing status of last operation:\n`),
   192  				Say(`\s+status:\s+%s %s\n`, lastOperationType, lastOperationState),
   193  				Say(`\s+message:\s+%s\n`, lastOperationDescription),
   194  				Say(`\s+started:\s+%s\n`, lastOperationStartTime),
   195  				Say(`\s+updated:\s+%s\n`, lastOperationUpdatedTime),
   196  				Say(`\n`),
   197  				Say(`Showing bound apps:\n`),
   198  				Say(`There are no bound apps for this service instance\.\n`),
   199  			))
   200  
   201  			Expect(testUI.Err).To(SatisfyAll(
   202  				Say("warning one"),
   203  				Say("warning two"),
   204  			))
   205  		})
   206  
   207  		When("last operation is not set", func() {
   208  			BeforeEach(func() {
   209  				fakeActor.GetServiceInstanceDetailsReturns(
   210  					v7action.ServiceInstanceDetails{
   211  						ServiceInstance: resources.ServiceInstance{
   212  							GUID:            serviceInstanceGUID,
   213  							Name:            serviceInstanceName,
   214  							Type:            resources.UserProvidedServiceInstance,
   215  							SyslogDrainURL:  types.NewOptionalString(syslogURL),
   216  							RouteServiceURL: types.NewOptionalString(routeServiceURL),
   217  							Tags:            types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   218  						},
   219  					},
   220  					v7action.Warnings{"warning one", "warning two"},
   221  					nil,
   222  				)
   223  			})
   224  
   225  			It("when last operation does not exist", func() {
   226  				Expect(executeErr).NotTo(HaveOccurred())
   227  
   228  				Expect(fakeActor.GetServiceInstanceDetailsCallCount()).To(Equal(1))
   229  				actualName, actualSpaceGUID, actualOmitApps := fakeActor.GetServiceInstanceDetailsArgsForCall(0)
   230  				Expect(actualName).To(Equal(serviceInstanceName))
   231  				Expect(actualSpaceGUID).To(Equal(spaceGUID))
   232  				Expect(actualOmitApps).To(BeFalse())
   233  
   234  				Expect(testUI.Out).To(SatisfyAll(
   235  					Say(`Showing info of service %s in org %s / space %s as %s...\n`, serviceInstanceName, orgName, spaceName, username),
   236  					Say(`\n`),
   237  					Say(`name:\s+%s\n`, serviceInstanceName),
   238  					Say(`guid:\s+\S+\n`),
   239  					Say(`type:\s+user-provided`),
   240  					Say(`tags:\s+%s\n`, tags),
   241  					Say(`route service url:\s+%s\n`, routeServiceURL),
   242  					Say(`syslog drain url:\s+%s\n`, syslogURL),
   243  					Say(`\n`),
   244  					Say(`Showing status of last operation:\n`),
   245  					Say(`There is no last operation available for this service instance\.\n`),
   246  					Say(`\n`),
   247  					Say(`Showing bound apps:\n`),
   248  					Say(`There are no bound apps for this service instance\.\n`),
   249  				))
   250  
   251  				Expect(testUI.Err).To(SatisfyAll(
   252  					Say("warning one"),
   253  					Say("warning two"),
   254  				))
   255  			})
   256  		})
   257  
   258  		When("there are bound apps", func() {
   259  			BeforeEach(func() {
   260  				fakeActor.GetServiceInstanceDetailsReturns(
   261  					v7action.ServiceInstanceDetails{
   262  						ServiceInstance: resources.ServiceInstance{
   263  							GUID:            serviceInstanceGUID,
   264  							Name:            serviceInstanceName,
   265  							Type:            resources.UserProvidedServiceInstance,
   266  							SyslogDrainURL:  types.NewOptionalString(syslogURL),
   267  							RouteServiceURL: types.NewOptionalString(routeServiceURL),
   268  							Tags:            types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   269  						},
   270  						BoundApps: []resources.ServiceCredentialBinding{
   271  							{
   272  								Name:    "named-binding",
   273  								AppName: "app-1",
   274  								LastOperation: resources.LastOperation{
   275  									Type:        resources.CreateOperation,
   276  									State:       resources.OperationSucceeded,
   277  									Description: "great",
   278  								},
   279  							},
   280  							{
   281  								AppName: "app-2",
   282  								LastOperation: resources.LastOperation{
   283  									Type:        resources.UpdateOperation,
   284  									State:       resources.OperationFailed,
   285  									Description: "sorry",
   286  								},
   287  							},
   288  						},
   289  					},
   290  					v7action.Warnings{"warning one", "warning two"},
   291  					nil,
   292  				)
   293  			})
   294  
   295  			It("prints the bound apps table", func() {
   296  				Expect(testUI.Out).To(SatisfyAll(
   297  					Say(`Showing bound apps:\n`),
   298  					Say(`   name\s+binding name\s+status\s+message\n`),
   299  					Say(`   app-1\s+named-binding\s+create succeeded\s+great\n`),
   300  					Say(`   app-2\s+update failed\s+sorry\n`),
   301  				))
   302  			})
   303  		})
   304  	})
   305  
   306  	When("it is a managed service instance", func() {
   307  		const (
   308  			dashboardURL               = "https://dashboard.com"
   309  			tags                       = "foo, bar"
   310  			servicePlanName            = "fake-service-plan-name"
   311  			serviceOfferingName        = "fake-service-offering-name"
   312  			serviceOfferingDescription = "an amazing service"
   313  			serviceOfferingDocs        = "https://service.docs.com"
   314  			serviceBrokerName          = "fake-service-broker-name"
   315  			lastOperationType          = "create"
   316  			lastOperationState         = "in progress"
   317  			lastOperationDescription   = "doing amazing work"
   318  			lastOperationStartTime     = "a second ago"
   319  			lastOperationUpdatedTime   = "just now"
   320  		)
   321  
   322  		BeforeEach(func() {
   323  			fakeActor.GetServiceInstanceDetailsReturns(
   324  				v7action.ServiceInstanceDetails{
   325  					ServiceInstance: resources.ServiceInstance{
   326  						GUID:         serviceInstanceGUID,
   327  						Name:         serviceInstanceName,
   328  						Type:         resources.ManagedServiceInstance,
   329  						DashboardURL: types.NewOptionalString(dashboardURL),
   330  						Tags:         types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   331  						LastOperation: resources.LastOperation{
   332  							Type:        lastOperationType,
   333  							State:       lastOperationState,
   334  							Description: lastOperationDescription,
   335  							CreatedAt:   lastOperationStartTime,
   336  							UpdatedAt:   lastOperationUpdatedTime,
   337  						},
   338  						SpaceGUID: spaceGUID,
   339  					},
   340  					SpaceName:        spaceName,
   341  					OrganizationName: orgName,
   342  					ServiceOffering: resources.ServiceOffering{
   343  						Name:             serviceOfferingName,
   344  						Description:      serviceOfferingDescription,
   345  						DocumentationURL: serviceOfferingDocs,
   346  					},
   347  					ServicePlan:       resources.ServicePlan{Name: servicePlanName},
   348  					ServiceBrokerName: serviceBrokerName,
   349  					SharedStatus: v7action.SharedStatus{
   350  						IsSharedToOtherSpaces: true,
   351  						UsageSummary:          []v7action.UsageSummaryWithSpaceAndOrg{{"shared-to-space", "some-org", 3}},
   352  					},
   353  				},
   354  				v7action.Warnings{"warning one", "warning two"},
   355  				nil,
   356  			)
   357  		})
   358  
   359  		It("looks up the service instance and prints the details and warnings", func() {
   360  			Expect(executeErr).NotTo(HaveOccurred())
   361  
   362  			Expect(fakeActor.GetServiceInstanceDetailsCallCount()).To(Equal(1))
   363  			actualName, actualSpaceGUID, actualOmitApps := fakeActor.GetServiceInstanceDetailsArgsForCall(0)
   364  			Expect(actualName).To(Equal(serviceInstanceName))
   365  			Expect(actualSpaceGUID).To(Equal(spaceGUID))
   366  			Expect(actualOmitApps).To(BeFalse())
   367  
   368  			Expect(testUI.Out).To(SatisfyAll(
   369  				Say(`Showing info of service %s in org %s / space %s as %s...\n`, serviceInstanceName, orgName, spaceName, username),
   370  				Say(`\n`),
   371  				Say(`name:\s+%s\n`, serviceInstanceName),
   372  				Say(`guid:\s+\S+\n`),
   373  				Say(`type:\s+managed`),
   374  				Say(`broker:\s+%s`, serviceBrokerName),
   375  				Say(`offering:\s+%s`, serviceOfferingName),
   376  				Say(`plan:\s+%s`, servicePlanName),
   377  				Say(`tags:\s+%s\n`, tags),
   378  				Say(`description:\s+%s\n`, serviceOfferingDescription),
   379  				Say(`documentation:\s+%s\n`, serviceOfferingDocs),
   380  				Say(`dashboard url:\s+%s\n`, dashboardURL),
   381  				Say(`\n`),
   382  				Say(`Showing status of last operation:`),
   383  				Say(`status:\s+%s %s\n`, lastOperationType, lastOperationState),
   384  				Say(`message:\s+%s\n`, lastOperationDescription),
   385  				Say(`started:\s+%s\n`, lastOperationStartTime),
   386  				Say(`updated:\s+%s\n`, lastOperationUpdatedTime),
   387  				Say(`\n`),
   388  				Say(`Showing bound apps:`),
   389  				Say(`There are no bound apps for this service instance\.\n`),
   390  				Say(`\n`),
   391  				Say(`Showing sharing info:`),
   392  				Say(`Shared with spaces:\n`),
   393  				Say(`Showing upgrade status:`),
   394  				Say(`Upgrades are not supported by this broker.\n`),
   395  			))
   396  
   397  			Expect(testUI.Err).To(SatisfyAll(
   398  				Say("warning one"),
   399  				Say("warning two"),
   400  			))
   401  		})
   402  
   403  		Context("sharing", func() {
   404  			When("targeting original space", func() {
   405  				When("service instance is shared", func() {
   406  					It("shows shared information", func() {
   407  						Expect(testUI.Out).To(SatisfyAll(
   408  							Say(`Showing sharing info:`),
   409  							Say(`Shared with spaces:`),
   410  							Say(`org\s+space\s+bindings\s*\n`),
   411  							Say(`some-org\s+shared-to-space\s+3\s*\n`),
   412  						))
   413  					})
   414  				})
   415  
   416  				When("service is not shared", func() {
   417  					BeforeEach(func() {
   418  						fakeActor.GetServiceInstanceDetailsReturns(
   419  							v7action.ServiceInstanceDetails{
   420  								ServiceInstance: resources.ServiceInstance{
   421  									SpaceGUID: spaceGUID,
   422  								},
   423  								SharedStatus: v7action.SharedStatus{
   424  									IsSharedToOtherSpaces: false,
   425  								},
   426  							},
   427  							v7action.Warnings{},
   428  							nil,
   429  						)
   430  					})
   431  
   432  					It("displays that the service is not shared", func() {
   433  						Expect(testUI.Out).To(SatisfyAll(
   434  							Say(`Showing sharing info:`),
   435  							Say(`This service instance is not currently being shared.`),
   436  						))
   437  					})
   438  				})
   439  
   440  				When("the service instance sharing feature is disabled", func() {
   441  					BeforeEach(func() {
   442  						fakeActor.GetServiceInstanceDetailsReturns(
   443  							v7action.ServiceInstanceDetails{
   444  								ServiceInstance: resources.ServiceInstance{
   445  									SpaceGUID: spaceGUID,
   446  								},
   447  								SharedStatus: v7action.SharedStatus{
   448  									FeatureFlagIsDisabled: true,
   449  								},
   450  							},
   451  							v7action.Warnings{},
   452  							nil,
   453  						)
   454  					})
   455  
   456  					It("displays that the sharing feature is disabled", func() {
   457  						Expect(testUI.Out).To(SatisfyAll(
   458  							Say(`Showing sharing info:\n`),
   459  							Say(`\n`),
   460  							Say(`The "service_instance_sharing" feature flag is disabled for this Cloud Foundry platform.\n`),
   461  							Say(`\n`),
   462  						))
   463  					})
   464  				})
   465  
   466  				When("the service instance sharing feature is enabled", func() {
   467  					BeforeEach(func() {
   468  						fakeActor.GetServiceInstanceDetailsReturns(
   469  							v7action.ServiceInstanceDetails{
   470  								ServiceInstance: resources.ServiceInstance{},
   471  								SharedStatus: v7action.SharedStatus{
   472  									FeatureFlagIsDisabled: false,
   473  								},
   474  							},
   475  							v7action.Warnings{},
   476  							nil,
   477  						)
   478  					})
   479  
   480  					It("does not display a warning", func() {
   481  						Expect(testUI.Out).NotTo(
   482  							Say(`The "service_instance_sharing" feature flag is disabled for this Cloud Foundry platform.`),
   483  						)
   484  					})
   485  				})
   486  
   487  				When("the offering does not allow service instance sharing", func() {
   488  					BeforeEach(func() {
   489  						fakeActor.GetServiceInstanceDetailsReturns(
   490  							v7action.ServiceInstanceDetails{
   491  								ServiceInstance: resources.ServiceInstance{
   492  									SpaceGUID: spaceGUID,
   493  								},
   494  								SharedStatus: v7action.SharedStatus{
   495  									OfferingDisablesSharing: true,
   496  								},
   497  							},
   498  							v7action.Warnings{},
   499  							nil,
   500  						)
   501  					})
   502  
   503  					It("displays that the sharing feature is disabled", func() {
   504  						Expect(testUI.Out).To(SatisfyAll(
   505  							Say(`Showing sharing info:\n`),
   506  							Say(`\n`),
   507  							Say(`Service instance sharing is disabled for this service offering.\n`),
   508  							Say(`\n`),
   509  						))
   510  					})
   511  				})
   512  
   513  				When("the offering does allow service instance sharing", func() {
   514  					BeforeEach(func() {
   515  						fakeActor.GetServiceInstanceDetailsReturns(
   516  							v7action.ServiceInstanceDetails{
   517  								ServiceInstance: resources.ServiceInstance{},
   518  								SharedStatus: v7action.SharedStatus{
   519  									OfferingDisablesSharing: false,
   520  								},
   521  							},
   522  							v7action.Warnings{},
   523  							nil,
   524  						)
   525  					})
   526  
   527  					It("does not display a warning", func() {
   528  						Expect(testUI.Out).NotTo(
   529  							Say(`Service instance sharing is disabled for this service offering.`),
   530  						)
   531  					})
   532  				})
   533  			})
   534  
   535  			When("targeting shared to space", func() {
   536  				BeforeEach(func() {
   537  					sharedToSpaceGUID := "fake-shared-to-space-guid"
   538  					sharedToSpaceName := "fake-shared-to-space-name"
   539  					fakeConfig.TargetedSpaceReturns(configv3.Space{
   540  						GUID: sharedToSpaceGUID,
   541  						Name: sharedToSpaceName,
   542  					})
   543  
   544  					fakeActor.GetServiceInstanceDetailsReturns(
   545  						v7action.ServiceInstanceDetails{
   546  							ServiceInstance: resources.ServiceInstance{
   547  								SpaceGUID: spaceGUID,
   548  							},
   549  							SpaceName:        spaceName,
   550  							OrganizationName: orgName,
   551  							SharedStatus: v7action.SharedStatus{
   552  								IsSharedFromOriginalSpace: true,
   553  								IsSharedToOtherSpaces:     true,
   554  								OfferingDisablesSharing:   true,
   555  								FeatureFlagIsDisabled:     true,
   556  							},
   557  						},
   558  						v7action.Warnings{},
   559  						nil,
   560  					)
   561  				})
   562  
   563  				It("shows original space information", func() {
   564  					Expect(testUI.Out).To(SatisfyAll(
   565  						Say(`Showing sharing info:\n`),
   566  						Say(`This service instance is shared from space %s of org %s.\n`, spaceName, orgName),
   567  					))
   568  				})
   569  
   570  				It("doesn't display any sharing warning", func() {
   571  					Expect(testUI.Out).NotTo(SatisfyAny(
   572  						Say(`This service instance is currently shared.`),
   573  						Say(`The "service_instance_sharing" feature flag is disabled for this Cloud Foundry platform.`),
   574  						Say(`Service instance sharing is disabled for this service offering.`),
   575  					))
   576  				})
   577  			})
   578  		})
   579  
   580  		Context("upgrading", func() {
   581  			When("an upgrade is available", func() {
   582  				BeforeEach(func() {
   583  					fakeActor.GetServiceInstanceDetailsReturns(
   584  						v7action.ServiceInstanceDetails{
   585  							ServiceInstance: resources.ServiceInstance{
   586  								GUID:         serviceInstanceGUID,
   587  								Name:         serviceInstanceName,
   588  								Type:         resources.ManagedServiceInstance,
   589  								DashboardURL: types.NewOptionalString(dashboardURL),
   590  								Tags:         types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   591  								LastOperation: resources.LastOperation{
   592  									Type:        lastOperationType,
   593  									State:       lastOperationState,
   594  									Description: lastOperationDescription,
   595  									CreatedAt:   lastOperationStartTime,
   596  									UpdatedAt:   lastOperationUpdatedTime,
   597  								},
   598  							},
   599  							ServiceOffering: resources.ServiceOffering{
   600  								Name:             serviceOfferingName,
   601  								Description:      serviceOfferingDescription,
   602  								DocumentationURL: serviceOfferingDocs,
   603  							},
   604  							ServicePlan:       resources.ServicePlan{Name: servicePlanName},
   605  							ServiceBrokerName: serviceBrokerName,
   606  							UpgradeStatus: v7action.ServiceInstanceUpgradeStatus{
   607  								State:       v7action.ServiceInstanceUpgradeAvailable,
   608  								Description: "really cool upgrade\nwith juicy bits",
   609  							},
   610  						},
   611  						v7action.Warnings{"warning one", "warning two"},
   612  						nil,
   613  					)
   614  				})
   615  
   616  				It("says an upgrade is available and shows the description", func() {
   617  					Expect(executeErr).NotTo(HaveOccurred())
   618  					Expect(testUI.Out).To(SatisfyAll(
   619  						Say(`Showing upgrade status:\n`),
   620  						Say(`There is an upgrade available for this service.\n`),
   621  						Say(`Upgrade description: really cool upgrade\n`),
   622  						Say(`with juicy bits\n`),
   623  						Say(`TIP: You can upgrade using 'cf upgrade-service %s'\n`, serviceInstanceName),
   624  					))
   625  				})
   626  			})
   627  
   628  			When("the service instance is up to date", func() {
   629  				BeforeEach(func() {
   630  					fakeActor.GetServiceInstanceDetailsReturns(
   631  						v7action.ServiceInstanceDetails{
   632  							ServiceInstance: resources.ServiceInstance{
   633  								GUID:         serviceInstanceGUID,
   634  								Name:         serviceInstanceName,
   635  								Type:         resources.ManagedServiceInstance,
   636  								DashboardURL: types.NewOptionalString(dashboardURL),
   637  								Tags:         types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   638  								LastOperation: resources.LastOperation{
   639  									Type:        lastOperationType,
   640  									State:       lastOperationState,
   641  									Description: lastOperationDescription,
   642  									CreatedAt:   lastOperationStartTime,
   643  									UpdatedAt:   lastOperationUpdatedTime,
   644  								},
   645  							},
   646  							ServiceOffering: resources.ServiceOffering{
   647  								Name:             serviceOfferingName,
   648  								Description:      serviceOfferingDescription,
   649  								DocumentationURL: serviceOfferingDocs,
   650  							},
   651  							ServicePlan:       resources.ServicePlan{Name: servicePlanName},
   652  							ServiceBrokerName: serviceBrokerName,
   653  							UpgradeStatus: v7action.ServiceInstanceUpgradeStatus{
   654  								State: v7action.ServiceInstanceUpgradeNotAvailable,
   655  							},
   656  						},
   657  						v7action.Warnings{"warning one", "warning two"},
   658  						nil,
   659  					)
   660  				})
   661  
   662  				It("says an upgrade is available and shows the description", func() {
   663  					Expect(executeErr).NotTo(HaveOccurred())
   664  					Expect(testUI.Out).To(SatisfyAll(
   665  						Say(`Showing upgrade status:\n`),
   666  						Say(`There is no upgrade available for this service.\n`),
   667  					))
   668  				})
   669  			})
   670  		})
   671  
   672  		Context("bound apps", func() {
   673  			BeforeEach(func() {
   674  				fakeActor.GetServiceInstanceDetailsReturns(
   675  					v7action.ServiceInstanceDetails{
   676  						ServiceInstance: resources.ServiceInstance{
   677  							GUID:         serviceInstanceGUID,
   678  							Name:         serviceInstanceName,
   679  							Type:         resources.ManagedServiceInstance,
   680  							DashboardURL: types.NewOptionalString(dashboardURL),
   681  							Tags:         types.NewOptionalStringSlice(strings.Split(tags, ", ")...),
   682  							LastOperation: resources.LastOperation{
   683  								Type:        lastOperationType,
   684  								State:       lastOperationState,
   685  								Description: lastOperationDescription,
   686  								CreatedAt:   lastOperationStartTime,
   687  								UpdatedAt:   lastOperationUpdatedTime,
   688  							},
   689  							SpaceGUID: spaceGUID,
   690  						},
   691  						SpaceName:        spaceName,
   692  						OrganizationName: orgName,
   693  						ServiceOffering: resources.ServiceOffering{
   694  							Name:             serviceOfferingName,
   695  							Description:      serviceOfferingDescription,
   696  							DocumentationURL: serviceOfferingDocs,
   697  						},
   698  						ServicePlan:       resources.ServicePlan{Name: servicePlanName},
   699  						ServiceBrokerName: serviceBrokerName,
   700  						SharedStatus: v7action.SharedStatus{
   701  							IsSharedToOtherSpaces: true,
   702  						},
   703  						BoundApps: []resources.ServiceCredentialBinding{
   704  							{
   705  								Name:    "named-binding",
   706  								AppName: "app-1",
   707  								LastOperation: resources.LastOperation{
   708  									Type:        resources.CreateOperation,
   709  									State:       resources.OperationSucceeded,
   710  									Description: "great",
   711  								},
   712  							},
   713  							{
   714  								AppName: "app-2",
   715  								LastOperation: resources.LastOperation{
   716  									Type:        resources.UpdateOperation,
   717  									State:       resources.OperationFailed,
   718  									Description: "sorry",
   719  								},
   720  							},
   721  						},
   722  					},
   723  					v7action.Warnings{"warning one", "warning two"},
   724  					nil,
   725  				)
   726  			})
   727  
   728  			It("prints the bound apps table", func() {
   729  				Expect(testUI.Out).To(SatisfyAll(
   730  					Say(`Showing bound apps:\n`),
   731  					Say(`name\s+binding name\s+status\s+message\n`),
   732  					Say(`app-1\s+named-binding\s+create succeeded\s+great\n`),
   733  					Say(`app-2\s+update failed\s+sorry\n`),
   734  				))
   735  			})
   736  		})
   737  	})
   738  
   739  	When("the --params flag is specified", func() {
   740  		BeforeEach(func() {
   741  			setFlag(&cmd, "--params")
   742  		})
   743  
   744  		When("parameters are set", func() {
   745  			BeforeEach(func() {
   746  				fakeActor.GetServiceInstanceParametersReturns(
   747  					map[string]interface{}{"foo": "bar"},
   748  					v7action.Warnings{"warning one", "warning two"},
   749  					nil,
   750  				)
   751  			})
   752  
   753  			It("returns parameters JSON", func() {
   754  				Expect(executeErr).NotTo(HaveOccurred())
   755  
   756  				Expect(fakeActor.GetServiceInstanceParametersCallCount()).To(Equal(1))
   757  				actualName, actualSpaceGUID := fakeActor.GetServiceInstanceParametersArgsForCall(0)
   758  				Expect(actualName).To(Equal(serviceInstanceName))
   759  				Expect(actualSpaceGUID).To(Equal(spaceGUID))
   760  
   761  				Expect(testUI.Out).To(SatisfyAll(
   762  					Say(`\{\n`),
   763  					Say(`  "foo": "bar"\n`),
   764  					Say(`\}\n`),
   765  				))
   766  				Expect(testUI.Err).To(SatisfyAll(
   767  					Say("warning one"),
   768  					Say("warning two"),
   769  				))
   770  			})
   771  		})
   772  
   773  		When("there was a problem retrieving the parameters", func() {
   774  			BeforeEach(func() {
   775  				fakeActor.GetServiceInstanceParametersReturns(
   776  					v7action.ServiceInstanceParameters{},
   777  					v7action.Warnings{"warning one", "warning two"},
   778  					errors.New("something happened"),
   779  				)
   780  			})
   781  
   782  			It("displays the reason", func() {
   783  				Expect(executeErr).To(MatchError("something happened"))
   784  				Expect(testUI.Err).To(SatisfyAll(
   785  					Say("warning one"),
   786  					Say("warning two"),
   787  				))
   788  			})
   789  		})
   790  
   791  		When("the parameters are empty", func() {
   792  			BeforeEach(func() {
   793  				fakeActor.GetServiceInstanceParametersReturns(
   794  					v7action.ServiceInstanceParameters{},
   795  					v7action.Warnings{"warning one", "warning two"},
   796  					nil,
   797  				)
   798  			})
   799  
   800  			It("displays empty parameters", func() {
   801  				Expect(executeErr).NotTo(HaveOccurred())
   802  				Expect(testUI.Out).To(Say(`{}\n`))
   803  			})
   804  		})
   805  	})
   806  
   807  	When("there is a problem looking up the service instance", func() {
   808  		BeforeEach(func() {
   809  			fakeActor.GetServiceInstanceDetailsReturns(
   810  				v7action.ServiceInstanceDetails{},
   811  				v7action.Warnings{"warning one", "warning two"},
   812  				errors.New("boom"),
   813  			)
   814  		})
   815  
   816  		It("prints warnings and returns an error", func() {
   817  			Expect(executeErr).To(MatchError("boom"))
   818  
   819  			Expect(testUI.Err).To(SatisfyAll(
   820  				Say("warning one"),
   821  				Say("warning two"),
   822  			))
   823  		})
   824  	})
   825  
   826  	When("checking the target returns an error", func() {
   827  		BeforeEach(func() {
   828  			fakeSharedActor.CheckTargetReturns(errors.New("explode"))
   829  		})
   830  
   831  		It("returns the error", func() {
   832  			Expect(executeErr).To(MatchError("explode"))
   833  		})
   834  	})
   835  })