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