Skip to content

Commit 2de3fa6

Browse files
committed
wip list
1 parent 544c357 commit 2de3fa6

File tree

2 files changed

+240
-18
lines changed

2 files changed

+240
-18
lines changed

internal/cmd/volume/backup/list/list.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,16 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
4040
Args: args.NoArgs,
4141
Example: examples.Build(
4242
examples.NewExample(
43-
`List all volume backups`,
43+
`List all backups`,
4444
"$ stackit volume backup list"),
4545
examples.NewExample(
46-
`List all volume backups in JSON format`,
46+
`List all backups in JSON format`,
4747
"$ stackit volume backup list --output-format json"),
4848
examples.NewExample(
49-
`List up to 10 volume backups`,
49+
`List up to 10 backups`,
5050
"$ stackit volume backup list --limit 10"),
5151
examples.NewExample(
52-
`List volume backups with specific labels`,
52+
`List backups with specific labels`,
5353
"$ stackit volume backup list --label-selector key1=value1,key2=value2"),
5454
),
5555
RunE: func(cmd *cobra.Command, _ []string) error {
@@ -69,7 +69,7 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
6969
req := buildRequest(ctx, model, apiClient)
7070
resp, err := req.Execute()
7171
if err != nil {
72-
return fmt.Errorf("get volume backups: %w", err)
72+
return fmt.Errorf("get backups: %w", err)
7373
}
7474
if resp.Items == nil || len(*resp.Items) == 0 {
7575
projectLabel, err := projectname.GetProjectName(ctx, params.Printer, params.CliVersion, cmd)
@@ -97,7 +97,7 @@ func NewCmd(params *params.CmdParams) *cobra.Command {
9797

9898
func configureFlags(cmd *cobra.Command) {
9999
cmd.Flags().Int64(limitFlag, 0, "Maximum number of entries to list")
100-
cmd.Flags().String(labelSelectorFlag, "", "Filter backups by labels (comma-separated key=value pairs)")
100+
cmd.Flags().String(labelSelectorFlag, "", "Filter backups by labels")
101101
}
102102

103103
func parseInput(p *print.Printer, cmd *cobra.Command) (*inputModel, error) {
@@ -153,15 +153,15 @@ func outputResult(p *print.Printer, outputFormat string, backups []iaas.Backup)
153153
case print.JSONOutputFormat:
154154
details, err := json.MarshalIndent(backups, "", " ")
155155
if err != nil {
156-
return fmt.Errorf("marshal volume backup list: %w", err)
156+
return fmt.Errorf("marshal backup list: %w", err)
157157
}
158158
p.Outputln(string(details))
159159
return nil
160160

161161
case print.YAMLOutputFormat:
162162
details, err := yaml.MarshalWithOptions(backups, yaml.IndentSequence(true), yaml.UseJSONMarshaler())
163163
if err != nil {
164-
return fmt.Errorf("marshal volume backup list: %w", err)
164+
return fmt.Errorf("marshal backup list: %w", err)
165165
}
166166
p.Outputln(string(details))
167167
return nil
@@ -173,23 +173,17 @@ func outputResult(p *print.Printer, outputFormat string, backups []iaas.Backup)
173173
for i := range backups {
174174
backup := backups[i]
175175

176-
// Format labels as a string
177-
labelsStr := ""
178-
if backup.Labels != nil {
179-
labelsStr = utils.FormatLabelsAsString(*backup.Labels)
180-
}
181-
182176
table.AddRow(
183177
utils.PtrString(backup.Id),
184178
utils.PtrString(backup.Name),
185-
utils.FormatSize(backup.Size),
179+
utils.PtrByteSizeDefault((*int64)(backup.Size), ""),
186180
utils.PtrString(backup.Status),
187181
utils.PtrString(backup.SnapshotId),
188182
utils.PtrString(backup.VolumeId),
189183
utils.PtrString(backup.AvailabilityZone),
190-
labelsStr,
191-
utils.FormatTimestamp(backup.CreatedAt),
192-
utils.FormatTimestamp(backup.UpdatedAt),
184+
utils.PtrStringDefault(backup.Labels, ""),
185+
utils.ConvertTimePToDateTimeString(backup.CreatedAt),
186+
utils.ConvertTimePToDateTimeString(backup.UpdatedAt),
193187
)
194188
}
195189

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
package list
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+
"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"
15+
"github.com/spf13/cobra"
16+
"github.com/stackitcloud/stackit-sdk-go/services/iaas"
17+
)
18+
19+
var projectIdFlag = globalflags.ProjectIdFlag
20+
21+
type testCtxKey struct{}
22+
23+
var (
24+
testCtx = context.WithValue(context.Background(), testCtxKey{}, "foo")
25+
testClient = &iaas.APIClient{}
26+
testProjectId = uuid.NewString()
27+
)
28+
29+
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
30+
flagValues := map[string]string{
31+
projectIdFlag: testProjectId,
32+
limitFlag: "10",
33+
labelSelectorFlag: "key1=value1",
34+
}
35+
for _, mod := range mods {
36+
mod(flagValues)
37+
}
38+
return flagValues
39+
}
40+
41+
func fixtureInputModel(mods ...func(model *inputModel)) *inputModel {
42+
model := &inputModel{
43+
GlobalFlagModel: &globalflags.GlobalFlagModel{
44+
ProjectId: testProjectId,
45+
Verbosity: globalflags.VerbosityDefault,
46+
},
47+
Limit: utils.Ptr(int64(10)),
48+
LabelSelector: utils.Ptr("key1=value1"),
49+
}
50+
for _, mod := range mods {
51+
mod(model)
52+
}
53+
return model
54+
}
55+
56+
func fixtureRequest(mods ...func(request *iaas.ApiListBackupsRequest)) iaas.ApiListBackupsRequest {
57+
request := testClient.ListBackups(testCtx, testProjectId)
58+
request = request.LabelSelector("key1=value1")
59+
for _, mod := range mods {
60+
mod(&request)
61+
}
62+
return request
63+
}
64+
65+
func TestParseInput(t *testing.T) {
66+
tests := []struct {
67+
description string
68+
flagValues map[string]string
69+
isValid bool
70+
expectedModel *inputModel
71+
}{
72+
{
73+
description: "base",
74+
flagValues: fixtureFlagValues(),
75+
isValid: true,
76+
expectedModel: fixtureInputModel(),
77+
},
78+
{
79+
description: "no values",
80+
flagValues: map[string]string{},
81+
isValid: false,
82+
},
83+
{
84+
description: "project id missing",
85+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
86+
delete(flagValues, projectIdFlag)
87+
}),
88+
isValid: false,
89+
},
90+
{
91+
description: "project id invalid 1",
92+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
93+
flagValues[projectIdFlag] = ""
94+
}),
95+
isValid: false,
96+
},
97+
{
98+
description: "project id invalid 2",
99+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
100+
flagValues[projectIdFlag] = "invalid-uuid"
101+
}),
102+
isValid: false,
103+
},
104+
{
105+
description: "limit invalid",
106+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
107+
flagValues[limitFlag] = "invalid"
108+
}),
109+
isValid: false,
110+
},
111+
{
112+
description: "limit invalid 2",
113+
flagValues: fixtureFlagValues(func(flagValues map[string]string) {
114+
flagValues[limitFlag] = "0"
115+
}),
116+
isValid: false,
117+
},
118+
}
119+
120+
for _, tt := range tests {
121+
t.Run(tt.description, func(t *testing.T) {
122+
cmd := &cobra.Command{}
123+
err := globalflags.Configure(cmd.Flags())
124+
if err != nil {
125+
t.Fatalf("configure global flags: %v", err)
126+
}
127+
128+
configureFlags(cmd)
129+
130+
for flag, value := range tt.flagValues {
131+
err := cmd.Flags().Set(flag, value)
132+
if err != nil {
133+
if !tt.isValid {
134+
return
135+
}
136+
t.Fatalf("setting flag --%s=%s: %v", flag, value, err)
137+
}
138+
}
139+
140+
err = cmd.ValidateRequiredFlags()
141+
if err != nil {
142+
if !tt.isValid {
143+
return
144+
}
145+
t.Fatalf("error validating flags: %v", err)
146+
}
147+
148+
p := print.NewPrinter()
149+
model, err := parseInput(p, cmd)
150+
if err != nil {
151+
if !tt.isValid {
152+
return
153+
}
154+
t.Fatalf("error parsing flags: %v", err)
155+
}
156+
157+
if !tt.isValid {
158+
t.Fatalf("did not fail on invalid input")
159+
}
160+
diff := cmp.Diff(model, tt.expectedModel)
161+
if diff != "" {
162+
t.Fatalf("Data does not match: %s", diff)
163+
}
164+
})
165+
}
166+
}
167+
168+
func TestBuildRequest(t *testing.T) {
169+
tests := []struct {
170+
description string
171+
model *inputModel
172+
expectedRequest iaas.ApiListBackupsRequest
173+
}{
174+
{
175+
description: "base",
176+
model: fixtureInputModel(),
177+
expectedRequest: fixtureRequest(),
178+
},
179+
}
180+
181+
for _, tt := range tests {
182+
t.Run(tt.description, func(t *testing.T) {
183+
request := buildRequest(testCtx, tt.model, testClient)
184+
185+
diff := cmp.Diff(request, tt.expectedRequest,
186+
cmp.AllowUnexported(tt.expectedRequest),
187+
cmpopts.EquateComparable(testCtx),
188+
)
189+
if diff != "" {
190+
t.Fatalf("Data does not match: %s", diff)
191+
}
192+
})
193+
}
194+
}
195+
196+
func TestOutputResult(t *testing.T) {
197+
type args struct {
198+
outputFormat string
199+
backups []iaas.Backup
200+
}
201+
tests := []struct {
202+
name string
203+
args args
204+
wantErr bool
205+
}{
206+
{
207+
name: "empty",
208+
args: args{},
209+
wantErr: true,
210+
},
211+
{
212+
name: "set empty create backup response",
213+
args: args{
214+
backups: []iaas.Backup{},
215+
},
216+
wantErr: false,
217+
},
218+
}
219+
p := print.NewPrinter()
220+
p.Cmd = NewCmd(&params.CmdParams{Printer: p})
221+
for _, tt := range tests {
222+
t.Run(tt.name, func(t *testing.T) {
223+
if err := outputResult(p, tt.args.outputFormat, tt.args.backups); (err != nil) != tt.wantErr {
224+
t.Errorf("outputResult() error = %v, wantErr %v", err, tt.wantErr)
225+
}
226+
})
227+
}
228+
}

0 commit comments

Comments
 (0)