1/* 2Package mocks provides mocks and helpers used in testing. 3*/ 4package mocks 5 6// Copyright 2017 Microsoft Corporation 7// 8// Licensed under the Apache License, Version 2.0 (the "License"); 9// you may not use this file except in compliance with the License. 10// You may obtain a copy of the License at 11// 12// http://www.apache.org/licenses/LICENSE-2.0 13// 14// Unless required by applicable law or agreed to in writing, software 15// distributed under the License is distributed on an "AS IS" BASIS, 16// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17// See the License for the specific language governing permissions and 18// limitations under the License. 19 20import ( 21 "fmt" 22 "io" 23 "net/http" 24 "time" 25) 26 27// Body implements acceptable body over a string. 28type Body struct { 29 s string 30 b []byte 31 isOpen bool 32 closeAttempts int 33} 34 35// NewBody creates a new instance of Body. 36func NewBody(s string) *Body { 37 return (&Body{s: s}).reset() 38} 39 40// NewBodyClose creates a new instance of Body. 41func NewBodyClose(s string) *Body { 42 return &Body{s: s} 43} 44 45// Read reads into the passed byte slice and returns the bytes read. 46func (body *Body) Read(b []byte) (n int, err error) { 47 if !body.IsOpen() { 48 return 0, fmt.Errorf("ERROR: Body has been closed") 49 } 50 if len(body.b) == 0 { 51 return 0, io.EOF 52 } 53 n = copy(b, body.b) 54 body.b = body.b[n:] 55 return n, nil 56} 57 58// Close closes the body. 59func (body *Body) Close() error { 60 if body.isOpen { 61 body.isOpen = false 62 body.closeAttempts++ 63 } 64 return nil 65} 66 67// CloseAttempts returns the number of times Close was called. 68func (body *Body) CloseAttempts() int { 69 return body.closeAttempts 70} 71 72// IsOpen returns true if the Body has not been closed, false otherwise. 73func (body *Body) IsOpen() bool { 74 return body.isOpen 75} 76 77func (body *Body) reset() *Body { 78 body.isOpen = true 79 body.b = []byte(body.s) 80 return body 81} 82 83type response struct { 84 r *http.Response 85 e error 86 d time.Duration 87} 88 89// Sender implements a simple null sender. 90type Sender struct { 91 attempts int 92 responses []response 93 numResponses int 94 repeatResponse []int 95 err error 96 repeatError int 97 emitErrorAfter int 98} 99 100// NewSender creates a new instance of Sender. 101func NewSender() *Sender { 102 return &Sender{} 103} 104 105// Do accepts the passed request and, based on settings, emits a response and possible error. 106func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) { 107 c.attempts++ 108 109 if len(c.responses) > 0 { 110 resp = c.responses[0].r 111 if resp != nil { 112 if b, ok := resp.Body.(*Body); ok { 113 b.reset() 114 } 115 } else { 116 err = c.responses[0].e 117 } 118 time.Sleep(c.responses[0].d) 119 c.repeatResponse[0]-- 120 if c.repeatResponse[0] == 0 { 121 c.responses = c.responses[1:] 122 c.repeatResponse = c.repeatResponse[1:] 123 } 124 } else { 125 resp = NewResponse() 126 } 127 if resp != nil { 128 resp.Request = r 129 } 130 131 if c.emitErrorAfter > 0 { 132 c.emitErrorAfter-- 133 } else if c.err != nil { 134 err = c.err 135 c.repeatError-- 136 if c.repeatError == 0 { 137 c.err = nil 138 } 139 } 140 141 return 142} 143 144// AppendResponse adds the passed http.Response to the response stack. 145func (c *Sender) AppendResponse(resp *http.Response) { 146 c.AppendAndRepeatResponse(resp, 1) 147} 148 149// AppendResponseWithDelay adds the passed http.Response to the response stack with the specified delay. 150func (c *Sender) AppendResponseWithDelay(resp *http.Response, delay time.Duration) { 151 c.AppendAndRepeatResponseWithDelay(resp, delay, 1) 152} 153 154// AppendAndRepeatResponse adds the passed http.Response to the response stack along with a 155// repeat count. A negative repeat count will return the response for all remaining calls to Do. 156func (c *Sender) AppendAndRepeatResponse(resp *http.Response, repeat int) { 157 c.appendAndRepeat(response{r: resp}, repeat) 158} 159 160// AppendAndRepeatResponseWithDelay adds the passed http.Response to the response stack with the specified 161// delay along with a repeat count. A negative repeat count will return the response for all remaining calls to Do. 162func (c *Sender) AppendAndRepeatResponseWithDelay(resp *http.Response, delay time.Duration, repeat int) { 163 c.appendAndRepeat(response{r: resp, d: delay}, repeat) 164} 165 166// AppendError adds the passed error to the response stack. 167func (c *Sender) AppendError(err error) { 168 c.AppendAndRepeatError(err, 1) 169} 170 171// AppendAndRepeatError adds the passed error to the response stack along with a repeat 172// count. A negative repeat count will return the response for all remaining calls to Do. 173func (c *Sender) AppendAndRepeatError(err error, repeat int) { 174 c.appendAndRepeat(response{e: err}, repeat) 175} 176 177func (c *Sender) appendAndRepeat(resp response, repeat int) { 178 if c.responses == nil { 179 c.responses = []response{resp} 180 c.repeatResponse = []int{repeat} 181 } else { 182 c.responses = append(c.responses, resp) 183 c.repeatResponse = append(c.repeatResponse, repeat) 184 } 185 c.numResponses++ 186} 187 188// Attempts returns the number of times Do was called. 189func (c *Sender) Attempts() int { 190 return c.attempts 191} 192 193// SetError sets the error Do should return. 194func (c *Sender) SetError(err error) { 195 c.SetAndRepeatError(err, 1) 196} 197 198// SetAndRepeatError sets the error Do should return and how many calls to Do will return the error. 199// A negative repeat value will return the error for all remaining calls to Do. 200func (c *Sender) SetAndRepeatError(err error, repeat int) { 201 c.err = err 202 c.repeatError = repeat 203} 204 205// SetEmitErrorAfter sets the number of attempts to be made before errors are emitted. 206func (c *Sender) SetEmitErrorAfter(ea int) { 207 c.emitErrorAfter = ea 208} 209 210// NumResponses returns the number of responses that have been added to the sender. 211func (c *Sender) NumResponses() int { 212 return c.numResponses 213} 214 215// T is a simple testing struct. 216type T struct { 217 Name string `json:"name" xml:"Name"` 218 Age int `json:"age" xml:"Age"` 219} 220