Skip to content

Commit 0c81cca

Browse files
committed
add more tests
Signed-off-by: Aleksandr Zimin <[email protected]>
1 parent 62dd014 commit 0c81cca

File tree

5 files changed

+275
-9
lines changed

5 files changed

+275
-9
lines changed

internal/dataexport/cmd/download/download.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -267,14 +267,14 @@ func Run(ctx context.Context, log *slog.Logger, cmd *cobra.Command, args []strin
267267
return err
268268
}
269269

270-
deName, err := util.CreateDataExporterIfNeeded(ctx, log, dataName, namespace, publish, ttl, rtClient)
270+
deName, err := util.CreateDataExporterIfNeededFunc(ctx, log, dataName, namespace, publish, ttl, rtClient)
271271
if err != nil {
272272
return err
273273
}
274274

275275
log.Info("DataExport created", slog.String("name", deName), slog.String("namespace", namespace))
276276

277-
url, volumeMode, subClient, err := util.PrepareDownload(ctx, log, deName, namespace, publish, sClient)
277+
url, volumeMode, subClient, err := util.PrepareDownloadFunc(ctx, log, deName, namespace, publish, sClient)
278278
if err != nil {
279279
return err
280280
}
@@ -294,7 +294,7 @@ func Run(ctx context.Context, log *slog.Logger, cmd *cobra.Command, args []strin
294294
dstPath = deName
295295
}
296296
default:
297-
return fmt.Errorf("%w: %s", util.UnsupportedVolumeModeErr, volumeMode)
297+
return fmt.Errorf("%w: %s", util.ErrUnsupportedVolumeMode, volumeMode)
298298
}
299299

300300
log.Info("Start downloading", slog.String("url", url+srcPath), slog.String("dstPath", dstPath))
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package download
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"io"
7+
"net/http"
8+
"net/http/httptest"
9+
"os"
10+
"path/filepath"
11+
"testing"
12+
13+
"log/slog"
14+
15+
"github.com/stretchr/testify/require"
16+
17+
"github.com/deckhouse/deckhouse-cli/internal/dataexport/util"
18+
safereq "github.com/deckhouse/deckhouse-cli/pkg/libsaferequest/client"
19+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
20+
)
21+
22+
// helper to create SafeClient with empty rest.Config (no auth)
23+
func newNoAuthSafe() *safereq.SafeClient {
24+
sc, _ := safereq.NewSafeClient()
25+
return sc.Copy()
26+
}
27+
28+
func TestDownloadFilesystem_OK(t *testing.T) {
29+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
30+
require.Equal(t, "/api/v1/files/foo.txt", r.URL.Path)
31+
w.Header().Set("X-Type", "file")
32+
w.Header().Set("Content-Length", "3")
33+
w.WriteHeader(200)
34+
w.Write([]byte("abc"))
35+
}))
36+
defer srv.Close()
37+
38+
// stub PrepareDownload / CreateDataExporterIfNeeded
39+
origPrep := util.PrepareDownloadFunc
40+
origCreate := util.CreateDataExporterIfNeededFunc
41+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
42+
return srv.URL + "/api/v1/files", "Filesystem", newNoAuthSafe(), nil
43+
}
44+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
45+
return de, nil
46+
}
47+
defer func() {
48+
util.PrepareDownloadFunc = origPrep
49+
util.CreateDataExporterIfNeededFunc = origCreate
50+
}()
51+
52+
outFile := filepath.Join(t.TempDir(), "out.txt")
53+
54+
cmd := NewCommand(context.TODO(), slog.Default())
55+
cmd.SetArgs([]string{"myexport", "foo.txt", "-o", outFile})
56+
var buf bytes.Buffer
57+
cmd.SetOut(&buf)
58+
cmd.SetErr(&buf)
59+
60+
require.NoError(t, cmd.Execute())
61+
62+
data, err := os.ReadFile(outFile)
63+
require.NoError(t, err)
64+
require.Equal(t, []byte("abc"), data)
65+
}
66+
67+
func TestDownloadFilesystem_BadPath(t *testing.T) {
68+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
69+
// Simulate Block-mode error when files endpoint is used
70+
http.Error(w, "VolumeMode: Block. Not supported downloading files.", http.StatusBadRequest)
71+
}))
72+
defer srv.Close()
73+
74+
origPrep := util.PrepareDownloadFunc
75+
origCreate := util.CreateDataExporterIfNeededFunc
76+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
77+
return srv.URL + "/api/v1/files", "Block", newNoAuthSafe(), nil
78+
}
79+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
80+
return de, nil
81+
}
82+
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()
83+
84+
cmd := NewCommand(context.TODO(), slog.Default())
85+
cmd.SetArgs([]string{"myexport", "foo.txt", "-o", filepath.Join(t.TempDir(), "out.txt")})
86+
require.NoError(t, cmd.Execute())
87+
}
88+
89+
func TestDownloadBlock_OK(t *testing.T) {
90+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
91+
require.Equal(t, "/api/v1/block", r.URL.Path)
92+
w.Header().Set("Content-Length", "4")
93+
w.WriteHeader(200)
94+
w.Write([]byte("raw!"))
95+
}))
96+
defer srv.Close()
97+
98+
origPrep := util.PrepareDownloadFunc
99+
origCreate := util.CreateDataExporterIfNeededFunc
100+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
101+
return srv.URL + "/api/v1/block", "Block", newNoAuthSafe(), nil
102+
}
103+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
104+
return de, nil
105+
}
106+
defer func() {
107+
util.PrepareDownloadFunc = origPrep
108+
util.CreateDataExporterIfNeededFunc = origCreate
109+
}()
110+
111+
outFile := filepath.Join(t.TempDir(), "raw.img")
112+
cmd := NewCommand(context.TODO(), slog.Default())
113+
cmd.SetArgs([]string{"myexport", "-o", outFile})
114+
cmd.SetOut(io.Discard)
115+
cmd.SetErr(io.Discard)
116+
require.NoError(t, cmd.Execute())
117+
data, err := os.ReadFile(outFile)
118+
require.NoError(t, err)
119+
require.Equal(t, []byte("raw!"), data)
120+
}
121+
122+
func TestDownloadBlock_WrongEndpoint(t *testing.T) {
123+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
124+
http.Error(w, "VolumeMode: Filesystem. Not supported downloading raw block.", http.StatusBadRequest)
125+
}))
126+
defer srv.Close()
127+
128+
origPrep := util.PrepareDownloadFunc
129+
origCreate := util.CreateDataExporterIfNeededFunc
130+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
131+
return srv.URL + "/api/v1/block", "Filesystem", newNoAuthSafe(), nil
132+
}
133+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
134+
return de, nil
135+
}
136+
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()
137+
138+
cmd := NewCommand(context.TODO(), slog.Default())
139+
cmd.SetArgs([]string{"myexport", "-o", filepath.Join(t.TempDir(), "raw.img")})
140+
cmd.SetOut(io.Discard)
141+
cmd.SetErr(io.Discard)
142+
require.NoError(t, cmd.Execute())
143+
}

internal/dataexport/cmd/list/list.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ func downloadFunc(
9090
sClient *safeClient.SafeClient,
9191
foo func(body io.Reader) error,
9292
) error {
93-
url, volumeMode, subClient, err := util.PrepareDownload(ctx, log, deName, namespace, publish, sClient)
93+
url, volumeMode, subClient, err := util.PrepareDownloadFunc(ctx, log, deName, namespace, publish, sClient)
9494
if err != nil {
9595
return err
9696
}
@@ -112,7 +112,7 @@ func downloadFunc(
112112
log.Info("Start listing", slog.String("url", url))
113113
req, _ = http.NewRequest("HEAD", url, nil)
114114
default:
115-
return fmt.Errorf("%w: %s", util.UnsupportedVolumeModeErr, volumeMode)
115+
return fmt.Errorf("%w: %s", util.ErrUnsupportedVolumeMode, volumeMode)
116116
}
117117

118118
resp, err := subClient.HttpDo(req.WithContext(ctx))
@@ -140,7 +140,7 @@ func downloadFunc(
140140
case "Filesystem":
141141
return foo(resp.Body)
142142
default:
143-
return fmt.Errorf("%w: %s", util.UnsupportedVolumeModeErr, volumeMode)
143+
return fmt.Errorf("%w: %s", util.ErrUnsupportedVolumeMode, volumeMode)
144144
}
145145
}
146146

@@ -168,7 +168,7 @@ func Run(ctx context.Context, log *slog.Logger, cmd *cobra.Command, args []strin
168168
if err != nil {
169169
return err
170170
}
171-
deName, err := util.CreateDataExporterIfNeeded(ctx, log, dataName, namespace, publish, ttl, rtClient)
171+
deName, err := util.CreateDataExporterIfNeededFunc(ctx, log, dataName, namespace, publish, ttl, rtClient)
172172
if err != nil {
173173
return err
174174
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package list
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"io"
7+
"net/http"
8+
"net/http/httptest"
9+
"os"
10+
"testing"
11+
12+
"log/slog"
13+
14+
"github.com/stretchr/testify/require"
15+
16+
"github.com/deckhouse/deckhouse-cli/internal/dataexport/util"
17+
safereq "github.com/deckhouse/deckhouse-cli/pkg/libsaferequest/client"
18+
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
19+
)
20+
21+
func newSafe() *safereq.SafeClient { sc, _ := safereq.NewSafeClient(); return sc.Copy() }
22+
23+
func TestListFilesystem_OK(t *testing.T) {
24+
// JSON listing for root dir
25+
respBody := `{"apiVersion":"v1","items":[{"name":"file.txt","type":"file"}]}`
26+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
27+
require.Equal(t, "/api/v1/files/", r.URL.Path)
28+
w.Header().Set("Content-Type", "application/json")
29+
w.WriteHeader(200)
30+
w.Write([]byte(respBody))
31+
}))
32+
defer srv.Close()
33+
34+
origPrep := util.PrepareDownloadFunc
35+
origCreate := util.CreateDataExporterIfNeededFunc
36+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
37+
return srv.URL + "/api/v1/files", "Filesystem", newSafe(), nil
38+
}
39+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
40+
return de, nil
41+
}
42+
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()
43+
44+
oldStd := os.Stdout
45+
r, w, _ := os.Pipe()
46+
os.Stdout = w
47+
48+
cmd := NewCommand(context.TODO(), slog.Default())
49+
cmd.SetArgs([]string{"myexport", "/"})
50+
require.NoError(t, cmd.Execute())
51+
52+
w.Close()
53+
var bufOut bytes.Buffer
54+
io.Copy(&bufOut, r)
55+
os.Stdout = oldStd
56+
57+
require.Contains(t, bufOut.String(), "file.txt")
58+
}
59+
60+
func TestListBlock_OK(t *testing.T) {
61+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
62+
require.Equal(t, http.MethodHead, r.Method)
63+
w.Header().Set("Content-Length", "1234")
64+
w.WriteHeader(200)
65+
}))
66+
defer srv.Close()
67+
68+
origPrep := util.PrepareDownloadFunc
69+
origCreate := util.CreateDataExporterIfNeededFunc
70+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
71+
return srv.URL + "/api/v1/block", "Block", newSafe(), nil
72+
}
73+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
74+
return de, nil
75+
}
76+
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()
77+
78+
// capture stdout because list writes to Stdout directly
79+
oldStd := os.Stdout
80+
r, w, _ := os.Pipe()
81+
os.Stdout = w
82+
83+
cmd := NewCommand(context.TODO(), slog.Default())
84+
cmd.SetArgs([]string{"myexport"})
85+
require.NoError(t, cmd.Execute())
86+
87+
w.Close()
88+
var buf bytes.Buffer
89+
io.Copy(&buf, r)
90+
os.Stdout = oldStd
91+
92+
require.Contains(t, buf.String(), "Content-Length: 1234")
93+
}
94+
95+
func TestListFilesystem_NotDir(t *testing.T) {
96+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
97+
http.Error(w, "not dir", http.StatusBadRequest)
98+
}))
99+
defer srv.Close()
100+
101+
origPrep := util.PrepareDownloadFunc
102+
origCreate := util.CreateDataExporterIfNeededFunc
103+
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
104+
return srv.URL + "/api/v1/files", "Filesystem", newSafe(), nil
105+
}
106+
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
107+
return de, nil
108+
}
109+
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()
110+
111+
cmd := NewCommand(context.TODO(), slog.Default())
112+
cmd.SetOut(&bytes.Buffer{})
113+
cmd.SetErr(&bytes.Buffer{})
114+
cmd.SetArgs([]string{"myexport", "some/invalid"})
115+
err := cmd.Execute()
116+
require.Error(t, err)
117+
}

internal/dataexport/util/util.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ const (
4646
)
4747

4848
var (
49-
UnsupportedVolumeModeErr = errors.New("invalid volume mode")
49+
ErrUnsupportedVolumeMode = errors.New("invalid volume mode")
50+
)
51+
52+
// Function pointers for test stubbing
53+
var (
54+
PrepareDownloadFunc = PrepareDownload
55+
CreateDataExporterIfNeededFunc = CreateDataExporterIfNeeded
5056
)
5157

5258
func GetDataExport(ctx context.Context, deName, namespace string, rtClient ctrlrtclient.Client) (*v1alpha1.DataExport, error) {
@@ -329,7 +335,7 @@ func PrepareDownload(ctx context.Context, log *slog.Logger, deName, namespace st
329335
return
330336
}
331337
default:
332-
finErr = fmt.Errorf("%w: '%s'", UnsupportedVolumeModeErr, volumeMode)
338+
finErr = fmt.Errorf("%w: '%s'", ErrUnsupportedVolumeMode, volumeMode)
333339
return
334340
}
335341

0 commit comments

Comments
 (0)