github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/uniter/relation/statemanager_test.go (about)

     1  // Copyright 2020 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package relation_test
     5  
     6  import (
     7  	"github.com/juju/collections/set"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/loggo"
    10  	"github.com/juju/names/v5"
    11  	jc "github.com/juju/testing/checkers"
    12  	"go.uber.org/mock/gomock"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/yaml.v2"
    15  
    16  	"github.com/juju/juju/rpc/params"
    17  	"github.com/juju/juju/worker/uniter/operation/mocks"
    18  	"github.com/juju/juju/worker/uniter/relation"
    19  	relmocks "github.com/juju/juju/worker/uniter/relation/mocks"
    20  )
    21  
    22  type stateManagerSuite struct {
    23  	mockUnitRW     *mocks.MockUnitStateReadWriter
    24  	mockUnitGetter *relmocks.MockUnitGetter
    25  }
    26  
    27  func (s *stateManagerSuite) TestNewStateManagerHasState(c *gc.C) {
    28  	defer s.setupMocks(c).Finish()
    29  	states := s.setupFourStates(c)
    30  
    31  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
    32  	c.Assert(err, jc.ErrorIsNil)
    33  	for _, st := range states {
    34  		v, err := mgr.Relation(st.RelationId)
    35  		c.Assert(err, jc.ErrorIsNil)
    36  		c.Assert(*v, gc.DeepEquals, st)
    37  	}
    38  }
    39  
    40  func (s *stateManagerSuite) TestNewStateManagerNoState(c *gc.C) {
    41  	defer s.setupMocks(c).Finish()
    42  	s.expectStateEmpty()
    43  
    44  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	c.Assert(mgr.KnownIDs(), gc.HasLen, 0)
    47  }
    48  
    49  func (s *stateManagerSuite) TestNewStateManagerErr(c *gc.C) {
    50  	defer s.setupMocks(c).Finish()
    51  	s.expectStateEmptyError()
    52  
    53  	_, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
    54  	c.Assert(err, jc.Satisfies, errors.IsBadRequest)
    55  }
    56  
    57  func (s *stateManagerSuite) TestKnownIds(c *gc.C) {
    58  	defer s.setupMocks(c).Finish()
    59  	states := s.setupFourStates(c)
    60  
    61  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	ids := mgr.KnownIDs()
    64  	intSet := set.NewInts(ids...)
    65  	c.Assert(intSet.Size(), gc.Equals, 4, gc.Commentf("obtained %v", intSet.Values()))
    66  	for _, exp := range states {
    67  		c.Assert(intSet.Contains(exp.RelationId), jc.IsTrue)
    68  	}
    69  }
    70  
    71  func (s *stateManagerSuite) TestRelation(c *gc.C) {
    72  	defer s.setupMocks(c).Finish()
    73  	states := s.setupFourStates(c)
    74  
    75  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
    76  	c.Assert(err, jc.ErrorIsNil)
    77  	st, err := mgr.Relation(states[1].RelationId)
    78  	c.Assert(err, jc.ErrorIsNil)
    79  	c.Assert(*st, gc.DeepEquals, states[1])
    80  }
    81  
    82  func (s *stateManagerSuite) TestRelationNotFound(c *gc.C) {
    83  	defer s.setupMocks(c).Finish()
    84  	_ = s.setupFourStates(c)
    85  
    86  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
    87  	c.Assert(err, jc.ErrorIsNil)
    88  	_, err = mgr.Relation(42)
    89  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    90  }
    91  
    92  func (s *stateManagerSuite) TestSetNew(c *gc.C) {
    93  	defer s.setupMocks(c).Finish()
    94  	s.expectStateEmpty()
    95  	st2 := &relation.State{RelationId: 456}
    96  	st2.Members = map[string]int64{
    97  		"bar/0": 3,
    98  		"bar/1": 4,
    99  	}
   100  	s.expectSetState(c, *st2)
   101  
   102  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   103  	c.Assert(err, jc.ErrorIsNil)
   104  	err = mgr.SetRelation(st2)
   105  	c.Assert(err, jc.ErrorIsNil)
   106  	found := mgr.RelationFound(456)
   107  	c.Assert(found, jc.IsTrue)
   108  }
   109  
   110  func (s *stateManagerSuite) TestSetChangeExisting(c *gc.C) {
   111  	defer s.setupMocks(c).Finish()
   112  	states := s.setupFourStates(c)
   113  
   114  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   115  	c.Assert(err, jc.ErrorIsNil)
   116  
   117  	states[3].ChangedPending = "foo/1"
   118  	s.expectSetState(c, states...)
   119  	st := states[3]
   120  
   121  	err = mgr.SetRelation(&st)
   122  	c.Assert(err, jc.ErrorIsNil)
   123  
   124  	obtained, err := mgr.Relation(st.RelationId)
   125  	c.Assert(err, jc.ErrorIsNil)
   126  	c.Assert(*obtained, gc.DeepEquals, st)
   127  }
   128  
   129  func (s *stateManagerSuite) TestSetChangeExistingFail(c *gc.C) {
   130  	defer s.setupMocks(c).Finish()
   131  	states := s.setupFourStates(c)
   132  	s.expectSetStateError()
   133  
   134  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   135  	c.Assert(err, jc.ErrorIsNil)
   136  
   137  	st := states[3]
   138  	st.ChangedPending = "foo/1"
   139  	err = mgr.SetRelation(&st)
   140  	c.Assert(err, jc.Satisfies, errors.IsBadRequest)
   141  
   142  	obtained, err := mgr.Relation(st.RelationId)
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	c.Assert(*obtained, gc.DeepEquals, states[3])
   145  }
   146  
   147  func (s *stateManagerSuite) TestRemove(c *gc.C) {
   148  	defer s.setupMocks(c).Finish()
   149  	state := relation.State{RelationId: 1}
   150  	s.expectState(c, state)
   151  	s.expectSetStateEmpty(c)
   152  
   153  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	err = mgr.RemoveRelation(1, s.mockUnitGetter, map[string]bool{})
   156  	c.Assert(err, jc.ErrorIsNil)
   157  }
   158  
   159  func (s *stateManagerSuite) TestRemoveNotFound(c *gc.C) {
   160  	defer s.setupMocks(c).Finish()
   161  	stateTwo := relation.State{RelationId: 99}
   162  	stateTwo.Members = map[string]int64{"foo/1": 0}
   163  	s.expectState(c, stateTwo)
   164  
   165  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	err = mgr.RemoveRelation(1, s.mockUnitGetter, map[string]bool{})
   168  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   169  }
   170  
   171  func (s *stateManagerSuite) TestRemoveFailHasMembers(c *gc.C) {
   172  	defer s.setupMocks(c).Finish()
   173  	stateTwo := relation.State{RelationId: 99}
   174  	stateTwo.Members = map[string]int64{"foo/1": 0}
   175  	s.expectState(c, stateTwo)
   176  	s.mockUnitGetter.EXPECT().Unit(names.NewUnitTag("foo/1")).Return(nil, nil)
   177  
   178  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	err = mgr.RemoveRelation(99, s.mockUnitGetter, map[string]bool{})
   181  	c.Assert(err, gc.ErrorMatches, `*has members: \[foo/1\]`)
   182  }
   183  
   184  func (s *stateManagerSuite) TestRemoveIgnoresMissingUnits(c *gc.C) {
   185  	defer s.setupMocks(c).Finish()
   186  	stateTwo := relation.State{RelationId: 99}
   187  	stateTwo.Members = map[string]int64{"foo/1": 0}
   188  	s.expectState(c, stateTwo)
   189  	s.expectSetStateEmpty(c)
   190  	s.mockUnitGetter.EXPECT().Unit(names.NewUnitTag("foo/1")).Return(nil, &params.Error{Code: "not found"})
   191  
   192  	logger := loggo.GetLogger("test")
   193  	var tw loggo.TestWriter
   194  	c.Assert(loggo.RegisterWriter("relations-tester", &tw), gc.IsNil)
   195  
   196  	mgr, err := relation.NewStateManager(s.mockUnitRW, logger)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	err = mgr.RemoveRelation(99, s.mockUnitGetter, map[string]bool{})
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	c.Assert(tw.Log(), jc.LogMatches, jc.SimpleMessages{{
   201  		loggo.WARNING,
   202  		`unit foo/1 in relation 99 no longer exists`},
   203  	})
   204  }
   205  
   206  func (s *stateManagerSuite) TestRemoveCachesUnits(c *gc.C) {
   207  	defer s.setupMocks(c).Finish()
   208  	stateTwo := relation.State{RelationId: 99}
   209  	stateTwo.Members = map[string]int64{"foo/1": 0}
   210  	stateThree := relation.State{RelationId: 100}
   211  	stateThree.Members = map[string]int64{"foo/1": 0}
   212  	s.expectState(c, stateTwo, stateThree)
   213  	s.expectSetState(c, stateThree)
   214  	s.mockUnitGetter.EXPECT().Unit(names.NewUnitTag("foo/1")).Return(nil, &params.Error{Code: "not found"})
   215  
   216  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   217  	c.Assert(err, jc.ErrorIsNil)
   218  	knownUnits := make(map[string]bool)
   219  	err = mgr.RemoveRelation(99, s.mockUnitGetter, knownUnits)
   220  	c.Assert(err, jc.ErrorIsNil)
   221  	c.Assert(knownUnits, jc.DeepEquals, map[string]bool{"foo/1": false})
   222  
   223  	s.expectSetStateEmpty(c)
   224  	err = mgr.RemoveRelation(100, s.mockUnitGetter, knownUnits)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  }
   227  
   228  func (s *stateManagerSuite) TestRemoveFailRequest(c *gc.C) {
   229  	defer s.setupMocks(c).Finish()
   230  	stateTwo := relation.State{RelationId: 99}
   231  	s.expectState(c, stateTwo)
   232  	s.expectSetStateError()
   233  
   234  	mgr, err := relation.NewStateManager(s.mockUnitRW, loggo.GetLogger("test"))
   235  	c.Assert(err, jc.ErrorIsNil)
   236  	err = mgr.RemoveRelation(99, s.mockUnitGetter, map[string]bool{})
   237  	c.Assert(err, jc.Satisfies, errors.IsBadRequest)
   238  	found := mgr.RelationFound(99)
   239  	c.Assert(found, jc.IsTrue)
   240  }
   241  
   242  var _ = gc.Suite(&stateManagerSuite{})
   243  
   244  func (s *stateManagerSuite) setupMocks(c *gc.C) *gomock.Controller {
   245  	ctlr := gomock.NewController(c)
   246  	s.mockUnitRW = mocks.NewMockUnitStateReadWriter(ctlr)
   247  	s.mockUnitGetter = relmocks.NewMockUnitGetter(ctlr)
   248  	return ctlr
   249  }
   250  
   251  func (s *stateManagerSuite) setupFourStates(c *gc.C) []relation.State {
   252  	st1 := relation.NewState(123)
   253  	st1.Members = map[string]int64{
   254  		"foo/0": 1,
   255  		"foo/1": 2,
   256  	}
   257  	st1.ChangedPending = "foo/1"
   258  	st2 := relation.NewState(456)
   259  	st2.Members = map[string]int64{
   260  		"bar/0": 3,
   261  		"bar/1": 4,
   262  	}
   263  	st3 := relation.NewState(789)
   264  	st4 := relation.NewState(10)
   265  	st4.ApplicationMembers = map[string]int64{
   266  		"baz-app": 2,
   267  	}
   268  	states := []relation.State{*st1, *st2, *st3, *st4}
   269  	s.expectState(c, states...)
   270  	return states
   271  }
   272  
   273  func (s *stateManagerSuite) expectStateEmpty() {
   274  	exp := s.mockUnitRW.EXPECT()
   275  	exp.State().Return(params.UnitStateResult{}, nil)
   276  }
   277  
   278  func (s *stateManagerSuite) expectStateEmptyError() {
   279  	exp := s.mockUnitRW.EXPECT()
   280  	exp.State().Return(params.UnitStateResult{}, errors.BadRequestf("testing"))
   281  }
   282  
   283  func (s *stateManagerSuite) expectSetState(c *gc.C, states ...relation.State) {
   284  	expectedStates := make(map[int]string, len(states))
   285  	for _, s := range states {
   286  		str, err := s.YamlString()
   287  		c.Assert(err, jc.ErrorIsNil)
   288  		expectedStates[s.RelationId] = str
   289  	}
   290  	exp := s.mockUnitRW.EXPECT()
   291  	exp.SetState(unitStateMatcher{c: c, expected: expectedStates}).Return(nil)
   292  }
   293  
   294  func (s *stateManagerSuite) expectSetStateEmpty(c *gc.C) {
   295  	exp := s.mockUnitRW.EXPECT()
   296  	exp.SetState(unitStateMatcher{c: c, expected: map[int]string{}}).Return(nil)
   297  }
   298  
   299  func (s *stateManagerSuite) expectSetStateError() {
   300  	exp := s.mockUnitRW.EXPECT()
   301  	exp.SetState(gomock.Any()).Return(errors.BadRequestf("testing"))
   302  }
   303  
   304  func (s *stateManagerSuite) expectState(c *gc.C, states ...relation.State) {
   305  	relationMap := make(map[int]string, len(states))
   306  	for _, state := range states {
   307  		data, err := yaml.Marshal(state)
   308  		c.Assert(err, jc.ErrorIsNil)
   309  		strState := string(data)
   310  		relationMap[state.RelationId] = strState
   311  	}
   312  	exp := s.mockUnitRW.EXPECT()
   313  	exp.State().Return(params.UnitStateResult{
   314  		RelationState: relationMap,
   315  	}, nil)
   316  }
   317  
   318  type unitStateMatcher struct {
   319  	c        *gc.C
   320  	expected map[int]string
   321  }
   322  
   323  func (m unitStateMatcher) Matches(x interface{}) bool {
   324  	obtained, ok := x.(params.SetUnitStateArg)
   325  	if !ok {
   326  		return false
   327  	}
   328  
   329  	m.c.Assert(*obtained.RelationState, gc.DeepEquals, m.expected)
   330  
   331  	return true
   332  }
   333  
   334  func (m unitStateMatcher) String() string {
   335  	return "Match the contents of the RelationState pointer in params.SetUnitStateArg"
   336  }