2024-01-28 19:36:17 +01:00
|
|
|
package gitannex
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2024-08-04 16:43:49 +02:00
|
|
|
"context"
|
2024-01-28 19:36:17 +01:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2024-05-11 03:54:42 +02:00
|
|
|
"regexp"
|
2024-08-04 16:43:49 +02:00
|
|
|
"runtime"
|
2024-01-28 19:36:17 +01:00
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"testing"
|
2024-08-04 16:43:49 +02:00
|
|
|
"time"
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
// Without this import, the various backends would be unavailable. It looks
|
|
|
|
// unused, but the act of importing runs the package's `init()` function.
|
|
|
|
_ "github.com/rclone/rclone/backend/all"
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
"github.com/rclone/rclone/fs/fspath"
|
|
|
|
"github.com/rclone/rclone/fstest"
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
func TestFixArgsForSymlinkIdentity(t *testing.T) {
|
|
|
|
for _, argList := range [][]string{
|
|
|
|
[]string{},
|
|
|
|
[]string{"foo"},
|
|
|
|
[]string{"foo", "bar"},
|
|
|
|
[]string{"foo", "bar", "baz"},
|
|
|
|
} {
|
|
|
|
assert.Equal(t, maybeTransformArgs(argList), argList)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestFixArgsForSymlinkCorrectName(t *testing.T) {
|
|
|
|
assert.Equal(t,
|
|
|
|
maybeTransformArgs([]string{"git-annex-remote-rclone-builtin"}),
|
|
|
|
[]string{"git-annex-remote-rclone-builtin", "gitannex"})
|
|
|
|
assert.Equal(t,
|
|
|
|
maybeTransformArgs([]string{"/path/to/git-annex-remote-rclone-builtin"}),
|
|
|
|
[]string{"/path/to/git-annex-remote-rclone-builtin", "gitannex"})
|
|
|
|
}
|
|
|
|
|
|
|
|
type messageParserTestCase struct {
|
|
|
|
label string
|
|
|
|
testFunc func(*testing.T)
|
|
|
|
}
|
|
|
|
|
|
|
|
var messageParserTestCases = []messageParserTestCase{
|
|
|
|
{
|
|
|
|
"OneParam",
|
|
|
|
func(t *testing.T) {
|
|
|
|
m := messageParser{"foo\n"}
|
|
|
|
|
|
|
|
param, err := m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "foo")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Equal(t, param, "")
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param = m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "")
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param = m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Equal(t, param, "")
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"TwoParams",
|
|
|
|
func(t *testing.T) {
|
|
|
|
m := messageParser{"foo bar\n"}
|
|
|
|
|
|
|
|
param, err := m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "foo")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "bar")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Equal(t, param, "")
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param = m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"TwoParamsNoTrailingNewline",
|
|
|
|
|
|
|
|
func(t *testing.T) {
|
|
|
|
m := messageParser{"foo bar"}
|
|
|
|
|
|
|
|
param, err := m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "foo")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "bar")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.Error(t, err)
|
|
|
|
assert.Equal(t, param, "")
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param = m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"ThreeParamsWhereFinalParamContainsSpaces",
|
|
|
|
func(t *testing.T) {
|
|
|
|
m := messageParser{"firstparam secondparam final param with spaces"}
|
|
|
|
|
|
|
|
param, err := m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "firstparam")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "secondparam")
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param = m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "final param with spaces")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"OneLongFinalParameter",
|
|
|
|
func(t *testing.T) {
|
|
|
|
for _, lineEnding := range []string{"", "\n", "\r", "\r\n", "\n\r"} {
|
|
|
|
testName := fmt.Sprintf("lineEnding%x", lineEnding)
|
|
|
|
|
|
|
|
t.Run(testName, func(t *testing.T) {
|
|
|
|
m := messageParser{"one long final parameter" + lineEnding}
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param := m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "one long final parameter")
|
|
|
|
|
2024-04-11 17:06:33 +02:00
|
|
|
param = m.finalParameter()
|
2024-01-28 19:36:17 +01:00
|
|
|
assert.Equal(t, param, "")
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"MultipleSpaces",
|
|
|
|
func(t *testing.T) {
|
|
|
|
m := messageParser{"foo bar\n\r"}
|
|
|
|
|
|
|
|
param, err := m.nextSpaceDelimitedParameter()
|
|
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, param, "foo")
|
|
|
|
|
|
|
|
param, err = m.nextSpaceDelimitedParameter()
|
|
|
|
assert.Error(t, err, "blah")
|
|
|
|
assert.Equal(t, param, "")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"StartsWithSpace",
|
|
|
|
func(t *testing.T) {
|
|
|
|
m := messageParser{" foo"}
|
|
|
|
|
|
|
|
param, err := m.nextSpaceDelimitedParameter()
|
|
|
|
assert.Error(t, err, "blah")
|
|
|
|
assert.Equal(t, param, "")
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMessageParser(t *testing.T) {
|
|
|
|
for _, testCase := range messageParserTestCases {
|
|
|
|
t.Run(testCase.label, func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
testCase.testFunc(t)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-11 03:54:42 +02:00
|
|
|
func TestConfigDefinitionOneName(t *testing.T) {
|
|
|
|
var parsed string
|
|
|
|
var defaultValue = "abc"
|
|
|
|
|
|
|
|
configFoo := configDefinition{
|
|
|
|
names: []string{"foo"},
|
|
|
|
description: "The foo config is utterly useless.",
|
|
|
|
destination: &parsed,
|
|
|
|
defaultValue: &defaultValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.Equal(t, "foo",
|
|
|
|
configFoo.getCanonicalName())
|
|
|
|
|
|
|
|
assert.Equal(t,
|
|
|
|
configFoo.description,
|
|
|
|
configFoo.fullDescription())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConfigDefinitionTwoNames(t *testing.T) {
|
|
|
|
var parsed string
|
|
|
|
var defaultValue = "abc"
|
|
|
|
|
|
|
|
configFoo := configDefinition{
|
|
|
|
names: []string{"foo", "bar"},
|
|
|
|
description: "The foo config is utterly useless.",
|
|
|
|
destination: &parsed,
|
|
|
|
defaultValue: &defaultValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.Equal(t, "foo",
|
|
|
|
configFoo.getCanonicalName())
|
|
|
|
|
|
|
|
assert.Equal(t,
|
|
|
|
"(synonyms: bar) The foo config is utterly useless.",
|
|
|
|
configFoo.fullDescription())
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestConfigDefinitionThreeNames(t *testing.T) {
|
|
|
|
var parsed string
|
|
|
|
var defaultValue = "abc"
|
|
|
|
|
|
|
|
configFoo := configDefinition{
|
|
|
|
names: []string{"foo", "bar", "baz"},
|
|
|
|
description: "The foo config is utterly useless.",
|
|
|
|
destination: &parsed,
|
|
|
|
defaultValue: &defaultValue,
|
|
|
|
}
|
|
|
|
|
|
|
|
assert.Equal(t, "foo",
|
|
|
|
configFoo.getCanonicalName())
|
|
|
|
|
|
|
|
assert.Equal(t,
|
|
|
|
`(synonyms: bar, baz) The foo config is utterly useless.`,
|
|
|
|
configFoo.fullDescription())
|
|
|
|
}
|
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
type testState struct {
|
|
|
|
t *testing.T
|
|
|
|
server *server
|
|
|
|
mockStdinW *io.PipeWriter
|
|
|
|
mockStdoutReader *bufio.Reader
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
fstestRun *fstest.Run
|
|
|
|
remoteName string
|
|
|
|
remotePrefix string
|
2024-01-28 19:36:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func makeTestState(t *testing.T) testState {
|
|
|
|
stdinR, stdinW := io.Pipe()
|
|
|
|
stdoutR, stdoutW := io.Pipe()
|
|
|
|
|
|
|
|
return testState{
|
|
|
|
t: t,
|
|
|
|
server: &server{
|
|
|
|
reader: bufio.NewReader(stdinR),
|
|
|
|
writer: stdoutW,
|
|
|
|
},
|
|
|
|
mockStdinW: stdinW,
|
|
|
|
mockStdoutReader: bufio.NewReader(stdoutR),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
func (h *testState) requireRemoteIsEmpty() {
|
|
|
|
h.fstestRun.CheckRemoteItems(h.t)
|
|
|
|
}
|
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
func (h *testState) requireReadLineExact(line string) {
|
|
|
|
receivedLine, err := h.mockStdoutReader.ReadString('\n')
|
|
|
|
require.NoError(h.t, err)
|
|
|
|
require.Equal(h.t, line+"\n", receivedLine)
|
|
|
|
}
|
|
|
|
|
2024-05-11 03:54:42 +02:00
|
|
|
func (h *testState) requireReadLine() string {
|
|
|
|
receivedLine, err := h.mockStdoutReader.ReadString('\n')
|
|
|
|
require.NoError(h.t, err)
|
|
|
|
return receivedLine
|
|
|
|
}
|
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
func (h *testState) requireWriteLine(line string) {
|
|
|
|
_, err := h.mockStdinW.Write([]byte(line + "\n"))
|
|
|
|
require.NoError(h.t, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preconfigure the handle. This enables the calling test to skip the PREPARE
|
|
|
|
// handshake.
|
|
|
|
func (h *testState) preconfigureServer() {
|
|
|
|
h.server.configRcloneRemoteName = h.remoteName
|
2024-08-04 16:43:49 +02:00
|
|
|
h.server.configPrefix = h.remotePrefix
|
2024-04-11 17:10:16 +02:00
|
|
|
h.server.configRcloneLayout = string(layoutModeNodir)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.server.configsDone = true
|
|
|
|
}
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
// Drop-in replacement for `filepath.Rel()` that works around a Windows-specific
|
|
|
|
// quirk when one of the paths begins with `\\?\` or `//?/`. It seems that
|
|
|
|
// fstest gives us paths with this prefix on Windows, which throws a wrench in
|
|
|
|
// the gitannex tests that need to construct relative paths from absolute paths.
|
|
|
|
// For a demonstration, see `TestWindowsFilepathRelQuirk` below.
|
|
|
|
//
|
|
|
|
// The `\\?\` prefix tells Windows APIs to pass strings unmodified to the
|
|
|
|
// filesystem without additional parsing [1]. Our workaround is roughly to add
|
|
|
|
// the prefix to whichever parameter doesn't have it (when the OS is Windows).
|
|
|
|
// I'm not sure this generalizes, but it works for the the kinds of inputs we're
|
|
|
|
// throwing at it.
|
|
|
|
//
|
|
|
|
// [1]: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#win32-file-namespaces
|
|
|
|
func relativeFilepathWorkaround(basepath, targpath string) (string, error) {
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
return filepath.Rel(basepath, targpath)
|
|
|
|
}
|
|
|
|
// Canonicalize paths to use backslashes.
|
|
|
|
basepath = filepath.Clean(basepath)
|
|
|
|
targpath = filepath.Clean(targpath)
|
|
|
|
|
|
|
|
const winFilePrefixDisableStringParsing = `\\?\`
|
|
|
|
baseHasPrefix := strings.HasPrefix(basepath, winFilePrefixDisableStringParsing)
|
|
|
|
targHasPrefix := strings.HasPrefix(targpath, winFilePrefixDisableStringParsing)
|
|
|
|
|
|
|
|
if baseHasPrefix && !targHasPrefix {
|
|
|
|
targpath = winFilePrefixDisableStringParsing + targpath
|
|
|
|
}
|
|
|
|
if !baseHasPrefix && targHasPrefix {
|
|
|
|
basepath = winFilePrefixDisableStringParsing + basepath
|
|
|
|
}
|
|
|
|
return filepath.Rel(basepath, targpath)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestWindowsFilepathRelQuirk(t *testing.T) {
|
|
|
|
if runtime.GOOS != "windows" {
|
|
|
|
t.Skip()
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Run("filepathRelQuirk", func(t *testing.T) {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
_, err = filepath.Rel(`C:\foo`, `\\?\C:\foo\bar`)
|
|
|
|
require.Error(t, err)
|
|
|
|
|
|
|
|
_, err = filepath.Rel(`C:/foo`, `//?/C:/foo/bar`)
|
|
|
|
require.Error(t, err)
|
|
|
|
|
|
|
|
_, err = filepath.Rel(`\\?\C:\foo`, `C:\foo\bar`)
|
|
|
|
require.Error(t, err)
|
|
|
|
|
|
|
|
_, err = filepath.Rel(`//?/C:/foo`, `C:/foo/bar`)
|
|
|
|
require.Error(t, err)
|
|
|
|
|
|
|
|
path, err := filepath.Rel(`\\?\C:\foo`, `\\?\C:\foo\bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, `bar`)
|
|
|
|
|
|
|
|
path, err = filepath.Rel(`//?/C:/foo`, `//?/C:/foo/bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, `bar`)
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("fstestAndTempDirHaveDifferentPrefixes", func(t *testing.T) {
|
|
|
|
r := fstest.NewRun(t)
|
|
|
|
p := r.Flocal.Root()
|
|
|
|
require.True(t, strings.HasPrefix(p, `//?/`))
|
|
|
|
|
|
|
|
tempDir := t.TempDir()
|
|
|
|
require.False(t, strings.HasPrefix(tempDir, `//?/`))
|
|
|
|
require.False(t, strings.HasPrefix(tempDir, `\\?\`))
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("workaroundWorks", func(t *testing.T) {
|
|
|
|
path, err := relativeFilepathWorkaround(`C:\foo`, `\\?\C:\foo\bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, "bar")
|
|
|
|
|
|
|
|
path, err = relativeFilepathWorkaround(`C:/foo`, `//?/C:/foo/bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, "bar")
|
|
|
|
|
|
|
|
path, err = relativeFilepathWorkaround(`\\?\C:\foo`, `C:\foo\bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, `bar`)
|
|
|
|
|
|
|
|
path, err = relativeFilepathWorkaround(`//?/C:/foo`, `C:/foo/bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, `bar`)
|
|
|
|
|
|
|
|
path, err = relativeFilepathWorkaround(`\\?\C:\foo`, `\\?\C:\foo\bar`)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, path, `bar`)
|
|
|
|
})
|
2024-01-28 19:36:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
type testCase struct {
|
|
|
|
label string
|
|
|
|
testProtocolFunc func(*testing.T, *testState)
|
|
|
|
expectedError string
|
|
|
|
}
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
// These test cases run against a backend selected by the `-remote` flag.
|
|
|
|
var fstestTestCases = []testCase{
|
2024-01-28 19:36:17 +01:00
|
|
|
{
|
|
|
|
label: "HandlesInit",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
2024-05-11 03:54:42 +02:00
|
|
|
{
|
|
|
|
label: "HandlesListConfigs",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("LISTCONFIGS")
|
|
|
|
|
|
|
|
require.Regexp(t,
|
|
|
|
regexp.MustCompile(`^CONFIG rcloneremotename \(synonyms: target\) (.|\n)*$`),
|
|
|
|
h.requireReadLine(),
|
|
|
|
)
|
|
|
|
require.Regexp(t,
|
|
|
|
regexp.MustCompile(`^CONFIG rcloneprefix \(synonyms: prefix\) (.|\n)*$`),
|
|
|
|
h.requireReadLine(),
|
|
|
|
)
|
|
|
|
require.Regexp(t,
|
|
|
|
regexp.MustCompile(`^CONFIG rclonelayout \(synonyms: rclone_layout\) (.|\n)*$`),
|
|
|
|
h.requireReadLine(),
|
|
|
|
)
|
|
|
|
h.requireReadLineExact("CONFIGEND")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
2024-01-28 19:36:17 +01:00
|
|
|
{
|
|
|
|
label: "HandlesPrepare",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("EXTENSIONS INFO") // Advertise that we support the INFO extension
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
|
2024-08-05 02:28:46 +02:00
|
|
|
require.True(t, h.server.extensionInfo)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE " + h.remoteName)
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("VALUE " + h.remotePrefix)
|
2024-04-11 17:10:16 +02:00
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, h.server.configRcloneRemoteName, h.remoteName)
|
2024-08-04 16:43:49 +02:00
|
|
|
require.Equal(t, h.server.configPrefix, h.remotePrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithNonexistentRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO") // Advertise that we support the INFO extension
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
|
2024-08-05 02:28:46 +02:00
|
|
|
require.True(t, h.server.extensionInfo)
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE thisRemoteDoesNotExist")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE " + h.remotePrefix)
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, h.server.configRcloneRemoteName, "thisRemoteDoesNotExist")
|
|
|
|
require.Equal(t, h.server.configPrefix, h.remotePrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-FAILURE remote does not exist: thisRemoteDoesNotExist")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
expectedError: "remote does not exist: thisRemoteDoesNotExist",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithPathAsRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO") // Advertise that we support the INFO extension
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
|
2024-08-05 02:28:46 +02:00
|
|
|
require.True(t, h.server.extensionInfo)
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE " + h.remotePrefix)
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE /foo")
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, h.server.configRcloneRemoteName, h.remotePrefix)
|
|
|
|
require.Equal(t, h.server.configPrefix, "/foo")
|
2024-01-28 19:36:17 +01:00
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
|
|
|
|
require.Regexp(t,
|
|
|
|
regexp.MustCompile("^INITREMOTE-FAILURE remote does not exist: "),
|
|
|
|
h.requireReadLine(),
|
|
|
|
)
|
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
2024-08-04 16:43:49 +02:00
|
|
|
expectedError: "remote does not exist:",
|
2024-01-28 19:36:17 +01:00
|
|
|
},
|
2025-02-28 04:41:52 +01:00
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithNonexistentBackendAsRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE :nonexistentBackend:")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE /foo")
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, ":nonexistentBackend:", h.server.configRcloneRemoteName)
|
|
|
|
require.Equal(t, "/foo", h.server.configPrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-FAILURE backend does not exist: nonexistentBackend")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
expectedError: "backend does not exist:",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithBackendAsRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE :local:")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE /foo")
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, ":local:", h.server.configRcloneRemoteName)
|
|
|
|
require.Equal(t, "/foo", h.server.configPrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithBackendMissingTrailingColonAsRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE :local")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE /foo")
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, ":local", h.server.configRcloneRemoteName)
|
|
|
|
require.Equal(t, "/foo", h.server.configPrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-FAILURE remote could not be parsed as a backend: :local")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
expectedError: "remote could not be parsed as a backend:",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithBackendContainingOptionsAsRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE :local,description=banana:")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE /foo")
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, ":local,description=banana:", h.server.configRcloneRemoteName)
|
|
|
|
require.Equal(t, "/foo", h.server.configPrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithBackendContainingOptionsAndIllegalPathAsRemote",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("VALUE :local,description=banana:/bad/path")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
|
|
|
h.requireWriteLine("VALUE /foo")
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, ":local,description=banana:/bad/path", h.server.configRcloneRemoteName)
|
|
|
|
require.Equal(t, "/foo", h.server.configPrefix)
|
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
require.Regexp(t,
|
|
|
|
regexp.MustCompile("^INITREMOTE-FAILURE backend must not have a path: "),
|
|
|
|
h.requireReadLine(),
|
|
|
|
)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
expectedError: "backend must not have a path:",
|
|
|
|
},
|
2024-05-11 03:54:42 +02:00
|
|
|
{
|
|
|
|
label: "HandlesPrepareWithSynonyms",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.requireReadLineExact("VERSION 1")
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO") // Advertise that we support the INFO extension
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
|
2024-08-05 02:28:46 +02:00
|
|
|
require.True(t, h.server.extensionInfo)
|
2024-05-11 03:54:42 +02:00
|
|
|
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
// TODO check what git-annex does when asked for a config value it does not have.
|
|
|
|
h.requireWriteLine("VALUE")
|
|
|
|
h.requireReadLineExact("GETCONFIG target")
|
|
|
|
h.requireWriteLine("VALUE " + h.remoteName)
|
|
|
|
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("VALUE " + h.remotePrefix)
|
2024-05-11 03:54:42 +02:00
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE foo")
|
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, h.server.configRcloneRemoteName, h.remoteName)
|
2024-08-04 16:43:49 +02:00
|
|
|
require.Equal(t, h.server.configPrefix, h.remotePrefix)
|
2024-05-11 03:54:42 +02:00
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
2024-01-28 19:36:17 +01:00
|
|
|
{
|
|
|
|
label: "HandlesPrepareAndDoesNotTrimWhitespaceFromValue",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("EXTENSIONS INFO") // Advertise that we support the INFO extension
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
|
2024-08-05 02:28:46 +02:00
|
|
|
require.True(t, h.server.extensionInfo)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
|
|
|
|
remoteNameWithSpaces := fmt.Sprintf(" %s ", h.remoteName)
|
2024-08-04 16:43:49 +02:00
|
|
|
prefixWithWhitespace := fmt.Sprintf(" %s\t", h.remotePrefix)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine(fmt.Sprintf("VALUE %s", remoteNameWithSpaces))
|
|
|
|
|
2024-04-11 17:10:16 +02:00
|
|
|
h.requireReadLineExact("GETCONFIG rcloneprefix")
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine(fmt.Sprintf("VALUE %s", prefixWithWhitespace))
|
2024-04-11 17:10:16 +02:00
|
|
|
|
|
|
|
h.requireReadLineExact("GETCONFIG rclonelayout")
|
|
|
|
h.requireWriteLine("VALUE")
|
2024-05-11 03:54:42 +02:00
|
|
|
h.requireReadLineExact("GETCONFIG rclone_layout")
|
|
|
|
h.requireWriteLine("VALUE")
|
2024-04-11 17:10:16 +02:00
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("PREPARE-SUCCESS")
|
|
|
|
|
|
|
|
require.Equal(t, h.server.configRcloneRemoteName, remoteNameWithSpaces)
|
2024-08-04 16:43:49 +02:00
|
|
|
require.Equal(t, h.server.configPrefix, prefixWithWhitespace)
|
2024-01-28 19:36:17 +01:00
|
|
|
require.True(t, h.server.configsDone)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "HandlesEarlyError",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("ERROR foo")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
expectedError: "received error message from git-annex: foo",
|
|
|
|
},
|
|
|
|
// Test what happens when the git-annex client sends "GETCONFIG", but
|
|
|
|
// doesn't understand git-annex's response.
|
|
|
|
{
|
|
|
|
label: "ConfigFail",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("EXTENSIONS INFO") // Advertise that we support the INFO extension
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
|
|
|
|
h.requireWriteLine("PREPARE")
|
|
|
|
h.requireReadLineExact("GETCONFIG rcloneremotename")
|
|
|
|
h.requireWriteLine("ERROR ineffable error")
|
|
|
|
h.requireReadLineExact("PREPARE-FAILURE Error getting configs")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
expectedError: "failed to parse config value: ERROR ineffable error",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "TransferStoreEmptyPath",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
// Note the whitespace following the key.
|
|
|
|
h.requireWriteLine("TRANSFER STORE Key ")
|
2024-04-11 17:10:16 +02:00
|
|
|
h.requireReadLineExact("TRANSFER-FAILURE failed to parse file path")
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
2024-04-11 17:10:16 +02:00
|
|
|
expectedError: "failed to parse file",
|
2024-01-28 19:36:17 +01:00
|
|
|
},
|
|
|
|
// Repeated EXTENSIONS messages add to each other rather than overriding
|
|
|
|
// prior advertised extensions. This behavior is not mandated by the
|
|
|
|
// protocol design.
|
|
|
|
{
|
|
|
|
label: "ExtensionsCompound",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.False(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS ASYNC")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.True(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS GETGITREMOTENAME")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.True(t, h.server.extensionAsync)
|
|
|
|
require.True(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS UNAVAILABLERESPONSE")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.True(t, h.server.extensionAsync)
|
|
|
|
require.True(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.True(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "ExtensionsIdempotent",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.False(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.False(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS ASYNC ASYNC")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.True(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "ExtensionsSupportsMultiple",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.False(t, h.server.extensionInfo)
|
|
|
|
require.False(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
h.requireWriteLine("EXTENSIONS INFO ASYNC")
|
|
|
|
h.requireReadLineExact("EXTENSIONS")
|
|
|
|
require.True(t, h.server.extensionInfo)
|
|
|
|
require.True(t, h.server.extensionAsync)
|
|
|
|
require.False(t, h.server.extensionGetGitRemoteName)
|
|
|
|
require.False(t, h.server.extensionUnavailableResponse)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "TransferStoreAbsolute",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
// Create temp file for transfer with an absolute path.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
require.True(t, filepath.IsAbs(absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
// Specify an absolute path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE KeyAbsolute " + absPath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE KeyAbsolute")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
// Check that the file was transferred.
|
|
|
|
remoteItem := fstest.NewItem("KeyAbsolute", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
// Transfer the same absolute path a second time, but with a different key.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE KeyAbsolute2 " + absPath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE KeyAbsolute2")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
// Check that the same file was transferred to a new name.
|
|
|
|
remoteItem2 := fstest.NewItem("KeyAbsolute2", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem, remoteItem2)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyAbsolute2")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS KeyAbsolute2")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyThatDoesNotExist")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE KeyThatDoesNotExist")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Test that the TRANSFER command understands simple relative paths
|
|
|
|
// consisting only of a file name.
|
|
|
|
{
|
|
|
|
label: "TransferStoreRelative",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
// Save the current working directory so we can restore it when this
|
|
|
|
// test ends.
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
tempDir := t.TempDir()
|
|
|
|
|
|
|
|
require.NoError(t, os.Chdir(tempDir))
|
2024-01-28 19:36:17 +01:00
|
|
|
t.Cleanup(func() { require.NoError(t, os.Chdir(cwd)) })
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
|
|
|
|
relativePath, err := relativeFilepathWorkaround(tempDir, absPath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, filepath.IsAbs(relativePath))
|
|
|
|
require.FileExists(t, relativePath)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
// Specify a relative path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE KeyRelative " + relativePath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE KeyRelative")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem("KeyRelative", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyRelative")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS KeyRelative")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyThatDoesNotExist")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE KeyThatDoesNotExist")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "TransferStorePathWithInteriorWhitespace",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
// Save the current working directory so we can restore it when this
|
|
|
|
// test ends.
|
|
|
|
cwd, err := os.Getwd()
|
|
|
|
require.NoError(t, err)
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
tempDir := t.TempDir()
|
|
|
|
require.NoError(t, os.Chdir(tempDir))
|
2024-01-28 19:36:17 +01:00
|
|
|
t.Cleanup(func() { require.NoError(t, os.Chdir(cwd)) })
|
|
|
|
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
// Create temp file for transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("filename with spaces.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
relativePath, err := relativeFilepathWorkaround(tempDir, absPath)
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.False(t, filepath.IsAbs(relativePath))
|
|
|
|
require.FileExists(t, relativePath)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
// Specify a relative path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE KeyRelative " + relativePath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE KeyRelative")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem("KeyRelative", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyRelative")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS KeyRelative")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyThatDoesNotExist")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE KeyThatDoesNotExist")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "CheckPresentAndTransfer",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
|
|
|
// Create temp file for transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
require.True(t, filepath.IsAbs(absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT KeyThatDoesNotExist")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE KeyThatDoesNotExist")
|
|
|
|
|
|
|
|
// Specify an absolute path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE KeyAbsolute " + absPath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE KeyAbsolute")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem("KeyAbsolute", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Check whether a key is present, transfer a file with that key, then check
|
|
|
|
// again whether it is present.
|
|
|
|
//
|
|
|
|
// This is a regression test for a bug where the second CHECKPRESENT would
|
|
|
|
// generate the following response:
|
|
|
|
//
|
|
|
|
// CHECKPRESENT-UNKNOWN ${key} failed to read directory entry: readdirent ${filepath}: not a directory
|
|
|
|
//
|
|
|
|
// This message was generated by the local backend's `List()` function. When
|
|
|
|
// checking whether a file exists, we were erroneously listing its contents as
|
|
|
|
// if it were a directory.
|
|
|
|
{
|
|
|
|
label: "CheckpresentTransferCheckpresent",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
|
|
|
// Create temp file for transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
require.True(t, filepath.IsAbs(absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT foo")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE foo")
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE foo " + absPath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE foo")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem("foo", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT foo")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS foo")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "TransferAndCheckpresentWithRealisticKey",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
|
|
|
// Create temp file for transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
require.True(t, filepath.IsAbs(absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
realisticKey := "SHA256E-s1048576--7ba87e06b9b7903cfbaf4a38736766c161e3e7b42f06fe57f040aa410a8f0701.this-is-a-test-key"
|
|
|
|
|
|
|
|
// Specify an absolute path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine(fmt.Sprintf("TRANSFER STORE %s %s", realisticKey, absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE " + realisticKey)
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem(realisticKey, "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT " + realisticKey)
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS " + realisticKey)
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "RetrieveNonexistentFile",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("TRANSFER RETRIEVE SomeKey path")
|
|
|
|
h.requireReadLineExact("TRANSFER-FAILURE RETRIEVE SomeKey not found")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "StoreCheckpresentRetrieve",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
|
|
|
// Create temp file for transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
require.True(t, filepath.IsAbs(absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
// Specify an absolute path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE SomeKey " + absPath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem("SomeKey", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS SomeKey")
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
h.fstestRun.CheckLocalItems(t,
|
|
|
|
fstest.NewItem("file.txt", "HELLO", item.ModTime),
|
|
|
|
)
|
|
|
|
|
|
|
|
retrievedFilePath := absPath + ".retrieved"
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("TRANSFER RETRIEVE SomeKey " + retrievedFilePath)
|
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS RETRIEVE SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.fstestRun.CheckLocalItems(t,
|
|
|
|
fstest.NewItem("file.txt", "HELLO", item.ModTime),
|
|
|
|
fstest.NewItem("file.txt.retrieved", "HELLO", item.ModTime),
|
|
|
|
)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "RemovePreexistingFile",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
ctx := context.WithoutCancel(context.Background())
|
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
// Write a file into the remote without using the git-annex
|
|
|
|
// protocol.
|
2024-08-04 16:43:49 +02:00
|
|
|
remoteItem := h.fstestRun.WriteObject(ctx, "SomeKey", "HELLO", time.Now())
|
|
|
|
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("REMOVE SomeKey")
|
|
|
|
h.requireReadLineExact("REMOVE-SUCCESS SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.requireRemoteIsEmpty()
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.requireRemoteIsEmpty()
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "Remove",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
|
|
|
// Create temp file for transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
item := h.fstestRun.WriteFile("file.txt", "HELLO", time.Now())
|
|
|
|
absPath := filepath.Join(h.fstestRun.Flocal.Root(), item.Path)
|
|
|
|
require.True(t, filepath.IsAbs(absPath))
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE SomeKey")
|
|
|
|
|
|
|
|
// Specify an absolute path to transfer.
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireWriteLine("TRANSFER STORE SomeKey " + absPath)
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireReadLineExact("TRANSFER-SUCCESS STORE SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
remoteItem := fstest.NewItem("SomeKey", "HELLO", item.ModTime)
|
|
|
|
h.fstestRun.CheckRemoteItems(t, remoteItem)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-SUCCESS SomeKey")
|
|
|
|
|
|
|
|
h.requireWriteLine("REMOVE SomeKey")
|
|
|
|
h.requireReadLineExact("REMOVE-SUCCESS SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.requireRemoteIsEmpty()
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE SomeKey")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "RemoveNonexistentFile",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE SomeKey")
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
h.requireRemoteIsEmpty()
|
|
|
|
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("REMOVE SomeKey")
|
|
|
|
h.requireReadLineExact("REMOVE-SUCCESS SomeKey")
|
2024-08-04 16:43:49 +02:00
|
|
|
|
|
|
|
h.requireRemoteIsEmpty()
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
h.requireWriteLine("CHECKPRESENT SomeKey")
|
|
|
|
h.requireReadLineExact("CHECKPRESENT-FAILURE SomeKey")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: "ExportNotSupported",
|
|
|
|
testProtocolFunc: func(t *testing.T, h *testState) {
|
|
|
|
h.preconfigureServer()
|
|
|
|
|
2024-04-04 15:16:15 +02:00
|
|
|
h.requireReadLineExact("VERSION 1")
|
2024-01-28 19:36:17 +01:00
|
|
|
h.requireWriteLine("INITREMOTE")
|
|
|
|
h.requireReadLineExact("INITREMOTE-SUCCESS")
|
|
|
|
|
|
|
|
h.requireWriteLine("EXPORTSUPPORTED")
|
|
|
|
h.requireReadLineExact("EXPORTSUPPORTED-FAILURE")
|
|
|
|
|
|
|
|
require.NoError(t, h.mockStdinW.Close())
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
// TestMain drives the tests
|
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
fstest.TestMain(m)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run fstest-compatible test cases with backend selected by `-remote`.
|
|
|
|
func TestGitAnnexFstestBackendCases(t *testing.T) {
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
for _, testCase := range fstestTestCases {
|
2024-01-28 19:36:17 +01:00
|
|
|
t.Run(testCase.label, func(t *testing.T) {
|
2024-08-04 16:43:49 +02:00
|
|
|
r := fstest.NewRun(t)
|
|
|
|
t.Cleanup(func() { r.Finalise() })
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
// Parse the fstest-provided remote string. It might have a path!
|
|
|
|
remoteName, remotePath, err := fspath.SplitFs(r.FremoteName)
|
|
|
|
require.NoError(t, err)
|
2024-01-28 19:36:17 +01:00
|
|
|
|
2024-08-04 16:43:49 +02:00
|
|
|
// The gitannex command requires the `rcloneremotename` is the name
|
2025-02-28 04:41:52 +01:00
|
|
|
// of a remote or a colon-prefixed backend name like ":local:", so
|
|
|
|
// the empty string will not suffice.
|
2024-08-04 16:43:49 +02:00
|
|
|
if remoteName == "" {
|
|
|
|
require.True(t, r.Fremote.Features().IsLocal)
|
2025-02-28 04:41:52 +01:00
|
|
|
remoteName = ":local:"
|
2024-01-28 19:36:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
handle := makeTestState(t)
|
2024-08-04 16:43:49 +02:00
|
|
|
handle.fstestRun = r
|
2024-01-28 19:36:17 +01:00
|
|
|
handle.remoteName = remoteName
|
2024-08-04 16:43:49 +02:00
|
|
|
handle.remotePrefix = remotePath
|
2024-01-28 19:36:17 +01:00
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
err := handle.server.run()
|
|
|
|
|
|
|
|
if testCase.expectedError == "" {
|
|
|
|
require.NoError(t, err)
|
|
|
|
} else {
|
|
|
|
require.ErrorContains(t, err, testCase.expectedError)
|
|
|
|
}
|
|
|
|
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
defer wg.Wait()
|
|
|
|
|
|
|
|
testCase.testProtocolFunc(t, &handle)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|