github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/api/base/testing/apicaller.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package testing 5 6 import ( 7 "net/http" 8 "net/url" 9 10 "github.com/juju/errors" 11 "github.com/juju/httprequest" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/names.v2" 16 "gopkg.in/macaroon-bakery.v2-unstable/httpbakery" 17 18 "github.com/juju/juju/api/base" 19 coretesting "github.com/juju/juju/testing" 20 ) 21 22 // APICallerFunc is a function type that implements APICaller. 23 // The only method that actually does anything is APICall itself 24 // which calls the function. The other methods are just stubs. 25 type APICallerFunc func(objType string, version int, id, request string, params, response interface{}) error 26 27 func (f APICallerFunc) APICall(objType string, version int, id, request string, params, response interface{}) error { 28 return f(objType, version, id, request, params, response) 29 } 30 31 func (APICallerFunc) BestFacadeVersion(facade string) int { 32 // TODO(fwereade): this should return something arbitrary (e.g. 37) 33 // so that it can't be confused with mere uninitialized data. 34 return 0 35 } 36 37 func (APICallerFunc) ModelTag() (names.ModelTag, bool) { 38 return coretesting.ModelTag, true 39 } 40 41 func (APICallerFunc) Close() error { 42 return nil 43 } 44 45 func (APICallerFunc) HTTPClient() (*httprequest.Client, error) { 46 return nil, errors.New("no HTTP client available in this test") 47 } 48 49 func (APICallerFunc) BakeryClient() *httpbakery.Client { 50 panic("no bakery client available in this test") 51 } 52 53 func (APICallerFunc) ConnectStream(path string, attrs url.Values) (base.Stream, error) { 54 return nil, errors.NotImplementedf("stream connection") 55 } 56 57 func (APICallerFunc) ConnectControllerStream(path string, attrs url.Values, headers http.Header) (base.Stream, error) { 58 return nil, errors.NotImplementedf("controller stream connection") 59 } 60 61 // BestVersionCaller is an APICallerFunc that has a particular best version. 62 type BestVersionCaller struct { 63 APICallerFunc 64 BestVersion int 65 } 66 67 func (c BestVersionCaller) BestFacadeVersion(facade string) int { 68 return c.BestVersion 69 } 70 71 // CallChecker is an APICaller implementation that checks 72 // calls as they are made. 73 type CallChecker struct { 74 APICallerFunc 75 76 // CallCount records the current call count. 77 CallCount int 78 } 79 80 // APICall describes an expected API call. 81 type APICall struct { 82 // If Check is non-nil, all other fields will be ignored and Check 83 // will be called to check the call. 84 Check func(objType string, version int, id, request string, params, response interface{}) error 85 86 // Facade holds the expected call facade. If it's empty, 87 // any facade will be accepted. 88 Facade string 89 90 // Version holds the expected call version. If it's zero, 91 // any version will be accepted unless VersionIsZero is true. 92 Version int 93 94 // VersionIsZero holds whether the version is expected to be zero. 95 VersionIsZero bool 96 97 // Id holds the expected call id. If it's empty, any id will be 98 // accepted unless IdIsEmpty is true. 99 Id string 100 101 // IdIsEmpty holds whether the call id is expected to be empty. 102 IdIsEmpty bool 103 104 // Method holds the expected method. 105 Method string 106 107 // Args holds the expected value of the call's argument. 108 Args interface{} 109 110 // Results is assigned to the result parameter of the call on return. 111 Results interface{} 112 113 // Error is returned from the call. 114 Error error 115 } 116 117 // APICallChecker returns an APICaller implementation that checks 118 // API calls. Each element of calls corresponds to an expected 119 // API call. If more calls are made than there are elements, they 120 // will not be checked - check the value of the Count field 121 // to ensure that the expected number of calls have been made. 122 // 123 // Note that the returned value is not thread-safe - do not 124 // use it if the client is making concurrent calls. 125 func APICallChecker(c *gc.C, calls ...APICall) *CallChecker { 126 var checker CallChecker 127 checker.APICallerFunc = func(facade string, version int, id, method string, inArgs, outResults interface{}) error { 128 call := checker.CallCount 129 checker.CallCount++ 130 if call >= len(calls) { 131 return nil 132 } 133 return checkArgs(c, calls[call], facade, version, id, method, inArgs, outResults) 134 } 135 return &checker 136 } 137 138 func checkArgs(c *gc.C, args APICall, facade string, version int, id, method string, inArgs, outResults interface{}) error { 139 if args.Facade != "" { 140 c.Check(facade, gc.Equals, args.Facade) 141 } 142 if args.Version != 0 { 143 c.Check(version, gc.Equals, args.Version) 144 } else if args.VersionIsZero { 145 c.Check(version, gc.Equals, 0) 146 } 147 if args.Id != "" { 148 c.Check(id, gc.Equals, args.Id) 149 } else if args.IdIsEmpty { 150 c.Check(id, gc.Equals, "") 151 } 152 if args.Method != "" { 153 c.Check(method, gc.Equals, args.Method) 154 } 155 if args.Args != nil { 156 c.Check(inArgs, jc.DeepEquals, args.Args) 157 } 158 if args.Results != nil { 159 c.Check(outResults, gc.NotNil) 160 testing.PatchValue(outResults, args.Results) 161 } 162 return args.Error 163 } 164 165 type notifyingAPICaller struct { 166 base.APICaller 167 called chan<- struct{} 168 } 169 170 func (c notifyingAPICaller) APICall(objType string, version int, id, request string, params, response interface{}) error { 171 c.called <- struct{}{} 172 return c.APICaller.APICall(objType, version, id, request, params, response) 173 } 174 175 // NotifyingAPICaller returns an APICaller implementation which sends a 176 // message on the given channel every time it receives a call. 177 func NotifyingAPICaller(c *gc.C, called chan<- struct{}, caller base.APICaller) base.APICaller { 178 return notifyingAPICaller{ 179 APICaller: caller, 180 called: called, 181 } 182 } 183 184 type apiCallerWithBakery struct { 185 APICallerFunc 186 bakeryClient *httpbakery.Client 187 } 188 189 func (a *apiCallerWithBakery) BakeryClient() *httpbakery.Client { 190 return a.bakeryClient 191 } 192 193 // APICallerWithBakery returns an api caller with a bakery client which uses the 194 // specified discharge acquirer. 195 func APICallerWithBakery(callerFunc APICallerFunc, dischargeAcquirer httpbakery.DischargeAcquirer) *apiCallerWithBakery { 196 client := &httpbakery.Client{DischargeAcquirer: dischargeAcquirer} 197 return &apiCallerWithBakery{callerFunc, client} 198 } 199 200 // StubFacadeCaller is a testing stub implementation of api/base.FacadeCaller. 201 type StubFacadeCaller struct { 202 // Stub is the raw stub used to track calls and errors. 203 Stub *testing.Stub 204 // These control the values returned by the stub's methods. 205 FacadeCallFn func(name string, params, response interface{}) error 206 ReturnName string 207 ReturnBestAPIVersion int 208 ReturnRawAPICaller base.APICaller 209 } 210 211 // FacadeCall implements api/base.FacadeCaller. 212 func (s *StubFacadeCaller) FacadeCall(request string, params, response interface{}) error { 213 s.Stub.AddCall("FacadeCall", request, params, response) 214 if err := s.Stub.NextErr(); err != nil { 215 return errors.Trace(err) 216 } 217 218 if s.FacadeCallFn != nil { 219 return s.FacadeCallFn(request, params, response) 220 } 221 return nil 222 } 223 224 // Name implements api/base.FacadeCaller. 225 func (s *StubFacadeCaller) Name() string { 226 s.Stub.AddCall("Name") 227 s.Stub.PopNoErr() 228 229 return s.ReturnName 230 } 231 232 // BestAPIVersion implements api/base.FacadeCaller. 233 func (s *StubFacadeCaller) BestAPIVersion() int { 234 s.Stub.AddCall("BestAPIVersion") 235 s.Stub.PopNoErr() 236 237 return s.ReturnBestAPIVersion 238 } 239 240 // RawAPICaller implements api/base.FacadeCaller. 241 func (s *StubFacadeCaller) RawAPICaller() base.APICaller { 242 s.Stub.AddCall("RawAPICaller") 243 s.Stub.PopNoErr() 244 245 return s.ReturnRawAPICaller 246 }