Skip to content

Commit 9f1881e

Browse files
authored
chore(project): Add nil pointer checks and tests for the outputResult functions (#622)
* fix: fix potential nil-pointer exceptions and add testcase * fix: add review findings
1 parent ab5ae06 commit 9f1881e

File tree

9 files changed

+226
-28
lines changed

9 files changed

+226
-28
lines changed

internal/cmd/project/create/create.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func NewCmd(p *print.Printer) *cobra.Command {
9595
return fmt.Errorf("create project: %w", err)
9696
}
9797

98-
return outputResult(p, model, resp)
98+
return outputResult(p, *model, resp)
9999
},
100100
}
101101
configureFlags(cmd)
@@ -212,7 +212,13 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *resourceman
212212
return req, nil
213213
}
214214

215-
func outputResult(p *print.Printer, model *inputModel, resp *resourcemanager.Project) error {
215+
func outputResult(p *print.Printer, model inputModel, resp *resourcemanager.Project) error {
216+
if resp == nil {
217+
return fmt.Errorf("response is empty")
218+
}
219+
if model.GlobalFlagModel == nil {
220+
return fmt.Errorf("globalflags are empty")
221+
}
216222
switch model.OutputFormat {
217223
case print.JSONOutputFormat:
218224
details, err := json.MarshalIndent(resp, "", " ")

internal/cmd/project/create/create_test.go

+28-4
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import (
44
"context"
55
"testing"
66

7+
"github.com/google/go-cmp/cmp"
8+
"github.com/google/go-cmp/cmp/cmpopts"
9+
"github.com/google/uuid"
710
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
811
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
912
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
1013
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
11-
12-
"github.com/google/go-cmp/cmp"
13-
"github.com/google/go-cmp/cmp/cmpopts"
14-
"github.com/google/uuid"
1514
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
1615
"github.com/zalando/go-keyring"
1716
)
@@ -359,3 +358,28 @@ func TestBuildRequest(t *testing.T) {
359358
})
360359
}
361360
}
361+
362+
func Test_outputResult(t *testing.T) {
363+
type args struct {
364+
model inputModel
365+
resp *resourcemanager.Project
366+
}
367+
tests := []struct {
368+
name string
369+
args args
370+
wantErr bool
371+
}{
372+
{"empty", args{model: inputModel{GlobalFlagModel: &globalflags.GlobalFlagModel{}}}, true},
373+
{"base", args{inputModel{GlobalFlagModel: &globalflags.GlobalFlagModel{}}, &resourcemanager.Project{}}, false},
374+
}
375+
376+
p := print.NewPrinter()
377+
p.Cmd = NewCmd(p)
378+
for _, tt := range tests {
379+
t.Run(tt.name, func(t *testing.T) {
380+
if err := outputResult(p, tt.args.model, tt.args.resp); (err != nil) != tt.wantErr {
381+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
382+
}
383+
})
384+
}
385+
}

internal/cmd/project/describe/describe.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *resourceman
119119
}
120120

121121
func outputResult(p *print.Printer, outputFormat string, project *resourcemanager.GetProjectResponse) error {
122+
if project == nil {
123+
return fmt.Errorf("response not set")
124+
}
122125
switch outputFormat {
123126
case print.JSONOutputFormat:
124127
details, err := json.MarshalIndent(project, "", " ")
@@ -146,7 +149,9 @@ func outputResult(p *print.Printer, outputFormat string, project *resourcemanage
146149
table.AddSeparator()
147150
table.AddRow("STATE", utils.PtrString(project.LifecycleState))
148151
table.AddSeparator()
149-
table.AddRow("PARENT ID", utils.PtrString(project.Parent.Id))
152+
if project.Parent != nil {
153+
table.AddRow("PARENT ID", utils.PtrString(project.Parent.Id))
154+
}
150155
err := table.Display(p)
151156
if err != nil {
152157
return fmt.Errorf("render table: %w", err)

internal/cmd/project/describe/describe_test.go

+36-3
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package describe
33
import (
44
"context"
55
"testing"
6-
7-
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
8-
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
6+
"time"
97

108
"github.com/google/go-cmp/cmp"
119
"github.com/google/go-cmp/cmp/cmpopts"
1210
"github.com/google/uuid"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1314
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
1415
)
1516

@@ -214,3 +215,35 @@ func TestBuildRequest(t *testing.T) {
214215
})
215216
}
216217
}
218+
219+
func Test_outputResult(t *testing.T) {
220+
type args struct {
221+
outputFormat string
222+
project *resourcemanager.GetProjectResponse
223+
}
224+
tests := []struct {
225+
name string
226+
args args
227+
wantErr bool
228+
}{
229+
{"empty", args{}, true},
230+
{"base", args{"", &resourcemanager.GetProjectResponse{}}, false},
231+
{"complete", args{"", &resourcemanager.GetProjectResponse{
232+
ProjectId: utils.Ptr("4711"),
233+
Name: utils.Ptr("name"),
234+
CreationTime: utils.Ptr(time.Now()),
235+
LifecycleState: utils.Ptr(resourcemanager.LIFECYCLESTATE_CREATING),
236+
Parent: &resourcemanager.Parent{Id: utils.Ptr("parent id")},
237+
},
238+
}, false},
239+
}
240+
p := print.NewPrinter()
241+
p.Cmd = NewCmd(p)
242+
for _, tt := range tests {
243+
t.Run(tt.name, func(t *testing.T) {
244+
if err := outputResult(p, tt.args.outputFormat, tt.args.project); (err != nil) != tt.wantErr {
245+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
246+
}
247+
})
248+
}
249+
}

internal/cmd/project/list/list.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,16 @@ func outputResult(p *print.Printer, outputFormat string, projects []resourcemana
242242
table.SetHeader("ID", "NAME", "STATE", "PARENT ID")
243243
for i := range projects {
244244
p := projects[i]
245+
246+
var parentId *string
247+
if p.Parent != nil {
248+
parentId = p.Parent.Id
249+
}
245250
table.AddRow(
246251
utils.PtrString(p.ProjectId),
247252
utils.PtrString(p.Name),
248253
utils.PtrString(p.LifecycleState),
249-
utils.PtrString(p.Parent.Id),
254+
utils.PtrString(parentId),
250255
)
251256
}
252257

internal/cmd/project/list/list_test.go

+54-5
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,16 @@ import (
99
"testing"
1010
"time"
1111

12+
"github.com/google/go-cmp/cmp"
13+
"github.com/google/go-cmp/cmp/cmpopts"
14+
"github.com/google/uuid"
1215
"github.com/stackitcloud/stackit-cli/internal/pkg/auth"
1316
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1417
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
1518
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
16-
"github.com/zalando/go-keyring"
17-
18-
"github.com/google/go-cmp/cmp"
19-
"github.com/google/go-cmp/cmp/cmpopts"
20-
"github.com/google/uuid"
2119
sdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config"
2220
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
21+
"github.com/zalando/go-keyring"
2322
)
2423

2524
type testCtxKey struct{}
@@ -495,3 +494,53 @@ func TestFetchProjects(t *testing.T) {
495494
})
496495
}
497496
}
497+
498+
func Test_outputResult(t *testing.T) {
499+
type args struct {
500+
outputFormat string
501+
projects []resourcemanager.Project
502+
}
503+
tests := []struct {
504+
name string
505+
args args
506+
wantErr bool
507+
}{
508+
{"empty", args{}, false},
509+
{"base", args{"", []resourcemanager.Project{{}}}, false},
510+
{"complete", args{"", []resourcemanager.Project{
511+
{
512+
ContainerId: utils.Ptr("container-id1"),
513+
CreationTime: utils.Ptr(time.Now()),
514+
Labels: &map[string]string{"foo": "bar"},
515+
LifecycleState: utils.Ptr(resourcemanager.LIFECYCLESTATE_CREATING),
516+
Name: utils.Ptr("some name"),
517+
Parent: &resourcemanager.Parent{
518+
Id: utils.Ptr("parent-id"),
519+
},
520+
ProjectId: utils.Ptr("project-id1"),
521+
},
522+
{
523+
ContainerId: utils.Ptr("container-id2"),
524+
CreationTime: utils.Ptr(time.Now()),
525+
Labels: &map[string]string{"foo": "bar"},
526+
LifecycleState: utils.Ptr(resourcemanager.LIFECYCLESTATE_CREATING),
527+
Name: utils.Ptr("some name"),
528+
Parent: &resourcemanager.Parent{
529+
Id: utils.Ptr("parent-id"),
530+
},
531+
ProjectId: utils.Ptr("project-id2"),
532+
},
533+
}}, false},
534+
}
535+
536+
p := print.NewPrinter()
537+
p.Cmd = NewCmd(p)
538+
539+
for _, tt := range tests {
540+
t.Run(tt.name, func(t *testing.T) {
541+
if err := outputResult(p, tt.args.outputFormat, tt.args.projects); (err != nil) != tt.wantErr {
542+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
543+
}
544+
})
545+
}
546+
}

internal/cmd/project/member/list/list.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func NewCmd(p *print.Printer) *cobra.Command {
8989
members = members[:*model.Limit]
9090
}
9191

92-
return outputResult(p, model, members)
92+
return outputResult(p, *model, members)
9393
},
9494
}
9595
configureFlags(cmd)
@@ -145,13 +145,16 @@ func buildRequest(ctx context.Context, model *inputModel, apiClient *authorizati
145145
return req
146146
}
147147

148-
func outputResult(p *print.Printer, model *inputModel, members []authorization.Member) error {
148+
func outputResult(p *print.Printer, model inputModel, members []authorization.Member) error {
149+
if model.GlobalFlagModel == nil {
150+
return fmt.Errorf("globalflags are empty")
151+
}
149152
sortFn := func(i, j int) bool {
150153
switch model.SortBy {
151154
case "subject":
152-
return *members[i].Subject < *members[j].Subject
155+
return utils.PtrString(members[i].Subject) < utils.PtrString(members[j].Subject)
153156
case "role":
154-
return *members[i].Role < *members[j].Role
157+
return utils.PtrString(members[i].Role) < utils.PtrString(members[j].Role)
155158
default:
156159
return false
157160
}

internal/cmd/project/member/list/list_test.go

+44-4
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import (
44
"context"
55
"testing"
66

7-
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
8-
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
9-
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
10-
117
"github.com/google/go-cmp/cmp"
128
"github.com/google/go-cmp/cmp/cmpopts"
139
"github.com/google/uuid"
1410
"github.com/spf13/cobra"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1514
"github.com/stackitcloud/stackit-sdk-go/services/authorization"
1615
)
1716

@@ -209,3 +208,44 @@ func TestBuildRequest(t *testing.T) {
209208
})
210209
}
211210
}
211+
212+
func Test_outputResult(t *testing.T) {
213+
type args struct {
214+
model inputModel
215+
members []authorization.Member
216+
}
217+
tests := []struct {
218+
name string
219+
args args
220+
wantErr bool
221+
}{
222+
{"empty", args{model: inputModel{GlobalFlagModel: &globalflags.GlobalFlagModel{}}}, false},
223+
{"base", args{inputModel{
224+
GlobalFlagModel: &globalflags.GlobalFlagModel{},
225+
Subject: utils.Ptr("subject"),
226+
Limit: nil,
227+
SortBy: "",
228+
}, nil}, false},
229+
{"complete", args{inputModel{
230+
GlobalFlagModel: &globalflags.GlobalFlagModel{},
231+
Subject: utils.Ptr("subject"),
232+
Limit: nil,
233+
SortBy: "",
234+
},
235+
[]authorization.Member{
236+
{Role: utils.Ptr("role1"), Subject: utils.Ptr("subject1")},
237+
{Role: utils.Ptr("role2"), Subject: utils.Ptr("subject2")},
238+
{Role: utils.Ptr("role3"), Subject: utils.Ptr("subject3")},
239+
}},
240+
false},
241+
}
242+
p := print.NewPrinter()
243+
p.Cmd = NewCmd(p)
244+
for _, tt := range tests {
245+
t.Run(tt.name, func(t *testing.T) {
246+
if err := outputResult(p, tt.args.model, tt.args.members); (err != nil) != tt.wantErr {
247+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
248+
}
249+
})
250+
}
251+
}

internal/cmd/project/role/list/list_test.go

+37-4
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import (
44
"context"
55
"testing"
66

7-
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
8-
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
9-
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
10-
117
"github.com/google/go-cmp/cmp"
128
"github.com/google/go-cmp/cmp/cmpopts"
139
"github.com/google/uuid"
1410
"github.com/spf13/cobra"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
12+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
13+
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1514
"github.com/stackitcloud/stackit-sdk-go/services/authorization"
1615
)
1716

@@ -172,3 +171,37 @@ func TestBuildRequest(t *testing.T) {
172171
})
173172
}
174173
}
174+
175+
func Test_outputRolesResult(t *testing.T) {
176+
type args struct {
177+
outputFormat string
178+
roles []authorization.Role
179+
}
180+
tests := []struct {
181+
name string
182+
args args
183+
wantErr bool
184+
}{
185+
{"empty", args{}, false},
186+
{"standard", args{"", nil}, false},
187+
{"complete", args{"", []authorization.Role{
188+
{
189+
Description: utils.Ptr("description"),
190+
Id: utils.Ptr("id"),
191+
Name: utils.Ptr("name"),
192+
Permissions: &[]authorization.Permission{
193+
{Description: utils.Ptr("description"), Name: utils.Ptr("name")},
194+
},
195+
},
196+
}}, false},
197+
}
198+
p := print.NewPrinter()
199+
p.Cmd = NewCmd(p)
200+
for _, tt := range tests {
201+
t.Run(tt.name, func(t *testing.T) {
202+
if err := outputRolesResult(p, tt.args.outputFormat, tt.args.roles); (err != nil) != tt.wantErr {
203+
t.Errorf("outputRolesResult() error = %v, wantErr %v", err, tt.wantErr)
204+
}
205+
})
206+
}
207+
}

0 commit comments

Comments
 (0)