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, ¶ms.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, ¶ms.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 }