github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/httpserverargs/manifold_test.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package httpserverargs_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock" 10 "github.com/juju/clock/testclock" 11 "github.com/juju/errors" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/worker/v3" 15 "github.com/juju/worker/v3/dependency" 16 dt "github.com/juju/worker/v3/dependency/testing" 17 "github.com/juju/worker/v3/workertest" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/apiserver/apiserverhttp" 21 "github.com/juju/juju/apiserver/authentication" 22 "github.com/juju/juju/apiserver/authentication/macaroon" 23 "github.com/juju/juju/state" 24 "github.com/juju/juju/worker/httpserverargs" 25 ) 26 27 type ManifoldSuite struct { 28 testing.IsolationSuite 29 30 config httpserverargs.ManifoldConfig 31 manifold dependency.Manifold 32 context dependency.Context 33 clock *testclock.Clock 34 state stubStateTracker 35 authenticator mockLocalMacaroonAuthenticator 36 37 stub testing.Stub 38 } 39 40 var _ = gc.Suite(&ManifoldSuite{}) 41 42 func (s *ManifoldSuite) SetUpTest(c *gc.C) { 43 s.IsolationSuite.SetUpTest(c) 44 45 s.clock = testclock.NewClock(time.Time{}) 46 s.state = stubStateTracker{} 47 s.stub.ResetCalls() 48 49 s.context = s.newContext(nil) 50 s.config = httpserverargs.ManifoldConfig{ 51 ClockName: "clock", 52 StateName: "state", 53 ControllerPortName: "controller-port", 54 NewStateAuthenticator: s.newStateAuthenticator, 55 } 56 s.manifold = httpserverargs.Manifold(s.config) 57 } 58 59 func (s *ManifoldSuite) newContext(overlay map[string]interface{}) dependency.Context { 60 resources := map[string]interface{}{ 61 "clock": s.clock, 62 "state": &s.state, 63 "controller-port": nil, 64 } 65 for k, v := range overlay { 66 resources[k] = v 67 } 68 return dt.StubContext(nil, resources) 69 } 70 71 func (s *ManifoldSuite) newStateAuthenticator( 72 statePool *state.StatePool, 73 mux *apiserverhttp.Mux, 74 clock clock.Clock, 75 abort <-chan struct{}, 76 ) (macaroon.LocalMacaroonAuthenticator, error) { 77 s.stub.MethodCall(s, "NewStateAuthenticator", statePool, mux, clock, abort) 78 if err := s.stub.NextErr(); err != nil { 79 return nil, err 80 } 81 return &s.authenticator, nil 82 } 83 84 var expectedInputs = []string{"state", "clock", "controller-port"} 85 86 func (s *ManifoldSuite) TestInputs(c *gc.C) { 87 c.Assert(s.manifold.Inputs, jc.SameContents, expectedInputs) 88 } 89 90 func (s *ManifoldSuite) TestMissingInputs(c *gc.C) { 91 for _, input := range expectedInputs { 92 context := s.newContext(map[string]interface{}{ 93 input: dependency.ErrMissing, 94 }) 95 _, err := s.manifold.Start(context) 96 c.Assert(errors.Cause(err), gc.Equals, dependency.ErrMissing) 97 } 98 } 99 100 func (s *ManifoldSuite) TestMuxOutput(c *gc.C) { 101 w := s.startWorkerClean(c) 102 defer workertest.CleanKill(c, w) 103 104 var mux *apiserverhttp.Mux 105 err := s.manifold.Output(w, &mux) 106 c.Assert(err, jc.ErrorIsNil) 107 c.Assert(mux, gc.NotNil) 108 } 109 110 func (s *ManifoldSuite) TestAuthenticatorOutput(c *gc.C) { 111 w := s.startWorkerClean(c) 112 defer workertest.CleanKill(c, w) 113 114 var auth1 authentication.RequestAuthenticator 115 var auth2 macaroon.LocalMacaroonAuthenticator 116 for _, out := range []interface{}{&auth1, &auth2} { 117 err := s.manifold.Output(w, out) 118 c.Assert(err, jc.ErrorIsNil) 119 } 120 c.Assert(auth1, gc.NotNil) 121 c.Assert(auth1, gc.Equals, auth2) 122 } 123 124 func (s *ManifoldSuite) startWorkerClean(c *gc.C) worker.Worker { 125 w, err := s.manifold.Start(s.context) 126 c.Assert(err, jc.ErrorIsNil) 127 workertest.CheckAlive(c, w) 128 return w 129 } 130 131 func (s *ManifoldSuite) TestStopWorkerClosesState(c *gc.C) { 132 w := s.startWorkerClean(c) 133 defer workertest.CleanKill(c, w) 134 135 s.state.CheckCallNames(c, "Use") 136 137 workertest.CleanKill(c, w) 138 s.state.CheckCallNames(c, "Use", "Done") 139 } 140 141 func (s *ManifoldSuite) TestStoppingWorkerClosesAuthenticator(c *gc.C) { 142 w := s.startWorkerClean(c) 143 s.stub.CheckCallNames(c, "NewStateAuthenticator") 144 authArgs := s.stub.Calls()[0].Args 145 c.Assert(authArgs, gc.HasLen, 4) 146 abort := authArgs[3].(<-chan struct{}) 147 148 // abort should still be open at this point. 149 select { 150 case <-abort: 151 c.Fatalf("abort closed while worker still running") 152 default: 153 } 154 155 workertest.CleanKill(c, w) 156 select { 157 case <-abort: 158 default: 159 c.Fatalf("authenticator abort channel not closed") 160 } 161 } 162 163 func (s *ManifoldSuite) TestValidate(c *gc.C) { 164 type test struct { 165 f func(*httpserverargs.ManifoldConfig) 166 expect string 167 } 168 tests := []test{{ 169 func(cfg *httpserverargs.ManifoldConfig) { cfg.StateName = "" }, 170 "empty StateName not valid", 171 }, { 172 func(cfg *httpserverargs.ManifoldConfig) { cfg.ClockName = "" }, 173 "empty ClockName not valid", 174 }, { 175 func(cfg *httpserverargs.ManifoldConfig) { cfg.ControllerPortName = "" }, 176 "empty ControllerPortName not valid", 177 }, { 178 func(cfg *httpserverargs.ManifoldConfig) { cfg.NewStateAuthenticator = nil }, 179 "nil NewStateAuthenticator not valid", 180 }} 181 for i, test := range tests { 182 c.Logf("test #%d (%s)", i, test.expect) 183 config := s.config 184 test.f(&config) 185 manifold := httpserverargs.Manifold(config) 186 w, err := manifold.Start(s.context) 187 workertest.CheckNilOrKill(c, w) 188 c.Check(err, gc.ErrorMatches, test.expect) 189 } 190 } 191 192 type mockLocalMacaroonAuthenticator struct { 193 macaroon.LocalMacaroonAuthenticator 194 } 195 196 type stubStateTracker struct { 197 testing.Stub 198 pool state.StatePool 199 } 200 201 func (s *stubStateTracker) Use() (*state.StatePool, error) { 202 s.MethodCall(s, "Use") 203 return &s.pool, s.NextErr() 204 } 205 206 func (s *stubStateTracker) Done() error { 207 s.MethodCall(s, "Done") 208 return s.NextErr() 209 } 210 211 func (s *stubStateTracker) Report() map[string]interface{} { 212 s.MethodCall(s, "Report") 213 return nil 214 }