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
83// Length returns the number of bytes in the body.
84func (body *Body) Length() int64 {
85	if body == nil {
86		return 0
87	}
88	return int64(len(body.b))
89}
90
91type response struct {
92	r *http.Response
93	e error
94	d time.Duration
95}
96
97// Sender implements a simple null sender.
98type Sender struct {
99	attempts       int
100	responses      []response
101	numResponses   int
102	repeatResponse []int
103	err            error
104	repeatError    int
105	emitErrorAfter int
106}
107
108// NewSender creates a new instance of Sender.
109func NewSender() *Sender {
110	return &Sender{}
111}
112
113// Do accepts the passed request and, based on settings, emits a response and possible error.
114func (c *Sender) Do(r *http.Request) (resp *http.Response, err error) {
115	c.attempts++
116
117	if len(c.responses) > 0 {
118		resp = c.responses[0].r
119		if resp != nil {
120			if b, ok := resp.Body.(*Body); ok {
121				b.reset()
122			}
123		} else {
124			err = c.responses[0].e
125		}
126		time.Sleep(c.responses[0].d)
127		c.repeatResponse[0]--
128		if c.repeatResponse[0] == 0 {
129			c.responses = c.responses[1:]
130			c.repeatResponse = c.repeatResponse[1:]
131		}
132	} else {
133		resp = NewResponse()
134	}
135	if resp != nil {
136		resp.Request = r
137	}
138
139	if c.emitErrorAfter > 0 {
140		c.emitErrorAfter--
141	} else if c.err != nil {
142		err = c.err
143		c.repeatError--
144		if c.repeatError == 0 {
145			c.err = nil
146		}
147	}
148
149	return
150}
151
152// AppendResponse adds the passed http.Response to the response stack.
153func (c *Sender) AppendResponse(resp *http.Response) {
154	c.AppendAndRepeatResponse(resp, 1)
155}
156
157// AppendResponseWithDelay adds the passed http.Response to the response stack with the specified delay.
158func (c *Sender) AppendResponseWithDelay(resp *http.Response, delay time.Duration) {
159	c.AppendAndRepeatResponseWithDelay(resp, delay, 1)
160}
161
162// AppendAndRepeatResponse adds the passed http.Response to the response stack along with a
163// repeat count. A negative repeat count will return the response for all remaining calls to Do.
164func (c *Sender) AppendAndRepeatResponse(resp *http.Response, repeat int) {
165	c.appendAndRepeat(response{r: resp}, repeat)
166}
167
168// AppendAndRepeatResponseWithDelay adds the passed http.Response to the response stack with the specified
169// delay along with a repeat count. A negative repeat count will return the response for all remaining calls to Do.
170func (c *Sender) AppendAndRepeatResponseWithDelay(resp *http.Response, delay time.Duration, repeat int) {
171	c.appendAndRepeat(response{r: resp, d: delay}, repeat)
172}
173
174// AppendError adds the passed error to the response stack.
175func (c *Sender) AppendError(err error) {
176	c.AppendAndRepeatError(err, 1)
177}
178
179// AppendAndRepeatError adds the passed error to the response stack along with a repeat
180// count. A negative repeat count will return the response for all remaining calls to Do.
181func (c *Sender) AppendAndRepeatError(err error, repeat int) {
182	c.appendAndRepeat(response{e: err}, repeat)
183}
184
185func (c *Sender) appendAndRepeat(resp response, repeat int) {
186	if c.responses == nil {
187		c.responses = []response{resp}
188		c.repeatResponse = []int{repeat}
189	} else {
190		c.responses = append(c.responses, resp)
191		c.repeatResponse = append(c.repeatResponse, repeat)
192	}
193	c.numResponses++
194}
195
196// Attempts returns the number of times Do was called.
197func (c *Sender) Attempts() int {
198	return c.attempts
199}
200
201// SetError sets the error Do should return.
202func (c *Sender) SetError(err error) {
203	c.SetAndRepeatError(err, 1)
204}
205
206// SetAndRepeatError sets the error Do should return and how many calls to Do will return the error.
207// A negative repeat value will return the error for all remaining calls to Do.
208func (c *Sender) SetAndRepeatError(err error, repeat int) {
209	c.err = err
210	c.repeatError = repeat
211}
212
213// SetEmitErrorAfter sets the number of attempts to be made before errors are emitted.
214func (c *Sender) SetEmitErrorAfter(ea int) {
215	c.emitErrorAfter = ea
216}
217
218// NumResponses returns the number of responses that have been added to the sender.
219func (c *Sender) NumResponses() int {
220	return c.numResponses
221}
222
223// T is a simple testing struct.
224type T struct {
225	Name string `json:"name" xml:"Name"`
226	Age  int    `json:"age" xml:"Age"`
227}
228