Skip to content

Commit

Permalink
Test cmd/server
Browse files Browse the repository at this point in the history
  • Loading branch information
lkysow committed Oct 29, 2017
1 parent 00642aa commit fe773fe
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 15 deletions.
5 changes: 3 additions & 2 deletions cmd/bootstrap.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package cmd

import (
"github.com/hootsuite/atlantis/bootstrap"
"github.com/spf13/cobra"
"fmt"
"os"

"github.com/hootsuite/atlantis/bootstrap"
"github.com/spf13/cobra"
)

// BootstrapCmd starts the bootstrap process for testing out Atlantis.
Expand Down
21 changes: 20 additions & 1 deletion cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/hootsuite/atlantis/server"
"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
Expand Down Expand Up @@ -104,7 +105,7 @@ type boolFlag struct {
// us to mock out starting the actual server.
type ServerCmd struct {
ServerCreator ServerCreator
Viper *viper.Viper
Viper *viper.Viper
// SilenceOutput set to true means nothing gets printed.
// Useful for testing to keep the logs clean.
SilenceOutput bool
Expand Down Expand Up @@ -140,6 +141,7 @@ func (s *ServerCmd) Init() *cobra.Command {
Flags can also be set in a yaml config file (see --` + ConfigFlag + `).
Config file values are overridden by environment variables which in turn are overridden by flags.`,
SilenceErrors: true,
SilenceUsage: s.SilenceOutput,
PreRunE: s.withErrPrint(func(cmd *cobra.Command, args []string) error {
return s.preRun()
}),
Expand Down Expand Up @@ -201,6 +203,9 @@ func (s *ServerCmd) run() error {
if err := setAtlantisURL(&config); err != nil {
return err
}
if err := setDataDir(&config); err != nil {
return err
}
sanitizeGithubUser(&config)

// config looks good, start the server
Expand Down Expand Up @@ -237,6 +242,20 @@ func setAtlantisURL(config *server.ServerConfig) error {
return nil
}

// setDataDir checks if ~ was used in data-dir and converts it to the actual
// home directory. If we don't do this, we'll create a directory called "~"
// instead of actually using home.
func setDataDir(config *server.ServerConfig) error {
if strings.HasPrefix(config.DataDir, "~/") {
expanded, err := homedir.Expand(config.DataDir)
if err != nil {
return errors.Wrap(err, "determining home directory")
}
config.DataDir = expanded
}
return nil
}

// sanitizeGithubUser trims @ from the front of the github username if it exists.
func sanitizeGithubUser(config *server.ServerConfig) {
config.GithubUser = strings.TrimPrefix(config.GithubUser, "@")
Expand Down
303 changes: 303 additions & 0 deletions cmd/server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
package cmd_test

import (
"io/ioutil"
"os"
"strings"
"testing"

"github.com/hootsuite/atlantis/cmd"
"github.com/hootsuite/atlantis/server"
. "github.com/hootsuite/atlantis/testing_util"
"github.com/mitchellh/go-homedir"
"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.ServerConfig

type ServerCreatorMock struct{}

func (s *ServerCreatorMock) NewServer(config server.ServerConfig) (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)
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)
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_Validation(t *testing.T) {
cases := []struct {
description string
flags map[string]interface{}
expErr string
}{
{
"should validate log level",
map[string]interface{}{
cmd.LogLevelFlag: "invalid",
cmd.GHUserFlag: "user",
cmd.GHTokenFlag: "token",
},
"invalid log level: not one of debug, info, warn, error",
},
{
"should ensure github user is set",
map[string]interface{}{
cmd.GHTokenFlag: "token",
},
"--gh-user must be set",
},
{
"should ensure github token is set",
map[string]interface{}{
cmd.GHUserFlag: "user",
},
"--gh-token must be set",
},
}
for _, testCase := range cases {
t.Log(testCase.description)
c := setup(testCase.flags)
err := c.Execute()
Assert(t, err != nil, "should be an error")
Equals(t, testCase.expErr, err.Error())
}
}

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",
})
err := c.Execute()
Ok(t, err)

Equals(t, "user", passedConfig.GithubUser)
Equals(t, "token", passedConfig.GithubToken)
Equals(t, "", passedConfig.GithubWebHookSecret)
// 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, "info", passedConfig.LogLevel)
Equals(t, false, passedConfig.RequireApproval)
Equals(t, 4141, passedConfig.Port)
}

func TestExecute_ExpandHomeDir(t *testing.T) {
t.Log("should expand the ~ in the home dir to the actual home dir")
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_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_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.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, "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"
log-level: "debug"
port: 8181
require-approval: true`)
defer os.Remove(tmpFile)
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, "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)
os.Setenv("ATLANTIS_GH_TOKEN", "override")
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")
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)
c := setup(map[string]interface{}{
cmd.ConfigFlag: tmpFile,
cmd.GHTokenFlag: "override",
})

err := c.Execute()
Ok(t, err)
Equals(t, "override", passedConfig.GithubToken)
}

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)
return newName
}
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ func main() {

server := &cmd.ServerCmd{
ServerCreator: &cmd.DefaultServerCreator{},
Viper: v,
Viper: v,
}
version := &cmd.VersionCmd{Viper: v}
bootstrap := &cmd.BootstrapCmd{}
Expand Down
Loading

0 comments on commit fe773fe

Please sign in to comment.