From dbff0545bd9f42017795eaaba9e002afb04555b4 Mon Sep 17 00:00:00 2001 From: Umputun Date: Wed, 3 May 2023 23:39:50 -0500 Subject: [PATCH] add glob support for Local --- app/executor/local.go | 22 +++++++- app/executor/local_test.go | 96 +++++++++++++++++++++++++++++++++ app/executor/testdata/data.txt | 5 -- app/executor/testdata/data1.txt | 1 + app/executor/testdata/data2.txt | 6 +-- 5 files changed, 118 insertions(+), 12 deletions(-) delete mode 100644 app/executor/testdata/data.txt create mode 100644 app/executor/testdata/data1.txt diff --git a/app/executor/local.go b/app/executor/local.go index 4ebadeab..b18ba8d5 100644 --- a/app/executor/local.go +++ b/app/executor/local.go @@ -41,13 +41,31 @@ func (l *Local) Run(ctx context.Context, cmd string, verbose bool) (out []string // Upload just copy file from one place to another func (l *Local) Upload(_ context.Context, src, dst string, mkdir bool) (err error) { + + // check if the local parameter contains a glob pattern + matches, err := filepath.Glob(src) + if err != nil { + return fmt.Errorf("failed to expand glob pattern %s: %w", src, err) + } + + if len(matches) == 0 { // no match + return fmt.Errorf("source file %q not found", src) + } + if mkdir { if err = os.MkdirAll(filepath.Dir(dst), 0o750); err != nil { return fmt.Errorf("can't create local dir %s: %w", filepath.Dir(dst), err) } } - if err = l.copyFile(src, dst); err != nil { - return fmt.Errorf("can't copy local file from %s to %s: %w", src, dst, err) + + for _, match := range matches { + destination := dst + if len(matches) > 1 { + destination = filepath.Join(dst, filepath.Base(match)) + } + if err = l.copyFile(match, destination); err != nil { + return fmt.Errorf("can't copy local file from %s to %s: %w", match, dst, err) + } } return nil } diff --git a/app/executor/local_test.go b/app/executor/local_test.go index 31306670..7e9a0873 100644 --- a/app/executor/local_test.go +++ b/app/executor/local_test.go @@ -2,6 +2,7 @@ package executor import ( "context" + "fmt" "os" "path/filepath" "testing" @@ -131,6 +132,101 @@ func TestUploadAndDownload(t *testing.T) { } } +func TestUploadDownloadWithGlob(t *testing.T) { + // create some temporary test files with content + tmpDir, err := os.MkdirTemp("", "test") + require.NoError(t, err) + defer os.RemoveAll(tmpDir) + + data1File := filepath.Join(tmpDir, "data1.txt") + err = os.WriteFile(data1File, []byte("data1 content"), 0644) + require.NoError(t, err) + + data2File := filepath.Join(tmpDir, "data2.txt") + err = os.WriteFile(data2File, []byte("data2 content"), 0644) + require.NoError(t, err) + + // create a temporary destination directory + dstDir, err := os.MkdirTemp("", "dst") + require.NoError(t, err) + defer os.RemoveAll(dstDir) + + type fn func(ctx context.Context, src, dst string, mkdir bool) (err error) + + l := &Local{} + fns := []struct { + name string + fn fn + }{{"upload", l.Upload}} + + for _, tc := range []struct { + name string + src string + dst string + mkdir bool + expectError bool + }{ + { + name: "successful upload with mkdir=true", + src: filepath.Join(tmpDir, "*.txt"), + dst: dstDir, + mkdir: true, + }, + { + name: "successful upload with mkdir=false", + src: filepath.Join(tmpDir, "*.txt"), + dst: dstDir, + }, + { + name: "failed upload with non-existent source file", + src: filepath.Join(tmpDir, "nonexistent.txt"), + dst: dstDir, + mkdir: false, + expectError: true, + }, + { + name: "failed upload with non-existent directory and mkdir=false", + src: filepath.Join(tmpDir, "*.txt"), + dst: filepath.Join(tmpDir, "nonexistent", "dst"), + mkdir: false, + expectError: true, + }, + { + name: "failed upload with invalid glob pattern", + src: filepath.Join(tmpDir, "*.txt["), + dst: dstDir, + mkdir: false, + expectError: true, + }, + } { + for _, fn := range fns { + t.Run(fmt.Sprintf("%s#%s", tc.name, fn.name), func(t *testing.T) { + err := fn.fn(context.Background(), tc.src, tc.dst, tc.mkdir) + + if tc.expectError { + assert.Error(t, err, "expected an error") + return + } + + assert.NoError(t, err, "unexpected error") + + // assert that all files were uploaded + files, err := os.ReadDir(dstDir) + require.NoError(t, err) + assert.Len(t, files, 2, "unexpected number of uploaded files") + + // assert that the contents of the uploaded files match the contents of the source files + for _, f := range files { + dstContent, err := os.ReadFile(filepath.Join(dstDir, f.Name())) + require.NoError(t, err) + assert.Equal(t, fmt.Sprintf("data%d content", f.Name()[4]-'0'), string(dstContent), + "uploaded content should match source content") + } + }) + } + } +} + func TestLocal_Sync(t *testing.T) { testCases := []struct { diff --git a/app/executor/testdata/data.txt b/app/executor/testdata/data.txt deleted file mode 100644 index 56b44389..00000000 --- a/app/executor/testdata/data.txt +++ /dev/null @@ -1,5 +0,0 @@ ---start of data file-- -some data file -blah -blah ---end of data file-- \ No newline at end of file diff --git a/app/executor/testdata/data1.txt b/app/executor/testdata/data1.txt new file mode 100644 index 00000000..60f2c5a9 --- /dev/null +++ b/app/executor/testdata/data1.txt @@ -0,0 +1 @@ +data1 content \ No newline at end of file diff --git a/app/executor/testdata/data2.txt b/app/executor/testdata/data2.txt index 6d4ffe52..7a56c634 100644 --- a/app/executor/testdata/data2.txt +++ b/app/executor/testdata/data2.txt @@ -1,5 +1 @@ ---start of data file2-- -some data file -blah -blah ---end of data file2-- \ No newline at end of file +data2 content \ No newline at end of file