github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/meterstatus/connected_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 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "go.uber.org/mock/gomock" 15 gc "gopkg.in/check.v1" 16 17 coretesting "github.com/juju/juju/testing" 18 "github.com/juju/juju/worker/common/charmrunner" 19 "github.com/juju/juju/worker/meterstatus" 20 "github.com/juju/juju/worker/meterstatus/mocks" 21 ) 22 23 type ConnectedWorkerSuite struct { 24 coretesting.BaseSuite 25 26 stub *testing.Stub 27 28 msClient *stubMeterStatusClient 29 config meterstatus.ConnectedConfig 30 } 31 32 var _ = gc.Suite(&ConnectedWorkerSuite{}) 33 34 func (s *ConnectedWorkerSuite) SetUpTest(c *gc.C) { 35 s.BaseSuite.SetUpTest(c) 36 s.stub = &testing.Stub{} 37 38 s.msClient = newStubMeterStatusClient(s.stub) 39 40 s.config = meterstatus.ConnectedConfig{ 41 Runner: &stubRunner{stub: s.stub}, 42 Status: s.msClient, 43 StateReadWriter: struct{ meterstatus.StateReadWriter }{}, 44 Logger: loggo.GetLogger("test"), 45 } 46 } 47 48 func assertSignal(c *gc.C, signal <-chan struct{}) { 49 select { 50 case <-signal: 51 case <-time.After(coretesting.LongWait): 52 c.Fatal("timed out waiting for signal") 53 } 54 } 55 56 func (s *ConnectedWorkerSuite) TestConfigValid(c *gc.C) { 57 c.Assert(s.config.Validate(), jc.ErrorIsNil) 58 } 59 60 func (s *ConnectedWorkerSuite) TestConfigMissingRunner(c *gc.C) { 61 s.config.Runner = nil 62 err := s.config.Validate() 63 c.Assert(err, jc.Satisfies, errors.IsNotValid) 64 c.Assert(err.Error(), gc.Equals, "missing Runner not valid") 65 } 66 67 func (s *ConnectedWorkerSuite) TestConfigMissingStateReadWriter(c *gc.C) { 68 s.config.StateReadWriter = nil 69 err := s.config.Validate() 70 c.Assert(err, jc.Satisfies, errors.IsNotValid) 71 c.Assert(err.Error(), gc.Equals, "missing StateReadWriter not valid") 72 } 73 74 func (s *ConnectedWorkerSuite) TestConfigMissingStatus(c *gc.C) { 75 s.config.Status = nil 76 err := s.config.Validate() 77 c.Assert(err, jc.Satisfies, errors.IsNotValid) 78 c.Assert(err.Error(), gc.Equals, "missing Status not valid") 79 } 80 81 func (s *ConnectedWorkerSuite) TestConfigMissingLogger(c *gc.C) { 82 s.config.Logger = nil 83 err := s.config.Validate() 84 c.Assert(err, jc.Satisfies, errors.IsNotValid) 85 c.Assert(err.Error(), gc.Equals, "missing Logger not valid") 86 } 87 88 // TestStatusHandlerDoesNotRerunNoChange ensures that the handler does not execute the hook if it 89 // detects no actual meter status change. 90 func (s *ConnectedWorkerSuite) TestStatusHandlerDoesNotRerunNoChange(c *gc.C) { 91 ctrl := gomock.NewController(c) 92 defer ctrl.Finish() 93 94 stateReadWriter := mocks.NewMockStateReadWriter(ctrl) 95 gomock.InOrder( 96 stateReadWriter.EXPECT().Read().Return(nil, errors.NotFoundf("no state")), 97 stateReadWriter.EXPECT().Write(&meterstatus.State{ 98 Code: "GREEN", 99 }).Return(nil), 100 ) 101 102 config := s.config 103 config.StateReadWriter = stateReadWriter 104 handler, err := meterstatus.NewConnectedStatusHandler(config) 105 c.Assert(err, jc.ErrorIsNil) 106 c.Assert(handler, gc.NotNil) 107 _, err = handler.SetUp() 108 c.Assert(err, jc.ErrorIsNil) 109 110 err = handler.Handle(nil) 111 c.Assert(err, jc.ErrorIsNil) 112 err = handler.Handle(nil) 113 c.Assert(err, jc.ErrorIsNil) 114 115 s.stub.CheckCallNames(c, "WatchMeterStatus", "MeterStatus", "RunHook", "MeterStatus") 116 } 117 118 // TestStatusHandlerRunsHookOnChanges ensures that the handler runs the meter-status-changed hook 119 // if an actual meter status change is detected. 120 func (s *ConnectedWorkerSuite) TestStatusHandlerRunsHookOnChanges(c *gc.C) { 121 ctrl := gomock.NewController(c) 122 defer ctrl.Finish() 123 124 stateReadWriter := mocks.NewMockStateReadWriter(ctrl) 125 gomock.InOrder( 126 stateReadWriter.EXPECT().Read().Return(nil, errors.NotFoundf("no state")), 127 // First Handle() invocation 128 stateReadWriter.EXPECT().Write(&meterstatus.State{ 129 Code: "GREEN", 130 }).Return(nil), 131 // Second Handle() invocation 132 stateReadWriter.EXPECT().Write(&meterstatus.State{ 133 Code: "RED", 134 }).Return(nil), 135 ) 136 137 config := s.config 138 config.StateReadWriter = stateReadWriter 139 handler, err := meterstatus.NewConnectedStatusHandler(config) 140 c.Assert(err, jc.ErrorIsNil) 141 c.Assert(handler, gc.NotNil) 142 _, err = handler.SetUp() 143 c.Assert(err, jc.ErrorIsNil) 144 145 handler.Handle(nil) 146 s.msClient.SetStatus("RED") 147 handler.Handle(nil) 148 149 c.Assert(err, jc.ErrorIsNil) 150 s.stub.CheckCallNames(c, "WatchMeterStatus", "MeterStatus", "RunHook", "MeterStatus", "RunHook") 151 } 152 153 // TestStatusHandlerHandlesHookMissingError tests that the handler does not report errors 154 // caused by a missing meter-status-changed hook. 155 func (s *ConnectedWorkerSuite) TestStatusHandlerHandlesHookMissingError(c *gc.C) { 156 ctrl := gomock.NewController(c) 157 defer ctrl.Finish() 158 159 stateReadWriter := mocks.NewMockStateReadWriter(ctrl) 160 gomock.InOrder( 161 stateReadWriter.EXPECT().Read().Return(nil, errors.NotFoundf("no state")), 162 stateReadWriter.EXPECT().Write(&meterstatus.State{ 163 Code: "GREEN", 164 }).Return(nil), 165 ) 166 167 s.stub.SetErrors(charmrunner.NewMissingHookError("meter-status-changed")) 168 config := s.config 169 config.StateReadWriter = stateReadWriter 170 handler, err := meterstatus.NewConnectedStatusHandler(config) 171 c.Assert(err, jc.ErrorIsNil) 172 c.Assert(handler, gc.NotNil) 173 _, err = handler.SetUp() 174 c.Assert(err, jc.ErrorIsNil) 175 176 err = handler.Handle(nil) 177 c.Assert(err, jc.ErrorIsNil) 178 s.stub.CheckCallNames(c, "WatchMeterStatus", "MeterStatus", "RunHook") 179 } 180 181 // TestStatusHandlerHandlesRandomHookError tests that the meter status handler does not return 182 // errors encountered while executing the hook. 183 func (s *ConnectedWorkerSuite) TestStatusHandlerHandlesRandomHookError(c *gc.C) { 184 ctrl := gomock.NewController(c) 185 defer ctrl.Finish() 186 187 stateReadWriter := mocks.NewMockStateReadWriter(ctrl) 188 gomock.InOrder( 189 stateReadWriter.EXPECT().Read().Return(nil, errors.NotFoundf("no state")), 190 stateReadWriter.EXPECT().Write(&meterstatus.State{ 191 Code: "GREEN", 192 }).Return(nil), 193 ) 194 195 s.stub.SetErrors(fmt.Errorf("blah")) 196 config := s.config 197 config.StateReadWriter = stateReadWriter 198 handler, err := meterstatus.NewConnectedStatusHandler(config) 199 c.Assert(err, jc.ErrorIsNil) 200 c.Assert(handler, gc.NotNil) 201 _, err = handler.SetUp() 202 c.Assert(err, jc.ErrorIsNil) 203 204 err = handler.Handle(nil) 205 c.Assert(err, jc.ErrorIsNil) 206 207 s.stub.CheckCallNames(c, "WatchMeterStatus", "MeterStatus", "RunHook") 208 } 209 210 // TestStatusHandlerDoesNotRerunAfterRestart tests that the status handler will not rerun a meter-status-changed 211 // hook if it is restarted, but no actual changes are recorded. 212 func (s *ConnectedWorkerSuite) TestStatusHandlerDoesNotRerunAfterRestart(c *gc.C) { 213 ctrl := gomock.NewController(c) 214 defer ctrl.Finish() 215 216 stateReadWriter := mocks.NewMockStateReadWriter(ctrl) 217 gomock.InOrder( 218 // First run 219 stateReadWriter.EXPECT().Read().Return(nil, errors.NotFoundf("no state")), 220 stateReadWriter.EXPECT().Write(&meterstatus.State{ 221 Code: "GREEN", 222 }).Return(nil), 223 224 // Second run 225 stateReadWriter.EXPECT().Read().Return(&meterstatus.State{ 226 Code: "GREEN", 227 }, nil), 228 ) 229 230 config := s.config 231 config.StateReadWriter = stateReadWriter 232 handler, err := meterstatus.NewConnectedStatusHandler(config) 233 c.Assert(err, jc.ErrorIsNil) 234 c.Assert(handler, gc.NotNil) 235 _, err = handler.SetUp() 236 c.Assert(err, jc.ErrorIsNil) 237 238 err = handler.Handle(nil) 239 c.Assert(err, jc.ErrorIsNil) 240 241 s.stub.CheckCallNames(c, "WatchMeterStatus", "MeterStatus", "RunHook") 242 s.stub.ResetCalls() 243 244 // Create a new handler (imitating worker restart). 245 handler, err = meterstatus.NewConnectedStatusHandler(config) 246 c.Assert(err, jc.ErrorIsNil) 247 c.Assert(handler, gc.NotNil) 248 _, err = handler.SetUp() 249 c.Assert(err, jc.ErrorIsNil) 250 251 err = handler.Handle(nil) 252 c.Assert(err, jc.ErrorIsNil) 253 254 s.stub.CheckCallNames(c, "WatchMeterStatus", "MeterStatus") 255 }