rclone/fs/rc/internal_test.go
Nick Craig-Wood b3710c962e rc: fix core/command giving 500 internal error - fixes #4914
Before this change calling core/command gave the error

    error: response object is required expecting *http.ResponseWriter value for key "_response" (was *http.response)

This was because the http.ResponseWriter is an interface not an object.

Removing the `*` fixes the problem.

This also indicates that this bit of code wasn't properly tested.
2021-01-10 16:34:46 +00:00

198 lines
4.8 KiB
Go

package rc
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"runtime"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/rclone/rclone/fs"
"github.com/rclone/rclone/fs/config/obscure"
)
func TestMain(m *testing.M) {
// Pretend to be rclone version if we have a version string parameter
if os.Args[len(os.Args)-1] == "version" {
fmt.Printf("rclone %s\n", fs.Version)
os.Exit(0)
}
// Pretend to error if we have an unknown command
if os.Args[len(os.Args)-1] == "unknown_command" {
fmt.Printf("rclone %s\n", fs.Version)
fmt.Fprintf(os.Stderr, "Unknown command\n")
os.Exit(1)
}
os.Exit(m.Run())
}
func TestInternalNoop(t *testing.T) {
call := Calls.Get("rc/noop")
assert.NotNil(t, call)
in := Params{
"String": "hello",
"Int": 42,
}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.NotNil(t, out)
assert.Equal(t, in, out)
}
func TestInternalError(t *testing.T) {
call := Calls.Get("rc/error")
assert.NotNil(t, call)
in := Params{}
out, err := call.Fn(context.Background(), in)
require.Error(t, err)
require.Nil(t, out)
}
func TestInternalList(t *testing.T) {
call := Calls.Get("rc/list")
assert.NotNil(t, call)
in := Params{}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.NotNil(t, out)
assert.Equal(t, Params{"commands": Calls.List()}, out)
}
func TestCorePid(t *testing.T) {
call := Calls.Get("core/pid")
assert.NotNil(t, call)
in := Params{}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.NotNil(t, out)
pid := out["pid"]
assert.NotEqual(t, nil, pid)
_, ok := pid.(int)
assert.Equal(t, true, ok)
}
func TestCoreMemstats(t *testing.T) {
call := Calls.Get("core/memstats")
assert.NotNil(t, call)
in := Params{}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.NotNil(t, out)
sys := out["Sys"]
assert.NotEqual(t, nil, sys)
_, ok := sys.(uint64)
assert.Equal(t, true, ok)
}
func TestCoreGC(t *testing.T) {
call := Calls.Get("core/gc")
assert.NotNil(t, call)
in := Params{}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.Nil(t, out)
assert.Equal(t, Params(nil), out)
}
func TestCoreVersion(t *testing.T) {
call := Calls.Get("core/version")
assert.NotNil(t, call)
in := Params{}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.NotNil(t, out)
assert.Equal(t, fs.Version, out["version"])
assert.Equal(t, runtime.GOOS, out["os"])
assert.Equal(t, runtime.GOARCH, out["arch"])
assert.Equal(t, runtime.Version(), out["goVersion"])
_ = out["isGit"].(bool)
v := out["decomposed"].([]int64)
assert.True(t, len(v) >= 2)
}
func TestCoreObscure(t *testing.T) {
call := Calls.Get("core/obscure")
assert.NotNil(t, call)
in := Params{
"clear": "potato",
}
out, err := call.Fn(context.Background(), in)
require.NoError(t, err)
require.NotNil(t, out)
assert.Equal(t, in["clear"], obscure.MustReveal(out["obscured"].(string)))
}
func TestCoreQuit(t *testing.T) {
//The call should return an error if param exitCode is not parsed to int
call := Calls.Get("core/quit")
assert.NotNil(t, call)
in := Params{
"exitCode": "potato",
}
_, err := call.Fn(context.Background(), in)
require.Error(t, err)
}
// core/command: Runs a raw rclone command
func TestCoreCommand(t *testing.T) {
call := Calls.Get("core/command")
test := func(command string, returnType string, wantOutput string, fail bool) {
var rec = httptest.NewRecorder()
var w http.ResponseWriter = rec
in := Params{
"command": command,
"opt": map[string]string{},
"arg": []string{},
"_response": w,
}
if returnType != "" {
in["returnType"] = returnType
} else {
returnType = "COMBINED_OUTPUT"
}
stream := strings.HasPrefix(returnType, "STREAM")
got, err := call.Fn(context.Background(), in)
if stream && fail {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
if !stream {
assert.Equal(t, wantOutput, got["result"])
assert.Equal(t, fail, got["error"])
} else {
assert.Equal(t, wantOutput, rec.Body.String())
}
assert.Equal(t, http.StatusOK, rec.Result().StatusCode)
}
version := fmt.Sprintf("rclone %s\n", fs.Version)
errorString := "Unknown command\n"
t.Run("OK", func(t *testing.T) {
test("version", "", version, false)
})
t.Run("Fail", func(t *testing.T) {
test("unknown_command", "", version+errorString, true)
})
t.Run("Combined", func(t *testing.T) {
test("unknown_command", "COMBINED_OUTPUT", version+errorString, true)
})
t.Run("Stderr", func(t *testing.T) {
test("unknown_command", "STREAM_ONLY_STDERR", errorString, true)
})
t.Run("Stdout", func(t *testing.T) {
test("unknown_command", "STREAM_ONLY_STDOUT", version, true)
})
t.Run("Stream", func(t *testing.T) {
test("unknown_command", "STREAM", version+errorString, true)
})
}