mocktopus 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +7 -0
- data/.travis.yml +9 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +19 -0
- data/README.md +309 -0
- data/Rakefile +11 -0
- data/ascii.rb +44 -0
- data/bin/mocktopus +14 -0
- data/config.ru +3 -0
- data/lib/mocktopus.rb +13 -0
- data/lib/mocktopus/app.rb +142 -0
- data/lib/mocktopus/cli.rb +31 -0
- data/lib/mocktopus/input.rb +107 -0
- data/lib/mocktopus/input_container.rb +90 -0
- data/lib/mocktopus/mock_api_call.rb +42 -0
- data/lib/mocktopus/mock_api_call_container.rb +20 -0
- data/lib/mocktopus/response.rb +64 -0
- data/mocktopus.gemspec +29 -0
- data/test/app_test.rb +276 -0
- data/test/cli_test.rb +25 -0
- data/test/input_container_test.rb +229 -0
- data/test/input_test.rb +166 -0
- data/test/mock_api_call_container_test.rb +26 -0
- data/test/mock_api_call_test.rb +35 -0
- data/test/response_test.rb +64 -0
- data/test/test_helper.rb +33 -0
- metadata +292 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5c7dd5e4c53bbd5b1aff16acb47abbffae3be411
|
4
|
+
data.tar.gz: 4517dbbda052ffb494852ec3586f698151e45e00
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 072bb9f8221692ce8b2ecf0a8222733096a0584068b4922d2c26be6dec79cfa9411f0e401203f947d93028aad4bacb5f114ad7643534ba0196fa482c99295480
|
7
|
+
data.tar.gz: 4a9c3716d2a2dad0681cc0c5643a4179ad366dad33f78c25c7644ae23753bd2ed82bf91ec694b3520aae5245a6b9c1eebb030ad5fe761078ba23ec7bbc31e334
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Rackspace, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,309 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/rackspaceautomationco/mocktopus.svg?branch=v0.0.1)](https://travis-ci.org/rackspaceautomationco/mocktopus) [![Coverage Status](https://coveralls.io/repos/rackspaceautomationco/mocktopus/badge.svg?branch=master)](https://coveralls.io/r/rackspaceautomationco/mocktopus?branch=master) [![Gem Version](https://badge.fury.io/rb/mocktopus.svg)](http://badge.fury.io/rb/mocktopus)
|
2
|
+
|
3
|
+
. .
|
4
|
+
-|-|-. .-,
|
5
|
+
'-' '-`'-
|
6
|
+
. .
|
7
|
+
,-,-. ,-. ,-. | , |- ,-. ,-. . . ,-.
|
8
|
+
| | | | | | |< | | | | | | | `-.
|
9
|
+
' ' ' `-' `-' ' ` `' `-' |-' `-^ `-'
|
10
|
+
|
|
11
|
+
'
|
12
|
+
.....
|
13
|
+
.
|
14
|
+
. ..
|
15
|
+
.. ......
|
16
|
+
.. ........ .....
|
17
|
+
.. .. .. .. .. .
|
18
|
+
. .......... .
|
19
|
+
. .. .. .
|
20
|
+
. ...... . .
|
21
|
+
. ...... . ..
|
22
|
+
.. ........ ...
|
23
|
+
............................ ......
|
24
|
+
....................... .
|
25
|
+
... ......... .
|
26
|
+
.. ........... .
|
27
|
+
.. .......... .. ..
|
28
|
+
. .... .. .. ...... ... .
|
29
|
+
.. .. .. ..... .. ... .
|
30
|
+
.. .. .. . .. . . ..... ...
|
31
|
+
...... . . . .. . . ...
|
32
|
+
. . . . . . .
|
33
|
+
. . . . .. . . .
|
34
|
+
. .. . .. .. . .
|
35
|
+
...... . . ... ..
|
36
|
+
. . ..... ....
|
37
|
+
. . ....
|
38
|
+
.. . ..
|
39
|
+
. . .
|
40
|
+
. ... ...
|
41
|
+
..
|
42
|
+
........
|
43
|
+
|
44
|
+
## About
|
45
|
+
|
46
|
+
The Mocktopus is a Sinatra/thin-based Web API that lets you mock your app's dependencies. A few setup-related endpoints allow you to tell The Mocktopus how to intercept and respond to any combination of uri/method/uri/headers/body.
|
47
|
+
|
48
|
+
Written in Ruby, The Mocktopus differs from similar tools in its lack of plugin or code-driven setups. The Mocktopus is meant to run standalone, primed, and configured exclusively by The Mocktopus Web API. This means that an instance of The Mocktopus can be configured on the fly and independently of your apps/code. [Inspiration](https://www.youtube.com/watch?v=JJ4S9khZKjk).
|
49
|
+
|
50
|
+
## Usage
|
51
|
+
|
52
|
+
1. Install The Mocktopus gem
|
53
|
+
|
54
|
+
`$ gem install mocktopus`
|
55
|
+
|
56
|
+
2. Start The Mocktopus (add -p {PORT} to specify a port other than default/8081)
|
57
|
+
|
58
|
+
`$ mocktopus start`
|
59
|
+
|
60
|
+
3. Tell The Mocktopus how to behave by [creating an input](#create-an-input)
|
61
|
+
|
62
|
+
`POST 'http://localhost:8081/mocktopus/inputs/:name' ...`
|
63
|
+
|
64
|
+
4. Point your app to The Mocktopus to fake dependencies or prototype new code
|
65
|
+
|
66
|
+
>The Mocktopus does not persist the mocks that have been set up, which means that if the service is restarted, any mock setups that have been created will have to be resubmitted.
|
67
|
+
|
68
|
+
## Endpoints
|
69
|
+
|
70
|
+
This section describes the endpoints that The Mocktopus provides for managing mocks as well as reviewing any calls that it receives.
|
71
|
+
|
72
|
+
### Create an input
|
73
|
+
|
74
|
+
`POST '/mocktopus/inputs/:name'`
|
75
|
+
|
76
|
+
Creates a setup instructing The Mocktopus on how to behave for a specific call. :name is for your use only in identifying, updating, or deleting the setup later. A sample payload follows:
|
77
|
+
|
78
|
+
```json
|
79
|
+
{
|
80
|
+
"uri" : "/domain/domain.com/users",
|
81
|
+
"headers" : {
|
82
|
+
"whitelisting_key_here" : "value"
|
83
|
+
},
|
84
|
+
"body" : {
|
85
|
+
"name" : "the mocktopus",
|
86
|
+
"email" : "the_mocktopus@the_mocktopus.com"
|
87
|
+
},
|
88
|
+
"verb" : "POST",
|
89
|
+
"response" : {
|
90
|
+
"code" : "202",
|
91
|
+
"delay": 5000,
|
92
|
+
"headers" : {},
|
93
|
+
"body" : "Thanks!"
|
94
|
+
}
|
95
|
+
}
|
96
|
+
```
|
97
|
+
|
98
|
+
> POSTing to the same :name will overwrite any existing payload at that :name with the new payload.
|
99
|
+
|
100
|
+
The uri, verb, and response properties are all required. Within the response property, code is the only required property.
|
101
|
+
|
102
|
+
| Property | Description |
|
103
|
+
|---|---|
|
104
|
+
| uri | The path and query for the mock |
|
105
|
+
| headers | Required headers for matching. Only the headers included will be used for matching purposes. Any additional headers supplied by a client hitting The Mocktopus will be ignored |
|
106
|
+
| body | The request body content used for matching |
|
107
|
+
| verb | The request verb used for matching |
|
108
|
+
| response | The response sent back to the client upon a successful match |
|
109
|
+
|
110
|
+
Response Properties
|
111
|
+
|
112
|
+
| Property | Description |
|
113
|
+
|---|---|
|
114
|
+
| code | The HTTP status code returned for the response
|
115
|
+
| delay | The amount of time in milliseconds to wait before sending the mocked response back on a successful match
|
116
|
+
| headers | A collection of headers to send back with the response |
|
117
|
+
| body | The content returned on a successful match
|
118
|
+
|
119
|
+
While The Mocktopus only accepts JSON for creating and managing mocked endpoints, it can return just about whatever you like: SOAP, XML, plain text or JSON:
|
120
|
+
|
121
|
+
```json
|
122
|
+
{
|
123
|
+
"uri" : "/domain/domain.com/users",
|
124
|
+
"headers" : {},
|
125
|
+
"body" : null,
|
126
|
+
"verb" : "GET",
|
127
|
+
"response" : {
|
128
|
+
"code" : "200",
|
129
|
+
"headers" : {
|
130
|
+
"Content-Type": "application/xml"
|
131
|
+
},
|
132
|
+
"body" : "<users><user><id>1</id><name>John</name></user><user><id>2</id><name>Jane</name></user></users>"
|
133
|
+
}
|
134
|
+
}
|
135
|
+
```
|
136
|
+
|
137
|
+
#### Matching Rules
|
138
|
+
|
139
|
+
The Mocktopus matches incoming requests against the uri, headers, body, and verb provided in the mock. In the case of headers, only the headers that are actually provided in the mock are validated. Any additional headers provided by the client are ignored.
|
140
|
+
|
141
|
+
Matches are **case-sensitive** when checking against the uri and body elements. For header matching, the header key is not case-sensitive, however the test against the header value is.
|
142
|
+
|
143
|
+
Query string parameters can be included in the uri property. Both the keys and values are case-sensitive, and the order of the query string parameters must be the same.
|
144
|
+
|
145
|
+
>Because The Mocktopus does exact matching, with JSON in particular an empty JSON object **{}** is different from a null or empty incoming payload. To create a mock to match against an empty payload, omit the **body** element from the mock setup or set it to null.
|
146
|
+
|
147
|
+
If The Mocktopus cannot find a match, it will return status code 428 and a text/html response describing the request it couldn't match:
|
148
|
+
|
149
|
+
```
|
150
|
+
Match not found
|
151
|
+
Unable to find a match from the following API call:
|
152
|
+
Path:
|
153
|
+
/domain/domain.com/usersaadsfad
|
154
|
+
Verb:
|
155
|
+
GET
|
156
|
+
Headers:
|
157
|
+
{"version"=>"HTTP/1.1", "host"=>"127.0.0.1:8081", "connection"=>"keep-alive", "cache_control"=>"no-cache", "accept"=>"*/*", "dnt"=>"1", "accept_encoding"=>"gzip, deflate, sdch", "accept_language"=>"en-US,en;q=0.8"}
|
158
|
+
Body:
|
159
|
+
```
|
160
|
+
|
161
|
+
#### Response Queueing
|
162
|
+
|
163
|
+
The Mocktopus allows you to queue up multiple responses for the same match. For example, suppose you have an external service that allows you to submit a request to create something. This process takes awhile, so the external services helpfully provide you with a URL where you can check the status of that thing, and when it's ready, the URL will return a 201 Created. You can simulate that polling process using response queuing.
|
164
|
+
|
165
|
+
The process you're trying to mock might look like this:
|
166
|
+
|
167
|
+
1. POST a request to create a thing
|
168
|
+
2. Receive back a payload with a URL you can use to poll
|
169
|
+
3. GET the URL you were given in step 2 until you receive back a 201 status code
|
170
|
+
|
171
|
+
With request queueing, you can set up several responses to the same request. Each match will pop a response off of the queue until there are no more, then that response will **always** be returned for any subsequent requests.
|
172
|
+
|
173
|
+
To mock this scenario, start with a mock for the initial POST and the response that is returned.
|
174
|
+
|
175
|
+
```json
|
176
|
+
{
|
177
|
+
"uri" : "/some-resource-collection-that-takes-forever",
|
178
|
+
"headers" : {},
|
179
|
+
"body" : {
|
180
|
+
"name" : "huge-thing",
|
181
|
+
"size" : "1000 petabytes"
|
182
|
+
},
|
183
|
+
"verb" : "POST",
|
184
|
+
"response" : {
|
185
|
+
"code" : "202",
|
186
|
+
"headers" : {},
|
187
|
+
"body" : {
|
188
|
+
"statusLink": "http://mocktopusip:8081/monitor/1",
|
189
|
+
"status": "creating"
|
190
|
+
}
|
191
|
+
}
|
192
|
+
}
|
193
|
+
```
|
194
|
+
With the initial POST setup, you can move to set up the monitoring calls, using the **statusLink** as set up in the body as your mocked endpoint:
|
195
|
+
|
196
|
+
```json
|
197
|
+
{
|
198
|
+
"uri" : "/monitor/1",
|
199
|
+
"headers" : {},
|
200
|
+
"body" : null,
|
201
|
+
"verb" : "GET",
|
202
|
+
"response" : {
|
203
|
+
"code" : "200",
|
204
|
+
"headers" : {},
|
205
|
+
"body" : {
|
206
|
+
"statusLink": "http://mocktopusip:8081/monitor/1",
|
207
|
+
"status": "Still going"
|
208
|
+
}
|
209
|
+
}
|
210
|
+
}
|
211
|
+
```
|
212
|
+
```json
|
213
|
+
{
|
214
|
+
"uri" : "/monitor/1",
|
215
|
+
"headers" : {},
|
216
|
+
"body" : null,
|
217
|
+
"verb" : "GET",
|
218
|
+
"response" : {
|
219
|
+
"code" : "200",
|
220
|
+
"headers" : {},
|
221
|
+
"body" : {
|
222
|
+
"statusLink": "http://mocktopusip:8081/monitor/1",
|
223
|
+
"status": "Yep, still going"
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
```
|
228
|
+
```json
|
229
|
+
{
|
230
|
+
"uri" : "/monitor/1",
|
231
|
+
"headers" : {},
|
232
|
+
"body" : null,
|
233
|
+
"verb" : "GET",
|
234
|
+
"response" : {
|
235
|
+
"code" : "200",
|
236
|
+
"headers" : {},
|
237
|
+
"body" : {
|
238
|
+
"statusLink": "http://mocktopusip:8081/monitor/1",
|
239
|
+
"status": "Almost there..."
|
240
|
+
}
|
241
|
+
}
|
242
|
+
}
|
243
|
+
```
|
244
|
+
|
245
|
+
With this, you have set up three responses for the same request match. While The Mocktopus doesn't have a concept of returning a request for a period of time, you can set up as many responses to the same request as you want.
|
246
|
+
|
247
|
+
Now that you have set up your request to simulate polling, you can add your final request which will be returned when a client hits the /monitor/1 resource:
|
248
|
+
|
249
|
+
|
250
|
+
```json
|
251
|
+
{
|
252
|
+
"uri" : "/monitor/1",
|
253
|
+
"headers" : {},
|
254
|
+
"body" : null,
|
255
|
+
"verb" : "GET",
|
256
|
+
"response" : {
|
257
|
+
"code" : "201",
|
258
|
+
"headers" : {},
|
259
|
+
"body" : {
|
260
|
+
"self": "http://mocktopusip:8081/some-resource-collection-that-takes-forever/huge-thing"
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
```
|
265
|
+
|
266
|
+
You could continue this mock setup by then mocking the /some-resource-collection-that-takes-forever/huge-thing endpoint with the correct payload.
|
267
|
+
|
268
|
+
|
269
|
+
It's important to note that even though these are matching against the same request, the mocks still have to be POSTed to unique endpoints within The Mocktopus, otherwise the responses would be overwritten, not queued:
|
270
|
+
|
271
|
+
- POST /mocktopus/inputs/1
|
272
|
+
- POST /mocktopus/inputs/2
|
273
|
+
- POST /mocktopus/inputs/3
|
274
|
+
|
275
|
+
### Retrieve an input
|
276
|
+
|
277
|
+
`GET '/mocktopus/inputs/[:name]' `
|
278
|
+
|
279
|
+
Retrieves a specific input by name (if specified), or a serialized list of all inputs.
|
280
|
+
|
281
|
+
### Delete input(s)
|
282
|
+
|
283
|
+
`DELETE '/mocktopus/inputs/[:name]' `
|
284
|
+
|
285
|
+
Deletes a specific input by name (if specified), or deletes all inputs.
|
286
|
+
|
287
|
+
### Get all calls received by The Mocktopus
|
288
|
+
|
289
|
+
`GET '/mocktopus/mock_api_calls' `
|
290
|
+
|
291
|
+
Retrieves a serialized list of all calls received by The Mocktopus (excluding setups).
|
292
|
+
|
293
|
+
### Delete all calls
|
294
|
+
|
295
|
+
`DELETE '/mocktopus/mock_api_calls' `
|
296
|
+
|
297
|
+
Deletes all stored calls.
|
298
|
+
|
299
|
+
## Contributing
|
300
|
+
|
301
|
+
1. Fork/clone The Mocktopus repo
|
302
|
+
2. Make sure tests pass by running `rake` at the root of the project
|
303
|
+
3. Add tests for your change. Make your change, and make sure that tests pass by running `rake` again
|
304
|
+
4. Commit to your fork using a good commit message
|
305
|
+
5. Push and submit a pull request
|
306
|
+
|
307
|
+
## License
|
308
|
+
|
309
|
+
Distributed under the [MIT-LICENSE](/MIT-LICENSE)
|
data/Rakefile
ADDED
data/ascii.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
ascii = <<-EOH
|
2
|
+
. .
|
3
|
+
-|-|-. .-,
|
4
|
+
'-' '-`'-
|
5
|
+
. .
|
6
|
+
,-,-. ,-. ,-. | , |- ,-. ,-. . . ,-.
|
7
|
+
| | | | | | |< | | | | | | | `-.
|
8
|
+
' ' ' `-' `-' ' ` `' `-' |-' `-^ `-'
|
9
|
+
|
|
10
|
+
'
|
11
|
+
.....
|
12
|
+
.
|
13
|
+
. ..
|
14
|
+
.. ......
|
15
|
+
.. ........ .....
|
16
|
+
.. .. .. .. .. .
|
17
|
+
. .......... .
|
18
|
+
. .. .. .
|
19
|
+
. ...... . .
|
20
|
+
. ...... . ..
|
21
|
+
.. ........ ...
|
22
|
+
............................ ......
|
23
|
+
....................... .
|
24
|
+
... ......... .
|
25
|
+
.. ........... .
|
26
|
+
.. .......... .. ..
|
27
|
+
. .... .. .. ...... ... .
|
28
|
+
.. .. .. ..... .. ... .
|
29
|
+
.. .. .. . .. . . ..... ...
|
30
|
+
...... . . . .. . . ...
|
31
|
+
. . . . . . .
|
32
|
+
. . . . .. . . .
|
33
|
+
. .. . .. .. . .
|
34
|
+
...... . . ... ..
|
35
|
+
. . ..... ....
|
36
|
+
. . ....
|
37
|
+
.. . ..
|
38
|
+
. . .
|
39
|
+
. ... ...
|
40
|
+
..
|
41
|
+
........
|
42
|
+
EOH
|
43
|
+
|
44
|
+
puts ascii
|
data/bin/mocktopus
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "pathname"
|
3
|
+
pn = Pathname.new(__FILE__)
|
4
|
+
bin_file = pn.realpath
|
5
|
+
$:.unshift File.expand_path("../../lib", bin_file)
|
6
|
+
|
7
|
+
require 'mocktopus/cli'
|
8
|
+
|
9
|
+
root = File.expand_path('../..', bin_file)
|
10
|
+
ENV['BUNDLE_GEMFILE'] = "#{root}/Gemfile"
|
11
|
+
ENV['CONFIG_RU'] = "#{root}/config.ru"
|
12
|
+
|
13
|
+
Mocktopus::CLI.source_root(File.expand_path('../../', bin_file))
|
14
|
+
Mocktopus::CLI.start(ARGV)
|
data/config.ru
ADDED
data/lib/mocktopus.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'logger'
|
2
|
+
LOGGER = Logger.new('mocktopus.log', 'daily')
|
3
|
+
|
4
|
+
require 'mocktopus/response'
|
5
|
+
require 'mocktopus/input'
|
6
|
+
require 'mocktopus/mock_api_call'
|
7
|
+
require 'mocktopus/input_container'
|
8
|
+
require 'mocktopus/mock_api_call_container'
|
9
|
+
require 'mocktopus/cli'
|
10
|
+
require 'mocktopus/app'
|
11
|
+
|
12
|
+
module Mocktopus
|
13
|
+
end
|