diff --git a/actor/v7action/service_app_binding.go b/actor/v7action/service_app_binding.go index 7ff7f47102..ccb7dcf023 100644 --- a/actor/v7action/service_app_binding.go +++ b/actor/v7action/service_app_binding.go @@ -17,12 +17,16 @@ type CreateServiceAppBindingParams struct { Parameters types.OptionalObject } -type DeleteServiceAppBindingParams struct { +type ListServiceAppBindingParams struct { SpaceGUID string ServiceInstanceName string AppName string } +type DeleteServiceAppBindingParams struct { + ServiceBindingGUID string +} + func (actor Actor) CreateServiceAppBinding(params CreateServiceAppBindingParams) (chan PollJobEvent, Warnings, error) { var ( serviceInstance resources.ServiceInstance @@ -60,13 +64,11 @@ func (actor Actor) CreateServiceAppBinding(params CreateServiceAppBindingParams) } } -func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams) (chan PollJobEvent, Warnings, error) { +func (actor Actor) ListServiceAppBindings(params ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, Warnings, error) { var ( serviceInstance resources.ServiceInstance app resources.Application - binding resources.ServiceCredentialBinding - jobURL ccv3.JobURL - stream chan PollJobEvent + bindings []resources.ServiceCredentialBinding ) warnings, err := railway.Sequentially( @@ -79,11 +81,30 @@ func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams) return }, func() (warnings ccv3.Warnings, err error) { - binding, warnings, err = actor.getServiceAppBinding(serviceInstance.GUID, app.GUID) + bindings, warnings, err = actor.getServiceAppBindings(serviceInstance.GUID, app.GUID) return }, + ) + + switch err.(type) { + case nil: + return bindings, Warnings(warnings), nil + case ccerror.ApplicationNotFoundError: + return nil, Warnings(warnings), actionerror.ApplicationNotFoundError{Name: params.AppName} + default: + return nil, Warnings(warnings), err + } +} + +func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams) (chan PollJobEvent, Warnings, error) { + var ( + jobURL ccv3.JobURL + stream chan PollJobEvent + ) + + warnings, err := railway.Sequentially( func() (warnings ccv3.Warnings, err error) { - jobURL, warnings, err = actor.CloudControllerClient.DeleteServiceCredentialBinding(binding.GUID) + jobURL, warnings, err = actor.CloudControllerClient.DeleteServiceCredentialBinding(params.ServiceBindingGUID) return }, func() (warnings ccv3.Warnings, err error) { @@ -94,10 +115,10 @@ func (actor Actor) DeleteServiceAppBinding(params DeleteServiceAppBindingParams) switch err.(type) { case nil: + return stream, Warnings(warnings), nil - case ccerror.ApplicationNotFoundError: - return nil, Warnings(warnings), actionerror.ApplicationNotFoundError{Name: params.AppName} default: + return nil, Warnings(warnings), err } } @@ -120,24 +141,22 @@ func (actor Actor) createServiceAppBinding(serviceInstanceGUID, appGUID, binding } } -func (actor Actor) getServiceAppBinding(serviceInstanceGUID, appGUID string) (resources.ServiceCredentialBinding, ccv3.Warnings, error) { +func (actor Actor) getServiceAppBindings(serviceInstanceGUID, appGUID string) ([]resources.ServiceCredentialBinding, ccv3.Warnings, error) { bindings, warnings, err := actor.CloudControllerClient.GetServiceCredentialBindings( ccv3.Query{Key: ccv3.TypeFilter, Values: []string{"app"}}, ccv3.Query{Key: ccv3.ServiceInstanceGUIDFilter, Values: []string{serviceInstanceGUID}}, ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}}, - ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}}, - ccv3.Query{Key: ccv3.Page, Values: []string{"1"}}, ) switch { case err != nil: - return resources.ServiceCredentialBinding{}, warnings, err + return []resources.ServiceCredentialBinding{}, warnings, err case len(bindings) == 0: - return resources.ServiceCredentialBinding{}, warnings, actionerror.ServiceBindingNotFoundError{ + return []resources.ServiceCredentialBinding{}, warnings, actionerror.ServiceBindingNotFoundError{ AppGUID: appGUID, ServiceInstanceGUID: serviceInstanceGUID, } default: - return bindings[0], warnings, nil + return bindings, warnings, nil } } diff --git a/actor/v7action/service_app_binding_test.go b/actor/v7action/service_app_binding_test.go index cf0c79083a..55e78b8f7a 100644 --- a/actor/v7action/service_app_binding_test.go +++ b/actor/v7action/service_app_binding_test.go @@ -248,7 +248,7 @@ var _ = Describe("Service App Binding Action", func() { }) }) - Describe("DeleteServiceAppBinding", func() { + Describe("ListServiceAppBindings", func() { const ( serviceInstanceName = "fake-service-instance-name" serviceInstanceGUID = "fake-service-instance-guid" @@ -256,14 +256,13 @@ var _ = Describe("Service App Binding Action", func() { appGUID = "fake-app-guid" spaceGUID = "fake-space-guid" bindingGUID = "fake-binding-guid" - fakeJobURL = ccv3.JobURL("fake-job-url") ) var ( - params DeleteServiceAppBindingParams - warnings Warnings - executionError error - stream chan PollJobEvent + params ListServiceAppBindingParams + warnings Warnings + executionError error + serviceCredentialBindings []resources.ServiceCredentialBinding ) BeforeEach(func() { @@ -294,47 +293,27 @@ var _ = Describe("Service App Binding Action", func() { nil, ) - fakeCloudControllerClient.DeleteServiceCredentialBindingReturns( - fakeJobURL, - ccv3.Warnings{"delete binding warning"}, - nil, - ) - - fakeStream := make(chan ccv3.PollJobEvent) - fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream) - go func() { - fakeStream <- ccv3.PollJobEvent{ - State: constant.JobPolling, - Warnings: ccv3.Warnings{"poll warning"}, - } - }() - - params = DeleteServiceAppBindingParams{ - SpaceGUID: spaceGUID, - ServiceInstanceName: serviceInstanceName, - AppName: appName, + params = ListServiceAppBindingParams{ + SpaceGUID: "fake-space-guid", + ServiceInstanceName: "fake-service-instance-name", + AppName: "fake-app-name", } }) JustBeforeEach(func() { - stream, warnings, executionError = actor.DeleteServiceAppBinding(params) + serviceCredentialBindings, warnings, executionError = actor.ListServiceAppBindings(params) }) - It("returns an event stream, warnings, and no errors", func() { + It("returns an event stream, warning, and no errors", func() { Expect(executionError).NotTo(HaveOccurred()) Expect(warnings).To(ConsistOf(Warnings{ "get instance warning", "get app warning", "get bindings warning", - "delete binding warning", })) - Eventually(stream).Should(Receive(Equal(PollJobEvent{ - State: JobPolling, - Warnings: Warnings{"poll warning"}, - Err: nil, - }))) + Expect(serviceCredentialBindings).To(Equal([]resources.ServiceCredentialBinding{{GUID: bindingGUID}})) }) Describe("service instance lookup", func() { @@ -425,8 +404,6 @@ var _ = Describe("Service App Binding Action", func() { ccv3.Query{Key: ccv3.TypeFilter, Values: []string{"app"}}, ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}}, ccv3.Query{Key: ccv3.ServiceInstanceGUIDFilter, Values: []string{serviceInstanceGUID}}, - ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}}, - ccv3.Query{Key: ccv3.Page, Values: []string{"1"}}, )) }) @@ -463,6 +440,67 @@ var _ = Describe("Service App Binding Action", func() { }) }) }) + }) + + Describe("DeleteServiceAppBinding", func() { + const ( + bindingGUID = "fake-binding-guid" + fakeJobURL = ccv3.JobURL("fake-job-url") + ) + + var ( + params DeleteServiceAppBindingParams + warnings Warnings + executionError error + stream chan PollJobEvent + ) + + BeforeEach(func() { + fakeCloudControllerClient.GetServiceCredentialBindingsReturns( + []resources.ServiceCredentialBinding{ + {GUID: bindingGUID}, + }, + ccv3.Warnings{"get bindings warning"}, + nil, + ) + + fakeCloudControllerClient.DeleteServiceCredentialBindingReturns( + fakeJobURL, + ccv3.Warnings{"delete binding warning"}, + nil, + ) + + fakeStream := make(chan ccv3.PollJobEvent) + fakeCloudControllerClient.PollJobToEventStreamReturns(fakeStream) + go func() { + fakeStream <- ccv3.PollJobEvent{ + State: constant.JobPolling, + Warnings: ccv3.Warnings{"poll warning"}, + } + }() + + params = DeleteServiceAppBindingParams{ + ServiceBindingGUID: bindingGUID, + } + }) + + JustBeforeEach(func() { + stream, warnings, executionError = actor.DeleteServiceAppBinding(params) + }) + + It("returns an event stream, warning, and no errors", func() { + Expect(executionError).NotTo(HaveOccurred()) + + Expect(warnings).To(ConsistOf(Warnings{ + "delete binding warning", + })) + + Eventually(stream).Should(Receive(Equal(PollJobEvent{ + State: JobPolling, + Warnings: Warnings{"poll warning"}, + Err: nil, + }))) + }) Describe("initiating the delete", func() { It("makes the correct call", func() { diff --git a/command/v7/actor.go b/command/v7/actor.go index 642ddcf706..872dd8a862 100644 --- a/command/v7/actor.go +++ b/command/v7/actor.go @@ -184,6 +184,7 @@ type Actor interface { GetUAAAPIVersion() (string, error) GetUnstagedNewestPackageGUID(appGuid string) (string, v7action.Warnings, error) GetUser(username, origin string) (resources.User, error) + ListServiceAppBindings(params v7action.ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, v7action.Warnings, error) MakeCurlRequest(httpMethod string, path string, customHeaders []string, httpData string, failOnHTTPError bool) ([]byte, *http.Response, error) MapRoute(routeGUID string, appGUID string, destinationProtocol string) (v7action.Warnings, error) Marketplace(filter v7action.MarketplaceFilter) ([]v7action.ServiceOfferingWithPlans, v7action.Warnings, error) diff --git a/command/v7/unbind_service_command.go b/command/v7/unbind_service_command.go index 9ba1d95ae5..e5a3ab1b87 100644 --- a/command/v7/unbind_service_command.go +++ b/command/v7/unbind_service_command.go @@ -5,6 +5,7 @@ import ( "code.cloudfoundry.org/cli/v8/actor/v7action" "code.cloudfoundry.org/cli/v8/command/flag" "code.cloudfoundry.org/cli/v8/command/v7/shared" + "code.cloudfoundry.org/cli/v8/resources" ) type UnbindServiceCommand struct { @@ -12,6 +13,7 @@ type UnbindServiceCommand struct { RequiredArgs flag.BindServiceArgs `positional-args:"yes"` Wait bool `short:"w" long:"wait" description:"Wait for the operation to complete"` + Guid string `long:"guid" description:"Only delete the service binding with the specified GUID"` relatedCommands interface{} `related_commands:"apps, delete-service, services"` } @@ -24,7 +26,7 @@ func (cmd UnbindServiceCommand) Execute(args []string) error { return err } - stream, warnings, err := cmd.Actor.DeleteServiceAppBinding(v7action.DeleteServiceAppBindingParams{ + bindings, warnings, err := cmd.Actor.ListServiceAppBindings(v7action.ListServiceAppBindingParams{ SpaceGUID: cmd.Config.TargetedSpace().GUID, ServiceInstanceName: cmd.RequiredArgs.ServiceInstanceName, AppName: cmd.RequiredArgs.AppName, @@ -33,25 +35,54 @@ func (cmd UnbindServiceCommand) Execute(args []string) error { switch err.(type) { case nil: case actionerror.ServiceBindingNotFoundError: - cmd.UI.DisplayText("Binding between {{.ServiceInstanceName}} and {{.AppName}} did not exist", cmd.names()) + cmd.UI.DisplayText("Binding between {{.ServiceInstanceName}} and {{.AppName}} does not exist", cmd.names()) cmd.UI.DisplayOK() return nil default: return err } - completed, err := shared.WaitForResult(stream, cmd.UI, cmd.Wait) - switch { - case err != nil: - return err - case completed: - cmd.UI.DisplayOK() - return nil - default: - cmd.UI.DisplayOK() - cmd.UI.DisplayText("Unbinding in progress. Use 'cf service {{.ServiceInstanceName}}' to check operation status.", cmd.names()) - return nil + // If a GUID is provided, narrow down to the binding matching that GUID. + if cmd.Guid != "" { + var filtered []resources.ServiceCredentialBinding + for _, b := range bindings { + if b.GUID == cmd.Guid { + filtered = append(filtered, b) + break + } + } + if len(filtered) == 0 { + cmd.UI.DisplayText("Service binding with GUID {{.BindingGUID}} does not exist", map[string]interface{}{"BindingGUID": cmd.Guid}) + cmd.UI.DisplayOK() + return nil + } + bindings = filtered } + + for _, binding := range bindings { + cmd.UI.DisplayText("Deleting service binding {{.BindingGUID}}...", map[string]interface{}{"BindingGUID": binding.GUID}) + stream, warnings, err := cmd.Actor.DeleteServiceAppBinding(v7action.DeleteServiceAppBindingParams{ + ServiceBindingGUID: binding.GUID, + }) + cmd.UI.DisplayWarnings(warnings) + switch err.(type) { + case nil: + default: + return err + } + + completed, err := shared.WaitForResult(stream, cmd.UI, cmd.Wait) + switch { + case err != nil: + return err + case completed: + cmd.UI.DisplayOK() + default: + cmd.UI.DisplayOK() + cmd.UI.DisplayText("Unbinding in progress. Use 'cf service {{.ServiceInstanceName}}' to check operation status.", cmd.names()) + } + } + return nil } func (cmd UnbindServiceCommand) Usage() string { diff --git a/command/v7/unbind_service_command_test.go b/command/v7/unbind_service_command_test.go index 8085bef869..405bef7d72 100644 --- a/command/v7/unbind_service_command_test.go +++ b/command/v7/unbind_service_command_test.go @@ -2,14 +2,15 @@ package v7_test import ( "errors" - "code.cloudfoundry.org/cli/v8/actor/actionerror" "code.cloudfoundry.org/cli/v8/actor/v7action" "code.cloudfoundry.org/cli/v8/command/commandfakes" v7 "code.cloudfoundry.org/cli/v8/command/v7" "code.cloudfoundry.org/cli/v8/command/v7/v7fakes" + "code.cloudfoundry.org/cli/v8/resources" "code.cloudfoundry.org/cli/v8/util/configv3" "code.cloudfoundry.org/cli/v8/util/ui" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" . "github.com/onsi/gomega/gbytes" @@ -32,6 +33,8 @@ var _ = Describe("unbind-service Command", func() { fakeOrgName = "fake-org-name" fakeSpaceName = "fake-space-name" fakeSpaceGUID = "fake-space-guid" + fakeBindingGUID = "fake-binding-guid" + fakeBindingGUID2 = "fake-binding-guid-2" ) BeforeEach(func() { @@ -58,9 +61,14 @@ var _ = Describe("unbind-service Command", func() { fakeActor.GetCurrentUserReturns(configv3.User{Name: fakeUserName}, nil) + fakeActor.ListServiceAppBindingsReturns( + []resources.ServiceCredentialBinding{{GUID: fakeBindingGUID}}, + v7action.Warnings{"fake warning"}, + nil, + ) fakeActor.DeleteServiceAppBindingReturns( nil, - v7action.Warnings{"fake warning"}, + v7action.Warnings{"delete warning"}, nil, ) @@ -80,19 +88,30 @@ var _ = Describe("unbind-service Command", func() { Expect(actualSpace).To(BeTrue()) }) - It("delegates to the actor", func() { - Expect(fakeActor.DeleteServiceAppBindingCallCount()).To(Equal(1)) - Expect(fakeActor.DeleteServiceAppBindingArgsForCall(0)).To(Equal(v7action.DeleteServiceAppBindingParams{ - SpaceGUID: fakeSpaceGUID, - ServiceInstanceName: fakeServiceInstanceName, - AppName: fakeAppName, - })) + Context("one binding exists", func() { + It("lists binding then delegates deletion to the actor by GUID", func() { + Expect(fakeActor.ListServiceAppBindingsCallCount()).To(Equal(1)) + Expect(fakeActor.ListServiceAppBindingsArgsForCall(0)).To(Equal(v7action.ListServiceAppBindingParams{ + SpaceGUID: fakeSpaceGUID, + ServiceInstanceName: fakeServiceInstanceName, + AppName: fakeAppName, + })) + + Expect(fakeActor.DeleteServiceAppBindingCallCount()).To(Equal(1)) + Expect(fakeActor.DeleteServiceAppBindingArgsForCall(0)).To(Equal(v7action.DeleteServiceAppBindingParams{ + ServiceBindingGUID: fakeBindingGUID, + })) + }) }) Describe("intro message", func() { It("prints an intro and warnings", func() { Expect(executeErr).NotTo(HaveOccurred()) - Expect(testUI.Err).To(Say("fake warning")) + // Warnings from list + delete + Expect(testUI.Err).To(SatisfyAll( + Say("fake warning"), + Say("delete warning"), + )) Expect(testUI.Out).To(Say( `Unbinding app %s from service %s in org %s / space %s as %s\.\.\.\n`, @@ -105,9 +124,9 @@ var _ = Describe("unbind-service Command", func() { }) }) - When("binding did not exist", func() { + When("binding does not exist", func() { BeforeEach(func() { - fakeActor.DeleteServiceAppBindingReturns( + fakeActor.ListServiceAppBindingsReturns( nil, v7action.Warnings{"fake warning"}, actionerror.ServiceBindingNotFoundError{}, @@ -117,7 +136,7 @@ var _ = Describe("unbind-service Command", func() { It("prints a message and warnings", func() { Expect(testUI.Out).To(SatisfyAll( Say( - `Binding between %s and %s did not exist\n`, + `Binding between %s and %s does not exist\n`, fakeServiceInstanceName, fakeAppName, ), @@ -130,9 +149,15 @@ var _ = Describe("unbind-service Command", func() { Describe("processing the response stream", func() { Context("nil stream", func() { - It("prints a message and warnings", func() { - Expect(testUI.Out).To(Say(`OK\n`)) - Expect(testUI.Err).To(Say("fake warning")) + It("prints per-binding delete message, OK, and warnings", func() { + Expect(testUI.Out).To(SatisfyAll( + Say(`Deleting service binding %s\.\.\.\n`, fakeBindingGUID), + Say(`OK\n`), + )) + Expect(testUI.Err).To(SatisfyAll( + Say("fake warning"), + Say("delete warning"), + )) }) }) @@ -153,16 +178,20 @@ var _ = Describe("unbind-service Command", func() { fakeActor.DeleteServiceAppBindingReturns( eventStream, - v7action.Warnings{"fake warning"}, + v7action.Warnings{"delete warning"}, nil, ) }) - It("prints a message and warnings", func() { - Expect(testUI.Out).To(Say(`OK\n`)) + It("prints delete message, OK, and warnings", func() { + Expect(testUI.Out).To(SatisfyAll( + Say(`Deleting service binding %s\.\.\.\n`, fakeBindingGUID), + Say(`OK\n`), + )) Expect(testUI.Err).To(SatisfyAll( Say("fake warning"), + Say("delete warning"), Say("job processing warning"), Say("job complete warning"), )) @@ -185,20 +214,22 @@ var _ = Describe("unbind-service Command", func() { fakeActor.DeleteServiceAppBindingReturns( eventStream, - v7action.Warnings{"fake warning"}, + v7action.Warnings{"delete warning"}, nil, ) }) - It("prints a message and warnings", func() { + It("prints delete message, OK, in-progress note, and warnings", func() { Expect(testUI.Out).To(SatisfyAll( + Say(`Deleting service binding %s\.\.\.\n`, fakeBindingGUID), Say(`OK\n`), Say(`\n`), - Say(`Unbinding in progress. Use 'cf service %s' to check operation status.\n`, fakeServiceInstanceName), + Say(`Unbinding in progress. Use 'cf service %s' to check operation status\.\n`, fakeServiceInstanceName), )) Expect(testUI.Err).To(SatisfyAll( Say("fake warning"), + Say("delete warning"), Say("job processing warning"), Say("job polling warning"), )) @@ -218,7 +249,7 @@ var _ = Describe("unbind-service Command", func() { fakeActor.DeleteServiceAppBindingReturns( eventStream, - v7action.Warnings{"fake warning"}, + v7action.Warnings{"delete warning"}, nil, ) }) @@ -228,6 +259,7 @@ var _ = Describe("unbind-service Command", func() { Expect(testUI.Err).To(SatisfyAll( Say("fake warning"), + Say("delete warning"), Say("job failed warning"), )) }) @@ -254,7 +286,7 @@ var _ = Describe("unbind-service Command", func() { fakeActor.DeleteServiceAppBindingReturns( eventStream, - v7action.Warnings{"fake warning"}, + v7action.Warnings{"delete warning"}, nil, ) @@ -263,13 +295,15 @@ var _ = Describe("unbind-service Command", func() { It("waits for the event stream to complete", func() { Expect(testUI.Out).To(SatisfyAll( - Say(`Waiting for the operation to complete\.\.\.\n`), + Say(`Deleting service binding %s\.\.\.\n`, fakeBindingGUID), + Say(`Waiting for the operation to complete\.+\n`), Say(`\n`), Say(`OK\n`), )) Expect(testUI.Err).To(SatisfyAll( Say("fake warning"), + Say("delete warning"), Say("job processing warning"), Say("job polling warning"), Say("job complete warning"), @@ -288,9 +322,9 @@ var _ = Describe("unbind-service Command", func() { }) }) - When("actor returns error", func() { + When("list returns error", func() { BeforeEach(func() { - fakeActor.DeleteServiceAppBindingReturns( + fakeActor.ListServiceAppBindingsReturns( nil, v7action.Warnings{"fake warning"}, errors.New("boom"), @@ -303,6 +337,24 @@ var _ = Describe("unbind-service Command", func() { }) }) + When("delete returns error", func() { + BeforeEach(func() { + fakeActor.DeleteServiceAppBindingReturns( + nil, + v7action.Warnings{"delete warning"}, + errors.New("boom"), + ) + }) + + It("prints warnings and returns the error", func() { + Expect(testUI.Err).To(SatisfyAll( + Say("fake warning"), + Say("delete warning"), + )) + Expect(executeErr).To(MatchError("boom")) + }) + }) + When("getting the username returns an error", func() { BeforeEach(func() { fakeActor.GetCurrentUserReturns(configv3.User{}, errors.New("bad thing")) @@ -312,4 +364,80 @@ var _ = Describe("unbind-service Command", func() { Expect(executeErr).To(MatchError("bad thing")) }) }) + + Context("multiple bindings exist", func() { + BeforeEach(func() { + fakeActor.ListServiceAppBindingsReturns( + []resources.ServiceCredentialBinding{{GUID: fakeBindingGUID}, {GUID: fakeBindingGUID2}}, + v7action.Warnings{"fake warning"}, + nil, + ) + }) + + It("deletes each binding by GUID and prints messages for both", func() { + Expect(fakeActor.DeleteServiceAppBindingCallCount()).To(Equal(2)) + + firstArgs := fakeActor.DeleteServiceAppBindingArgsForCall(0) + secondArgs := fakeActor.DeleteServiceAppBindingArgsForCall(1) + Expect([]string{firstArgs.ServiceBindingGUID, secondArgs.ServiceBindingGUID}).To(ConsistOf(fakeBindingGUID, fakeBindingGUID2)) + + Expect(testUI.Out).To(SatisfyAll( + Say(`Deleting service binding %s\.+\n`, fakeBindingGUID), + Say(`OK\n`), + Say(`Deleting service binding %s\.+\n`, fakeBindingGUID2), + Say(`OK\n`), + )) + + Expect(testUI.Err).To(Say("fake warning")) + }) + }) + + Context("--guid selects a single binding among multiple", func() { + BeforeEach(func() { + fakeActor.ListServiceAppBindingsReturns( + []resources.ServiceCredentialBinding{{GUID: fakeBindingGUID}, {GUID: fakeBindingGUID2}}, + v7action.Warnings{"fake warning"}, + nil, + ) + setFlag(&cmd, "--guid", fakeBindingGUID2) + }) + + It("deletes only the specified GUID", func() { + Expect(fakeActor.DeleteServiceAppBindingCallCount()).To(Equal(1)) + args := fakeActor.DeleteServiceAppBindingArgsForCall(0) + Expect(args.ServiceBindingGUID).To(Equal(fakeBindingGUID2)) + + Expect(testUI.Out).To(SatisfyAll( + Say(`Deleting service binding %s\.+\n`, fakeBindingGUID2), + Say(`OK\n`), + )) + + Expect(testUI.Err).To(SatisfyAll( + Say("fake warning"), + Say("delete warning"), + )) + }) + }) + + Context("--guid does not match any binding", func() { + BeforeEach(func() { + fakeActor.ListServiceAppBindingsReturns( + []resources.ServiceCredentialBinding{{GUID: fakeBindingGUID}}, + v7action.Warnings{"fake warning"}, + nil, + ) + setFlag(&cmd, "--guid", "unknown-guid") + }) + + It("prints a specific not-found message and OK, without calling delete", func() { + Expect(fakeActor.DeleteServiceAppBindingCallCount()).To(Equal(0)) + + Expect(testUI.Out).To(SatisfyAll( + Say(`Service binding with GUID unknown-guid does not exist\n`), + Say(`OK\n`), + )) + + Expect(testUI.Err).To(Say("fake warning")) + }) + }) }) diff --git a/command/v7/v7fakes/fake_actor.go b/command/v7/v7fakes/fake_actor.go index 80cd8d0e79..2598de0ff9 100644 --- a/command/v7/v7fakes/fake_actor.go +++ b/command/v7/v7fakes/fake_actor.go @@ -2503,6 +2503,21 @@ type FakeActor struct { result1 resources.User result2 error } + ListServiceAppBindingsStub func(v7action.ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, v7action.Warnings, error) + listServiceAppBindingsMutex sync.RWMutex + listServiceAppBindingsArgsForCall []struct { + arg1 v7action.ListServiceAppBindingParams + } + listServiceAppBindingsReturns struct { + result1 []resources.ServiceCredentialBinding + result2 v7action.Warnings + result3 error + } + listServiceAppBindingsReturnsOnCall map[int]struct { + result1 []resources.ServiceCredentialBinding + result2 v7action.Warnings + result3 error + } MakeCurlRequestStub func(string, string, []string, string, bool) ([]byte, *http.Response, error) makeCurlRequestMutex sync.RWMutex makeCurlRequestArgsForCall []struct { @@ -14528,6 +14543,73 @@ func (fake *FakeActor) GetUserReturnsOnCall(i int, result1 resources.User, resul }{result1, result2} } +func (fake *FakeActor) ListServiceAppBindings(arg1 v7action.ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, v7action.Warnings, error) { + fake.listServiceAppBindingsMutex.Lock() + ret, specificReturn := fake.listServiceAppBindingsReturnsOnCall[len(fake.listServiceAppBindingsArgsForCall)] + fake.listServiceAppBindingsArgsForCall = append(fake.listServiceAppBindingsArgsForCall, struct { + arg1 v7action.ListServiceAppBindingParams + }{arg1}) + stub := fake.ListServiceAppBindingsStub + fakeReturns := fake.listServiceAppBindingsReturns + fake.recordInvocation("ListServiceAppBindings", []interface{}{arg1}) + fake.listServiceAppBindingsMutex.Unlock() + if stub != nil { + return stub(arg1) + } + if specificReturn { + return ret.result1, ret.result2, ret.result3 + } + return fakeReturns.result1, fakeReturns.result2, fakeReturns.result3 +} + +func (fake *FakeActor) ListServiceAppBindingsCallCount() int { + fake.listServiceAppBindingsMutex.RLock() + defer fake.listServiceAppBindingsMutex.RUnlock() + return len(fake.listServiceAppBindingsArgsForCall) +} + +func (fake *FakeActor) ListServiceAppBindingsCalls(stub func(v7action.ListServiceAppBindingParams) ([]resources.ServiceCredentialBinding, v7action.Warnings, error)) { + fake.listServiceAppBindingsMutex.Lock() + defer fake.listServiceAppBindingsMutex.Unlock() + fake.ListServiceAppBindingsStub = stub +} + +func (fake *FakeActor) ListServiceAppBindingsArgsForCall(i int) v7action.ListServiceAppBindingParams { + fake.listServiceAppBindingsMutex.RLock() + defer fake.listServiceAppBindingsMutex.RUnlock() + argsForCall := fake.listServiceAppBindingsArgsForCall[i] + return argsForCall.arg1 +} + +func (fake *FakeActor) ListServiceAppBindingsReturns(result1 []resources.ServiceCredentialBinding, result2 v7action.Warnings, result3 error) { + fake.listServiceAppBindingsMutex.Lock() + defer fake.listServiceAppBindingsMutex.Unlock() + fake.ListServiceAppBindingsStub = nil + fake.listServiceAppBindingsReturns = struct { + result1 []resources.ServiceCredentialBinding + result2 v7action.Warnings + result3 error + }{result1, result2, result3} +} + +func (fake *FakeActor) ListServiceAppBindingsReturnsOnCall(i int, result1 []resources.ServiceCredentialBinding, result2 v7action.Warnings, result3 error) { + fake.listServiceAppBindingsMutex.Lock() + defer fake.listServiceAppBindingsMutex.Unlock() + fake.ListServiceAppBindingsStub = nil + if fake.listServiceAppBindingsReturnsOnCall == nil { + fake.listServiceAppBindingsReturnsOnCall = make(map[int]struct { + result1 []resources.ServiceCredentialBinding + result2 v7action.Warnings + result3 error + }) + } + fake.listServiceAppBindingsReturnsOnCall[i] = struct { + result1 []resources.ServiceCredentialBinding + result2 v7action.Warnings + result3 error + }{result1, result2, result3} +} + func (fake *FakeActor) MakeCurlRequest(arg1 string, arg2 string, arg3 []string, arg4 string, arg5 bool) ([]byte, *http.Response, error) { var arg3Copy []string if arg3 != nil {