Files
atlantis/cmd/server_test.go
Luke Kysow 486d3615de Merge pull request #10 from runatlantis/data-dir
Convert relative --data-dir paths to absolute.
2018-02-18 21:34:51 -08:00

495 lines
13 KiB
Go

package cmd_test
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"testing"
"github.com/mitchellh/go-homedir"
"github.com/runatlantis/atlantis/cmd"
"github.com/runatlantis/atlantis/server"
. "github.com/runatlantis/atlantis/testing"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// passedConfig is set to whatever config ended up being passed to NewServer.
// Used for testing.
var passedConfig server.Config
type ServerCreatorMock struct{}
func (s *ServerCreatorMock) NewServer(config server.Config) (cmd.ServerStarter, error) {
passedConfig = config
return &ServerStarterMock{}, nil
}
type ServerStarterMock struct{}
func (s *ServerStarterMock) Start() error {
return nil
}
func TestExecute_NoConfigFlag(t *testing.T) {
t.Log("If there is no config flag specified Execute should return nil.")
c := setup(map[string]interface{}{
cmd.ConfigFlag: "",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Ok(t, err)
}
func TestExecute_ConfigFileExtension(t *testing.T) {
t.Log("If the config file doesn't have an extension then error.")
c := setup(map[string]interface{}{
cmd.ConfigFlag: "does-not-exist",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Equals(t, "invalid config: reading does-not-exist: Unsupported Config Type \"\"", err.Error())
}
func TestExecute_ConfigFileMissing(t *testing.T) {
t.Log("If the config file doesn't exist then error.")
c := setup(map[string]interface{}{
cmd.ConfigFlag: "does-not-exist.yaml",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Equals(t, "invalid config: reading does-not-exist.yaml: open does-not-exist.yaml: no such file or directory", err.Error())
}
func TestExecute_ConfigFileExists(t *testing.T) {
t.Log("If the config file exists then there should be no error.")
tmpFile := tempFile(t, "")
defer os.Remove(tmpFile) // nolint: errcheck
c := setup(map[string]interface{}{
cmd.ConfigFlag: tmpFile,
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Ok(t, err)
}
func TestExecute_InvalidConfig(t *testing.T) {
t.Log("If the config file contains invalid yaml there should be an error.")
tmpFile := tempFile(t, "invalidyaml")
defer os.Remove(tmpFile) // nolint: errcheck
c := setup(map[string]interface{}{
cmd.ConfigFlag: tmpFile,
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Assert(t, strings.Contains(err.Error(), "unmarshal errors"), "should be an unmarshal error")
}
func TestExecute_ValidateLogLevel(t *testing.T) {
t.Log("Should validate log level.")
c := setup(map[string]interface{}{
cmd.LogLevelFlag: "invalid",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Assert(t, err != nil, "should be an error")
Equals(t, "invalid log level: not one of debug, info, warn, error", err.Error())
}
func TestExecute_ValidateSSLConfig(t *testing.T) {
expErr := "--ssl-key-file and --ssl-cert-file are both required for ssl"
cases := []struct {
description string
flags map[string]interface{}
expectError bool
}{
{
"neither option set",
make(map[string]interface{}),
false,
},
{
"just ssl-key-file set",
map[string]interface{}{
cmd.SSLKeyFileFlag: "file",
},
true,
},
{
"just ssl-cert-file set",
map[string]interface{}{
cmd.SSLCertFileFlag: "flag",
},
true,
},
{
"both flags set",
map[string]interface{}{
cmd.SSLCertFileFlag: "cert",
cmd.SSLKeyFileFlag: "key",
},
false,
},
}
for _, testCase := range cases {
t.Log("Should validate ssl config when " + testCase.description)
// Add in required flags.
testCase.flags[cmd.GHUserFlag] = "user"
testCase.flags[cmd.GHTokenFlag] = "token"
c := setup(testCase.flags)
err := c.Execute()
if testCase.expectError {
Assert(t, err != nil, "should be an error")
Equals(t, expErr, err.Error())
} else {
Ok(t, err)
}
}
}
func TestExecute_ValidateVCSConfig(t *testing.T) {
expErr := "--gh-user/--gh-token or --gitlab-user/--gitlab-token must be set"
cases := []struct {
description string
flags map[string]interface{}
expectError bool
}{
{
"no config set",
nil,
true,
},
{
"just github token set",
map[string]interface{}{
cmd.GHTokenFlag: "token",
},
true,
},
{
"just gitlab token set",
map[string]interface{}{
cmd.GitlabTokenFlag: "token",
},
true,
},
{
"just github user set",
map[string]interface{}{
cmd.GHUserFlag: "user",
},
true,
},
{
"just gitlab user set",
map[string]interface{}{
cmd.GitlabUserFlag: "user",
},
true,
},
{
"github user and gitlab token set",
map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GitlabTokenFlag: "token",
},
true,
},
{
"gitlab user and github token set",
map[string]interface{}{
cmd.GitlabUserFlag: "user",
cmd.GHTokenFlag: "token",
},
true,
},
{
"github user and github token set and should be successful",
map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
},
false,
},
{
"gitlab user and gitlab token set and should be successful",
map[string]interface{}{
cmd.GitlabUserFlag: "user",
cmd.GitlabTokenFlag: "token",
},
false,
},
{
"github and gitlab user and github and gitlab token set and should be successful",
map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GitlabUserFlag: "user",
cmd.GitlabTokenFlag: "token",
},
false,
},
}
for _, testCase := range cases {
t.Log("Should validate vcs config when " + testCase.description)
c := setup(testCase.flags)
err := c.Execute()
if testCase.expectError {
Assert(t, err != nil, "should be an error")
Equals(t, expErr, err.Error())
} else {
Ok(t, err)
}
}
}
func TestExecute_Defaults(t *testing.T) {
t.Log("Should set the defaults for all unspecified flags.")
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GitlabUserFlag: "gitlab-user",
cmd.GitlabTokenFlag: "gitlab-token",
})
err := c.Execute()
Ok(t, err)
Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-user", passedConfig.GitlabUser)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "", passedConfig.GitlabWebHookSecret)
// Get our hostname since that's what gets defaulted to
hostname, err := os.Hostname()
Ok(t, err)
Equals(t, "http://"+hostname+":4141", passedConfig.AtlantisURL)
// Get our home dir since that's what gets defaulted to
dataDir, err := homedir.Expand("~/.atlantis")
Ok(t, err)
Equals(t, dataDir, passedConfig.DataDir)
Equals(t, "github.com", passedConfig.GithubHostname)
Equals(t, "gitlab.com", passedConfig.GitlabHostname)
Equals(t, "info", passedConfig.LogLevel)
Equals(t, false, passedConfig.RequireApproval)
Equals(t, 4141, passedConfig.Port)
}
func TestExecute_ExpandHomeInDataDir(t *testing.T) {
t.Log("If ~ is used as a data-dir path, should expand to absolute home path")
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.DataDirFlag: "~/this/is/a/path",
})
err := c.Execute()
Ok(t, err)
home, err := homedir.Dir()
Ok(t, err)
Equals(t, home+"/this/is/a/path", passedConfig.DataDir)
}
func TestExecute_RelativeDataDir(t *testing.T) {
t.Log("Should convert relative dir to absolute.")
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.DataDirFlag: "../",
})
// Figure out what ../ should be as an absolute path.
expectedAbsolutePath, err := filepath.Abs("../")
Ok(t, err)
err = c.Execute()
Ok(t, err)
Equals(t, expectedAbsolutePath, passedConfig.DataDir)
}
func TestExecute_GithubUser(t *testing.T) {
t.Log("Should remove the @ from the github username if it's passed.")
c := setup(map[string]interface{}{
cmd.GHUserFlag: "@user",
cmd.GHTokenFlag: "token",
})
err := c.Execute()
Ok(t, err)
Equals(t, "user", passedConfig.GithubUser)
}
func TestExecute_GitlabUser(t *testing.T) {
t.Log("Should remove the @ from the gitlab username if it's passed.")
c := setup(map[string]interface{}{
cmd.GitlabUserFlag: "@user",
cmd.GitlabTokenFlag: "token",
})
err := c.Execute()
Ok(t, err)
Equals(t, "user", passedConfig.GitlabUser)
}
func TestExecute_Flags(t *testing.T) {
t.Log("Should use all flags that are set.")
c := setup(map[string]interface{}{
cmd.AtlantisURLFlag: "url",
cmd.DataDirFlag: "/path",
cmd.GHHostnameFlag: "ghhostname",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
cmd.GHWebHookSecret: "secret",
cmd.GitlabHostnameFlag: "gitlab-hostname",
cmd.GitlabUserFlag: "gitlab-user",
cmd.GitlabTokenFlag: "gitlab-token",
cmd.GitlabWebHookSecret: "gitlab-secret",
cmd.LogLevelFlag: "debug",
cmd.PortFlag: 8181,
cmd.RequireApprovalFlag: true,
})
err := c.Execute()
Ok(t, err)
Equals(t, "url", passedConfig.AtlantisURL)
Equals(t, "/path", passedConfig.DataDir)
Equals(t, "ghhostname", passedConfig.GithubHostname)
Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "secret", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-hostname", passedConfig.GitlabHostname)
Equals(t, "gitlab-user", passedConfig.GitlabUser)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "gitlab-secret", passedConfig.GitlabWebHookSecret)
Equals(t, "debug", passedConfig.LogLevel)
Equals(t, 8181, passedConfig.Port)
Equals(t, true, passedConfig.RequireApproval)
}
func TestExecute_ConfigFile(t *testing.T) {
t.Log("Should use all the values from the config file.")
tmpFile := tempFile(t, `---
atlantis-url: "url"
data-dir: "/path"
gh-hostname: "ghhostname"
gh-user: "user"
gh-token: "token"
gh-webhook-secret: "secret"
gitlab-hostname: "gitlab-hostname"
gitlab-user: "gitlab-user"
gitlab-token: "gitlab-token"
gitlab-webhook-secret: "gitlab-secret"
log-level: "debug"
port: 8181
require-approval: true`)
defer os.Remove(tmpFile) // nolint: errcheck
c := setup(map[string]interface{}{
cmd.ConfigFlag: tmpFile,
})
err := c.Execute()
Ok(t, err)
Equals(t, "url", passedConfig.AtlantisURL)
Equals(t, "/path", passedConfig.DataDir)
Equals(t, "ghhostname", passedConfig.GithubHostname)
Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "secret", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-hostname", passedConfig.GitlabHostname)
Equals(t, "gitlab-user", passedConfig.GitlabUser)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "gitlab-secret", passedConfig.GitlabWebHookSecret)
Equals(t, "debug", passedConfig.LogLevel)
Equals(t, 8181, passedConfig.Port)
Equals(t, true, passedConfig.RequireApproval)
}
func TestExecute_EnvironmentOverride(t *testing.T) {
t.Log("Environment variables should override config file flags.")
tmpFile := tempFile(t, "gh-user: config\ngh-token: config2")
defer os.Remove(tmpFile) // nolint: errcheck
os.Setenv("ATLANTIS_GH_TOKEN", "override") // nolint: errcheck
c := setup(map[string]interface{}{
cmd.ConfigFlag: tmpFile,
})
err := c.Execute()
Ok(t, err)
Equals(t, "override", passedConfig.GithubToken)
}
func TestExecute_FlagConfigOverride(t *testing.T) {
t.Log("Flags should override config file flags.")
os.Setenv("ATLANTIS_GH_TOKEN", "env-var") // nolint: errcheck
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "override",
})
err := c.Execute()
Ok(t, err)
Equals(t, "override", passedConfig.GithubToken)
}
func TestExecute_FlagEnvVarOverride(t *testing.T) {
t.Log("Flags should override environment variables.")
tmpFile := tempFile(t, "gh-user: config\ngh-token: config2")
defer os.Remove(tmpFile) // nolint: errcheck
c := setup(map[string]interface{}{
cmd.ConfigFlag: tmpFile,
cmd.GHTokenFlag: "override",
})
err := c.Execute()
Ok(t, err)
Equals(t, "override", passedConfig.GithubToken)
}
func TestExecute_EnvVars(t *testing.T) {
t.Log("Setting flags by env var should work.")
os.Setenv("ATLANTIS_GH_TOKEN", "gh-token") // nolint: errcheck
os.Setenv("ATLANTIS_GH_WEBHOOK_SECRET", "gh-webhook") // nolint: errcheck
os.Setenv("ATLANTIS_GITLAB_TOKEN", "gitlab-token") // nolint: errcheck
os.Setenv("ATLANTIS_GITLAB_WEBHOOK_SECRET", "gitlab-webhook") // nolint: errcheck
c := setup(map[string]interface{}{
cmd.GHUserFlag: "user",
cmd.GitlabUserFlag: "user",
})
err := c.Execute()
Ok(t, err)
Equals(t, "gh-token", passedConfig.GithubToken)
Equals(t, "gh-webhook", passedConfig.GithubWebHookSecret)
Equals(t, "gitlab-token", passedConfig.GitlabToken)
Equals(t, "gitlab-webhook", passedConfig.GitlabWebHookSecret)
}
func setup(flags map[string]interface{}) *cobra.Command {
viper := viper.New()
for k, v := range flags {
viper.Set(k, v)
}
c := &cmd.ServerCmd{
ServerCreator: &ServerCreatorMock{},
Viper: viper,
SilenceOutput: true,
}
return c.Init()
}
func tempFile(t *testing.T, contents string) string {
f, err := ioutil.TempFile("", "")
Ok(t, err)
newName := f.Name() + ".yaml"
err = os.Rename(f.Name(), newName)
Ok(t, err)
ioutil.WriteFile(newName, []byte(contents), 0644) // nolint: errcheck
return newName
}