github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/cmd/juju/user/change_password_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package user_test
     5  
     6  import (
     7  	"path/filepath"
     8  
     9  	"github.com/juju/cmd"
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	"github.com/juju/juju/cmd/envcmd"
    15  	"github.com/juju/juju/cmd/juju/user"
    16  	"github.com/juju/juju/environs/configstore"
    17  	"github.com/juju/juju/testing"
    18  )
    19  
    20  type ChangePasswordCommandSuite struct {
    21  	BaseSuite
    22  	mockAPI         *mockChangePasswordAPI
    23  	mockEnvironInfo *mockEnvironInfo
    24  }
    25  
    26  var _ = gc.Suite(&ChangePasswordCommandSuite{})
    27  
    28  func (s *ChangePasswordCommandSuite) SetUpTest(c *gc.C) {
    29  	s.BaseSuite.SetUpTest(c)
    30  	s.mockAPI = &mockChangePasswordAPI{}
    31  	s.mockEnvironInfo = &mockEnvironInfo{
    32  		creds: configstore.APICredentials{"user-name", "password"},
    33  	}
    34  	s.PatchValue(user.GetChangePasswordAPI, func(c *user.ChangePasswordCommand) (user.ChangePasswordAPI, error) {
    35  		return s.mockAPI, nil
    36  	})
    37  	s.PatchValue(user.GetEnvironInfoWriter, func(c *user.ChangePasswordCommand) (user.EnvironInfoCredsWriter, error) {
    38  		return s.mockEnvironInfo, nil
    39  	})
    40  	s.PatchValue(user.GetConnectionCredentials, func(c *user.ChangePasswordCommand) (configstore.APICredentials, error) {
    41  		return s.mockEnvironInfo.creds, nil
    42  	})
    43  }
    44  
    45  func newUserChangePassword() cmd.Command {
    46  	return envcmd.Wrap(&user.ChangePasswordCommand{})
    47  }
    48  
    49  func (s *ChangePasswordCommandSuite) TestInit(c *gc.C) {
    50  	for i, test := range []struct {
    51  		args        []string
    52  		user        string
    53  		outPath     string
    54  		generate    bool
    55  		errorString string
    56  	}{
    57  		{
    58  		// no args is fine
    59  		}, {
    60  			args:     []string{"--generate"},
    61  			generate: true,
    62  		}, {
    63  			args:        []string{"--foobar"},
    64  			errorString: "flag provided but not defined: --foobar",
    65  		}, {
    66  			args: []string{"foobar"},
    67  			user: "foobar",
    68  		}, {
    69  			args:        []string{"foobar", "extra"},
    70  			errorString: `unrecognized args: \["extra"\]`,
    71  		}, {
    72  			args:        []string{"--output", "somefile"},
    73  			errorString: "output is only a valid option when changing another user's password",
    74  		}, {
    75  			args:        []string{"-o", "somefile"},
    76  			errorString: "output is only a valid option when changing another user's password",
    77  		}, {
    78  			args:     []string{"foobar", "--generate"},
    79  			user:     "foobar",
    80  			generate: true,
    81  		}, {
    82  			args:    []string{"foobar", "--output", "somefile"},
    83  			user:    "foobar",
    84  			outPath: "somefile",
    85  		}, {
    86  			args:    []string{"foobar", "-o", "somefile"},
    87  			user:    "foobar",
    88  			outPath: "somefile",
    89  		},
    90  	} {
    91  		c.Logf("test %d", i)
    92  		command := &user.ChangePasswordCommand{}
    93  		err := testing.InitCommand(command, test.args)
    94  		if test.errorString == "" {
    95  			c.Check(command.User, gc.Equals, test.user)
    96  			c.Check(command.OutPath, gc.Equals, test.outPath)
    97  			c.Check(command.Generate, gc.Equals, test.generate)
    98  		} else {
    99  			c.Check(err, gc.ErrorMatches, test.errorString)
   100  		}
   101  	}
   102  }
   103  
   104  func (s *ChangePasswordCommandSuite) TestFailedToReadInfo(c *gc.C) {
   105  	s.PatchValue(user.GetEnvironInfoWriter, func(c *user.ChangePasswordCommand) (user.EnvironInfoCredsWriter, error) {
   106  		return s.mockEnvironInfo, errors.New("something failed")
   107  	})
   108  	_, err := testing.RunCommand(c, newUserChangePassword(), "--generate")
   109  	c.Assert(err, gc.ErrorMatches, "something failed")
   110  }
   111  
   112  func (s *ChangePasswordCommandSuite) TestChangePassword(c *gc.C) {
   113  	context, err := testing.RunCommand(c, newUserChangePassword())
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	c.Assert(s.mockAPI.username, gc.Equals, "user-name")
   116  	c.Assert(s.mockAPI.password, gc.Equals, "sekrit")
   117  	expected := `
   118  password:
   119  type password again:
   120  `[1:]
   121  	c.Assert(testing.Stdout(context), gc.Equals, expected)
   122  	c.Assert(testing.Stderr(context), gc.Equals, "Your password has been updated.\n")
   123  }
   124  
   125  func (s *ChangePasswordCommandSuite) TestChangePasswordGenerate(c *gc.C) {
   126  	context, err := testing.RunCommand(c, newUserChangePassword(), "--generate")
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	c.Assert(s.mockAPI.username, gc.Equals, "user-name")
   129  	c.Assert(s.mockAPI.password, gc.Not(gc.Equals), "sekrit")
   130  	c.Assert(s.mockAPI.password, gc.HasLen, 24)
   131  	c.Assert(testing.Stderr(context), gc.Equals, "Your password has been updated.\n")
   132  }
   133  
   134  func (s *ChangePasswordCommandSuite) TestChangePasswordFail(c *gc.C) {
   135  	s.mockAPI.failMessage = "failed to do something"
   136  	s.mockAPI.failOps = []bool{true, false}
   137  	_, err := testing.RunCommand(c, newUserChangePassword(), "--generate")
   138  	c.Assert(err, gc.ErrorMatches, "failed to do something")
   139  	c.Assert(s.mockAPI.username, gc.Equals, "")
   140  }
   141  
   142  // The first write fails, so we try to revert the password which succeeds
   143  func (s *ChangePasswordCommandSuite) TestRevertPasswordAfterFailedWrite(c *gc.C) {
   144  	// Fail to Write the new jenv file
   145  	s.mockEnvironInfo.failMessage = "failed to write"
   146  	_, err := testing.RunCommand(c, newUserChangePassword(), "--generate")
   147  	c.Assert(err, gc.ErrorMatches, "failed to write new password to environments file: failed to write")
   148  	// Last api call was to set the password back to the original.
   149  	c.Assert(s.mockAPI.password, gc.Equals, "password")
   150  }
   151  
   152  // SetPassword api works the first time, but the write fails, our second call to set password fails
   153  func (s *ChangePasswordCommandSuite) TestChangePasswordRevertApiFails(c *gc.C) {
   154  	s.mockAPI.failMessage = "failed to do something"
   155  	s.mockEnvironInfo.failMessage = "failed to write"
   156  	s.mockAPI.failOps = []bool{false, true}
   157  	_, err := testing.RunCommand(c, newUserChangePassword(), "--generate")
   158  	c.Assert(err, gc.ErrorMatches, "failed to set password back: failed to do something")
   159  }
   160  
   161  func (s *ChangePasswordCommandSuite) TestChangeOthersPassword(c *gc.C) {
   162  	// The checks for user existence and admin rights are tested
   163  	// at the apiserver level.
   164  	context, err := testing.RunCommand(c, newUserChangePassword(), "other")
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(s.mockAPI.username, gc.Equals, "other")
   167  	c.Assert(s.mockAPI.password, gc.Equals, "sekrit")
   168  	filename := context.AbsPath("other.jenv")
   169  	expected := `
   170  password:
   171  type password again:
   172  environment file written to `[1:] + filename + "\n"
   173  	c.Assert(testing.Stdout(context), gc.Equals, expected)
   174  	c.Assert(testing.Stderr(context), gc.Equals, "")
   175  	assertJENVContents(c, context.AbsPath("other.jenv"), "other", "sekrit")
   176  
   177  }
   178  
   179  func (s *ChangePasswordCommandSuite) TestChangeOthersPasswordWithFile(c *gc.C) {
   180  	// The checks for user existence and admin rights are tested
   181  	// at the apiserver level.
   182  	filename := filepath.Join(c.MkDir(), "test.jenv")
   183  	context, err := testing.RunCommand(c, newUserChangePassword(), "other", "-o", filename)
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	c.Assert(s.mockAPI.username, gc.Equals, "other")
   186  	c.Assert(s.mockAPI.password, gc.Equals, "sekrit")
   187  	expected := `
   188  password:
   189  type password again:
   190  environment file written to `[1:] + filename + "\n"
   191  	c.Assert(testing.Stdout(context), gc.Equals, expected)
   192  	c.Assert(testing.Stderr(context), gc.Equals, "")
   193  	assertJENVContents(c, filename, "other", "sekrit")
   194  }
   195  
   196  type mockEnvironInfo struct {
   197  	failMessage string
   198  	creds       configstore.APICredentials
   199  }
   200  
   201  func (m *mockEnvironInfo) Write() error {
   202  	if m.failMessage != "" {
   203  		return errors.New(m.failMessage)
   204  	}
   205  	return nil
   206  }
   207  
   208  func (m *mockEnvironInfo) SetAPICredentials(creds configstore.APICredentials) {
   209  	m.creds = creds
   210  }
   211  
   212  func (m *mockEnvironInfo) APICredentials() configstore.APICredentials {
   213  	return m.creds
   214  }
   215  
   216  func (m *mockEnvironInfo) Location() string {
   217  	return "location"
   218  }
   219  
   220  type mockChangePasswordAPI struct {
   221  	failMessage string
   222  	currentOp   int
   223  	failOps     []bool // Can be used to make the call pass/ fail in a known order
   224  	username    string
   225  	password    string
   226  }
   227  
   228  func (m *mockChangePasswordAPI) SetPassword(username, password string) error {
   229  	if len(m.failOps) > 0 && m.failOps[m.currentOp] {
   230  		m.currentOp++
   231  		return errors.New(m.failMessage)
   232  	}
   233  	m.currentOp++
   234  	m.username = username
   235  	m.password = password
   236  	return nil
   237  }
   238  
   239  func (*mockChangePasswordAPI) Close() error {
   240  	return nil
   241  }