diff --git a/internal/pkg/cli/command/organization/delete.go b/internal/pkg/cli/command/organization/delete.go index da8e83ee..0487f27d 100644 --- a/internal/pkg/cli/command/organization/delete.go +++ b/internal/pkg/cli/command/organization/delete.go @@ -2,6 +2,7 @@ package organization import ( "bufio" + "context" "fmt" "os" "strings" @@ -13,6 +14,7 @@ import ( "github.com/pinecone-io/cli/internal/pkg/utils/pcio" "github.com/pinecone-io/cli/internal/pkg/utils/sdk" "github.com/pinecone-io/cli/internal/pkg/utils/style" + "github.com/pinecone-io/cli/internal/pkg/utils/text" "github.com/spf13/cobra" ) @@ -22,6 +24,10 @@ type deleteOrganizationCmdOptions struct { json bool } +type deleteOrganizationService interface { + Delete(ctx context.Context, id string) error +} + func NewDeleteOrganizationCmd() *cobra.Command { options := deleteOrganizationCmdOptions{} @@ -48,20 +54,19 @@ func NewDeleteOrganizationCmd() *cobra.Command { confirmDelete(org.Name, org.Id) } - err = ac.Organization.Delete(cmd.Context(), options.organizationID) + err = runDeleteOrganizationCmd(ctx, ac.Organization, options, org.Name, org.Id) if err != nil { msg.FailMsg("Failed to delete organization %s: %s\n", options.organizationID, err) exit.Errorf(err, "Failed to delete organization %s", style.Emphasis(options.organizationID)) } - // Clear target project if the deleted project is the target project + // Clear target org if the deleted org is the target org if state.TargetOrg.Get().Id == options.organizationID { state.TargetOrg.Set(state.TargetOrganization{ Id: "", Name: "", }) } - msg.SuccessMsg("Organization %s (ID: %s) deleted.\n", style.Emphasis(org.Name), style.Emphasis(options.organizationID)) }, } @@ -76,6 +81,24 @@ func NewDeleteOrganizationCmd() *cobra.Command { return cmd } +func runDeleteOrganizationCmd(ctx context.Context, svc deleteOrganizationService, opts deleteOrganizationCmdOptions, name, id string) error { + if err := svc.Delete(ctx, id); err != nil { + return err + } + + if opts.json { + fmt.Println(text.IndentJSON(struct { + Deleted bool `json:"deleted"` + Name string `json:"name"` + Id string `json:"id"` + }{Deleted: true, Name: name, Id: id})) + return nil + } + + msg.SuccessMsg("Organization %s (ID: %s) deleted.\n", style.Emphasis(name), style.Emphasis(id)) + return nil +} + func confirmDelete(organizationName string, organizationID string) { msg.WarnMsg("This will delete the organization %s (ID: %s).", style.Emphasis(organizationName), style.Emphasis(organizationID)) msg.WarnMsg("This action cannot be undone.") diff --git a/internal/pkg/cli/command/organization/delete_test.go b/internal/pkg/cli/command/organization/delete_test.go new file mode 100644 index 00000000..56ad78fc --- /dev/null +++ b/internal/pkg/cli/command/organization/delete_test.go @@ -0,0 +1,59 @@ +package organization + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/pinecone-io/cli/internal/pkg/cli/testutils" + "github.com/stretchr/testify/assert" +) + +type mockDeleteOrganizationService struct { + lastDeleteId string + deleteErr error +} + +func (m *mockDeleteOrganizationService) Delete(ctx context.Context, id string) error { + m.lastDeleteId = id + return m.deleteErr +} + +func TestMain(m *testing.M) { + reset := testutils.SilenceOutput() + code := m.Run() + reset() + os.Exit(code) +} + +func Test_runDeleteOrganizationCmd_Succeeds(t *testing.T) { + svc := &mockDeleteOrganizationService{} + opts := deleteOrganizationCmdOptions{organizationID: "org-123"} + + err := runDeleteOrganizationCmd(context.Background(), svc, opts, "my-org", "org-123") + + assert.NoError(t, err) + assert.Equal(t, "org-123", svc.lastDeleteId) +} + +func Test_runDeleteOrganizationCmd_SucceedsJSON(t *testing.T) { + svc := &mockDeleteOrganizationService{} + opts := deleteOrganizationCmdOptions{organizationID: "org-123", json: true} + + out := testutils.CaptureStdout(t, func() { + err := runDeleteOrganizationCmd(context.Background(), svc, opts, "my-org", "org-123") + assert.NoError(t, err) + }) + + assert.JSONEq(t, `{"deleted":true,"name":"my-org","id":"org-123"}`, out) +} + +func Test_runDeleteOrganizationCmd_PropagatesError(t *testing.T) { + svc := &mockDeleteOrganizationService{deleteErr: errors.New("service error")} + opts := deleteOrganizationCmdOptions{organizationID: "org-123"} + + err := runDeleteOrganizationCmd(context.Background(), svc, opts, "my-org", "org-123") + + assert.Error(t, err) +} diff --git a/internal/pkg/cli/command/project/delete.go b/internal/pkg/cli/command/project/delete.go index 3d15c6e6..1380b7ba 100644 --- a/internal/pkg/cli/command/project/delete.go +++ b/internal/pkg/cli/command/project/delete.go @@ -14,6 +14,7 @@ import ( "github.com/pinecone-io/cli/internal/pkg/utils/pcio" "github.com/pinecone-io/cli/internal/pkg/utils/sdk" "github.com/pinecone-io/cli/internal/pkg/utils/style" + "github.com/pinecone-io/cli/internal/pkg/utils/text" "github.com/spf13/cobra" ) @@ -23,6 +24,10 @@ type deleteProjectCmdOptions struct { json bool } +type deleteProjectService interface { + Delete(ctx context.Context, id string) error +} + func NewDeleteProjectCmd() *cobra.Command { options := deleteProjectCmdOptions{} @@ -65,7 +70,7 @@ func NewDeleteProjectCmd() *cobra.Command { confirmDelete(projToDelete.Name) } - err = ac.Project.Delete(ctx, projToDelete.Id) + err = runDeleteProjectCmd(ctx, ac.Project, options, projToDelete.Name, projToDelete.Id) if err != nil { msg.FailMsg("Failed to delete project %s: %s\n", style.Emphasis(projToDelete.Name), err) exit.Errorf(err, "Failed to delete project %s", style.Emphasis(projToDelete.Name)) @@ -78,7 +83,6 @@ func NewDeleteProjectCmd() *cobra.Command { Name: "", }) } - msg.SuccessMsg("Project %s deleted.\n", style.Emphasis(projToDelete.Name)) }, } @@ -90,6 +94,24 @@ func NewDeleteProjectCmd() *cobra.Command { return cmd } +func runDeleteProjectCmd(ctx context.Context, svc deleteProjectService, opts deleteProjectCmdOptions, name, id string) error { + if err := svc.Delete(ctx, id); err != nil { + return err + } + + if opts.json { + fmt.Println(text.IndentJSON(struct { + Deleted bool `json:"deleted"` + Name string `json:"name"` + Id string `json:"id"` + }{Deleted: true, Name: name, Id: id})) + return nil + } + + msg.SuccessMsg("Project %s deleted.\n", style.Emphasis(name)) + return nil +} + func confirmDelete(projectName string) { msg.WarnMsg("This will delete the project %s in organization %s.", style.Emphasis(projectName), style.Emphasis(state.TargetOrg.Get().Name)) msg.WarnMsg("This action cannot be undone.") diff --git a/internal/pkg/cli/command/project/delete_test.go b/internal/pkg/cli/command/project/delete_test.go new file mode 100644 index 00000000..e707adb6 --- /dev/null +++ b/internal/pkg/cli/command/project/delete_test.go @@ -0,0 +1,59 @@ +package project + +import ( + "context" + "errors" + "os" + "testing" + + "github.com/pinecone-io/cli/internal/pkg/cli/testutils" + "github.com/stretchr/testify/assert" +) + +type mockDeleteProjectService struct { + lastDeleteId string + deleteErr error +} + +func (m *mockDeleteProjectService) Delete(ctx context.Context, id string) error { + m.lastDeleteId = id + return m.deleteErr +} + +func TestMain(m *testing.M) { + reset := testutils.SilenceOutput() + code := m.Run() + reset() + os.Exit(code) +} + +func Test_runDeleteProjectCmd_Succeeds(t *testing.T) { + svc := &mockDeleteProjectService{} + opts := deleteProjectCmdOptions{projectId: "proj-123"} + + err := runDeleteProjectCmd(context.Background(), svc, opts, "my-project", "proj-123") + + assert.NoError(t, err) + assert.Equal(t, "proj-123", svc.lastDeleteId) +} + +func Test_runDeleteProjectCmd_SucceedsJSON(t *testing.T) { + svc := &mockDeleteProjectService{} + opts := deleteProjectCmdOptions{projectId: "proj-123", json: true} + + out := testutils.CaptureStdout(t, func() { + err := runDeleteProjectCmd(context.Background(), svc, opts, "my-project", "proj-123") + assert.NoError(t, err) + }) + + assert.JSONEq(t, `{"deleted":true,"name":"my-project","id":"proj-123"}`, out) +} + +func Test_runDeleteProjectCmd_PropagatesError(t *testing.T) { + svc := &mockDeleteProjectService{deleteErr: errors.New("service error")} + opts := deleteProjectCmdOptions{projectId: "proj-123"} + + err := runDeleteProjectCmd(context.Background(), svc, opts, "my-project", "proj-123") + + assert.Error(t, err) +}