github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/meterstatus/isolated_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  	"fmt"
     8  	"path"
     9  	"time"
    10  
    11  	"github.com/juju/clock"
    12  	"github.com/juju/clock/testclock"
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/worker.v1"
    17  
    18  	coretesting "github.com/juju/juju/testing"
    19  	"github.com/juju/juju/worker/common/charmrunner"
    20  	"github.com/juju/juju/worker/meterstatus"
    21  )
    22  
    23  const (
    24  	AmberGracePeriod = time.Minute
    25  	RedGracePeriod   = time.Minute * 5
    26  )
    27  
    28  type IsolatedWorkerConfigSuite struct {
    29  	coretesting.BaseSuite
    30  
    31  	stub *testing.Stub
    32  
    33  	dataDir string
    34  }
    35  
    36  var _ = gc.Suite(&IsolatedWorkerConfigSuite{})
    37  
    38  func (s *IsolatedWorkerConfigSuite) SetUpTest(c *gc.C) {
    39  	s.BaseSuite.SetUpTest(c)
    40  	s.stub = &testing.Stub{}
    41  	s.dataDir = c.MkDir()
    42  }
    43  
    44  func (s *IsolatedWorkerConfigSuite) TestConfigValidation(c *gc.C) {
    45  	tests := []struct {
    46  		cfg      meterstatus.IsolatedConfig
    47  		expected string
    48  	}{{
    49  		cfg: meterstatus.IsolatedConfig{
    50  			Runner:    &stubRunner{stub: s.stub},
    51  			StateFile: meterstatus.NewStateFile(path.Join(s.dataDir, "meter-status.yaml")),
    52  		},
    53  		expected: "clock not provided",
    54  	}, {
    55  		cfg: meterstatus.IsolatedConfig{
    56  			Clock:     testclock.NewClock(time.Now()),
    57  			StateFile: meterstatus.NewStateFile(path.Join(s.dataDir, "meter-status.yaml")),
    58  		},
    59  		expected: "hook runner not provided",
    60  	}, {
    61  		cfg: meterstatus.IsolatedConfig{
    62  			Clock:  testclock.NewClock(time.Now()),
    63  			Runner: &stubRunner{stub: s.stub},
    64  		},
    65  		expected: "state file not provided",
    66  	}}
    67  	for i, test := range tests {
    68  		c.Logf("running test %d", i)
    69  		err := test.cfg.Validate()
    70  		c.Assert(err, gc.ErrorMatches, test.expected)
    71  	}
    72  }
    73  
    74  type IsolatedWorkerSuite struct {
    75  	coretesting.BaseSuite
    76  
    77  	stub *testing.Stub
    78  
    79  	dataDir string
    80  	clk     *testclock.Clock
    81  
    82  	hookRan         chan struct{}
    83  	triggersCreated chan struct{}
    84  
    85  	worker worker.Worker
    86  }
    87  
    88  var _ = gc.Suite(&IsolatedWorkerSuite{})
    89  
    90  func (s *IsolatedWorkerSuite) SetUpTest(c *gc.C) {
    91  	s.BaseSuite.SetUpTest(c)
    92  	s.stub = &testing.Stub{}
    93  
    94  	s.dataDir = c.MkDir()
    95  
    96  	s.hookRan = make(chan struct{})
    97  	s.triggersCreated = make(chan struct{})
    98  
    99  	triggerFactory := func(state meterstatus.WorkerState, status string, disconectedAt time.Time, clk clock.Clock, amber time.Duration, red time.Duration) (<-chan time.Time, <-chan time.Time) {
   100  		select {
   101  		case s.triggersCreated <- struct{}{}:
   102  		case <-time.After(coretesting.LongWait):
   103  			c.Fatalf("failed to signal trigger creation")
   104  		}
   105  		return meterstatus.GetTriggers(state, status, disconectedAt, clk, amber, red)
   106  	}
   107  
   108  	s.clk = testclock.NewClock(time.Now())
   109  	wrk, err := meterstatus.NewIsolatedStatusWorker(
   110  		meterstatus.IsolatedConfig{
   111  			Runner:           &stubRunner{stub: s.stub, ran: s.hookRan},
   112  			StateFile:        meterstatus.NewStateFile(path.Join(s.dataDir, "meter-status.yaml")),
   113  			Clock:            s.clk,
   114  			AmberGracePeriod: AmberGracePeriod,
   115  			RedGracePeriod:   RedGracePeriod,
   116  			TriggerFactory:   triggerFactory,
   117  		})
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Assert(wrk, gc.NotNil)
   120  	s.worker = wrk
   121  }
   122  
   123  func (s *IsolatedWorkerSuite) TearDownTest(c *gc.C) {
   124  	s.worker.Kill()
   125  	err := s.worker.Wait()
   126  	c.Assert(err, jc.ErrorIsNil)
   127  }
   128  
   129  func (s *IsolatedWorkerSuite) TestTriggering(c *gc.C) {
   130  	assertSignal(c, s.triggersCreated)
   131  	s.clk.Advance(AmberGracePeriod + time.Second)
   132  	assertSignal(c, s.hookRan)
   133  	s.clk.Advance(RedGracePeriod + time.Second)
   134  	assertSignal(c, s.hookRan)
   135  
   136  	s.stub.CheckCallNames(c, "RunHook", "RunHook")
   137  }
   138  
   139  // TestMissingHookError tests that errors caused by missing hooks do not stop the worker.
   140  func (s *IsolatedWorkerSuite) TestMissingHookError(c *gc.C) {
   141  	s.stub.SetErrors(charmrunner.NewMissingHookError("meter-status-changed"))
   142  
   143  	assertSignal(c, s.triggersCreated)
   144  	s.clk.Advance(AmberGracePeriod + time.Second)
   145  	assertSignal(c, s.hookRan)
   146  
   147  	s.stub.CheckCallNames(c, "RunHook")
   148  }
   149  
   150  // TestRandomHookError tests that errors returned by hooks do not stop the worker.
   151  func (s *IsolatedWorkerSuite) TestRandomHookError(c *gc.C) {
   152  	s.stub.SetErrors(fmt.Errorf("blah"))
   153  
   154  	assertSignal(c, s.triggersCreated)
   155  	s.clk.Advance(AmberGracePeriod + time.Second)
   156  	assertSignal(c, s.hookRan)
   157  
   158  	s.stub.CheckCallNames(c, "RunHook")
   159  }