github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/meterstatus/manifold_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package meterstatus_test
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils/clock"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/names.v2"
    15  
    16  	"github.com/juju/juju/agent"
    17  	"github.com/juju/juju/api/base"
    18  	msapi "github.com/juju/juju/api/meterstatus"
    19  	coretesting "github.com/juju/juju/testing"
    20  	"github.com/juju/juju/watcher"
    21  	"github.com/juju/juju/worker"
    22  	"github.com/juju/juju/worker/dependency"
    23  	dt "github.com/juju/juju/worker/dependency/testing"
    24  	"github.com/juju/juju/worker/meterstatus"
    25  	"github.com/juju/juju/worker/uniter/runner"
    26  )
    27  
    28  type ManifoldSuite struct {
    29  	coretesting.BaseSuite
    30  
    31  	stub *testing.Stub
    32  
    33  	dataDir string
    34  
    35  	manifoldConfig meterstatus.ManifoldConfig
    36  	manifold       dependency.Manifold
    37  	resources      dt.StubResources
    38  }
    39  
    40  var _ = gc.Suite(&ManifoldSuite{})
    41  
    42  func (s *ManifoldSuite) SetUpTest(c *gc.C) {
    43  	s.BaseSuite.SetUpTest(c)
    44  	s.stub = &testing.Stub{}
    45  
    46  	s.manifoldConfig = meterstatus.ManifoldConfig{
    47  		AgentName:               "agent-name",
    48  		APICallerName:           "apicaller-name",
    49  		MachineLockName:         "machine-lock-name",
    50  		Clock:                   testing.NewClock(time.Now()),
    51  		NewHookRunner:           meterstatus.NewHookRunner,
    52  		NewMeterStatusAPIClient: msapi.NewClient,
    53  
    54  		NewConnectedStatusWorker: meterstatus.NewConnectedStatusWorker,
    55  		NewIsolatedStatusWorker:  meterstatus.NewIsolatedStatusWorker,
    56  	}
    57  	s.manifold = meterstatus.Manifold(s.manifoldConfig)
    58  	s.dataDir = c.MkDir()
    59  
    60  	s.resources = dt.StubResources{
    61  		"agent-name":     dt.StubResource{Output: &dummyAgent{dataDir: s.dataDir}},
    62  		"apicaller-name": dt.StubResource{Output: &dummyAPICaller{}},
    63  	}
    64  }
    65  
    66  // TestInputs ensures the collect manifold has the expected defined inputs.
    67  func (s *ManifoldSuite) TestInputs(c *gc.C) {
    68  	c.Check(s.manifold.Inputs, jc.DeepEquals, []string{
    69  		"agent-name", "apicaller-name",
    70  	})
    71  }
    72  
    73  // TestStartMissingDeps ensures that the manifold correctly handles a missing
    74  // resource dependency.
    75  func (s *ManifoldSuite) TestStartMissingDeps(c *gc.C) {
    76  	for _, missingDep := range []string{
    77  		"agent-name",
    78  	} {
    79  		testResources := dt.StubResources{}
    80  		for k, v := range s.resources {
    81  			if k == missingDep {
    82  				testResources[k] = dt.StubResource{Error: dependency.ErrMissing}
    83  			} else {
    84  				testResources[k] = v
    85  			}
    86  		}
    87  		worker, err := s.manifold.Start(testResources.Context())
    88  		c.Check(worker, gc.IsNil)
    89  		c.Check(err, gc.Equals, dependency.ErrMissing)
    90  	}
    91  }
    92  
    93  type PatchedManifoldSuite struct {
    94  	coretesting.BaseSuite
    95  	msClient       *stubMeterStatusClient
    96  	manifoldConfig meterstatus.ManifoldConfig
    97  	stub           *testing.Stub
    98  	resources      dt.StubResources
    99  }
   100  
   101  func (s *PatchedManifoldSuite) SetUpTest(c *gc.C) {
   102  	s.BaseSuite.SetUpTest(c)
   103  
   104  	s.stub = &testing.Stub{}
   105  	s.msClient = &stubMeterStatusClient{stub: s.stub, changes: make(chan struct{})}
   106  	newMSClient := func(_ base.APICaller, _ names.UnitTag) msapi.MeterStatusClient {
   107  		return s.msClient
   108  	}
   109  	newHookRunner := func(_ names.UnitTag, _ string, _ agent.Config, _ clock.Clock) meterstatus.HookRunner {
   110  		return &stubRunner{stub: s.stub}
   111  	}
   112  
   113  	s.manifoldConfig = meterstatus.ManifoldConfig{
   114  		AgentName:               "agent-name",
   115  		APICallerName:           "apicaller-name",
   116  		MachineLockName:         "machine-lock-name",
   117  		NewHookRunner:           newHookRunner,
   118  		NewMeterStatusAPIClient: newMSClient,
   119  	}
   120  }
   121  
   122  // TestStatusWorkerStarts ensures that the manifold correctly sets up the connected worker.
   123  func (s *PatchedManifoldSuite) TestStatusWorkerStarts(c *gc.C) {
   124  	var called bool
   125  	s.manifoldConfig.NewConnectedStatusWorker = func(cfg meterstatus.ConnectedConfig) (worker.Worker, error) {
   126  		called = true
   127  		return meterstatus.NewConnectedStatusWorker(cfg)
   128  	}
   129  	manifold := meterstatus.Manifold(s.manifoldConfig)
   130  	worker, err := manifold.Start(s.resources.Context())
   131  	c.Assert(called, jc.IsTrue)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	c.Assert(worker, gc.NotNil)
   134  	worker.Kill()
   135  	err = worker.Wait()
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	s.stub.CheckCallNames(c, "MeterStatus", "RunHook", "WatchMeterStatus")
   138  }
   139  
   140  // TestInactiveWorker ensures that the manifold correctly sets up the isolated worker.
   141  func (s *PatchedManifoldSuite) TestIsolatedWorker(c *gc.C) {
   142  	delete(s.resources, "apicaller-name")
   143  	var called bool
   144  	s.manifoldConfig.NewIsolatedStatusWorker = func(cfg meterstatus.IsolatedConfig) (worker.Worker, error) {
   145  		called = true
   146  		return meterstatus.NewIsolatedStatusWorker(cfg)
   147  	}
   148  	manifold := meterstatus.Manifold(s.manifoldConfig)
   149  	worker, err := manifold.Start(s.resources.Context())
   150  	c.Assert(called, jc.IsTrue)
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	c.Assert(worker, gc.NotNil)
   153  	worker.Kill()
   154  	err = worker.Wait()
   155  	c.Assert(err, jc.ErrorIsNil)
   156  	s.stub.CheckCallNames(c, "MeterStatus", "RunHook", "WatchMeterStatus")
   157  }
   158  
   159  type dummyAgent struct {
   160  	agent.Agent
   161  	dataDir string
   162  }
   163  
   164  func (a dummyAgent) CurrentConfig() agent.Config {
   165  	return &dummyAgentConfig{dataDir: a.dataDir}
   166  }
   167  
   168  type dummyAgentConfig struct {
   169  	agent.Config
   170  	dataDir string
   171  }
   172  
   173  // Tag implements agent.AgentConfig.
   174  func (ac dummyAgentConfig) Tag() names.Tag {
   175  	return names.NewUnitTag("u/0")
   176  }
   177  
   178  // DataDir implements agent.AgentConfig.
   179  func (ac dummyAgentConfig) DataDir() string {
   180  	return ac.dataDir
   181  }
   182  
   183  type dummyAPICaller struct {
   184  	base.APICaller
   185  }
   186  
   187  func (dummyAPICaller) BestFacadeVersion(facade string) int {
   188  	return 42
   189  }
   190  
   191  type stubMeterStatusClient struct {
   192  	sync.RWMutex
   193  	stub    *testing.Stub
   194  	changes chan struct{}
   195  	code    string
   196  }
   197  
   198  func newStubMeterStatusClient(stub *testing.Stub) *stubMeterStatusClient {
   199  	changes := make(chan struct{})
   200  	return &stubMeterStatusClient{stub: stub, changes: changes}
   201  }
   202  
   203  func (s *stubMeterStatusClient) SignalStatus(codes ...string) {
   204  	if len(codes) == 0 {
   205  		codes = []string{s.code}
   206  	}
   207  	for _, code := range codes {
   208  		s.SetStatus(code)
   209  		select {
   210  		case s.changes <- struct{}{}:
   211  		case <-time.After(coretesting.LongWait):
   212  			panic("timed out signaling meter status change")
   213  		}
   214  	}
   215  }
   216  
   217  func (s *stubMeterStatusClient) SetStatus(code string) {
   218  	s.Lock()
   219  	defer s.Unlock()
   220  	s.code = code
   221  }
   222  
   223  func (s *stubMeterStatusClient) MeterStatus() (string, string, error) {
   224  	s.RLock()
   225  	defer s.RUnlock()
   226  	s.stub.MethodCall(s, "MeterStatus")
   227  	if s.code == "" {
   228  		return "GREEN", "", nil
   229  	} else {
   230  		return s.code, "", nil
   231  	}
   232  
   233  }
   234  
   235  func (s *stubMeterStatusClient) WatchMeterStatus() (watcher.NotifyWatcher, error) {
   236  	s.stub.MethodCall(s, "WatchMeterStatus")
   237  	return s, nil
   238  }
   239  
   240  func (s *stubMeterStatusClient) Changes() watcher.NotifyChannel {
   241  	return s.changes
   242  }
   243  
   244  func (s *stubMeterStatusClient) Kill() {
   245  }
   246  
   247  func (s *stubMeterStatusClient) Wait() error {
   248  	return nil
   249  }
   250  
   251  type stubRunner struct {
   252  	runner.Runner
   253  	stub *testing.Stub
   254  	ran  chan struct{}
   255  }
   256  
   257  func (r *stubRunner) RunHook(code, info string, abort <-chan struct{}) error {
   258  	r.stub.MethodCall(r, "RunHook", code, info)
   259  	if r.ran != nil {
   260  		select {
   261  		case r.ran <- struct{}{}:
   262  		case <-time.After(coretesting.LongWait):
   263  			panic("timed out signaling hook run")
   264  		}
   265  	}
   266  	return r.stub.NextErr()
   267  }