github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/uniter/relation/statetracker_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 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/juju/charm/v12" 12 "github.com/juju/charm/v12/hooks" 13 "github.com/juju/errors" 14 "github.com/juju/names/v5" 15 jc "github.com/juju/testing/checkers" 16 "go.uber.org/mock/gomock" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/api/agent/uniter" 20 "github.com/juju/juju/core/life" 21 corerelation "github.com/juju/juju/core/relation" 22 "github.com/juju/juju/core/watcher/watchertest" 23 coretesting "github.com/juju/juju/testing" 24 "github.com/juju/juju/worker/uniter/hook" 25 "github.com/juju/juju/worker/uniter/relation" 26 "github.com/juju/juju/worker/uniter/relation/mocks" 27 "github.com/juju/juju/worker/uniter/remotestate" 28 "github.com/juju/juju/worker/uniter/runner/context" 29 ) 30 31 type stateTrackerSuite struct { 32 baseStateTrackerSuite 33 } 34 35 type baseStateTrackerSuite struct { 36 leadershipContext context.LeadershipContext 37 unitTag names.UnitTag 38 unitChanges chan struct{} 39 40 state *mocks.MockStateTrackerState 41 unit *mocks.MockUnit 42 relation *mocks.MockRelation 43 relationer *mocks.MockRelationer 44 relationUnit *mocks.MockRelationUnit 45 stateMgr *mocks.MockStateManager 46 watcher *watchertest.MockNotifyWatcher 47 } 48 49 var _ = gc.Suite(&stateTrackerSuite{}) 50 51 func (s *stateTrackerSuite) SetUpTest(c *gc.C) { 52 s.leadershipContext = &stubLeadershipContext{isLeader: true} 53 s.unitTag, _ = names.ParseUnitTag("ntp/0") 54 } 55 56 func (s *stateTrackerSuite) TestLoadInitialStateNoRelations(c *gc.C) { 57 // Green field config, no known relations, no relation status. 58 defer s.setupMocks(c).Finish() 59 s.expectRelationsStatusEmpty() 60 s.expectStateMgrKnownIDs([]int{}) 61 62 r := s.newStateTracker(c) 63 //No relations created. 64 c.Assert(r.GetInfo(), gc.HasLen, 0) 65 } 66 67 func (s *stateTrackerSuite) TestLoadInitialState(c *gc.C) { 68 // The state manager knows about 2 relations, 1 & 2. 69 // Relation status returns 1 relation. 70 // Make sure we have 1 at the end and 2 has been deleted. 71 defer s.setupMocks(c).Finish() 72 relTag, _ := names.ParseRelationTag("ubuntu:juju-info ntp:juju-info") 73 status := []uniter.RelationStatus{{ 74 Tag: relTag, 75 InScope: true, 76 }} 77 s.expectRelationsStatus(status) 78 s.expectRelation(relTag) 79 s.expectRelationID(1) 80 s.expectStateMgrKnownIDs([]int{1, 2}) 81 s.expectStateMgrRemoveRelation(2) 82 s.expectStateMgrRelationFound(1) 83 s.expectRelationerJoin() 84 s.expectRelationSetStatusJoined() 85 s.expectUnitName() 86 s.expectUnitTag() 87 s.expectRelationUnit() 88 s.expectWatch(c) 89 90 r := s.newStateTracker(c) 91 92 c.Assert(r.RelationCreated(1), jc.IsTrue) 93 c.Assert(r.RelationCreated(2), jc.IsFalse) 94 } 95 96 func (s *stateTrackerSuite) TestLoadInitialStateSuspended(c *gc.C) { 97 // The state manager knows about 1 suspended relation. 98 // Relation status returns 1 relation. 99 // Remove known suspended out of scope relation. 100 defer s.setupMocks(c).Finish() 101 relTag, _ := names.ParseRelationTag("ubuntu:juju-info ntp:juju-info") 102 status := []uniter.RelationStatus{{ 103 Tag: relTag, 104 Suspended: true, 105 }} 106 s.expectRelationsStatus(status) 107 s.expectStateMgrKnownIDs([]int{1}) 108 s.expectStateMgrRemoveRelation(1) 109 110 r := s.newStateTracker(c) 111 112 c.Assert(r.RelationCreated(1), jc.IsFalse) 113 } 114 115 func (s *stateTrackerSuite) TestLoadInitialStateInScopeSuspended(c *gc.C) { 116 // The state manager knows about 1 in-scope suspended relation. 117 // Relation status returns 1 relation. 118 defer s.setupMocks(c).Finish() 119 relTag, _ := names.ParseRelationTag("ubuntu:juju-info ntp:juju-info") 120 status := []uniter.RelationStatus{{ 121 Tag: relTag, 122 InScope: true, 123 Suspended: true, 124 }} 125 s.expectRelationsStatus(status) 126 s.expectStateMgrKnownIDs([]int{1}) 127 s.expectStateMgrRelationFound(1) 128 s.expectRelation(relTag) 129 s.expectRelationID(1) 130 s.expectUnitName() 131 s.expectUnitTag() 132 s.expectRelationUnit() 133 s.expectWatch(c) 134 s.expectRelationerJoin() 135 s.expectRelationSetStatusJoined() 136 137 r := s.newStateTracker(c) 138 139 c.Assert(r.RelationCreated(1), jc.IsTrue) 140 } 141 142 func (s *stateTrackerSuite) TestLoadInitialStateKnownOnly(c *gc.C) { 143 defer s.setupMocks(c).Finish() 144 s.expectRelationsStatusEmpty() 145 s.expectStateMgrKnownIDs([]int{1}) 146 s.expectStateMgrRemoveRelation(1) 147 148 r := s.newStateTracker(c) 149 150 //No relations created. 151 c.Assert(r.GetInfo(), gc.HasLen, 0) 152 } 153 154 func (s *stateTrackerSuite) TestPrepareHook(c *gc.C) { 155 defer s.setupMocks(c).Finish() 156 s.expectRelationerPrepareHook() 157 cfg := relation.StateTrackerForTestConfig{ 158 Relationers: map[int]relation.Relationer{1: s.relationer}, 159 RemoteAppName: make(map[int]string), 160 } 161 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 162 c.Assert(err, jc.ErrorIsNil) 163 164 info := hook.Info{ 165 Kind: hooks.RelationJoined, 166 RelationId: 1, 167 } 168 hookString, err := rst.PrepareHook(info) 169 c.Assert(err, jc.ErrorIsNil) 170 c.Assert(hookString, gc.Equals, "testing") 171 } 172 173 func (s *stateTrackerSuite) TestPrepareHookNotFound(c *gc.C) { 174 defer s.setupMocks(c).Finish() 175 cfg := relation.StateTrackerForTestConfig{ 176 Relationers: make(map[int]relation.Relationer), 177 RemoteAppName: make(map[int]string), 178 } 179 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 180 c.Assert(err, jc.ErrorIsNil) 181 182 info := hook.Info{ 183 Kind: hooks.RelationCreated, 184 RelationId: 1, 185 } 186 _, err = rst.PrepareHook(info) 187 c.Assert(err, gc.ErrorMatches, "operation already executed") 188 } 189 190 func (s *stateTrackerSuite) TestPrepareHookOnlyRelationHooks(c *gc.C) { 191 defer s.setupMocks(c).Finish() 192 cfg := relation.StateTrackerForTestConfig{ 193 Relationers: map[int]relation.Relationer{1: s.relationer}, 194 RemoteAppName: make(map[int]string), 195 } 196 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 197 c.Assert(err, jc.ErrorIsNil) 198 199 info := hook.Info{ 200 Kind: hooks.MeterStatusChanged, 201 RelationId: 1, 202 } 203 _, err = rst.PrepareHook(info) 204 c.Assert(err, gc.ErrorMatches, "not a relation hook.*") 205 } 206 207 func (s *stateTrackerSuite) TestCommitHookOnlyRelationHooks(c *gc.C) { 208 defer s.setupMocks(c).Finish() 209 cfg := relation.StateTrackerForTestConfig{ 210 Relationers: map[int]relation.Relationer{1: s.relationer}, 211 RemoteAppName: make(map[int]string), 212 } 213 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 214 c.Assert(err, jc.ErrorIsNil) 215 216 info := hook.Info{ 217 Kind: hooks.MeterStatusChanged, 218 RelationId: 1, 219 } 220 err = rst.CommitHook(info) 221 c.Assert(err, gc.ErrorMatches, "not a relation hook.*") 222 } 223 224 func (s *stateTrackerSuite) TestCommitHookNotFound(c *gc.C) { 225 defer s.setupMocks(c).Finish() 226 cfg := relation.StateTrackerForTestConfig{ 227 Relationers: make(map[int]relation.Relationer), 228 RemoteAppName: make(map[int]string), 229 } 230 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 231 c.Assert(err, jc.ErrorIsNil) 232 233 info := hook.Info{ 234 Kind: hooks.RelationCreated, 235 RelationId: 1, 236 } 237 err = rst.CommitHook(info) 238 c.Assert(err, jc.ErrorIsNil) 239 } 240 241 func (s *stateTrackerSuite) TestCommitHookRelationCreated(c *gc.C) { 242 defer s.setupMocks(c).Finish() 243 s.expectRelationerCommitHook() 244 cfg := relation.StateTrackerForTestConfig{ 245 Relationers: map[int]relation.Relationer{1: s.relationer}, 246 RemoteAppName: make(map[int]string), 247 } 248 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 249 c.Assert(err, jc.ErrorIsNil) 250 251 info := hook.Info{ 252 Kind: hooks.RelationCreated, 253 RelationId: 1, 254 } 255 err = rst.CommitHook(info) 256 c.Assert(err, jc.ErrorIsNil) 257 c.Assert(rst.RelationCreated(1), jc.IsTrue) 258 } 259 260 func (s *stateTrackerSuite) TestCommitHookRelationCreatedFail(c *gc.C) { 261 defer s.setupMocks(c).Finish() 262 s.expectRelationerCommitHookFail() 263 cfg := relation.StateTrackerForTestConfig{ 264 Relationers: map[int]relation.Relationer{1: s.relationer}, 265 RemoteAppName: make(map[int]string), 266 } 267 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 268 c.Assert(err, jc.ErrorIsNil) 269 270 info := hook.Info{ 271 Kind: hooks.RelationCreated, 272 RelationId: 1, 273 } 274 err = rst.CommitHook(info) 275 c.Assert(err, gc.NotNil) 276 c.Assert(rst.RelationCreated(1), jc.IsFalse) 277 } 278 279 func (s *stateTrackerSuite) TestCommitHookRelationBroken(c *gc.C) { 280 defer s.setupMocks(c).Finish() 281 s.expectRelationerCommitHook() 282 cfg := relation.StateTrackerForTestConfig{ 283 Relationers: map[int]relation.Relationer{1: s.relationer}, 284 RemoteAppName: make(map[int]string), 285 } 286 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 287 c.Assert(err, jc.ErrorIsNil) 288 289 info := hook.Info{ 290 Kind: hooks.RelationBroken, 291 RelationId: 1, 292 } 293 err = rst.CommitHook(info) 294 c.Assert(err, jc.ErrorIsNil) 295 c.Assert(rst.IsKnown(1), jc.IsFalse) 296 } 297 298 func (s *stateTrackerSuite) TestCommitHookRelationBrokenFail(c *gc.C) { 299 defer s.setupMocks(c).Finish() 300 s.expectRelationerCommitHookFail() 301 cfg := relation.StateTrackerForTestConfig{ 302 Relationers: map[int]relation.Relationer{1: s.relationer}, 303 RemoteAppName: make(map[int]string), 304 } 305 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 306 c.Assert(err, jc.ErrorIsNil) 307 308 info := hook.Info{ 309 Kind: hooks.RelationBroken, 310 RelationId: 1, 311 } 312 err = rst.CommitHook(info) 313 c.Assert(err, gc.NotNil) 314 c.Assert(rst.IsKnown(1), jc.IsTrue) 315 } 316 317 func (s *baseStateTrackerSuite) setupMocks(c *gc.C) *gomock.Controller { 318 ctrl := gomock.NewController(c) 319 s.state = mocks.NewMockStateTrackerState(ctrl) 320 s.unit = mocks.NewMockUnit(ctrl) 321 s.relation = mocks.NewMockRelation(ctrl) 322 s.relationer = mocks.NewMockRelationer(ctrl) 323 s.relationUnit = mocks.NewMockRelationUnit(ctrl) 324 s.stateMgr = mocks.NewMockStateManager(ctrl) 325 return ctrl 326 } 327 328 type syncScopesSuite struct { 329 baseStateTrackerSuite 330 331 charmDir string 332 } 333 334 var _ = gc.Suite(&syncScopesSuite{}) 335 336 func (s *syncScopesSuite) SetUpTest(c *gc.C) { 337 s.leadershipContext = &stubLeadershipContext{isLeader: true} 338 s.unitTag, _ = names.ParseUnitTag("wordpress/0") 339 } 340 341 func (s *syncScopesSuite) setupCharmDir(c *gc.C) { 342 // cleanup? 343 s.charmDir = filepath.Join(c.MkDir(), "charm") 344 err := os.MkdirAll(s.charmDir, 0755) 345 c.Assert(err, jc.ErrorIsNil) 346 err = os.WriteFile(filepath.Join(s.charmDir, "metadata.yaml"), []byte(minimalMetadata), 0755) 347 c.Assert(err, jc.ErrorIsNil) 348 } 349 350 func (s *syncScopesSuite) TestSynchronizeScopesNoRemoteRelations(c *gc.C) { 351 defer s.setupMocks(c).Finish() 352 s.expectRelationsStatusEmpty() 353 s.expectStateMgrKnownIDs([]int{}) 354 355 r := s.newStateTracker(c) 356 357 remote := remotestate.Snapshot{} 358 err := r.SynchronizeScopes(remote) 359 c.Assert(err, jc.ErrorIsNil) 360 } 361 362 func (s *syncScopesSuite) TestSynchronizeScopesNoRemoteRelationsDestroySubordinate(c *gc.C) { 363 defer s.setupMocks(c).Finish() 364 s.expectRelationsStatusEmpty() 365 s.expectStateMgrKnownIDs([]int{}) 366 s.expectUnitDestroy() 367 368 cfg := relation.StateTrackerForTestConfig{ 369 St: s.state, 370 Unit: s.unit, 371 LeadershipContext: s.leadershipContext, 372 StateManager: s.stateMgr, 373 Subordinate: true, 374 PrincipalName: "ubuntu/0", 375 NewRelationerFunc: func(_ relation.RelationUnit, _ relation.StateManager, _ relation.UnitGetter, _ relation.Logger) relation.Relationer { 376 return s.relationer 377 }, 378 } 379 rst, err := relation.NewStateTrackerForTest(cfg) 380 c.Assert(err, jc.ErrorIsNil) 381 382 remote := remotestate.Snapshot{} 383 err = rst.SynchronizeScopes(remote) 384 c.Assert(err, jc.ErrorIsNil) 385 } 386 387 func (s *syncScopesSuite) TestSynchronizeScopesDying(c *gc.C) { 388 rst := s.testSynchronizeScopesDying(c, false) 389 c.Assert(rst.IsKnown(1), jc.IsTrue) 390 } 391 392 func (s *syncScopesSuite) TestSynchronizeScopesDyingImplicit(c *gc.C) { 393 rst := s.testSynchronizeScopesDying(c, true) 394 c.Assert(rst.IsKnown(1), jc.IsFalse) 395 } 396 397 func (s *syncScopesSuite) testSynchronizeScopesDying(c *gc.C, implicit bool) relation.RelationStateTracker { 398 // Setup 399 defer s.setupMocks(c).Finish() 400 401 rst := s.newSyncScopesStateTracker(c, 402 map[int]relation.Relationer{1: s.relationer}, 403 map[int]string{1: ""}, 404 ) 405 406 // Setup for SynchronizeScopes 407 s.expectRelationerRelationUnit() 408 s.expectRelationUnitRelation() 409 s.expectRelationUpdateSuspended(false) 410 s.expectRelationerIsImplicit(implicit) 411 412 // What the test is looking for 413 s.expectRelationerSetDying() 414 415 remoteState := remotestate.Snapshot{ 416 Relations: map[int]remotestate.RelationSnapshot{ 417 1: { 418 Life: life.Dying, 419 Members: map[string]int64{ 420 "mysql/0": 1, 421 }, 422 }, 423 }, 424 } 425 426 err := rst.SynchronizeScopes(remoteState) 427 c.Assert(err, jc.ErrorIsNil) 428 return rst 429 } 430 431 func (s *syncScopesSuite) TestSynchronizeScopesSuspendedDying(c *gc.C) { 432 // Setup 433 defer s.setupMocks(c).Finish() 434 435 rst := s.newSyncScopesStateTracker(c, 436 map[int]relation.Relationer{1: s.relationer}, 437 map[int]string{1: "mysql"}, 438 ) 439 440 // Setup for SynchronizeScopes 441 s.expectRelationerRelationUnit() 442 s.expectRelationUnitRelation() 443 s.expectRelationUpdateSuspended(true) 444 s.expectRelationerIsImplicit(true) 445 446 // What the test is looking for 447 s.expectRelationerSetDying() 448 449 remoteState := remotestate.Snapshot{ 450 Relations: map[int]remotestate.RelationSnapshot{ 451 1: { 452 Life: life.Alive, 453 Suspended: true, 454 Members: map[string]int64{ 455 "mysql/0": 1, 456 }, 457 ApplicationMembers: map[string]int64{ 458 "mysql": 1, 459 }, 460 }, 461 }, 462 } 463 464 err := rst.SynchronizeScopes(remoteState) 465 c.Assert(err, jc.ErrorIsNil) 466 c.Assert(rst.IsKnown(1), jc.IsFalse) 467 } 468 469 func (s *syncScopesSuite) TestSynchronizeScopesJoinRelation(c *gc.C) { 470 // wordpress unit with mysql relation 471 s.setupCharmDir(c) 472 defer s.setupMocks(c).Finish() 473 // Setup for SynchronizeScopes() 474 s.expectRelationById(1) 475 ep := &uniter.Endpoint{ 476 charm.Relation{ 477 Role: charm.RoleRequirer, 478 Name: "mysql", 479 Interface: "db", 480 Scope: charm.ScopeGlobal, 481 }} 482 s.expectRelationEndpoint(ep) 483 s.expectRelationUnit() 484 s.expectRelationOtherApplication() 485 486 // Setup for joinRelation() 487 s.expectUnitName() 488 s.expectUnitTag() 489 s.expectWatch(c) 490 s.expectRelationerJoin() 491 s.expectRelationSetStatusJoined() 492 s.expectRelationID(1) 493 494 rst := s.newSyncScopesStateTracker(c, 495 make(map[int]relation.Relationer), 496 make(map[int]string), 497 ) 498 499 remoteState := remotestate.Snapshot{ 500 Relations: map[int]remotestate.RelationSnapshot{ 501 1: { 502 Life: life.Alive, 503 Members: map[string]int64{ 504 "mysql/0": 1, 505 }, 506 ApplicationMembers: map[string]int64{ 507 "mysql": 1, 508 }, 509 }, 510 }, 511 } 512 513 err := rst.SynchronizeScopes(remoteState) 514 c.Assert(err, jc.ErrorIsNil) 515 c.Assert(rst.RemoteApplication(1), gc.Equals, "mysql") 516 } 517 518 func (s *syncScopesSuite) assertSynchronizeScopesFailImplementedBy(c *gc.C, createCharmDir bool) { 519 if createCharmDir { 520 // wordpress unit with mysql relation 521 s.setupCharmDir(c) 522 } 523 defer s.setupMocks(c).Finish() 524 // Setup for SynchronizeScopes() 525 s.expectRelationById(1) 526 ep := &uniter.Endpoint{ 527 charm.Relation{ 528 // changing to RoleProvider will cause ImplementedBy to fail. 529 Role: charm.RoleProvider, 530 Name: "mysql", 531 Interface: "db", 532 Scope: charm.ScopeGlobal, 533 }} 534 s.expectRelationOtherApplication() 535 s.expectRelationEndpoint(ep) 536 s.expectString() 537 538 rst := s.newSyncScopesStateTracker(c, 539 make(map[int]relation.Relationer), 540 make(map[int]string), 541 ) 542 543 remoteState := remotestate.Snapshot{ 544 Relations: map[int]remotestate.RelationSnapshot{ 545 1: { 546 Life: life.Alive, 547 Members: map[string]int64{ 548 "mysql/0": 1, 549 }, 550 ApplicationMembers: map[string]int64{ 551 "mysql": 1, 552 }, 553 }, 554 }, 555 } 556 557 err := rst.SynchronizeScopes(remoteState) 558 c.Assert(err, jc.ErrorIsNil) 559 } 560 561 func (s *syncScopesSuite) TestSynchronizeScopesFailImplementedBy(c *gc.C) { 562 s.assertSynchronizeScopesFailImplementedBy(c, true) 563 } 564 565 func (s *syncScopesSuite) TestSynchronizeScopesIgnoresMissingCharmDir(c *gc.C) { 566 s.assertSynchronizeScopesFailImplementedBy(c, false) 567 } 568 569 func (s *syncScopesSuite) TestSynchronizeScopesSeenNotDying(c *gc.C) { 570 // Setup 571 defer s.setupMocks(c).Finish() 572 573 rst := s.newSyncScopesStateTracker(c, 574 map[int]relation.Relationer{1: s.relationer}, 575 map[int]string{1: "mysql"}, 576 ) 577 578 // Setup for SynchronizeScopes 579 s.expectRelationerRelationUnit() 580 s.expectRelationUnitRelation() 581 s.expectRelationUpdateSuspended(false) 582 583 remoteState := remotestate.Snapshot{ 584 Relations: map[int]remotestate.RelationSnapshot{ 585 1: { 586 Life: life.Alive, 587 Members: map[string]int64{ 588 "mysql/0": 1, 589 }, 590 ApplicationMembers: map[string]int64{ 591 "mysql": 1, 592 }, 593 }, 594 }, 595 } 596 597 err := rst.SynchronizeScopes(remoteState) 598 c.Assert(err, jc.ErrorIsNil) 599 c.Assert(rst.RemoteApplication(1), gc.Equals, "mysql") 600 } 601 602 // Relationer 603 func (s *baseStateTrackerSuite) expectRelationerPrepareHook() { 604 s.relationer.EXPECT().PrepareHook(gomock.Any()).Return("testing", nil) 605 } 606 607 func (s *baseStateTrackerSuite) expectRelationerCommitHook() { 608 s.relationer.EXPECT().CommitHook(gomock.Any()).Return(nil) 609 } 610 611 func (s *baseStateTrackerSuite) expectRelationerCommitHookFail() { 612 s.relationer.EXPECT().CommitHook(gomock.Any()).Return(errors.NotFoundf("testing")) 613 } 614 615 func (s *baseStateTrackerSuite) expectRelationerJoin() { 616 s.relationer.EXPECT().Join().Return(nil) 617 } 618 619 func (s *baseStateTrackerSuite) expectRelationerRelationUnit() { 620 s.relationer.EXPECT().RelationUnit().Return(s.relationUnit) 621 } 622 623 func (s *baseStateTrackerSuite) expectRelationerSetDying() { 624 s.relationer.EXPECT().SetDying().Return(nil) 625 } 626 627 func (s *baseStateTrackerSuite) expectRelationerIsImplicit(imp bool) { 628 s.relationer.EXPECT().IsImplicit().Return(imp) 629 } 630 631 // RelationUnit 632 func (s *baseStateTrackerSuite) expectRelationUnitRelation() { 633 s.relationUnit.EXPECT().Relation().Return(s.relation) 634 } 635 636 // Relation 637 func (s *baseStateTrackerSuite) expectRelationUpdateSuspended(suspend bool) { 638 s.relation.EXPECT().UpdateSuspended(suspend) 639 } 640 641 func (s *baseStateTrackerSuite) expectRelationUnit() { 642 s.relation.EXPECT().Unit(s.unitTag).Return(s.relationUnit, nil).AnyTimes() 643 } 644 645 func (s *baseStateTrackerSuite) expectRelationSetStatusJoined() { 646 s.relation.EXPECT().SetStatus(corerelation.Joined) 647 } 648 649 func (s *baseStateTrackerSuite) expectRelationID(id int) { 650 s.relation.EXPECT().Id().Return(id).AnyTimes() 651 } 652 653 func (s *baseStateTrackerSuite) expectRelationEndpoint(ep *uniter.Endpoint) { 654 s.relation.EXPECT().Endpoint().Return(ep, nil) 655 } 656 657 func (s *syncScopesSuite) expectRelationOtherApplication() { 658 s.relation.EXPECT().OtherApplication().Return("mysql") 659 } 660 661 func (s *syncScopesSuite) expectString() { 662 s.relation.EXPECT().String().Return("test me").AnyTimes() 663 } 664 665 // StateManager 666 func (s *baseStateTrackerSuite) expectStateMgrRemoveRelation(id int) { 667 s.stateMgr.EXPECT().RemoveRelation(id, s.state, map[string]bool{}).Return(nil) 668 } 669 670 func (s *baseStateTrackerSuite) expectStateMgrKnownIDs(ids []int) { 671 s.stateMgr.EXPECT().KnownIDs().Return(ids) 672 } 673 674 func (s *baseStateTrackerSuite) expectStateMgrRelationFound(id int) { 675 s.stateMgr.EXPECT().RelationFound(id).Return(true) 676 } 677 678 // State 679 func (s *baseStateTrackerSuite) expectRelation(relTag names.RelationTag) { 680 s.state.EXPECT().Relation(relTag).Return(s.relation, nil) 681 } 682 683 func (s *syncScopesSuite) expectRelationById(id int) { 684 s.state.EXPECT().RelationById(id).Return(s.relation, nil) 685 } 686 687 // Unit 688 func (s *baseStateTrackerSuite) expectUnitTag() { 689 s.unit.EXPECT().Tag().Return(s.unitTag) 690 } 691 692 func (s *baseStateTrackerSuite) expectUnitName() { 693 s.unit.EXPECT().Name().Return(s.unitTag.Id()) 694 } 695 696 func (s *baseStateTrackerSuite) expectUnitDestroy() { 697 s.unit.EXPECT().Destroy().Return(nil) 698 } 699 700 func (s *baseStateTrackerSuite) expectRelationsStatusEmpty() { 701 s.unit.EXPECT().RelationsStatus().Return([]uniter.RelationStatus{}, nil) 702 } 703 704 func (s *baseStateTrackerSuite) expectRelationsStatus(status []uniter.RelationStatus) { 705 s.unit.EXPECT().RelationsStatus().Return(status, nil) 706 } 707 708 func (s *baseStateTrackerSuite) expectWatch(c *gc.C) { 709 do := func() { 710 go func() { 711 select { 712 case s.unitChanges <- struct{}{}: 713 case <-time.After(coretesting.LongWait): 714 c.Fatal("timed out unit change") 715 } 716 }() 717 } 718 s.unitChanges = make(chan struct{}) 719 s.watcher = watchertest.NewMockNotifyWatcher(s.unitChanges) 720 s.unit.EXPECT().Watch().Return(s.watcher, nil).Do(do) 721 } 722 723 func (s *baseStateTrackerSuite) newStateTracker(c *gc.C) relation.RelationStateTracker { 724 cfg := relation.StateTrackerForTestConfig{ 725 St: s.state, 726 Unit: s.unit, 727 LeadershipContext: s.leadershipContext, 728 StateManager: s.stateMgr, 729 NewRelationerFunc: func(_ relation.RelationUnit, _ relation.StateManager, _ relation.UnitGetter, _ relation.Logger) relation.Relationer { 730 return s.relationer 731 }, 732 } 733 rst, err := relation.NewStateTrackerForTest(cfg) 734 c.Assert(err, jc.ErrorIsNil) 735 return rst 736 } 737 738 func (s *syncScopesSuite) newSyncScopesStateTracker(c *gc.C, relationers map[int]relation.Relationer, appNames map[int]string) relation.RelationStateTracker { 739 cfg := relation.StateTrackerForTestConfig{ 740 St: s.state, 741 Unit: s.unit, 742 LeadershipContext: s.leadershipContext, 743 StateManager: s.stateMgr, 744 NewRelationerFunc: func(_ relation.RelationUnit, _ relation.StateManager, _ relation.UnitGetter, _ relation.Logger) relation.Relationer { 745 return s.relationer 746 }, 747 Relationers: relationers, 748 RemoteAppName: appNames, 749 CharmDir: s.charmDir, 750 } 751 rst, err := relation.NewStateTrackerForSyncScopesTest(cfg) 752 c.Assert(err, jc.ErrorIsNil) 753 return rst 754 }