rclone/cmdtest/environment_test.go
Ole Frost 5e91b93e59 cmdtest: end-to-end test for commands, flags and environment variables
There was no easy way to automatically test the end-to-end functionality
of commands, flags, environment variables etc.

The need for end-to-end testing was highlighted by the issues fixed
in #5341. There was no automated test to continually verify current
behaviour, nor a framework to quickly test the correctness of the fixes.

This change adds an end-to-end testing framework in the cmdtest folder.
It has some simple examples in func TestCmdTest in cmdtest_test.go. The
tests should be readable by anybody familiar with rclone and look like
this:

    // Test the rclone version command with debug logging (-vv)
    out, err = rclone("version", "-vv")
    if assert.NoError(t, err) {
        assert.Contains(t, out, "rclone v")
        assert.Contains(t, out, "os/version:")
        assert.Contains(t, out, " DEBUG : ")
    }

The end-to-end tests are executed just like the Go unit tests, that is:

    go test ./cmdtest -v

The change also contains a thorough test of environment variables in
environment_test.go.

Thanks to @ncw for encouragement and introduction to the TestMain trick.
2021-07-05 16:38:20 +01:00

276 lines
9 KiB
Go

// environment_test tests the use and precedence of environment variables
//
// The tests rely on functions defined in cmdtest_test.go
package cmdtest
import (
"os"
"runtime"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestCmdTest demonstrates and verifies the test functions for end-to-end testing of rclone
func TestEnvironmentVariables(t *testing.T) {
createTestEnvironment(t)
defer removeTestEnvironment(t)
testdataPath := createSimpleTestData(t)
defer removeSimpleTestData(t)
// Non backend flags
// =================
// First verify default behaviour of the implicit max_depth=-1
env := ""
out, err := rcloneEnv(env, "lsl", testFolder)
//t.Logf("\n" + out)
if assert.NoError(t, err) {
assert.Contains(t, out, "rclone.config") // depth 1
assert.Contains(t, out, "file1.txt") // depth 2
assert.Contains(t, out, "fileA1.txt") // depth 3
assert.Contains(t, out, "fileAA1.txt") // depth 4
}
// Test of flag.Value
env = "RCLONE_MAX_DEPTH=2"
out, err = rcloneEnv(env, "lsl", testFolder)
if assert.NoError(t, err) {
assert.Contains(t, out, "file1.txt") // depth 2
assert.NotContains(t, out, "fileA1.txt") // depth 3
}
// Test of flag.Changed (tests #5341 Issue1)
env = "RCLONE_LOG_LEVEL=DEBUG"
out, err = rcloneEnv(env, "version", "--quiet")
if assert.Error(t, err) {
assert.Contains(t, out, " DEBUG : ")
assert.Contains(t, out, "Can't set -q and --log-level")
assert.Contains(t, "exit status 1", err.Error())
}
// Test of flag.DefValue
env = "RCLONE_STATS=173ms"
out, err = rcloneEnv(env, "help", "flags")
if assert.NoError(t, err) {
assert.Contains(t, out, "(default 173ms)")
}
// Test of command line flags overriding environment flags
env = "RCLONE_MAX_DEPTH=2"
out, err = rcloneEnv(env, "lsl", testFolder, "--max-depth", "3")
if assert.NoError(t, err) {
assert.Contains(t, out, "fileA1.txt") // depth 3
assert.NotContains(t, out, "fileAA1.txt") // depth 4
}
// Test of debug logging while initialising flags from environment (tests #5241 Enhance1)
env = "RCLONE_STATS=173ms"
out, err = rcloneEnv(env, "version", "-vv")
if assert.NoError(t, err) {
assert.Contains(t, out, " DEBUG : ")
assert.Contains(t, out, "--stats")
assert.Contains(t, out, "173ms")
assert.Contains(t, out, "RCLONE_STATS=")
}
// Backend flags and option precedence
// ===================================
// Test approach:
// Verify no symlink warning when skip_links=true one the level with highest precedence
// and skip_links=false on all levels with lower precedence
//
// Reference: https://rclone.org/docs/#precedence
// Create a symlink in test data
err = os.Symlink(testdataPath+"/folderA", testdataPath+"/symlinkA")
if runtime.GOOS == "windows" {
errNote := "The policy settings on Windows often prohibit the creation of symlinks due to security issues.\n"
errNote += "You can safely ignore this test, if your change didn't affect environment variables."
require.NoError(t, err, errNote)
} else {
require.NoError(t, err)
}
// Create a local remote with explicit skip_links=false
out, err = rclone("config", "create", "myLocal", "local", "skip_links", "false")
if assert.NoError(t, err) {
assert.Contains(t, out, "[myLocal]")
assert.Contains(t, out, "type = local")
assert.Contains(t, out, "skip_links = false")
}
// Verify symlink warning when skip_links=false on all levels
env = "RCLONE_SKIP_LINKS=false;RCLONE_LOCAL_SKIP_LINKS=false;RCLONE_CONFIG_MYLOCAL_SKIP_LINKS=false"
out, err = rcloneEnv(env, "lsd", "myLocal,skip_links=false:"+testdataPath, "--skip-links=false")
//t.Logf("\n" + out)
if assert.NoError(t, err) {
assert.Contains(t, out, "NOTICE: symlinkA:")
assert.Contains(t, out, "folderA")
}
// Test precedence of connection strings
env = "RCLONE_SKIP_LINKS=false;RCLONE_LOCAL_SKIP_LINKS=false;RCLONE_CONFIG_MYLOCAL_SKIP_LINKS=false"
out, err = rcloneEnv(env, "lsd", "myLocal,skip_links:"+testdataPath, "--skip-links=false")
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test precedence of command line flags
env = "RCLONE_SKIP_LINKS=false;RCLONE_LOCAL_SKIP_LINKS=false;RCLONE_CONFIG_MYLOCAL_SKIP_LINKS=false"
out, err = rcloneEnv(env, "lsd", "myLocal:"+testdataPath, "--skip-links")
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test precedence of remote specific environment variables (tests #5341 Issue2)
env = "RCLONE_SKIP_LINKS=false;RCLONE_LOCAL_SKIP_LINKS=false;RCLONE_CONFIG_MYLOCAL_SKIP_LINKS=true"
out, err = rcloneEnv(env, "lsd", "myLocal:"+testdataPath)
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test precedence of backend specific environment variables (tests #5341 Issue3)
env = "RCLONE_SKIP_LINKS=false;RCLONE_LOCAL_SKIP_LINKS=true"
out, err = rcloneEnv(env, "lsd", "myLocal:"+testdataPath)
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test precedence of backend generic environment variables
env = "RCLONE_SKIP_LINKS=true"
out, err = rcloneEnv(env, "lsd", "myLocal:"+testdataPath)
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Recreate the test remote with explicit skip_links=true
out, err = rclone("config", "create", "myLocal", "local", "skip_links", "true")
if assert.NoError(t, err) {
assert.Contains(t, out, "[myLocal]")
assert.Contains(t, out, "type = local")
assert.Contains(t, out, "skip_links = true")
}
// Test precedence of config file options
env = ""
out, err = rcloneEnv(env, "lsd", "myLocal:"+testdataPath)
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Recreate the test remote with rclone defaults, that is implicit skip_links=false
out, err = rclone("config", "create", "myLocal", "local")
if assert.NoError(t, err) {
assert.Contains(t, out, "[myLocal]")
assert.Contains(t, out, "type = local")
assert.NotContains(t, out, "skip_links")
}
// Verify the rclone default value (implicit skip_links=false)
env = ""
out, err = rcloneEnv(env, "lsd", "myLocal:"+testdataPath)
if assert.NoError(t, err) {
assert.Contains(t, out, "NOTICE: symlinkA:")
assert.Contains(t, out, "folderA")
}
// Display of backend defaults (tests #4659)
//------------------------------------------
env = "RCLONE_DRIVE_CHUNK_SIZE=111M"
out, err = rcloneEnv(env, "help", "flags")
if assert.NoError(t, err) {
assert.Regexp(t, "--drive-chunk-size[^\\(]+\\(default 111M\\)", out)
}
// Options on referencing remotes (alias, crypt, etc.)
//----------------------------------------------------
// Create alias remote on myLocal having implicit skip_links=false
out, err = rclone("config", "create", "myAlias", "alias", "remote", "myLocal:"+testdataPath)
if assert.NoError(t, err) {
assert.Contains(t, out, "[myAlias]")
assert.Contains(t, out, "type = alias")
assert.Contains(t, out, "remote = myLocal:")
}
// Verify symlink warnings on the alias
env = ""
out, err = rcloneEnv(env, "lsd", "myAlias:")
if assert.NoError(t, err) {
assert.Contains(t, out, "NOTICE: symlinkA:")
assert.Contains(t, out, "folderA")
}
// Test backend generic flags
// having effect on the underlying local remote
env = "RCLONE_SKIP_LINKS=true"
out, err = rcloneEnv(env, "lsd", "myAlias:")
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test backend specific flags
// having effect on the underlying local remote
env = "RCLONE_LOCAL_SKIP_LINKS=true"
out, err = rcloneEnv(env, "lsd", "myAlias:")
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test remote specific flags
// having no effect unless supported by the immediate remote (alias)
env = "RCLONE_CONFIG_MYALIAS_SKIP_LINKS=true"
out, err = rcloneEnv(env, "lsd", "myAlias:")
if assert.NoError(t, err) {
assert.Contains(t, out, "NOTICE: symlinkA:")
assert.Contains(t, out, "folderA")
}
env = "RCLONE_CONFIG_MYALIAS_REMOTE=" + "myLocal:" + testdataPath + "/folderA"
out, err = rcloneEnv(env, "lsl", "myAlias:")
if assert.NoError(t, err) {
assert.Contains(t, out, "fileA1.txt")
assert.NotContains(t, out, "fileB1.txt")
}
// Test command line flags
// having effect on the underlying local remote
env = ""
out, err = rcloneEnv(env, "lsd", "myAlias:", "--skip-links")
if assert.NoError(t, err) {
assert.NotContains(t, out, "symlinkA")
assert.Contains(t, out, "folderA")
}
// Test connection specific flags
// having no effect unless supported by the immediate remote (alias)
env = ""
out, err = rcloneEnv(env, "lsd", "myAlias,skip_links:")
if assert.NoError(t, err) {
assert.Contains(t, out, "NOTICE: symlinkA:")
assert.Contains(t, out, "folderA")
}
env = ""
out, err = rcloneEnv(env, "lsl", "myAlias,remote='myLocal:"+testdataPath+"/folderA':", "-vv")
if assert.NoError(t, err) {
assert.Contains(t, out, "fileA1.txt")
assert.NotContains(t, out, "fileB1.txt")
}
}