Always copy source directory contents, not the directory itself

This commit is contained in:
2026-07-17 18:40:16 -04:00
parent 340370a43b
commit b2172145e7
3 changed files with 101 additions and 8 deletions
+80 -6
View File
@@ -40,8 +40,8 @@ func TestBuildArgs_Push(t *testing.T) {
if args[0] != "-aP" {
t.Errorf("first flag = %q, want %q", args[0], "-aP")
}
if args[len(args)-2] != "/local/src" {
t.Errorf("source = %q, want %q", args[len(args)-2], "/local/src")
if args[len(args)-2] != "/local/src/" {
t.Errorf("source = %q, want %q (auto-appended trailing slash)", args[len(args)-2], "/local/src/")
}
if args[len(args)-1] != "admin@10.5.0.144:/remote/dst" {
t.Errorf("dest = %q, want %q", args[len(args)-1], "admin@10.5.0.144:/remote/dst")
@@ -61,8 +61,8 @@ func TestBuildArgs_Pull(t *testing.T) {
if args[len(args)-2] != "/local/dst" {
t.Errorf("pull: second-to-last (dest) = %q, want %q", args[len(args)-2], "/local/dst")
}
if args[len(args)-1] != "admin@10.5.0.144:/remote/src" {
t.Errorf("pull: last (source) = %q, want %q", args[len(args)-1], "admin@10.5.0.144:/remote/src")
if args[len(args)-1] != "admin@10.5.0.144:/remote/src/" {
t.Errorf("pull: last (source) = %q, want %q (auto-appended trailing slash)", args[len(args)-1], "admin@10.5.0.144:/remote/src/")
}
}
@@ -162,8 +162,8 @@ func TestRunRemote_PushSrcAndDestStayAsIs(t *testing.T) {
}
srcArg, dstArg := tc.build()
if srcArg != "/share/homes/admin/media" {
t.Errorf("push src = %q, want raw path '/share/homes/admin/media' (no user@host: prefix added)", srcArg)
if srcArg != "/share/homes/admin/media/" {
t.Errorf("push src = %q, want '/share/homes/admin/media/' (auto-appended trailing slash, no user@host: prefix added)", srcArg)
}
if dstArg != "/share/media/peliculas" {
t.Errorf("push dst = %q, want raw path '/share/media/peliculas'", dstArg)
@@ -193,6 +193,80 @@ func TestRunRemote_PullSrcAndDestStayAsIs(t *testing.T) {
}
}
func TestBuildArgs_AutoAppendsTrailingSlashToSource(t *testing.T) {
cases := []struct {
name string
source string
dest string
direction string
wantSrc string
}{
{
name: "push, source without trailing slash",
source: "/mnt/storage/multimedia/series",
dest: "/share/media/series",
direction: "push",
wantSrc: "/mnt/storage/multimedia/series/",
},
{
name: "push, source already has trailing slash (idempotent)",
source: "/mnt/storage/multimedia/series/",
dest: "/share/media/series",
direction: "push",
wantSrc: "/mnt/storage/multimedia/series/",
},
{
name: "push, remote source without trailing slash",
source: "admin@baby-nas:/mnt/storage/multimedia/series",
dest: "/share/media/series",
direction: "push",
wantSrc: "admin@baby-nas:/mnt/storage/multimedia/series/",
},
{
name: "pull, source without trailing slash",
source: "admin@baby-nas:/mnt/storage/multimedia/series",
dest: "/share/media/series",
direction: "pull",
wantSrc: "admin@baby-nas:/mnt/storage/multimedia/series/",
},
{
name: "mirror, source without trailing slash",
source: "/mnt/storage/multimedia/series",
dest: "admin@10.5.0.144:/share/media/series",
direction: "mirror",
wantSrc: "/mnt/storage/multimedia/series/",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
runner := NewRsyncRunner("/tmp/ssh", "")
pair := &SyncPairConfig{
Source: tc.source,
Dest: tc.dest,
Direction: tc.direction,
}
args := runner.buildArgs(pair)
var gotSrc, gotDest string
if tc.direction == "pull" {
gotDest = args[len(args)-2]
gotSrc = args[len(args)-1]
} else {
gotSrc = args[len(args)-2]
gotDest = args[len(args)-1]
}
if gotSrc != tc.wantSrc {
t.Errorf("source = %q, want %q (dest must never be touched)", gotSrc, tc.wantSrc)
}
if gotDest != tc.dest {
t.Errorf("dest = %q, want %q (dest must never be normalized)", gotDest, tc.dest)
}
})
}
}
func countOccurrences(s, substr string) int {
return strings.Count(s, substr)
}