Skip to content

Commit 544c357

Browse files
committed
add volume backup create tests
1 parent ab287eb commit 544c357

File tree

2 files changed

+292
-14
lines changed

2 files changed

+292
-14
lines changed

internal/cmd/volume/backup/create/create.go

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,6 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
8181
}
8282
}
8383

84-
// TODO: why not necessary here? utils for each service seperately?
85-
// Check if the project is enabled before trying to create
86-
// enabled, err := utils.ProjectEnabled(ctx, apiClient, model.ProjectId)
87-
// if err != nil {
88-
// return fmt.Errorf("check if IaaS is enabled: %w", err)
89-
// }
90-
// if !enabled {
91-
// return &errors.ServiceDisabledError{
92-
// Service: "iaas",
93-
// }
94-
// }
95-
9684
// Call API
9785
req := buildRequest(model, apiClient, ctx)
9886
resp, err := req.Execute()
@@ -150,14 +138,17 @@ func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
150138
return nil, fmt.Errorf("source-type must be either 'volume' or 'snapshot'")
151139
}
152140

153-
name := flags.FlagToStringValue(p, cmd, nameFlag)
141+
name := flags.FlagToStringPointer(p, cmd, nameFlag)
154142
labels := flags.FlagToStringToStringPointer(p, cmd, labelsFlag)
143+
if labels == nil {
144+
labels = &map[string]string{}
145+
}
155146

156147
model := inputModel{
157148
GlobalFlagModel: globalFlags,
158149
SourceID: sourceID,
159150
SourceType: sourceType,
160-
Name: &name,
151+
Name: name,
161152
Labels: *labels,
162153
}
163154

Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
package create
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/stackitcloud/stackit-cli/internal/cmd/params"
8+
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
9+
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
10+
11+
"github.com/google/go-cmp/cmp"
12+
"github.com/google/go-cmp/cmp/cmpopts"
13+
"github.com/google/uuid"
14+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
15+
)
16+
17+
var projectIdFlag = globalflags.ProjectIdFlag
18+
19+
type testCtxKey struct{}
20+
21+
var (
22+
testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
23+
testClient = &iaas.APIClient{}
24+
testProjectId = uuid.NewString()
25+
testSourceId = uuid.NewString()
26+
testName = "my-backup"
27+
testLabels = map[string]string{"key1": "value1"}
28+
)
29+
30+
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
31+
flagValues := map[string]string{
32+
projectIdFlag: testProjectId,
33+
sourceIdFlag: testSourceId,
34+
sourceTypeFlag: "volume",
35+
nameFlag: testName,
36+
labelsFlag: "key1=value1",
37+
}
38+
for _, mod := range mods {
39+
mod(flagValues)
40+
}
41+
return flagValues
42+
}
43+
44+
func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
45+
model := &inputModel{
46+
GlobalFlagModel: &globalflags.GlobalFlagModel{
47+
ProjectId: testProjectId,
48+
Verbosity: globalflags.VerbosityDefault,
49+
},
50+
SourceID: testSourceId,
51+
SourceType: "volume",
52+
Name: &testName,
53+
Labels: testLabels,
54+
}
55+
for _, mod := range mods {
56+
mod(model)
57+
}
58+
return model
59+
}
60+
61+
func fixtureRequest(mods ...func(request *iaas.ApiCreateBackupRequest)) iaas.ApiCreateBackupRequest {
62+
request := testClient.CreateBackup(testCtx, testProjectId)
63+
for _, mod := range mods {
64+
mod(&request)
65+
}
66+
return request
67+
}
68+
69+
func TestParseInput(t *testing.T) {
70+
tests := []struct {
71+
description string
72+
flagValues map[string]string
73+
isValid bool
74+
expectedModel *inputModel
75+
}{
76+
{
77+
description: "base",
78+
flagValues: fixtureFlagValues(),
79+
isValid: true,
80+
expectedModel: fixtureInputModel(),
81+
},
82+
{
83+
description: "no values",
84+
flagValues: map[string]string{},
85+
isValid: false,
86+
},
87+
{
88+
description: "no source id",
89+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
90+
delete(flagValues, sourceIdFlag)
91+
}),
92+
isValid: false,
93+
},
94+
{
95+
description: "no source type",
96+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
97+
delete(flagValues, sourceTypeFlag)
98+
}),
99+
isValid: false,
100+
},
101+
{
102+
description: "invalid source type",
103+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
104+
flagValues[sourceTypeFlag] = "invalid"
105+
}),
106+
isValid: false,
107+
},
108+
{
109+
description: "project id missing",
110+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
111+
delete(flagValues, projectIdFlag)
112+
}),
113+
isValid: false,
114+
},
115+
{
116+
description: "project id invalid",
117+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
118+
flagValues[projectIdFlag] = "invalid-uuid"
119+
}),
120+
isValid: false,
121+
},
122+
{
123+
description: "only required flags",
124+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
125+
delete(flagValues, nameFlag)
126+
delete(flagValues, labelsFlag)
127+
}),
128+
isValid: true,
129+
expectedModel: fixtureInputModel(func(model *inputModel) {
130+
model.Name = nil
131+
model.Labels = make(map[string]string)
132+
}),
133+
},
134+
}
135+
136+
for _, tt := range tests {
137+
t.Run(tt.description, func(t *testing.T) {
138+
p := print.NewPrinter()
139+
cmd := NewCmd(&params.CmdParams{Printer: p})
140+
err := globalflags.Configure(cmd.Flags())
141+
if err != nil {
142+
t.Fatalf("configure global flags: %v", err)
143+
}
144+
145+
for flag, value := range tt.flagValues {
146+
err := cmd.Flags().Set(flag, value)
147+
if err != nil {
148+
if !tt.isValid {
149+
return
150+
}
151+
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
152+
}
153+
}
154+
155+
err = cmd.ValidateRequiredFlags()
156+
if err != nil {
157+
if !tt.isValid {
158+
return
159+
}
160+
t.Fatalf("error validating flags: %v", err)
161+
}
162+
163+
model, err := parseInput(p, cmd)
164+
if err != nil {
165+
if !tt.isValid {
166+
return
167+
}
168+
t.Fatalf("error parsing input: %v", err)
169+
}
170+
171+
if !tt.isValid {
172+
t.Fatalf("did not fail on invalid input")
173+
}
174+
diff := cmp.Diff(model, tt.expectedModel)
175+
if diff != "" {
176+
t.Fatalf("Data does not match: %s", diff)
177+
}
178+
})
179+
}
180+
}
181+
182+
func TestBuildRequest(t *testing.T) {
183+
tests := []struct {
184+
description string
185+
model *inputModel
186+
expectedRequest iaas.ApiCreateBackupRequest
187+
}{
188+
{
189+
description: "base",
190+
model: fixtureInputModel(),
191+
expectedRequest: fixtureRequest(),
192+
},
193+
}
194+
195+
for _, tt := range tests {
196+
t.Run(tt.description, func(t *testing.T) {
197+
request := buildRequest(tt.model, testClient, testCtx)
198+
199+
diff := cmp.Diff(request, tt.expectedRequest,
200+
cmp.AllowUnexported(tt.expectedRequest),
201+
cmpopts.EquateComparable(testCtx),
202+
)
203+
if diff != "" {
204+
t.Fatalf("Data does not match: %s", diff)
205+
}
206+
})
207+
}
208+
}
209+
210+
func TestOutputResult(t *testing.T) {
211+
backupId := "test-backup-id"
212+
213+
type args struct {
214+
outputFormat string
215+
async bool
216+
sourceLabel string
217+
projectLabel string
218+
backup *iaas.Backup
219+
}
220+
221+
tests := []struct {
222+
name string
223+
args args
224+
wantErr bool
225+
}{
226+
{
227+
name: "empty backup",
228+
args: args{},
229+
wantErr: true,
230+
},
231+
{
232+
name: "minimal backup",
233+
args: args{
234+
backup: &iaas.Backup{
235+
Id: &backupId,
236+
},
237+
sourceLabel: "test-source",
238+
projectLabel: "test-project",
239+
},
240+
wantErr: false,
241+
},
242+
{
243+
name: "async mode",
244+
args: args{
245+
backup: &iaas.Backup{
246+
Id: &backupId,
247+
},
248+
sourceLabel: "test-source",
249+
projectLabel: "test-project",
250+
async: true,
251+
},
252+
wantErr: false,
253+
},
254+
{
255+
name: "json output",
256+
args: args{
257+
backup: &iaas.Backup{
258+
Id: &backupId,
259+
},
260+
outputFormat: print.JSONOutputFormat,
261+
},
262+
wantErr: false,
263+
},
264+
{
265+
name: "yaml output",
266+
args: args{
267+
backup: &iaas.Backup{
268+
Id: &backupId,
269+
},
270+
outputFormat: print.YAMLOutputFormat,
271+
},
272+
wantErr: false,
273+
},
274+
}
275+
276+
p := print.NewPrinter()
277+
cmd := NewCmd(&params.CmdParams{Printer: p})
278+
p.Cmd = cmd
279+
280+
for _, tt := range tests {
281+
t.Run(tt.name, func(t *testing.T) {
282+
if err := outputResult(p, tt.args.outputFormat, tt.args.async, tt.args.sourceLabel, tt.args.projectLabel, tt.args.backup); (err != nil) != tt.wantErr {
283+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
284+
}
285+
})
286+
}
287+
}

0 commit comments

Comments
 (0)