mirror of
https://github.com/rclone/rclone.git
synced 2025-12-11 22:14:05 +01:00
- Added Tests - Fixed file name handling - Added concurrent downloads - Limited downloads to --transfers - Fixes #8127
158 lines
4.0 KiB
Go
158 lines
4.0 KiB
Go
package copyurl
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
_ "github.com/rclone/rclone/backend/local"
|
|
"github.com/rclone/rclone/fs"
|
|
"github.com/rclone/rclone/fs/operations"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func resetGlobals() {
|
|
autoFilename = false
|
|
headerFilename = false
|
|
printFilename = false
|
|
stdout = false
|
|
noClobber = false
|
|
urls = false
|
|
copyURL = operations.CopyURL
|
|
}
|
|
|
|
func TestRun_RequiresTwoArgsWhenNotStdout(t *testing.T) {
|
|
t.Cleanup(resetGlobals)
|
|
resetGlobals()
|
|
|
|
err := run([]string{"https://example.com/foo"})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "need 2 arguments if not using --stdout")
|
|
}
|
|
|
|
func TestRun_CallsCopyURL_WithExplicitFilename_Success(t *testing.T) {
|
|
t.Cleanup(resetGlobals)
|
|
resetGlobals()
|
|
|
|
tmp := t.TempDir()
|
|
dstPath := filepath.Join(tmp, "out.txt")
|
|
|
|
var called int32
|
|
|
|
copyURL = func(_ctx context.Context, _dst fs.Fs, dstFileName, url string, auto, header, noclobber bool) (fs.Object, error) {
|
|
atomic.AddInt32(&called, 1)
|
|
assert.Equal(t, "https://example.com/file", url)
|
|
assert.Equal(t, "out.txt", dstFileName)
|
|
assert.False(t, auto)
|
|
assert.False(t, header)
|
|
assert.False(t, noclobber)
|
|
return nil, nil
|
|
}
|
|
|
|
err := run([]string{"https://example.com/file", dstPath})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, int32(1), atomic.LoadInt32(&called))
|
|
}
|
|
|
|
func TestRun_CallsCopyURL_WithAutoFilename_AndPropagatesError(t *testing.T) {
|
|
t.Cleanup(resetGlobals)
|
|
resetGlobals()
|
|
|
|
tmp := t.TempDir()
|
|
autoFilename = true
|
|
|
|
want := errors.New("boom")
|
|
var called int32
|
|
|
|
copyURL = func(_ctx context.Context, _dst fs.Fs, dstFileName, url string, auto, header, noclobber bool) (fs.Object, error) {
|
|
atomic.AddInt32(&called, 1)
|
|
assert.Equal(t, "", dstFileName) // auto filename -> empty
|
|
assert.True(t, auto)
|
|
return nil, want
|
|
}
|
|
|
|
err := run([]string{"https://example.com/auto/name", tmp})
|
|
require.Error(t, err)
|
|
assert.Equal(t, want, err)
|
|
assert.Equal(t, int32(1), atomic.LoadInt32(&called))
|
|
}
|
|
|
|
func TestRunURLS_ErrorsWithStdoutAndWithPrintFilename(t *testing.T) {
|
|
t.Cleanup(resetGlobals)
|
|
resetGlobals()
|
|
|
|
stdout = true
|
|
err := runURLS([]string{"dummy.csv", "destDir"})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "can't use --stdout with --urls")
|
|
|
|
resetGlobals()
|
|
printFilename = true
|
|
err = runURLS([]string{"dummy.csv", "destDir"})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "can't use --print-filename with --urls")
|
|
}
|
|
|
|
func TestRunURLS_ProcessesCSV_ParallelCalls_AndAggregatesError(t *testing.T) {
|
|
t.Cleanup(resetGlobals)
|
|
resetGlobals()
|
|
|
|
tmp := t.TempDir()
|
|
csvPath := filepath.Join(tmp, "urls.csv")
|
|
csvContent := []byte(
|
|
"https://example.com/a,aaa.txt\n" + // success
|
|
"https://example.com/b\n" + // auto filename
|
|
"https://example.com/c,ccc.txt\n") // error
|
|
require.NoError(t, os.WriteFile(csvPath, csvContent, 0o600))
|
|
|
|
// destination dir (local backend)
|
|
dest := t.TempDir()
|
|
|
|
// mock copyURL: succeed for /a and /b, fail for /c
|
|
|
|
var calls int32
|
|
var mu sync.Mutex
|
|
var seen []string
|
|
|
|
copyURL = func(_ctx context.Context, _dst fs.Fs, dstFileName, url string, auto, header, noclobber bool) (fs.Object, error) {
|
|
atomic.AddInt32(&calls, 1)
|
|
mu.Lock()
|
|
seen = append(seen, url+"|"+dstFileName)
|
|
mu.Unlock()
|
|
|
|
switch {
|
|
case url == "https://example.com/a":
|
|
require.Equal(t, "aaa.txt", dstFileName)
|
|
return nil, nil
|
|
case url == "https://example.com/b":
|
|
require.Equal(t, "", dstFileName) // auto-name path
|
|
return nil, nil
|
|
case url == "https://example.com/c":
|
|
return nil, errors.New("network down")
|
|
default:
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
err := runURLS([]string{csvPath, dest})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not all URLs copied successfully")
|
|
// 3 lines => 3 calls
|
|
assert.Equal(t, int32(3), atomic.LoadInt32(&calls))
|
|
|
|
// sanity: all expected URLs were seen
|
|
assert.ElementsMatch(t,
|
|
[]string{
|
|
"https://example.com/a|aaa.txt",
|
|
"https://example.com/b|",
|
|
"https://example.com/c|ccc.txt",
|
|
},
|
|
seen,
|
|
)
|
|
}
|