github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/crossmodelrelations/crossmodelrelations_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package crossmodelrelations_test
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  
    10  	"github.com/juju/clock"
    11  	"github.com/juju/errors"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/macaroon.v2-unstable"
    15  
    16  	"github.com/juju/juju/api/base/testing"
    17  	"github.com/juju/juju/api/crossmodelrelations"
    18  	apitesting "github.com/juju/juju/api/testing"
    19  	"github.com/juju/juju/apiserver/params"
    20  	coretesting "github.com/juju/juju/testing"
    21  )
    22  
    23  var _ = gc.Suite(&CrossModelRelationsSuite{})
    24  
    25  type CrossModelRelationsSuite struct {
    26  	coretesting.BaseSuite
    27  
    28  	cache *crossmodelrelations.MacaroonCache
    29  }
    30  
    31  func (s *CrossModelRelationsSuite) SetUpTest(c *gc.C) {
    32  	s.cache = crossmodelrelations.NewMacaroonCache(clock.WallClock)
    33  }
    34  
    35  func (s *CrossModelRelationsSuite) TestNewClient(c *gc.C) {
    36  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
    37  		return nil
    38  	})
    39  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
    40  	c.Assert(client, gc.NotNil)
    41  }
    42  
    43  type mockDischargeAcquirer struct{}
    44  
    45  func (m *mockDischargeAcquirer) AcquireDischarge(cav macaroon.Caveat) (*macaroon.Macaroon, error) {
    46  	if !bytes.Equal(cav.Id, []byte("third party caveat")) {
    47  		return nil, errors.New("permission denied")
    48  	}
    49  	return apitesting.NewMacaroon("discharge mac")
    50  }
    51  
    52  func (s *CrossModelRelationsSuite) newDischargeMacaroon(c *gc.C) *macaroon.Macaroon {
    53  	mac, err := apitesting.NewMacaroon("id")
    54  	c.Assert(err, jc.ErrorIsNil)
    55  	err = mac.AddThirdPartyCaveat(nil, []byte("third party caveat"), "third party location")
    56  	c.Assert(err, jc.ErrorIsNil)
    57  	return mac
    58  }
    59  
    60  func (s *CrossModelRelationsSuite) fillResponse(c *gc.C, resp interface{}, value interface{}) {
    61  	b, err := json.Marshal(value)
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	err = json.Unmarshal(b, resp)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  }
    66  
    67  func (s *CrossModelRelationsSuite) TestPublishRelationChange(c *gc.C) {
    68  	var callCount int
    69  	mac, err := apitesting.NewMacaroon("id")
    70  	c.Assert(err, jc.ErrorIsNil)
    71  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
    72  		c.Check(objType, gc.Equals, "CrossModelRelations")
    73  		c.Check(version, gc.Equals, 0)
    74  		c.Check(id, gc.Equals, "")
    75  		c.Check(request, gc.Equals, "PublishRelationChanges")
    76  		c.Check(arg, gc.DeepEquals, params.RemoteRelationsChanges{
    77  			Changes: []params.RemoteRelationChangeEvent{{
    78  				RelationToken: "token",
    79  				DepartedUnits: []int{1}, Macaroons: macaroon.Slice{mac}}},
    80  		})
    81  		c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
    82  		*(result.(*params.ErrorResults)) = params.ErrorResults{
    83  			Results: []params.ErrorResult{{
    84  				Error: &params.Error{Message: "FAIL"},
    85  			}},
    86  		}
    87  		callCount++
    88  		return nil
    89  	})
    90  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
    91  	err = client.PublishRelationChange(params.RemoteRelationChangeEvent{
    92  		RelationToken: "token",
    93  		DepartedUnits: []int{1},
    94  		Macaroons:     macaroon.Slice{mac},
    95  	})
    96  	c.Check(err, gc.ErrorMatches, "FAIL")
    97  	// Call again with a different macaroon but the first one will be
    98  	// cached and override the passed in macaroon.
    99  	different, err := apitesting.NewMacaroon("different")
   100  	c.Assert(err, jc.ErrorIsNil)
   101  	s.cache.Upsert("token", macaroon.Slice{mac})
   102  	err = client.PublishRelationChange(params.RemoteRelationChangeEvent{
   103  		RelationToken: "token",
   104  		DepartedUnits: []int{1},
   105  		Macaroons:     macaroon.Slice{different},
   106  	})
   107  	c.Check(err, gc.ErrorMatches, "FAIL")
   108  	c.Check(callCount, gc.Equals, 2)
   109  }
   110  
   111  func (s *CrossModelRelationsSuite) TestPublishRelationChangeDischargeRequired(c *gc.C) {
   112  	var (
   113  		callCount    int
   114  		mac          *macaroon.Macaroon
   115  		dischargeMac macaroon.Slice
   116  	)
   117  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   118  		var resultErr *params.Error
   119  		if callCount == 0 {
   120  			mac = s.newDischargeMacaroon(c)
   121  			resultErr = &params.Error{
   122  				Code: params.CodeDischargeRequired,
   123  				Info: &params.ErrorInfo{
   124  					Macaroon: mac,
   125  				},
   126  			}
   127  		}
   128  		argParam := arg.(params.RemoteRelationsChanges)
   129  		dischargeMac = argParam.Changes[0].Macaroons
   130  		resp := params.ErrorResults{
   131  			Results: []params.ErrorResult{{Error: resultErr}},
   132  		}
   133  		s.fillResponse(c, result, resp)
   134  		callCount++
   135  		return nil
   136  	})
   137  	acquirer := &mockDischargeAcquirer{}
   138  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   139  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   140  	err := client.PublishRelationChange(params.RemoteRelationChangeEvent{
   141  		RelationToken: "token",
   142  		DepartedUnits: []int{1},
   143  	})
   144  	c.Check(callCount, gc.Equals, 2)
   145  	c.Check(err, jc.ErrorIsNil)
   146  	c.Check(dischargeMac, gc.HasLen, 2)
   147  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   148  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   149  	// Macaroon has been cached.
   150  	ms, ok := s.cache.Get("token")
   151  	c.Assert(ok, jc.IsTrue)
   152  	apitesting.MacaroonEquals(c, ms[0], mac)
   153  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   154  }
   155  
   156  func (s *CrossModelRelationsSuite) TestRegisterRemoteRelations(c *gc.C) {
   157  	var callCount int
   158  	mac, err := apitesting.NewMacaroon("id")
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   161  		c.Check(objType, gc.Equals, "CrossModelRelations")
   162  		c.Check(version, gc.Equals, 0)
   163  		c.Check(id, gc.Equals, "")
   164  		c.Check(request, gc.Equals, "RegisterRemoteRelations")
   165  		c.Check(arg, gc.DeepEquals, params.RegisterRemoteRelationArgs{
   166  			Relations: []params.RegisterRemoteRelationArg{{
   167  				RelationToken: "token",
   168  				OfferUUID:     "offer-uuid",
   169  				Macaroons:     macaroon.Slice{mac}}}})
   170  		c.Assert(result, gc.FitsTypeOf, &params.RegisterRemoteRelationResults{})
   171  		*(result.(*params.RegisterRemoteRelationResults)) = params.RegisterRemoteRelationResults{
   172  			Results: []params.RegisterRemoteRelationResult{{
   173  				Error: &params.Error{Message: "FAIL"},
   174  			}},
   175  		}
   176  		callCount++
   177  		return nil
   178  	})
   179  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   180  	result, err := client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{
   181  		RelationToken: "token",
   182  		OfferUUID:     "offer-uuid",
   183  		Macaroons:     macaroon.Slice{mac},
   184  	})
   185  	c.Check(err, jc.ErrorIsNil)
   186  	c.Assert(result, gc.HasLen, 1)
   187  	c.Check(result[0].Error, gc.ErrorMatches, "FAIL")
   188  	// Call again with a different macaroon but the first one will be
   189  	// cached and override the passed in macaroon.
   190  	different, err := apitesting.NewMacaroon("different")
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	s.cache.Upsert("token", macaroon.Slice{mac})
   193  	result, err = client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{
   194  		RelationToken: "token",
   195  		OfferUUID:     "offer-uuid",
   196  		Macaroons:     macaroon.Slice{different},
   197  	})
   198  	c.Check(err, jc.ErrorIsNil)
   199  	c.Assert(result, gc.HasLen, 1)
   200  	c.Check(result[0].Error, gc.ErrorMatches, "FAIL")
   201  	c.Check(callCount, gc.Equals, 2)
   202  }
   203  
   204  func (s *CrossModelRelationsSuite) TestRegisterRemoteRelationCount(c *gc.C) {
   205  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   206  		*(result.(*params.RegisterRemoteRelationResults)) = params.RegisterRemoteRelationResults{
   207  			Results: []params.RegisterRemoteRelationResult{
   208  				{Error: &params.Error{Message: "FAIL"}},
   209  				{Error: &params.Error{Message: "FAIL"}},
   210  			},
   211  		}
   212  		return nil
   213  	})
   214  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   215  	_, err := client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{})
   216  	c.Check(err, gc.ErrorMatches, `expected 1 result\(s\), got 2`)
   217  }
   218  
   219  func (s *CrossModelRelationsSuite) TestRegisterRemoteRelationDischargeRequired(c *gc.C) {
   220  	var (
   221  		callCount    int
   222  		mac          *macaroon.Macaroon
   223  		dischargeMac macaroon.Slice
   224  	)
   225  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   226  		var resultErr *params.Error
   227  		if callCount == 0 {
   228  			mac = s.newDischargeMacaroon(c)
   229  			resultErr = &params.Error{
   230  				Code: params.CodeDischargeRequired,
   231  				Info: &params.ErrorInfo{
   232  					Macaroon: mac,
   233  				},
   234  			}
   235  		}
   236  		argParam := arg.(params.RegisterRemoteRelationArgs)
   237  		dischargeMac = argParam.Relations[0].Macaroons
   238  		resp := params.RegisterRemoteRelationResults{
   239  			Results: []params.RegisterRemoteRelationResult{{Error: resultErr}},
   240  		}
   241  		s.fillResponse(c, result, resp)
   242  		callCount++
   243  		return nil
   244  	})
   245  	acquirer := &mockDischargeAcquirer{}
   246  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   247  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   248  	result, err := client.RegisterRemoteRelations(params.RegisterRemoteRelationArg{
   249  		RelationToken: "token",
   250  		OfferUUID:     "offer-uuid"})
   251  	c.Check(err, jc.ErrorIsNil)
   252  	c.Check(callCount, gc.Equals, 2)
   253  	c.Assert(result, gc.HasLen, 1)
   254  	c.Check(result[0].Error, gc.IsNil)
   255  	c.Check(dischargeMac, gc.HasLen, 2)
   256  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   257  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   258  	// Macaroon has been cached.
   259  	ms, ok := s.cache.Get("token")
   260  	c.Assert(ok, jc.IsTrue)
   261  	apitesting.MacaroonEquals(c, ms[0], mac)
   262  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   263  }
   264  
   265  func (s *CrossModelRelationsSuite) TestWatchRelationUnits(c *gc.C) {
   266  	remoteRelationToken := "token"
   267  	mac, err := apitesting.NewMacaroon("id")
   268  	c.Assert(err, jc.ErrorIsNil)
   269  	var callCount int
   270  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   271  		c.Check(objType, gc.Equals, "CrossModelRelations")
   272  		c.Check(version, gc.Equals, 0)
   273  		c.Check(id, gc.Equals, "")
   274  		c.Check(arg, jc.DeepEquals, params.RemoteEntityArgs{Args: []params.RemoteEntityArg{{
   275  			Token: remoteRelationToken, Macaroons: macaroon.Slice{mac}}}})
   276  		c.Check(request, gc.Equals, "WatchRelationUnits")
   277  		c.Assert(result, gc.FitsTypeOf, &params.RelationUnitsWatchResults{})
   278  		*(result.(*params.RelationUnitsWatchResults)) = params.RelationUnitsWatchResults{
   279  			Results: []params.RelationUnitsWatchResult{{
   280  				Error: &params.Error{Message: "FAIL"},
   281  			}},
   282  		}
   283  		callCount++
   284  		return nil
   285  	})
   286  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   287  	_, err = client.WatchRelationUnits(params.RemoteEntityArg{
   288  		Token:     remoteRelationToken,
   289  		Macaroons: macaroon.Slice{mac},
   290  	})
   291  	c.Check(err, gc.ErrorMatches, "FAIL")
   292  	// Call again with a different macaroon but the first one will be
   293  	// cached and override the passed in macaroon.
   294  	different, err := apitesting.NewMacaroon("different")
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	s.cache.Upsert("token", macaroon.Slice{mac})
   297  	_, err = client.WatchRelationUnits(params.RemoteEntityArg{
   298  		Token:     remoteRelationToken,
   299  		Macaroons: macaroon.Slice{different},
   300  	})
   301  	c.Check(err, gc.ErrorMatches, "FAIL")
   302  	c.Check(callCount, gc.Equals, 2)
   303  }
   304  
   305  func (s *CrossModelRelationsSuite) TestWatchRelationUnitsDischargeRequired(c *gc.C) {
   306  	var (
   307  		callCount    int
   308  		mac          *macaroon.Macaroon
   309  		dischargeMac macaroon.Slice
   310  	)
   311  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   312  		var resultErr *params.Error
   313  		switch callCount {
   314  		case 2, 3: //Watcher Next, Stop
   315  			return nil
   316  		case 0:
   317  			mac = s.newDischargeMacaroon(c)
   318  			resultErr = &params.Error{
   319  				Code: params.CodeDischargeRequired,
   320  				Info: &params.ErrorInfo{
   321  					Macaroon: mac,
   322  				},
   323  			}
   324  		case 1:
   325  			argParam := arg.(params.RemoteEntityArgs)
   326  			dischargeMac = argParam.Args[0].Macaroons
   327  		}
   328  		resp := params.RelationUnitsWatchResults{
   329  			Results: []params.RelationUnitsWatchResult{{Error: resultErr}},
   330  		}
   331  		s.fillResponse(c, result, resp)
   332  		callCount++
   333  		return nil
   334  	})
   335  	acquirer := &mockDischargeAcquirer{}
   336  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   337  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   338  	_, err := client.WatchRelationUnits(params.RemoteEntityArg{Token: "token"})
   339  	c.Check(callCount, gc.Equals, 2)
   340  	c.Check(err, jc.ErrorIsNil)
   341  	c.Assert(dischargeMac, gc.HasLen, 2)
   342  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   343  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   344  	// Macaroon has been cached.
   345  	ms, ok := s.cache.Get("token")
   346  	c.Assert(ok, jc.IsTrue)
   347  	apitesting.MacaroonEquals(c, ms[0], mac)
   348  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   349  }
   350  
   351  func (s *CrossModelRelationsSuite) TestRelationUnitSettings(c *gc.C) {
   352  	remoteRelationToken := "token"
   353  	mac, err := apitesting.NewMacaroon("id")
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	var callCount int
   356  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   357  		c.Check(objType, gc.Equals, "CrossModelRelations")
   358  		c.Check(version, gc.Equals, 0)
   359  		c.Check(id, gc.Equals, "")
   360  		c.Check(request, gc.Equals, "RelationUnitSettings")
   361  		c.Check(arg, gc.DeepEquals, params.RemoteRelationUnits{
   362  			RelationUnits: []params.RemoteRelationUnit{{
   363  				RelationToken: remoteRelationToken, Unit: "u", Macaroons: macaroon.Slice{mac}}}})
   364  		c.Assert(result, gc.FitsTypeOf, &params.SettingsResults{})
   365  		*(result.(*params.SettingsResults)) = params.SettingsResults{
   366  			Results: []params.SettingsResult{{
   367  				Error: &params.Error{Message: "FAIL"},
   368  			}},
   369  		}
   370  		callCount++
   371  		return nil
   372  	})
   373  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   374  	result, err := client.RelationUnitSettings([]params.RemoteRelationUnit{
   375  		{RelationToken: remoteRelationToken, Unit: "u", Macaroons: macaroon.Slice{mac}}})
   376  	c.Check(err, jc.ErrorIsNil)
   377  	c.Assert(result, gc.HasLen, 1)
   378  	c.Check(result[0].Error, gc.ErrorMatches, "FAIL")
   379  	// Call again with a different macaroon but the first one will be
   380  	// cached and override the passed in macaroon.
   381  	different, err := apitesting.NewMacaroon("id")
   382  	c.Assert(err, jc.ErrorIsNil)
   383  	s.cache.Upsert("token", macaroon.Slice{mac})
   384  	result, err = client.RelationUnitSettings([]params.RemoteRelationUnit{
   385  		{RelationToken: remoteRelationToken, Unit: "u", Macaroons: macaroon.Slice{different}}})
   386  	c.Check(err, jc.ErrorIsNil)
   387  	c.Assert(result, gc.HasLen, 1)
   388  	c.Check(result[0].Error, gc.ErrorMatches, "FAIL")
   389  	c.Check(callCount, gc.Equals, 2)
   390  }
   391  
   392  func (s *CrossModelRelationsSuite) TestRelationUnitSettingsDischargeRequired(c *gc.C) {
   393  	var (
   394  		callCount    int
   395  		mac          *macaroon.Macaroon
   396  		dischargeMac macaroon.Slice
   397  	)
   398  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   399  		var resultErr *params.Error
   400  		if callCount == 0 {
   401  			mac = s.newDischargeMacaroon(c)
   402  			resultErr = &params.Error{
   403  				Code: params.CodeDischargeRequired,
   404  				Info: &params.ErrorInfo{
   405  					Macaroon: mac,
   406  				},
   407  			}
   408  		}
   409  		argParam := arg.(params.RemoteRelationUnits)
   410  		dischargeMac = argParam.RelationUnits[0].Macaroons
   411  		resp := params.SettingsResults{
   412  			Results: []params.SettingsResult{{Error: resultErr}},
   413  		}
   414  		s.fillResponse(c, result, resp)
   415  		callCount++
   416  		return nil
   417  	})
   418  	acquirer := &mockDischargeAcquirer{}
   419  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   420  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   421  	result, err := client.RelationUnitSettings([]params.RemoteRelationUnit{
   422  		{RelationToken: "token", Unit: "u"}})
   423  	c.Check(err, jc.ErrorIsNil)
   424  	c.Check(callCount, gc.Equals, 2)
   425  	c.Assert(result, gc.HasLen, 1)
   426  	c.Check(result[0].Error, gc.IsNil)
   427  	c.Check(dischargeMac, gc.HasLen, 2)
   428  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   429  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   430  	// Macaroon has been cached.
   431  	ms, ok := s.cache.Get("token")
   432  	c.Assert(ok, jc.IsTrue)
   433  	apitesting.MacaroonEquals(c, ms[0], mac)
   434  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   435  }
   436  
   437  func (s *CrossModelRelationsSuite) TestWatchRelationStatus(c *gc.C) {
   438  	remoteRelationToken := "token"
   439  	mac, err := apitesting.NewMacaroon("id")
   440  	c.Assert(err, jc.ErrorIsNil)
   441  	var callCount int
   442  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   443  		c.Check(objType, gc.Equals, "CrossModelRelations")
   444  		c.Check(version, gc.Equals, 0)
   445  		c.Check(id, gc.Equals, "")
   446  		c.Check(arg, jc.DeepEquals, params.RemoteEntityArgs{Args: []params.RemoteEntityArg{{
   447  			Token: remoteRelationToken, Macaroons: macaroon.Slice{mac}}}})
   448  		c.Check(request, gc.Equals, "WatchRelationsSuspendedStatus")
   449  		c.Assert(result, gc.FitsTypeOf, &params.RelationStatusWatchResults{})
   450  		*(result.(*params.RelationStatusWatchResults)) = params.RelationStatusWatchResults{
   451  			Results: []params.RelationLifeSuspendedStatusWatchResult{{
   452  				Error: &params.Error{Message: "FAIL"},
   453  			}},
   454  		}
   455  		callCount++
   456  		return nil
   457  	})
   458  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   459  	_, err = client.WatchRelationSuspendedStatus(params.RemoteEntityArg{
   460  		Token:     remoteRelationToken,
   461  		Macaroons: macaroon.Slice{mac},
   462  	})
   463  	c.Check(err, gc.ErrorMatches, "FAIL")
   464  	// Call again with a different macaroon but the first one will be
   465  	// cached and override the passed in macaroon.
   466  	different, err := apitesting.NewMacaroon("different")
   467  	c.Assert(err, jc.ErrorIsNil)
   468  	s.cache.Upsert("token", macaroon.Slice{mac})
   469  	_, err = client.WatchRelationSuspendedStatus(params.RemoteEntityArg{
   470  		Token:     remoteRelationToken,
   471  		Macaroons: macaroon.Slice{different},
   472  	})
   473  	c.Check(err, gc.ErrorMatches, "FAIL")
   474  	c.Check(callCount, gc.Equals, 2)
   475  }
   476  
   477  func (s *CrossModelRelationsSuite) TestWatchRelationStatusDischargeRequired(c *gc.C) {
   478  	var (
   479  		callCount    int
   480  		mac          *macaroon.Macaroon
   481  		dischargeMac macaroon.Slice
   482  	)
   483  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   484  		var resultErr *params.Error
   485  		switch callCount {
   486  		case 2, 3: //Watcher Next, Stop
   487  			return nil
   488  		case 0:
   489  			mac = s.newDischargeMacaroon(c)
   490  			resultErr = &params.Error{
   491  				Code: params.CodeDischargeRequired,
   492  				Info: &params.ErrorInfo{
   493  					Macaroon: mac,
   494  				},
   495  			}
   496  		case 1:
   497  			argParam := arg.(params.RemoteEntityArgs)
   498  			dischargeMac = argParam.Args[0].Macaroons
   499  		}
   500  		resp := params.RelationStatusWatchResults{
   501  			Results: []params.RelationLifeSuspendedStatusWatchResult{{Error: resultErr}},
   502  		}
   503  		s.fillResponse(c, result, resp)
   504  		callCount++
   505  		return nil
   506  	})
   507  	acquirer := &mockDischargeAcquirer{}
   508  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   509  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   510  	_, err := client.WatchRelationSuspendedStatus(params.RemoteEntityArg{Token: "token"})
   511  	c.Check(callCount, gc.Equals, 2)
   512  	c.Check(err, jc.ErrorIsNil)
   513  	c.Assert(dischargeMac, gc.HasLen, 2)
   514  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   515  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   516  	// Macaroon has been cached.
   517  	ms, ok := s.cache.Get("token")
   518  	c.Assert(ok, jc.IsTrue)
   519  	apitesting.MacaroonEquals(c, ms[0], mac)
   520  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   521  }
   522  
   523  func (s *CrossModelRelationsSuite) TestPublishIngressNetworkChange(c *gc.C) {
   524  	mac, err := apitesting.NewMacaroon("id")
   525  	c.Assert(err, jc.ErrorIsNil)
   526  	var callCount int
   527  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   528  		c.Check(objType, gc.Equals, "CrossModelRelations")
   529  		c.Check(version, gc.Equals, 0)
   530  		c.Check(id, gc.Equals, "")
   531  		c.Check(request, gc.Equals, "PublishIngressNetworkChanges")
   532  		c.Check(arg, gc.DeepEquals, params.IngressNetworksChanges{
   533  			Changes: []params.IngressNetworksChangeEvent{{
   534  				RelationToken: "token",
   535  				Networks:      []string{"1.2.3.4/32"}, Macaroons: macaroon.Slice{mac}}},
   536  		})
   537  		c.Assert(result, gc.FitsTypeOf, &params.ErrorResults{})
   538  		*(result.(*params.ErrorResults)) = params.ErrorResults{
   539  			Results: []params.ErrorResult{{
   540  				Error: &params.Error{Message: "FAIL"},
   541  			}},
   542  		}
   543  		callCount++
   544  		return nil
   545  	})
   546  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   547  	err = client.PublishIngressNetworkChange(params.IngressNetworksChangeEvent{
   548  		RelationToken: "token",
   549  		Networks:      []string{"1.2.3.4/32"}, Macaroons: macaroon.Slice{mac}})
   550  	c.Check(err, gc.ErrorMatches, "FAIL")
   551  	// Call again with a different macaroon but the first one will be
   552  	// cached and override the passed in macaroon.
   553  	different, err := apitesting.NewMacaroon("different")
   554  	c.Assert(err, jc.ErrorIsNil)
   555  	s.cache.Upsert("token", macaroon.Slice{mac})
   556  	err = client.PublishIngressNetworkChange(params.IngressNetworksChangeEvent{
   557  		RelationToken: "token",
   558  		Networks:      []string{"1.2.3.4/32"}, Macaroons: macaroon.Slice{different}})
   559  	c.Check(err, gc.ErrorMatches, "FAIL")
   560  	c.Check(callCount, gc.Equals, 2)
   561  }
   562  
   563  func (s *CrossModelRelationsSuite) TestPublishIngressNetworkChangeDischargeRequired(c *gc.C) {
   564  	var (
   565  		callCount    int
   566  		mac          *macaroon.Macaroon
   567  		dischargeMac macaroon.Slice
   568  	)
   569  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   570  		var resultErr *params.Error
   571  		if callCount == 0 {
   572  			mac = s.newDischargeMacaroon(c)
   573  			resultErr = &params.Error{
   574  				Code: params.CodeDischargeRequired,
   575  				Info: &params.ErrorInfo{
   576  					Macaroon: mac,
   577  				},
   578  			}
   579  		}
   580  		argParam := arg.(params.IngressNetworksChanges)
   581  		dischargeMac = argParam.Changes[0].Macaroons
   582  		resp := params.ErrorResults{
   583  			Results: []params.ErrorResult{{Error: resultErr}},
   584  		}
   585  		s.fillResponse(c, result, resp)
   586  		callCount++
   587  		return nil
   588  	})
   589  	acquirer := &mockDischargeAcquirer{}
   590  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   591  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   592  	err := client.PublishIngressNetworkChange(params.IngressNetworksChangeEvent{
   593  		RelationToken: "token",
   594  		Networks:      []string{"1.2.3.4/32"}})
   595  	c.Check(callCount, gc.Equals, 2)
   596  	c.Check(err, jc.ErrorIsNil)
   597  	c.Check(dischargeMac, gc.HasLen, 2)
   598  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   599  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   600  	// Macaroon has been cached.
   601  	ms, ok := s.cache.Get("token")
   602  	c.Assert(ok, jc.IsTrue)
   603  	apitesting.MacaroonEquals(c, ms[0], mac)
   604  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   605  }
   606  
   607  func (s *CrossModelRelationsSuite) TestWatchEgressAddressesForRelation(c *gc.C) {
   608  	var callCount int
   609  	remoteRelationToken := "token"
   610  	mac, err := apitesting.NewMacaroon("id")
   611  	relation := params.RemoteEntityArg{
   612  		Token: remoteRelationToken, Macaroons: macaroon.Slice{mac}}
   613  	c.Check(err, jc.ErrorIsNil)
   614  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   615  		c.Check(objType, gc.Equals, "CrossModelRelations")
   616  		c.Check(version, gc.Equals, 0)
   617  		c.Check(id, gc.Equals, "")
   618  		c.Check(request, gc.Equals, "WatchEgressAddressesForRelations")
   619  		c.Check(arg, gc.DeepEquals, params.RemoteEntityArgs{
   620  			Args: []params.RemoteEntityArg{relation}})
   621  		c.Assert(result, gc.FitsTypeOf, &params.StringsWatchResults{})
   622  		*(result.(*params.StringsWatchResults)) = params.StringsWatchResults{
   623  			Results: []params.StringsWatchResult{{
   624  				Error: &params.Error{Message: "FAIL"},
   625  			}},
   626  		}
   627  		callCount++
   628  		return nil
   629  	})
   630  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   631  	_, err = client.WatchEgressAddressesForRelation(relation)
   632  	c.Check(err, gc.ErrorMatches, "FAIL")
   633  	// Call again with a different macaroon but the first one will be
   634  	// cached and override the passed in macaroon.
   635  	different, err := apitesting.NewMacaroon("different")
   636  	c.Assert(err, jc.ErrorIsNil)
   637  	s.cache.Upsert("token", macaroon.Slice{mac})
   638  	rel2 := relation
   639  	rel2.Macaroons = macaroon.Slice{different}
   640  	_, err = client.WatchEgressAddressesForRelation(rel2)
   641  	c.Check(err, gc.ErrorMatches, "FAIL")
   642  	c.Check(callCount, gc.Equals, 2)
   643  }
   644  
   645  func (s *CrossModelRelationsSuite) TestWatchEgressAddressesForRelationDischargeRequired(c *gc.C) {
   646  	var (
   647  		callCount    int
   648  		mac          *macaroon.Macaroon
   649  		dischargeMac macaroon.Slice
   650  	)
   651  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   652  		var resultErr *params.Error
   653  		switch callCount {
   654  		case 2, 3: //Watcher Next, Stop
   655  			return nil
   656  		case 0:
   657  			mac = s.newDischargeMacaroon(c)
   658  			resultErr = &params.Error{
   659  				Code: params.CodeDischargeRequired,
   660  				Info: &params.ErrorInfo{
   661  					Macaroon: mac,
   662  				},
   663  			}
   664  		case 1:
   665  			argParam := arg.(params.RemoteEntityArgs)
   666  			dischargeMac = argParam.Args[0].Macaroons
   667  		}
   668  		resp := params.StringsWatchResults{
   669  			Results: []params.StringsWatchResult{{Error: resultErr}},
   670  		}
   671  		s.fillResponse(c, result, resp)
   672  		callCount++
   673  		return nil
   674  	})
   675  	acquirer := &mockDischargeAcquirer{}
   676  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   677  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   678  	_, err := client.WatchEgressAddressesForRelation(params.RemoteEntityArg{Token: "token"})
   679  	c.Check(callCount, gc.Equals, 2)
   680  	c.Check(err, jc.ErrorIsNil)
   681  	c.Assert(dischargeMac, gc.HasLen, 2)
   682  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   683  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   684  	// Macaroon has been cached.
   685  	ms, ok := s.cache.Get("token")
   686  	c.Assert(ok, jc.IsTrue)
   687  	apitesting.MacaroonEquals(c, ms[0], mac)
   688  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   689  }
   690  
   691  func (s *CrossModelRelationsSuite) TestWatchOfferStatus(c *gc.C) {
   692  	offerUUID := "offer-uuid"
   693  	mac, err := apitesting.NewMacaroon("id")
   694  	c.Assert(err, jc.ErrorIsNil)
   695  	var callCount int
   696  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   697  		c.Check(objType, gc.Equals, "CrossModelRelations")
   698  		c.Check(version, gc.Equals, 0)
   699  		c.Check(id, gc.Equals, "")
   700  		c.Check(arg, jc.DeepEquals, params.OfferArgs{Args: []params.OfferArg{{
   701  			OfferUUID: offerUUID, Macaroons: macaroon.Slice{mac}}}})
   702  		c.Check(request, gc.Equals, "WatchOfferStatus")
   703  		c.Assert(result, gc.FitsTypeOf, &params.OfferStatusWatchResults{})
   704  		*(result.(*params.OfferStatusWatchResults)) = params.OfferStatusWatchResults{
   705  			Results: []params.OfferStatusWatchResult{{
   706  				Error: &params.Error{Message: "FAIL"},
   707  			}},
   708  		}
   709  		callCount++
   710  		return nil
   711  	})
   712  	client := crossmodelrelations.NewClientWithCache(apiCaller, s.cache)
   713  	_, err = client.WatchOfferStatus(params.OfferArg{
   714  		OfferUUID: offerUUID,
   715  		Macaroons: macaroon.Slice{mac},
   716  	})
   717  	c.Check(err, gc.ErrorMatches, "FAIL")
   718  	// Call again with a different macaroon but the first one will be
   719  	// cached and override the passed in macaroon.
   720  	different, err := apitesting.NewMacaroon("different")
   721  	c.Assert(err, jc.ErrorIsNil)
   722  	s.cache.Upsert("offer-uuid", macaroon.Slice{mac})
   723  	_, err = client.WatchOfferStatus(params.OfferArg{
   724  		OfferUUID: offerUUID,
   725  		Macaroons: macaroon.Slice{different},
   726  	})
   727  	c.Check(err, gc.ErrorMatches, "FAIL")
   728  	c.Check(callCount, gc.Equals, 2)
   729  }
   730  
   731  func (s *CrossModelRelationsSuite) TestWatchOfferStatusDischargeRequired(c *gc.C) {
   732  	var (
   733  		callCount    int
   734  		mac          *macaroon.Macaroon
   735  		dischargeMac macaroon.Slice
   736  	)
   737  	apiCaller := testing.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
   738  		var resultErr *params.Error
   739  		switch callCount {
   740  		case 2, 3: //Watcher Next, Stop
   741  			return nil
   742  		case 0:
   743  			mac = s.newDischargeMacaroon(c)
   744  			resultErr = &params.Error{
   745  				Code: params.CodeDischargeRequired,
   746  				Info: &params.ErrorInfo{
   747  					Macaroon: mac,
   748  				},
   749  			}
   750  		case 1:
   751  			argParam := arg.(params.OfferArgs)
   752  			dischargeMac = argParam.Args[0].Macaroons
   753  		}
   754  		resp := params.OfferStatusWatchResults{
   755  			Results: []params.OfferStatusWatchResult{{Error: resultErr}},
   756  		}
   757  		s.fillResponse(c, result, resp)
   758  		callCount++
   759  		return nil
   760  	})
   761  	acquirer := &mockDischargeAcquirer{}
   762  	callerWithBakery := testing.APICallerWithBakery(apiCaller, acquirer)
   763  	client := crossmodelrelations.NewClientWithCache(callerWithBakery, s.cache)
   764  	_, err := client.WatchOfferStatus(params.OfferArg{OfferUUID: "offer-uuid"})
   765  	c.Check(callCount, gc.Equals, 2)
   766  	c.Check(err, jc.ErrorIsNil)
   767  	c.Assert(dischargeMac, gc.HasLen, 2)
   768  	apitesting.MacaroonEquals(c, dischargeMac[0], mac)
   769  	c.Assert(dischargeMac[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   770  	// Macaroon has been cached.
   771  	ms, ok := s.cache.Get("offer-uuid")
   772  	c.Assert(ok, jc.IsTrue)
   773  	apitesting.MacaroonEquals(c, ms[0], mac)
   774  	c.Assert(ms[1].Id(), jc.DeepEquals, []byte("discharge mac"))
   775  }