github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/caasbroker/fixture_test.go (about)

     1  // Copyright 2017 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package caasbroker_test
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/testing"
    12  	"github.com/juju/worker/v3"
    13  	"github.com/juju/worker/v3/workertest"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/caas"
    17  	"github.com/juju/juju/controller"
    18  	"github.com/juju/juju/core/watcher"
    19  	"github.com/juju/juju/environs"
    20  	environscloudspec "github.com/juju/juju/environs/cloudspec"
    21  	"github.com/juju/juju/environs/config"
    22  	jujutesting "github.com/juju/juju/testing"
    23  )
    24  
    25  type fixture struct {
    26  	watcherErr    error
    27  	observerErrs  []error
    28  	initialConfig map[string]interface{}
    29  	initialSpec   environscloudspec.CloudSpec
    30  }
    31  
    32  func (fix *fixture) Run(c *gc.C, test func(*runContext)) {
    33  	watcher := newNotifyWatcher(fix.watcherErr)
    34  	defer workertest.DirtyKill(c, watcher)
    35  	cloudWatcher := newNotifyWatcher(fix.watcherErr)
    36  	defer workertest.DirtyKill(c, cloudWatcher)
    37  	context := &runContext{
    38  		cloud:        fix.initialSpec,
    39  		config:       newModelConfig(c, fix.initialConfig),
    40  		watcher:      watcher,
    41  		cloudWatcher: cloudWatcher,
    42  	}
    43  	context.stub.SetErrors(fix.observerErrs...)
    44  	test(context)
    45  }
    46  
    47  type runContext struct {
    48  	mu           sync.Mutex
    49  	stub         testing.Stub
    50  	cloud        environscloudspec.CloudSpec
    51  	config       map[string]interface{}
    52  	watcher      *notifyWatcher
    53  	cloudWatcher *notifyWatcher
    54  	credWatcher  *notifyWatcher
    55  }
    56  
    57  // SetConfig updates the configuration returned by ModelConfig.
    58  func (context *runContext) SetConfig(c *gc.C, extraAttrs jujutesting.Attrs) {
    59  	context.mu.Lock()
    60  	defer context.mu.Unlock()
    61  	context.config = newModelConfig(c, extraAttrs)
    62  }
    63  
    64  // SetCloudSpec updates the spec returned by CloudSpec.
    65  func (context *runContext) SetCloudSpec(c *gc.C, spec environscloudspec.CloudSpec) {
    66  	context.mu.Lock()
    67  	defer context.mu.Unlock()
    68  	context.cloud = spec
    69  }
    70  
    71  // CloudSpec is part of the environ.ConfigObserver interface.
    72  func (context *runContext) CloudSpec() (environscloudspec.CloudSpec, error) {
    73  	context.mu.Lock()
    74  	defer context.mu.Unlock()
    75  	context.stub.AddCall("CloudSpec")
    76  	if err := context.stub.NextErr(); err != nil {
    77  		return environscloudspec.CloudSpec{}, err
    78  	}
    79  	return context.cloud, nil
    80  }
    81  
    82  // ModelConfig is part of the environ.ConfigObserver interface.
    83  func (context *runContext) ModelConfig() (*config.Config, error) {
    84  	context.mu.Lock()
    85  	defer context.mu.Unlock()
    86  	context.stub.AddCall("ModelConfig")
    87  	if err := context.stub.NextErr(); err != nil {
    88  		return nil, err
    89  	}
    90  	return config.New(config.UseDefaults, context.config)
    91  }
    92  
    93  func (context *runContext) ControllerConfig() (controller.Config, error) {
    94  	context.mu.Lock()
    95  	defer context.mu.Unlock()
    96  	context.stub.AddCall("ControllerConfig")
    97  	if err := context.stub.NextErr(); err != nil {
    98  		return nil, err
    99  	}
   100  	return jujutesting.FakeControllerConfig(), nil
   101  }
   102  
   103  // KillModelConfigNotify kills the watcher returned from WatchForModelConfigChanges with
   104  // the error configured in the enclosing fixture.
   105  func (context *runContext) KillModelConfigNotify() {
   106  	context.watcher.Kill()
   107  }
   108  
   109  // SendModelConfigNotify sends a value on the channel used by WatchForModelConfigChanges
   110  // results.
   111  func (context *runContext) SendModelConfigNotify() {
   112  	context.watcher.changes <- struct{}{}
   113  }
   114  
   115  // CloseModelConfigNotify closes the channel used by WatchForModelConfigChanges results.
   116  func (context *runContext) CloseModelConfigNotify() {
   117  	close(context.watcher.changes)
   118  }
   119  
   120  // KillCloudSpecNotify kills the watcher returned from WatchCloudSpecChanges with
   121  // the error configured in the enclosing fixture.
   122  func (context *runContext) KillCloudSpecNotify() {
   123  	context.cloudWatcher.Kill()
   124  }
   125  
   126  // SendCloudSpecNotify sends a value on the channel used by WatchCloudSpecChanges
   127  // results.
   128  func (context *runContext) SendCloudSpecNotify() {
   129  	context.cloudWatcher.changes <- struct{}{}
   130  }
   131  
   132  // CloseCloudSpecNotify closes the channel used by WatchCloudSpecChanges results.
   133  func (context *runContext) CloseCloudSpecNotify() {
   134  	close(context.cloudWatcher.changes)
   135  }
   136  
   137  // WatchForModelConfigChanges is part of the environ.ConfigObserver interface.
   138  func (context *runContext) WatchForModelConfigChanges() (watcher.NotifyWatcher, error) {
   139  	context.mu.Lock()
   140  	defer context.mu.Unlock()
   141  	context.stub.AddCall("WatchForModelConfigChanges")
   142  	if err := context.stub.NextErr(); err != nil {
   143  		return nil, err
   144  	}
   145  	return context.watcher, nil
   146  }
   147  
   148  func (context *runContext) WatchCloudSpecChanges() (watcher.NotifyWatcher, error) {
   149  	context.mu.Lock()
   150  	defer context.mu.Unlock()
   151  	context.stub.AddCall("WatchCloudSpecChanges")
   152  	if err := context.stub.NextErr(); err != nil {
   153  		return nil, err
   154  	}
   155  	return context.cloudWatcher, nil
   156  }
   157  
   158  // KillCredentialNotify kills the watcher returned from WatchCredentialChanges with
   159  // the error configured in the enclosing fixture.
   160  func (context *runContext) KillCredentialNotify() {
   161  	context.credWatcher.Kill()
   162  }
   163  
   164  // SendCredentialNotify sends a value on the channel used by WatchCredentialChanges
   165  // results.
   166  func (context *runContext) SendCredentialNotify() {
   167  	context.credWatcher.changes <- struct{}{}
   168  }
   169  
   170  // CloseCredentialNotify closes the channel used by WatchCredentialChanges results.
   171  func (context *runContext) CloseCredentialNotify() {
   172  	close(context.credWatcher.changes)
   173  }
   174  
   175  // WatchCredential is part of the environ.ConfigObserver interface.
   176  func (context *runContext) WatchCredential(cred names.CloudCredentialTag) (watcher.NotifyWatcher, error) {
   177  	context.mu.Lock()
   178  	defer context.mu.Unlock()
   179  	context.stub.AddCall("WatchCredential")
   180  	if err := context.stub.NextErr(); err != nil {
   181  		return nil, err
   182  	}
   183  	return context.watcher, nil
   184  }
   185  
   186  func (context *runContext) CheckCallNames(c *gc.C, names ...string) {
   187  	context.mu.Lock()
   188  	defer context.mu.Unlock()
   189  	context.stub.CheckCallNames(c, names...)
   190  }
   191  
   192  // newNotifyWatcher returns a watcher.NotifyWatcher that will fail with the
   193  // supplied error when Kill()ed.
   194  func newNotifyWatcher(err error) *notifyWatcher {
   195  	return &notifyWatcher{
   196  		Worker:  workertest.NewErrorWorker(err),
   197  		changes: make(chan struct{}, 1000),
   198  	}
   199  }
   200  
   201  type notifyWatcher struct {
   202  	worker.Worker
   203  	changes chan struct{}
   204  }
   205  
   206  // Changes is part of the watcher.NotifyWatcher interface.
   207  func (w *notifyWatcher) Changes() watcher.NotifyChannel {
   208  	return w.changes
   209  }
   210  
   211  // newModelConfig returns an environment config map with the supplied attrs
   212  // (on top of some default set), or fails the test.
   213  func newModelConfig(c *gc.C, extraAttrs jujutesting.Attrs) map[string]interface{} {
   214  	return jujutesting.CustomModelConfig(c, extraAttrs).AllAttrs()
   215  }
   216  
   217  type mockBroker struct {
   218  	caas.Broker
   219  	testing.Stub
   220  	spec      environscloudspec.CloudSpec
   221  	cfg       *config.Config
   222  	namespace string
   223  	mu        sync.Mutex
   224  }
   225  
   226  func newMockBroker(_ context.Context, args environs.OpenParams) (caas.Broker, error) {
   227  	return &mockBroker{spec: args.Cloud, namespace: args.Config.Name(), cfg: args.Config}, nil
   228  }
   229  
   230  func (e *mockBroker) CloudSpec() environscloudspec.CloudSpec {
   231  	e.mu.Lock()
   232  	defer e.mu.Unlock()
   233  	return e.spec
   234  }
   235  
   236  func (e *mockBroker) SetCloudSpec(_ context.Context, spec environscloudspec.CloudSpec) error {
   237  	e.mu.Lock()
   238  	defer e.mu.Unlock()
   239  	e.MethodCall(e, "SetCloudSpec", spec)
   240  	if err := e.NextErr(); err != nil {
   241  		return err
   242  	}
   243  	e.spec = spec
   244  	return nil
   245  }
   246  
   247  func (e *mockBroker) Config() *config.Config {
   248  	e.mu.Lock()
   249  	defer e.mu.Unlock()
   250  	e.MethodCall(e, "Config")
   251  	e.PopNoErr()
   252  	return e.cfg
   253  }
   254  
   255  func (e *mockBroker) SetConfig(cfg *config.Config) error {
   256  	e.mu.Lock()
   257  	defer e.mu.Unlock()
   258  	e.MethodCall(e, "SetConfig", cfg)
   259  	if err := e.NextErr(); err != nil {
   260  		return err
   261  	}
   262  	e.cfg = cfg
   263  	return nil
   264  }