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  }