package ssh import ( "errors" "os" "testing" "github.com/pkg/sftp" ) func TestRenamePromotedFileUsesPlainRenameWithoutOverwrite(t *testing.T) { client := &recordingRenamer{} if err := renamePromotedFile(client, "temp", "index.html", false); err != nil { t.Fatalf("renamePromotedFile() error = %v", err) } if got, want := client.calls, []string{"rename temp index.html"}; !equalStrings(got, want) { t.Fatalf("calls = %q, want %q", got, want) } } func TestRenamePromotedFileUsesPosixRenameForOverwrite(t *testing.T) { client := &recordingRenamer{} if err := renamePromotedFile(client, "temp", "index.html", true); err != nil { t.Fatalf("renamePromotedFile() error = %v", err) } if got, want := client.calls, []string{"posix temp index.html"}; !equalStrings(got, want) { t.Fatalf("calls = %q, want %q", got, want) } } func TestRenamePromotedFileFallsBackWhenReplaceRenameUnsupported(t *testing.T) { for _, err := range []error{ sftp.ErrSSHFxOpUnsupported, sftp.ErrSSHFxFailure, &sftp.StatusError{Code: uint32(sftp.ErrSSHFxOpUnsupported)}, &sftp.StatusError{Code: uint32(sftp.ErrSSHFxFailure)}, } { t.Run(err.Error(), func(t *testing.T) { client := &recordingRenamer{posixErr: err} if err := renamePromotedFile(client, "temp", "index.html", true); err != nil { t.Fatalf("renamePromotedFile() error = %v", err) } want := []string{"posix temp index.html", "remove index.html", "rename temp index.html"} if got := client.calls; !equalStrings(got, want) { t.Fatalf("calls = %q, want %q", got, want) } }) } } func TestRenamePromotedFileIgnoresMissingTargetDuringFallback(t *testing.T) { client := &recordingRenamer{ posixErr: sftp.ErrSSHFxOpUnsupported, removeErr: &os.PathError{ Op: "remove", Path: "index.html", Err: os.ErrNotExist, }, } if err := renamePromotedFile(client, "temp", "index.html", true); err != nil { t.Fatalf("renamePromotedFile() error = %v", err) } want := []string{"posix temp index.html", "remove index.html", "rename temp index.html"} if got := client.calls; !equalStrings(got, want) { t.Fatalf("calls = %q, want %q", got, want) } } func TestRenamePromotedFileDoesNotFallbackForPermissionError(t *testing.T) { client := &recordingRenamer{posixErr: sftp.ErrSSHFxPermissionDenied} if err := renamePromotedFile(client, "temp", "index.html", true); !errors.Is(err, sftp.ErrSSHFxPermissionDenied) { t.Fatalf("renamePromotedFile() error = %v, want permission denied", err) } if got, want := client.calls, []string{"posix temp index.html"}; !equalStrings(got, want) { t.Fatalf("calls = %q, want %q", got, want) } } func TestRenamePromotedFileReturnsRemoveFallbackError(t *testing.T) { client := &recordingRenamer{ posixErr: sftp.ErrSSHFxOpUnsupported, removeErr: sftp.ErrSSHFxPermissionDenied, } if err := renamePromotedFile(client, "temp", "index.html", true); !errors.Is(err, sftp.ErrSSHFxPermissionDenied) { t.Fatalf("renamePromotedFile() error = %v, want permission denied", err) } want := []string{"posix temp index.html", "remove index.html"} if got := client.calls; !equalStrings(got, want) { t.Fatalf("calls = %q, want %q", got, want) } } type recordingRenamer struct { calls []string posixErr error renameErr error removeErr error } func (r *recordingRenamer) PosixRename(oldname, newname string) error { r.calls = append(r.calls, "posix "+oldname+" "+newname) return r.posixErr } func (r *recordingRenamer) Rename(oldname, newname string) error { r.calls = append(r.calls, "rename "+oldname+" "+newname) return r.renameErr } func (r *recordingRenamer) Remove(path string) error { r.calls = append(r.calls, "remove "+path) return r.removeErr } func equalStrings(a, b []string) bool { if len(a) != len(b) { return false } for index := range a { if a[index] != b[index] { return false } } return true }