Skip to content

Commit 2d58b6f

Browse files
committed
Improve error messages for branch delete|promote|demote|list
Continuing on with the pattern set in: #1067
1 parent 4b1f54c commit 2d58b6f

8 files changed

Lines changed: 227 additions & 5 deletions

File tree

internal/cmd/branch/delete.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
4545
if err != nil {
4646
switch cmdutil.ErrCode(err) {
4747
case planetscale.ErrNotFound:
48-
return fmt.Errorf("branch %s does not exist in database %s (organization: %s)",
48+
return cmdutil.HandleNotFoundWithServiceTokenCheck(
49+
ctx, cmd, ch.Config, ch.Client, err, "delete_branch",
50+
"branch %s does not exist in database %s (organization: %s)",
4951
printer.BoldBlue(branch), printer.BoldBlue(source), printer.BoldBlue(ch.Config.Organization))
5052
default:
5153
return cmdutil.HandleError(err)
@@ -90,7 +92,9 @@ func DeleteCmd(ch *cmdutil.Helper) *cobra.Command {
9092
if err != nil {
9193
switch cmdutil.ErrCode(err) {
9294
case planetscale.ErrNotFound:
93-
return fmt.Errorf("database %s or branch %s does not exist in organization %s",
95+
return cmdutil.HandleNotFoundWithServiceTokenCheck(
96+
ctx, cmd, ch.Config, ch.Client, err, "delete_branch",
97+
"database %s or branch %s does not exist in organization %s",
9498
printer.BoldBlue(source), printer.BoldBlue(branch), printer.BoldBlue(ch.Config.Organization))
9599
default:
96100
return cmdutil.HandleError(err)

internal/cmd/branch/delete_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,56 @@ func TestBranch_DeleteCmd(t *testing.T) {
6060
c.Assert(svc.DeleteFnInvoked, qt.IsTrue)
6161
c.Assert(buf.String(), qt.JSONEquals, res)
6262
}
63+
64+
func TestBranch_DeleteCmd_ServiceTokenPermissionError(t *testing.T) {
65+
c := qt.New(t)
66+
67+
var buf bytes.Buffer
68+
format := printer.JSON
69+
p := printer.NewPrinter(&format)
70+
p.SetResourceOutput(&buf)
71+
72+
org := "planetscale"
73+
db := "planetscale"
74+
branch := "development"
75+
76+
// Mock service that returns 404 for branch deletion
77+
branchSvc := &mock.DatabaseBranchesService{
78+
DeleteFn: func(ctx context.Context, req *ps.DeleteDatabaseBranchRequest) error {
79+
return &ps.Error{Code: ps.ErrNotFound}
80+
},
81+
}
82+
83+
// Mock organization service that succeeds (simulating valid service token)
84+
orgSvc := &mock.OrganizationsService{
85+
ListFn: func(ctx context.Context) ([]*ps.Organization, error) {
86+
return []*ps.Organization{{Name: org}}, nil
87+
},
88+
}
89+
90+
ch := &cmdutil.Helper{
91+
Printer: p,
92+
Config: &config.Config{
93+
Organization: org,
94+
ServiceTokenID: "valid-token-id",
95+
ServiceToken: "valid-token",
96+
},
97+
Client: func() (*ps.Client, error) {
98+
return &ps.Client{
99+
DatabaseBranches: branchSvc,
100+
Organizations: orgSvc,
101+
}, nil
102+
},
103+
}
104+
105+
cmd := DeleteCmd(ch)
106+
cmd.SetArgs([]string{db, branch, "--force"})
107+
err := cmd.Execute()
108+
109+
c.Assert(err, qt.IsNotNil)
110+
c.Assert(err.Error(), qt.Contains, "does not exist")
111+
c.Assert(err.Error(), qt.Contains, "service token for authentication")
112+
c.Assert(err.Error(), qt.Contains, "delete_branch")
113+
c.Assert(branchSvc.DeleteFnInvoked, qt.IsTrue)
114+
c.Assert(orgSvc.ListFnInvoked, qt.IsTrue)
115+
}

internal/cmd/branch/demote.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ func DemoteCmd(ch *cmdutil.Helper) *cobra.Command {
3535
if err != nil {
3636
switch cmdutil.ErrCode(err) {
3737
case ps.ErrNotFound:
38-
return fmt.Errorf("branch %s does not exist in database %s",
38+
return cmdutil.HandleNotFoundWithServiceTokenCheck(
39+
cmd.Context(), cmd, ch.Config, ch.Client, err, "connect_production_branch",
40+
"branch %s does not exist in database %s",
3941
printer.BoldBlue(req.Branch), printer.BoldBlue(req.Database))
4042
default:
4143
return cmdutil.HandleError(err)

internal/cmd/branch/demote_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,56 @@ func TestBranch_DemoteCmd(t *testing.T) {
6161
c.Assert(svc.DemoteFnInvoked, qt.IsTrue)
6262
c.Assert(buf.String(), qt.JSONEquals, res)
6363
}
64+
65+
func TestBranch_DemoteCmd_ServiceTokenPermissionError(t *testing.T) {
66+
c := qt.New(t)
67+
68+
var buf bytes.Buffer
69+
format := printer.JSON
70+
p := printer.NewPrinter(&format)
71+
p.SetResourceOutput(&buf)
72+
73+
org := "planetscale"
74+
db := "planetscale"
75+
branch := "development"
76+
77+
// Mock service that returns 404 for branch demotion
78+
branchSvc := &mock.DatabaseBranchesService{
79+
DemoteFn: func(ctx context.Context, req *ps.DemoteRequest) (*ps.DatabaseBranch, error) {
80+
return nil, &ps.Error{Code: ps.ErrNotFound}
81+
},
82+
}
83+
84+
// Mock organization service that succeeds (simulating valid service token)
85+
orgSvc := &mock.OrganizationsService{
86+
ListFn: func(ctx context.Context) ([]*ps.Organization, error) {
87+
return []*ps.Organization{{Name: org}}, nil
88+
},
89+
}
90+
91+
ch := &cmdutil.Helper{
92+
Printer: p,
93+
Config: &config.Config{
94+
Organization: org,
95+
ServiceTokenID: "valid-token-id",
96+
ServiceToken: "valid-token",
97+
},
98+
Client: func() (*ps.Client, error) {
99+
return &ps.Client{
100+
DatabaseBranches: branchSvc,
101+
Organizations: orgSvc,
102+
}, nil
103+
},
104+
}
105+
106+
cmd := DemoteCmd(ch)
107+
cmd.SetArgs([]string{db, branch})
108+
err := cmd.Execute()
109+
110+
c.Assert(err, qt.IsNotNil)
111+
c.Assert(err.Error(), qt.Contains, "does not exist")
112+
c.Assert(err.Error(), qt.Contains, "service token for authentication")
113+
c.Assert(err.Error(), qt.Contains, "connect_production_branch")
114+
c.Assert(branchSvc.DemoteFnInvoked, qt.IsTrue)
115+
c.Assert(orgSvc.ListFnInvoked, qt.IsTrue)
116+
}

internal/cmd/branch/list.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,9 @@ func ListCmd(ch *cmdutil.Helper) *cobra.Command {
5555
if err != nil {
5656
switch cmdutil.ErrCode(err) {
5757
case planetscale.ErrNotFound:
58-
return fmt.Errorf("database %s does not exist in organization %s",
58+
return cmdutil.HandleNotFoundWithServiceTokenCheck(
59+
ctx, cmd, ch.Config, ch.Client, err, "read_branch",
60+
"database %s does not exist in organization %s",
5961
printer.BoldBlue(database), printer.BoldBlue(ch.Config.Organization))
6062
default:
6163
return cmdutil.HandleError(err)

internal/cmd/branch/list_test.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,55 @@ func TestBranch_ListCmd(t *testing.T) {
6060
c.Assert(svc.ListFnInvoked, qt.IsTrue)
6161
c.Assert(buf.String(), qt.JSONEquals, branches)
6262
}
63+
64+
func TestBranch_ListCmd_ServiceTokenPermissionError(t *testing.T) {
65+
c := qt.New(t)
66+
67+
var buf bytes.Buffer
68+
format := printer.JSON
69+
p := printer.NewPrinter(&format)
70+
p.SetResourceOutput(&buf)
71+
72+
org := "planetscale"
73+
db := "planetscale"
74+
75+
// Mock service that returns 404 for branch listing
76+
branchSvc := &mock.DatabaseBranchesService{
77+
ListFn: func(ctx context.Context, req *ps.ListDatabaseBranchesRequest) ([]*ps.DatabaseBranch, error) {
78+
return nil, &ps.Error{Code: ps.ErrNotFound}
79+
},
80+
}
81+
82+
// Mock organization service that succeeds (simulating valid service token)
83+
orgSvc := &mock.OrganizationsService{
84+
ListFn: func(ctx context.Context) ([]*ps.Organization, error) {
85+
return []*ps.Organization{{Name: org}}, nil
86+
},
87+
}
88+
89+
ch := &cmdutil.Helper{
90+
Printer: p,
91+
Config: &config.Config{
92+
Organization: org,
93+
ServiceTokenID: "valid-token-id",
94+
ServiceToken: "valid-token",
95+
},
96+
Client: func() (*ps.Client, error) {
97+
return &ps.Client{
98+
DatabaseBranches: branchSvc,
99+
Organizations: orgSvc,
100+
}, nil
101+
},
102+
}
103+
104+
cmd := ListCmd(ch)
105+
cmd.SetArgs([]string{db})
106+
err := cmd.Execute()
107+
108+
c.Assert(err, qt.IsNotNil)
109+
c.Assert(err.Error(), qt.Contains, "does not exist")
110+
c.Assert(err.Error(), qt.Contains, "service token for authentication")
111+
c.Assert(err.Error(), qt.Contains, "read_branch")
112+
c.Assert(branchSvc.ListFnInvoked, qt.IsTrue)
113+
c.Assert(orgSvc.ListFnInvoked, qt.IsTrue)
114+
}

internal/cmd/branch/promote.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,10 @@ func PromoteCmd(ch *cmdutil.Helper) *cobra.Command {
7070
if err != nil {
7171
switch cmdutil.ErrCode(err) {
7272
case ps.ErrNotFound:
73-
return fmt.Errorf("branch %s does not exist in database %s", printer.BoldBlue(branch), printer.BoldBlue(db))
73+
return cmdutil.HandleNotFoundWithServiceTokenCheck(
74+
cmd.Context(), cmd, ch.Config, ch.Client, err, "connect_production_branch",
75+
"branch %s does not exist in database %s",
76+
printer.BoldBlue(branch), printer.BoldBlue(db))
7477
default:
7578
return cmdutil.HandleError(err)
7679
}

internal/cmd/branch/promote_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,56 @@ func TestBranch_PromoteCmd(t *testing.T) {
6161
c.Assert(svc.PromoteFnInvoked, qt.IsTrue)
6262
c.Assert(buf.String(), qt.JSONEquals, res)
6363
}
64+
65+
func TestBranch_PromoteCmd_ServiceTokenPermissionError(t *testing.T) {
66+
c := qt.New(t)
67+
68+
var buf bytes.Buffer
69+
format := printer.JSON
70+
p := printer.NewPrinter(&format)
71+
p.SetResourceOutput(&buf)
72+
73+
org := "planetscale"
74+
db := "planetscale"
75+
branch := "development"
76+
77+
// Mock service that returns 404 for branch promotion
78+
branchSvc := &mock.DatabaseBranchesService{
79+
PromoteFn: func(ctx context.Context, req *ps.PromoteRequest) (*ps.DatabaseBranch, error) {
80+
return nil, &ps.Error{Code: ps.ErrNotFound}
81+
},
82+
}
83+
84+
// Mock organization service that succeeds (simulating valid service token)
85+
orgSvc := &mock.OrganizationsService{
86+
ListFn: func(ctx context.Context) ([]*ps.Organization, error) {
87+
return []*ps.Organization{{Name: org}}, nil
88+
},
89+
}
90+
91+
ch := &cmdutil.Helper{
92+
Printer: p,
93+
Config: &config.Config{
94+
Organization: org,
95+
ServiceTokenID: "valid-token-id",
96+
ServiceToken: "valid-token",
97+
},
98+
Client: func() (*ps.Client, error) {
99+
return &ps.Client{
100+
DatabaseBranches: branchSvc,
101+
Organizations: orgSvc,
102+
}, nil
103+
},
104+
}
105+
106+
cmd := PromoteCmd(ch)
107+
cmd.SetArgs([]string{db, branch})
108+
err := cmd.Execute()
109+
110+
c.Assert(err, qt.IsNotNil)
111+
c.Assert(err.Error(), qt.Contains, "does not exist")
112+
c.Assert(err.Error(), qt.Contains, "service token for authentication")
113+
c.Assert(err.Error(), qt.Contains, "connect_production_branch")
114+
c.Assert(branchSvc.PromoteFnInvoked, qt.IsTrue)
115+
c.Assert(orgSvc.ListFnInvoked, qt.IsTrue)
116+
}

0 commit comments

Comments
 (0)