github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/apicaller/retry_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package apicaller_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/testing"
     9  	jc "github.com/juju/testing/checkers"
    10  	"github.com/juju/utils"
    11  	gc "gopkg.in/check.v1"
    12  
    13  	"github.com/juju/juju/api"
    14  	"github.com/juju/juju/worker/apicaller"
    15  )
    16  
    17  // RetryStrategySuite exercises the cases where we need to connect
    18  // repeatedly, either to wait for provisioning errors or to fall
    19  // back to other possible passwords. It covers OnlyConnect in detail,
    20  // checking success and failure behaviour, but only checks suitable
    21  // error paths for ScaryConnect (which does extra complex things like
    22  // make api calls and rewrite passwords in config).
    23  //
    24  // Would be best of all to test all the ScaryConnect success/failure
    25  // paths explicitly, but the combinatorial explosion makes it tricky;
    26  // in the absence of a further decomposition of responsibilities, it
    27  // seems best to at least decompose the testing. Which is more detailed
    28  // than it was before, anyway.
    29  type RetryStrategySuite struct {
    30  	testing.IsolationSuite
    31  }
    32  
    33  var _ = gc.Suite(&RetryStrategySuite{})
    34  
    35  func (s *RetryStrategySuite) TestOnlyConnectSuccess(c *gc.C) {
    36  	stub := &testing.Stub{}
    37  	stub.SetErrors(
    38  		errNotProvisioned, // initial attempt, outside strategy
    39  		errNotProvisioned, // first strategy attempt
    40  		nil,               // success on second strategy attempt
    41  	)
    42  	strategy := utils.AttemptStrategy{Min: 3}
    43  	conn, err := strategyTest(stub, strategy, func(apiOpen api.OpenFunc) (api.Connection, error) {
    44  		return apicaller.OnlyConnect(&mockAgent{stub: stub}, apiOpen)
    45  	})
    46  	checkOpenCalls(c, stub, "new", "new", "new")
    47  	c.Check(conn, gc.NotNil)
    48  	c.Check(err, jc.ErrorIsNil)
    49  }
    50  
    51  func (s *RetryStrategySuite) TestOnlyConnectOldPasswordSuccess(c *gc.C) {
    52  	stub := &testing.Stub{}
    53  	stub.SetErrors(
    54  		errNotAuthorized,  // initial attempt, outside strategy
    55  		errNotProvisioned, // fallback attempt, outside strategy
    56  		errNotProvisioned, // first strategy attempt
    57  		nil,               // second strategy attempt
    58  	)
    59  	strategy := utils.AttemptStrategy{Min: 3}
    60  	conn, err := strategyTest(stub, strategy, func(apiOpen api.OpenFunc) (api.Connection, error) {
    61  		return apicaller.OnlyConnect(&mockAgent{stub: stub}, apiOpen)
    62  	})
    63  	checkOpenCalls(c, stub, "new", "old", "old", "old")
    64  	c.Check(err, jc.ErrorIsNil)
    65  	c.Check(conn, gc.NotNil)
    66  }
    67  
    68  func (s *RetryStrategySuite) TestOnlyConnectEventualError(c *gc.C) {
    69  	conn, err := checkWaitProvisionedError(c, apicaller.OnlyConnect)
    70  	c.Check(conn, gc.IsNil)
    71  	c.Check(err, gc.ErrorMatches, "splat pow")
    72  }
    73  
    74  func (s *RetryStrategySuite) TestScaryConnectEventualError(c *gc.C) {
    75  	conn, err := checkWaitProvisionedError(c, apicaller.ScaryConnect)
    76  	c.Check(conn, gc.IsNil)
    77  	c.Check(err, gc.ErrorMatches, "splat pow")
    78  }
    79  
    80  func checkWaitProvisionedError(c *gc.C, connect apicaller.ConnectFunc) (api.Connection, error) {
    81  	stub := &testing.Stub{}
    82  	stub.SetErrors(
    83  		errNotProvisioned,       // initial attempt, outside strategy
    84  		errNotProvisioned,       // first strategy attempt
    85  		errNotProvisioned,       // second strategy attempt
    86  		errors.New("splat pow"), // third strategy attempt
    87  	)
    88  	strategy := utils.AttemptStrategy{Min: 3}
    89  	conn, err := strategyTest(stub, strategy, func(apiOpen api.OpenFunc) (api.Connection, error) {
    90  		return connect(&mockAgent{stub: stub}, apiOpen)
    91  	})
    92  	checkOpenCalls(c, stub, "new", "new", "new", "new")
    93  	return conn, err
    94  }
    95  
    96  func (s *RetryStrategySuite) TestOnlyConnectNeverProvisioned(c *gc.C) {
    97  	conn, err := checkWaitNeverProvisioned(c, apicaller.OnlyConnect)
    98  	c.Check(conn, gc.IsNil)
    99  	c.Check(errors.Cause(err), gc.DeepEquals, errNotProvisioned)
   100  }
   101  
   102  func (s *RetryStrategySuite) TestScaryConnectNeverProvisioned(c *gc.C) {
   103  	conn, err := checkWaitNeverProvisioned(c, apicaller.ScaryConnect)
   104  	c.Check(conn, gc.IsNil)
   105  	c.Check(err, gc.Equals, apicaller.ErrConnectImpossible)
   106  }
   107  
   108  func checkWaitNeverProvisioned(c *gc.C, connect apicaller.ConnectFunc) (api.Connection, error) {
   109  	stub := &testing.Stub{}
   110  	stub.SetErrors(
   111  		errNotProvisioned, // initial attempt, outside strategy
   112  		errNotProvisioned, // first strategy attempt
   113  		errNotProvisioned, // second strategy attempt
   114  		errNotProvisioned, // third strategy attempt
   115  	)
   116  	strategy := utils.AttemptStrategy{Min: 3}
   117  	conn, err := strategyTest(stub, strategy, func(apiOpen api.OpenFunc) (api.Connection, error) {
   118  		return connect(&mockAgent{stub: stub}, apiOpen)
   119  	})
   120  	checkOpenCalls(c, stub, "new", "new", "new", "new")
   121  	return conn, err
   122  }