launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/cmd/jujud/main_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package main
     5  
     6  import (
     7  	"errors"
     8  	"flag"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"os"
    12  	"os/exec"
    13  	"path/filepath"
    14  	"strings"
    15  	stdtesting "testing"
    16  
    17  	"launchpad.net/gnuflag"
    18  	gc "launchpad.net/gocheck"
    19  
    20  	"launchpad.net/juju-core/cmd"
    21  	"launchpad.net/juju-core/environs"
    22  	"launchpad.net/juju-core/testing"
    23  	"launchpad.net/juju-core/worker/uniter/jujuc"
    24  )
    25  
    26  var caCertFile string
    27  
    28  func mktemp(prefix string, content string) string {
    29  	f, err := ioutil.TempFile("", prefix)
    30  	if err != nil {
    31  		panic(err)
    32  	}
    33  	_, err = f.WriteString(content)
    34  	if err != nil {
    35  		panic(err)
    36  	}
    37  	f.Close()
    38  	return f.Name()
    39  }
    40  
    41  func TestPackage(t *stdtesting.T) {
    42  	// Change the path to "juju-run", so that the
    43  	// tests don't try to write to /usr/local/bin.
    44  	jujuRun = mktemp("juju-run", "")
    45  	defer os.Remove(jujuRun)
    46  
    47  	// Create a CA certificate available for all tests.
    48  	caCertFile = mktemp("juju-test-cert", testing.CACert)
    49  	defer os.Remove(caCertFile)
    50  
    51  	testing.MgoTestPackage(t)
    52  }
    53  
    54  type MainSuite struct{}
    55  
    56  var _ = gc.Suite(&MainSuite{})
    57  
    58  var flagRunMain = flag.Bool("run-main", false, "Run the application's main function for recursive testing")
    59  
    60  // Reentrancy point for testing (something as close as possible to) the jujud
    61  // tool itself.
    62  func TestRunMain(t *stdtesting.T) {
    63  	if *flagRunMain {
    64  		Main(flag.Args())
    65  	}
    66  }
    67  
    68  func checkMessage(c *gc.C, msg string, cmd ...string) {
    69  	args := append([]string{"-test.run", "TestRunMain", "-run-main", "--", "jujud"}, cmd...)
    70  	c.Logf("check %#v", args)
    71  	ps := exec.Command(os.Args[0], args...)
    72  	output, err := ps.CombinedOutput()
    73  	c.Logf(string(output))
    74  	c.Assert(err, gc.ErrorMatches, "exit status 2")
    75  	lines := strings.Split(string(output), "\n")
    76  	c.Assert(lines[len(lines)-2], gc.Equals, "error: "+msg)
    77  }
    78  
    79  func (s *MainSuite) TestParseErrors(c *gc.C) {
    80  	// Check all the obvious parse errors
    81  	checkMessage(c, "unrecognized command: jujud cavitate", "cavitate")
    82  	msgf := "flag provided but not defined: --cheese"
    83  	checkMessage(c, msgf, "--cheese", "cavitate")
    84  
    85  	cmds := []string{"bootstrap-state", "unit", "machine"}
    86  	for _, cmd := range cmds {
    87  		checkMessage(c, msgf, cmd, "--cheese")
    88  	}
    89  
    90  	msga := `unrecognized args: ["toastie"]`
    91  	checkMessage(c, msga,
    92  		"bootstrap-state",
    93  		"--env-config", b64yaml{"blah": "blah"}.encode(),
    94  		"toastie")
    95  	checkMessage(c, msga, "unit",
    96  		"--unit-name", "un/0",
    97  		"toastie")
    98  	checkMessage(c, msga, "machine",
    99  		"--machine-id", "42",
   100  		"toastie")
   101  }
   102  
   103  var expectedProviders = []string{
   104  	"ec2",
   105  	"maas",
   106  	"openstack",
   107  }
   108  
   109  func (s *MainSuite) TestProvidersAreRegistered(c *gc.C) {
   110  	// check that all the expected providers are registered
   111  	for _, name := range expectedProviders {
   112  		_, err := environs.Provider(name)
   113  		c.Assert(err, gc.IsNil)
   114  	}
   115  }
   116  
   117  type RemoteCommand struct {
   118  	cmd.CommandBase
   119  	msg string
   120  }
   121  
   122  var expectUsage = `usage: remote [options]
   123  purpose: test jujuc
   124  
   125  options:
   126  --error (= "")
   127      if set, fail
   128  
   129  here is some documentation
   130  `
   131  
   132  func (c *RemoteCommand) Info() *cmd.Info {
   133  	return &cmd.Info{
   134  		Name:    "remote",
   135  		Purpose: "test jujuc",
   136  		Doc:     "here is some documentation",
   137  	}
   138  }
   139  
   140  func (c *RemoteCommand) SetFlags(f *gnuflag.FlagSet) {
   141  	f.StringVar(&c.msg, "error", "", "if set, fail")
   142  }
   143  
   144  func (c *RemoteCommand) Init(args []string) error {
   145  	return cmd.CheckEmpty(args)
   146  }
   147  
   148  func (c *RemoteCommand) Run(ctx *cmd.Context) error {
   149  	if c.msg != "" {
   150  		return errors.New(c.msg)
   151  	}
   152  	fmt.Fprintf(ctx.Stdout, "success!\n")
   153  	return nil
   154  }
   155  
   156  func run(c *gc.C, sockPath string, contextId string, exit int, cmd ...string) string {
   157  	args := append([]string{"-test.run", "TestRunMain", "-run-main", "--"}, cmd...)
   158  	c.Logf("check %v %#v", os.Args[0], args)
   159  	ps := exec.Command(os.Args[0], args...)
   160  	ps.Dir = c.MkDir()
   161  	ps.Env = []string{
   162  		fmt.Sprintf("JUJU_AGENT_SOCKET=%s", sockPath),
   163  		fmt.Sprintf("JUJU_CONTEXT_ID=%s", contextId),
   164  		// Code that imports launchpad.net/juju-core/testing needs to
   165  		// be able to find that module at runtime (via build.Import),
   166  		// so we have to preserve that env variable.
   167  		os.ExpandEnv("GOPATH=${GOPATH}"),
   168  	}
   169  	output, err := ps.CombinedOutput()
   170  	if exit == 0 {
   171  		c.Assert(err, gc.IsNil)
   172  	} else {
   173  		c.Assert(err, gc.ErrorMatches, fmt.Sprintf("exit status %d", exit))
   174  	}
   175  	return string(output)
   176  }
   177  
   178  type JujuCMainSuite struct {
   179  	sockPath string
   180  	server   *jujuc.Server
   181  }
   182  
   183  var _ = gc.Suite(&JujuCMainSuite{})
   184  
   185  func (s *JujuCMainSuite) SetUpSuite(c *gc.C) {
   186  	factory := func(contextId, cmdName string) (cmd.Command, error) {
   187  		if contextId != "bill" {
   188  			return nil, fmt.Errorf("bad context: %s", contextId)
   189  		}
   190  		if cmdName != "remote" {
   191  			return nil, fmt.Errorf("bad command: %s", cmdName)
   192  		}
   193  		return &RemoteCommand{}, nil
   194  	}
   195  	s.sockPath = filepath.Join(c.MkDir(), "test.sock")
   196  	srv, err := jujuc.NewServer(factory, s.sockPath)
   197  	c.Assert(err, gc.IsNil)
   198  	s.server = srv
   199  	go func() {
   200  		if err := s.server.Run(); err != nil {
   201  			c.Fatalf("server died: %s", err)
   202  		}
   203  	}()
   204  }
   205  
   206  func (s *JujuCMainSuite) TearDownSuite(c *gc.C) {
   207  	s.server.Close()
   208  }
   209  
   210  var argsTests = []struct {
   211  	args   []string
   212  	code   int
   213  	output string
   214  }{
   215  	{[]string{"jujuc", "whatever"}, 2, jujudDoc + "error: jujuc should not be called directly\n"},
   216  	{[]string{"remote"}, 0, "success!\n"},
   217  	{[]string{"/path/to/remote"}, 0, "success!\n"},
   218  	{[]string{"remote", "--help"}, 0, expectUsage},
   219  	{[]string{"unknown"}, 1, "error: bad request: bad command: unknown\n"},
   220  	{[]string{"remote", "--error", "borken"}, 1, "error: borken\n"},
   221  	{[]string{"remote", "--unknown"}, 2, "error: flag provided but not defined: --unknown\n"},
   222  	{[]string{"remote", "unwanted"}, 2, `error: unrecognized args: ["unwanted"]` + "\n"},
   223  }
   224  
   225  func (s *JujuCMainSuite) TestArgs(c *gc.C) {
   226  	for _, t := range argsTests {
   227  		fmt.Println(t.args)
   228  		output := run(c, s.sockPath, "bill", t.code, t.args...)
   229  		c.Assert(output, gc.Equals, t.output)
   230  	}
   231  }
   232  
   233  func (s *JujuCMainSuite) TestNoClientId(c *gc.C) {
   234  	output := run(c, s.sockPath, "", 1, "remote")
   235  	c.Assert(output, gc.Equals, "error: JUJU_CONTEXT_ID not set\n")
   236  }
   237  
   238  func (s *JujuCMainSuite) TestBadClientId(c *gc.C) {
   239  	output := run(c, s.sockPath, "ben", 1, "remote")
   240  	c.Assert(output, gc.Equals, "error: bad request: bad context: ben\n")
   241  }
   242  
   243  func (s *JujuCMainSuite) TestNoSockPath(c *gc.C) {
   244  	output := run(c, "", "bill", 1, "remote")
   245  	c.Assert(output, gc.Equals, "error: JUJU_AGENT_SOCKET not set\n")
   246  }
   247  
   248  func (s *JujuCMainSuite) TestBadSockPath(c *gc.C) {
   249  	badSock := filepath.Join(c.MkDir(), "bad.sock")
   250  	output := run(c, badSock, "bill", 1, "remote")
   251  	err := fmt.Sprintf("error: dial unix %s: .*\n", badSock)
   252  	c.Assert(output, gc.Matches, err)
   253  }