From 718002acea3d1eb5dc42dcfa9448b1cd6fd0cf8d Mon Sep 17 00:00:00 2001 From: Ying Li Date: Wed, 3 Feb 2016 11:09:38 -0800 Subject: [PATCH] Add some more tests for notary CLI Signed-off-by: Ying Li --- cmd/notary/main.go | 10 +-- cmd/notary/main_test.go | 169 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 172 insertions(+), 7 deletions(-) create mode 100644 cmd/notary/main_test.go diff --git a/cmd/notary/main.go b/cmd/notary/main.go index 72c9f3de7b..49e9c16637 100644 --- a/cmd/notary/main.go +++ b/cmd/notary/main.go @@ -72,17 +72,13 @@ func (n *notaryCommander) parseConfig() (*viper.Viper, error) { // If there was a commandline configFile set, we parse that. // If there wasn't we attempt to find it on the default location ~/.notary/config.json - configFileName, configFileExt, configPath := "config", "json", defaultTrustDir if n.configFile != "" { - configFileExt = strings.TrimPrefix(filepath.Ext(n.configFile), ".") - configFileName = strings.TrimSuffix(filepath.Base(n.configFile), filepath.Ext(n.configFile)) - configPath = filepath.Dir(n.configFile) + config.SetConfigFile(n.configFile) + } else { + config.SetConfigFile(filepath.Join(defaultTrustDir, "config.json")) } // Setup the configuration details into viper - config.SetConfigName(configFileName) - config.SetConfigType(configFileExt) - config.AddConfigPath(configPath) config.SetDefault("trust_dir", defaultTrustDir) config.SetDefault("remote_server", map[string]string{"url": defaultServerURL}) diff --git a/cmd/notary/main_test.go b/cmd/notary/main_test.go new file mode 100644 index 0000000000..53982fa21b --- /dev/null +++ b/cmd/notary/main_test.go @@ -0,0 +1,169 @@ +package main + +import ( + "bytes" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" + + "github.com/docker/notary/passphrase" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// the default location for the config file is in ~/.notary/config.json - even if it doesn't exist. +func TestNotaryConfigFileDefault(t *testing.T) { + commander := ¬aryCommander{ + getRetriever: func() passphrase.Retriever { return passphrase.ConstantRetriever("pass") }, + } + + config, err := commander.parseConfig() + assert.NoError(t, err) + configFileUsed := config.ConfigFileUsed() + assert.True(t, strings.HasSuffix(configFileUsed, + filepath.Join(".notary", "config.json")), "Unknown config file: %s", configFileUsed) +} + +// the default server address is notary-server +func TestRemoteServerDefault(t *testing.T) { + tempDir := tempDirWithConfig(t, "{}") + defer os.RemoveAll(tempDir) + configFile := filepath.Join(tempDir, "config.json") + + commander := ¬aryCommander{ + getRetriever: func() passphrase.Retriever { return passphrase.ConstantRetriever("pass") }, + } + + // set a blank config file, so it doesn't check ~/.notary/config.json by default + // and execute a random command so that the flags are parsed + cmd := commander.GetCommand() + cmd.SetArgs([]string{"-c", configFile, "list"}) + cmd.SetOutput(new(bytes.Buffer)) // eat the output + cmd.Execute() + + config, err := commander.parseConfig() + assert.NoError(t, err) + assert.Equal(t, "https://notary-server:4443", getRemoteTrustServer(config)) +} + +// providing a config file uses the config file's server url instead +func TestRemoteServerUsesConfigFile(t *testing.T) { + tempDir := tempDirWithConfig(t, `{"remote_server": {"url": "https://myserver"}}`) + defer os.RemoveAll(tempDir) + configFile := filepath.Join(tempDir, "config.json") + + commander := ¬aryCommander{ + getRetriever: func() passphrase.Retriever { return passphrase.ConstantRetriever("pass") }, + } + + // set a config file, so it doesn't check ~/.notary/config.json by default, + // and execute a random command so that the flags are parsed + cmd := commander.GetCommand() + cmd.SetArgs([]string{"-c", configFile, "list"}) + cmd.SetOutput(new(bytes.Buffer)) // eat the output + cmd.Execute() + + config, err := commander.parseConfig() + assert.NoError(t, err) + assert.Equal(t, "https://myserver", getRemoteTrustServer(config)) +} + +// a command line flag overrides the config file's server url +func TestRemoteServerCommandLineFlagOverridesConfig(t *testing.T) { + tempDir := tempDirWithConfig(t, `{"remote_server": {"url": "https://myserver"}}`) + defer os.RemoveAll(tempDir) + configFile := filepath.Join(tempDir, "config.json") + + commander := ¬aryCommander{ + getRetriever: func() passphrase.Retriever { return passphrase.ConstantRetriever("pass") }, + } + + // set a config file, so it doesn't check ~/.notary/config.json by default, + // and execute a random command so that the flags are parsed + cmd := commander.GetCommand() + cmd.SetArgs([]string{"-c", configFile, "-s", "http://overridden", "list"}) + cmd.SetOutput(new(bytes.Buffer)) // eat the output + cmd.Execute() + + config, err := commander.parseConfig() + assert.NoError(t, err) + assert.Equal(t, "http://overridden", getRemoteTrustServer(config)) +} + +var exampleValidCommands = []string{ + "init repo", + "list repo", + "status repo", + "publish repo", + "add repo v1 somefile", + "verify repo v1", + "key list", + "key rotate repo", + "key generate rsa", + "key backup tempfile.zip", + "key export e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 backup.pem", + "key restore tempfile.zip", + "key import backup.pem", + "key remove e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "key passwd e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "cert list", + "cert remove e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "delegation list repo", + "delegation add repo targets/releases path/to/pem/file.pem", + "delegation remove repo targets/releases", +} + +// config parsing bugs are propagated in all commands +func TestConfigParsingErrorsPropagatedByCommands(t *testing.T) { + tempdir, err := ioutil.TempDir("", "empty-dir") + require.NoError(t, err) + defer os.RemoveAll(tempdir) + + for _, args := range exampleValidCommands { + b := new(bytes.Buffer) + cmd := NewNotaryCommand() + cmd.SetOutput(b) + + cmd.SetArgs(append( + []string{"-c", filepath.Join(tempdir, "idonotexist.json"), "-d", tempdir}, + strings.Fields(args)...)) + err = cmd.Execute() + + require.Error(t, err, "expected error when running %s", args) + require.Contains(t, err.Error(), "error opening config file", "running %s", args) + require.NotContains(t, b.String(), "Usage:") + } +} + +// insufficient arguments produce an error before any parsing of configs happens +func TestInsufficientArgumentsReturnsErrorAndPrintsUsage(t *testing.T) { + tempdir, err := ioutil.TempDir("", "empty-dir") + require.NoError(t, err) + defer os.RemoveAll(tempdir) + + for _, args := range exampleValidCommands { + b := new(bytes.Buffer) + cmd := NewNotaryCommand() + cmd.SetOutput(b) + + arglist := strings.Fields(args) + if args == "key list" || args == "cert list" || args == "key generate rsa" { + // in these case, "key" or "cert" or "key generate" are valid commands, so add an arg to them instead + arglist = append(arglist, "extraArg") + } else { + arglist = arglist[:len(arglist)-1] + } + + invalid := strings.Join(arglist, " ") + + cmd.SetArgs(append( + []string{"-c", filepath.Join(tempdir, "idonotexist.json"), "-d", tempdir}, arglist...)) + err = cmd.Execute() + + require.NotContains(t, err.Error(), "error opening config file", "running %s", invalid) + // it's a usage error, so the usage is printed + require.Contains(t, b.String(), "Usage:", "expected usage when running %s", invalid) + } +}