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  }