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