harper 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +14 -1
- data/features/mock_http_rpc_service.feature +2 -0
- data/features/step_definitions/mock_definition_steps.rb +4 -0
- data/features/support/client_application.rb +4 -0
- data/lib/harper/app.rb +17 -5
- data/lib/harper/version.rb +1 -1
- data/spec/harper_spec.rb +116 -2
- metadata +20 -20
data/README.md
CHANGED
|
@@ -43,11 +43,24 @@ Harper will silently replace it with the new mock.
|
|
|
43
43
|
* *status*: The HTTP status code to provide. Optional. Defaults to 200.
|
|
44
44
|
* *content_type*: What content-type header to set when responding with
|
|
45
45
|
the mock. Required.
|
|
46
|
-
* *body*:
|
|
46
|
+
* *body*: Either a string to be used as the HTTP response body, or an
|
|
47
|
+
array of strings. If an array is provided Harper will cycle through
|
|
48
|
+
the array as the response body for requests. Required.
|
|
47
49
|
|
|
48
50
|
* *delay*: A delay to include when responding to the mock,
|
|
49
51
|
in milliseconds. Optional. Defaults to no delay.
|
|
50
52
|
|
|
53
|
+
* *request_body*: The params are used to compare with the actual request
|
|
54
|
+
body to find out whether the mock applies. This request_body is
|
|
55
|
+
optional and is required only in scenarios where you want multiple
|
|
56
|
+
requests to the same url return different response based on some
|
|
57
|
+
parameter sent in the request. The value passed in can actually be xml
|
|
58
|
+
read from a file, json read from a file or simply values in the request.
|
|
59
|
+
They need not be the whole request body - partial request params work.
|
|
60
|
+
However these must be conspicuously different across the requests i.e
|
|
61
|
+
they should not be so generic that they appear in all requests to
|
|
62
|
+
that url.
|
|
63
|
+
|
|
51
64
|
## Typical Use
|
|
52
65
|
|
|
53
66
|
1. Start Harper at the very start of your test run.
|
|
@@ -69,6 +69,7 @@ Feature: Mock an HTTP RPC web service
|
|
|
69
69
|
Given a defined response mock with a "status" of "<STATUS>"
|
|
70
70
|
When the application issues a "GET" request to the mock
|
|
71
71
|
Then the response code should be "<STATUS>"
|
|
72
|
+
When the application removes all registered mocks
|
|
72
73
|
Examples:
|
|
73
74
|
| STATUS |
|
|
74
75
|
| 200 |
|
|
@@ -86,6 +87,7 @@ Feature: Mock an HTTP RPC web service
|
|
|
86
87
|
Given a defined response mock with a "delay" of "<MILLISECONDS>"
|
|
87
88
|
When the application issues a "GET" request to the mock
|
|
88
89
|
Then the response time should be at least "<MILLISECONDS>"
|
|
90
|
+
When the application removes all registered mocks
|
|
89
91
|
Examples:
|
|
90
92
|
| MILLISECONDS |
|
|
91
93
|
| 0 |
|
data/lib/harper/app.rb
CHANGED
|
@@ -27,6 +27,15 @@ module Harper
|
|
|
27
27
|
def mock_id(url)
|
|
28
28
|
[url].pack('m').tr("+/=", "-_.").gsub("\n", '')
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
def retrieve_mock(mock_id, http_method, request_body)
|
|
32
|
+
if @@mocks[mock_id]
|
|
33
|
+
return @@mocks[mock_id].first if @@mocks[mock_id].length == 1
|
|
34
|
+
mocks_for_requested_http_method = @@mocks[mock_id].select { |m| m['method'] == http_method}
|
|
35
|
+
mock = mocks_for_requested_http_method.detect{|m| m["request_body"] && request_body =~ /#{m["request_body"]}/} if request_body
|
|
36
|
+
mock ||= mocks_for_requested_http_method.detect{|m| m["request_body"].nil?}
|
|
37
|
+
end
|
|
38
|
+
end
|
|
30
39
|
end
|
|
31
40
|
|
|
32
41
|
post '/h/mocks' do
|
|
@@ -37,10 +46,11 @@ module Harper
|
|
|
37
46
|
mock['id'] = mock_id(mock['url'])
|
|
38
47
|
mock['method'].upcase!
|
|
39
48
|
mock['delay'] = mock['delay'].to_f / 1000.0
|
|
40
|
-
@@mocks[mock['id']]
|
|
49
|
+
@@mocks[mock['id']] ||= []
|
|
50
|
+
@@mocks[mock['id']] << mock
|
|
41
51
|
|
|
42
52
|
logger.info("Created mock for endpoint: '#{mock['url']}'")
|
|
43
|
-
|
|
53
|
+
|
|
44
54
|
headers['location'] = "/h/mocks/#{mock['id']}"
|
|
45
55
|
status "201"
|
|
46
56
|
end
|
|
@@ -76,12 +86,14 @@ module Harper
|
|
|
76
86
|
|
|
77
87
|
logger.debug("#{request.request_method} request for a mock: '#{request.path}'")
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
request_body = request.body.read if request.body
|
|
90
|
+
mock = retrieve_mock(mock_id, request.request_method, request_body)
|
|
91
|
+
|
|
92
|
+
if mock
|
|
81
93
|
content_type mock['content-type']
|
|
82
94
|
status mock['status'] || "200"
|
|
83
95
|
sleep mock['delay']
|
|
84
|
-
|
|
96
|
+
|
|
85
97
|
logger.info("Serving mocked body for endpoint: '#{mock['url']}'")
|
|
86
98
|
|
|
87
99
|
case mock['body']
|
data/lib/harper/version.rb
CHANGED
data/spec/harper_spec.rb
CHANGED
|
@@ -5,7 +5,6 @@ require 'harper'
|
|
|
5
5
|
|
|
6
6
|
describe Harper::App do
|
|
7
7
|
include Rack::Test::Methods
|
|
8
|
-
|
|
9
8
|
supported_verbs = ["GET", "POST", "PUT", "DELETE"]
|
|
10
9
|
|
|
11
10
|
let(:app) { Harper::App.new }
|
|
@@ -68,7 +67,7 @@ describe Harper::App do
|
|
|
68
67
|
before(:each) do
|
|
69
68
|
post '/h/mocks', mock_def
|
|
70
69
|
end
|
|
71
|
-
|
|
70
|
+
|
|
72
71
|
it "should respond with a 201 created" do
|
|
73
72
|
last_response.status.should == 201
|
|
74
73
|
end
|
|
@@ -180,4 +179,119 @@ describe Harper::App do
|
|
|
180
179
|
|
|
181
180
|
end
|
|
182
181
|
|
|
182
|
+
context "supporting multiple mocks for same request url" do
|
|
183
|
+
it "should return the mock response corresponding to the request body for xml requests" do
|
|
184
|
+
url = "/service-url"
|
|
185
|
+
|
|
186
|
+
xml_string_for_request_one = <<-EOF
|
|
187
|
+
<mydoc>
|
|
188
|
+
<someelement attribute="nanoo">first request</someelement>
|
|
189
|
+
</mydoc>
|
|
190
|
+
EOF
|
|
191
|
+
body_one = "response body for request one"
|
|
192
|
+
mock_def_for_first_request =
|
|
193
|
+
{ :method => "POST",
|
|
194
|
+
:url => url,
|
|
195
|
+
:'content-type' => "application/xml",
|
|
196
|
+
:body => body_one,
|
|
197
|
+
:request_body => xml_string_for_request_one
|
|
198
|
+
}.to_json
|
|
199
|
+
|
|
200
|
+
post '/h/mocks', mock_def_for_first_request
|
|
201
|
+
|
|
202
|
+
xml_string_for_request_two = <<-EOF
|
|
203
|
+
<mydoc>
|
|
204
|
+
<someelement attribute="nanoo">Other request</someelement>
|
|
205
|
+
</mydoc>
|
|
206
|
+
EOF
|
|
207
|
+
body_two = "response body for request two"
|
|
208
|
+
mock_def_for_second_request =
|
|
209
|
+
{ :method => "POST",
|
|
210
|
+
:url => url,
|
|
211
|
+
:'content-type' => "application/xml",
|
|
212
|
+
:body => body_two,
|
|
213
|
+
:request_body => xml_string_for_request_two
|
|
214
|
+
}.to_json
|
|
215
|
+
|
|
216
|
+
post '/h/mocks', mock_def_for_second_request
|
|
217
|
+
|
|
218
|
+
post url, xml_string_for_request_one
|
|
219
|
+
last_response.body.should == body_one
|
|
220
|
+
|
|
221
|
+
post url, xml_string_for_request_two
|
|
222
|
+
last_response.body.should == body_two
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "should return the mock response corresponding to the request body for json requests" do
|
|
226
|
+
url = "/service-url"
|
|
227
|
+
|
|
228
|
+
request_json_one = {:param => "param 1"}.to_json
|
|
229
|
+
body_one = "response body for request one"
|
|
230
|
+
mock_def_for_first_request =
|
|
231
|
+
{ :method => "POST",
|
|
232
|
+
:url => url,
|
|
233
|
+
:'content-type' => "application/json",
|
|
234
|
+
:body => body_one,
|
|
235
|
+
:request_body => request_json_one
|
|
236
|
+
}.to_json
|
|
237
|
+
|
|
238
|
+
post '/h/mocks', mock_def_for_first_request
|
|
239
|
+
|
|
240
|
+
request_json_two = {:param => "param 2"}.to_json
|
|
241
|
+
body_two = "response body for request two"
|
|
242
|
+
mock_def_for_second_request =
|
|
243
|
+
{ :method => "POST",
|
|
244
|
+
:url => url,
|
|
245
|
+
:'content-type' => "application/json",
|
|
246
|
+
:body => body_two,
|
|
247
|
+
:request_body => request_json_two
|
|
248
|
+
}.to_json
|
|
249
|
+
|
|
250
|
+
post '/h/mocks', mock_def_for_second_request
|
|
251
|
+
|
|
252
|
+
post url, request_json_one
|
|
253
|
+
last_response.body.should == body_one
|
|
254
|
+
|
|
255
|
+
post url, request_json_two
|
|
256
|
+
last_response.body.should == body_two
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
it "should return the correct response for mocks registered without any request body" do
|
|
260
|
+
url = "/service-url"
|
|
261
|
+
|
|
262
|
+
xml_string_for_request_one = <<-EOF
|
|
263
|
+
<mydoc>
|
|
264
|
+
<someelement attribute="nanoo">first request</someelement>
|
|
265
|
+
</mydoc>
|
|
266
|
+
EOF
|
|
267
|
+
|
|
268
|
+
body_one = "response body for request one"
|
|
269
|
+
mock_def_for_first_request =
|
|
270
|
+
{ :method => "POST",
|
|
271
|
+
:url => url,
|
|
272
|
+
:'content-type' => "application/xml",
|
|
273
|
+
:body => body_one,
|
|
274
|
+
:request_body => xml_string_for_request_one
|
|
275
|
+
}.to_json
|
|
276
|
+
|
|
277
|
+
post '/h/mocks', mock_def_for_first_request
|
|
278
|
+
|
|
279
|
+
body_two = "response body for request two"
|
|
280
|
+
mock_def_for_second_request_without_request_body =
|
|
281
|
+
{ :method => "POST",
|
|
282
|
+
:url => url,
|
|
283
|
+
:'content-type' => "application/xml",
|
|
284
|
+
:body => body_two
|
|
285
|
+
}.to_json
|
|
286
|
+
|
|
287
|
+
post '/h/mocks', mock_def_for_second_request_without_request_body
|
|
288
|
+
|
|
289
|
+
post url, xml_string_for_request_one
|
|
290
|
+
last_response.body.should == body_one
|
|
291
|
+
|
|
292
|
+
post url
|
|
293
|
+
last_response.body.should == body_two
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
183
297
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: harper
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -9,11 +9,11 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2012-
|
|
12
|
+
date: 2012-07-10 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: sinatra
|
|
16
|
-
requirement: &
|
|
16
|
+
requirement: &70183758661480 !ruby/object:Gem::Requirement
|
|
17
17
|
none: false
|
|
18
18
|
requirements:
|
|
19
19
|
- - ! '>='
|
|
@@ -21,10 +21,10 @@ dependencies:
|
|
|
21
21
|
version: 1.0.0
|
|
22
22
|
type: :runtime
|
|
23
23
|
prerelease: false
|
|
24
|
-
version_requirements: *
|
|
24
|
+
version_requirements: *70183758661480
|
|
25
25
|
- !ruby/object:Gem::Dependency
|
|
26
26
|
name: httparty
|
|
27
|
-
requirement: &
|
|
27
|
+
requirement: &70183758661020 !ruby/object:Gem::Requirement
|
|
28
28
|
none: false
|
|
29
29
|
requirements:
|
|
30
30
|
- - ! '>='
|
|
@@ -32,10 +32,10 @@ dependencies:
|
|
|
32
32
|
version: '0'
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
|
-
version_requirements: *
|
|
35
|
+
version_requirements: *70183758661020
|
|
36
36
|
- !ruby/object:Gem::Dependency
|
|
37
37
|
name: json
|
|
38
|
-
requirement: &
|
|
38
|
+
requirement: &70183758676260 !ruby/object:Gem::Requirement
|
|
39
39
|
none: false
|
|
40
40
|
requirements:
|
|
41
41
|
- - ! '>='
|
|
@@ -43,10 +43,10 @@ dependencies:
|
|
|
43
43
|
version: 1.4.6
|
|
44
44
|
type: :runtime
|
|
45
45
|
prerelease: false
|
|
46
|
-
version_requirements: *
|
|
46
|
+
version_requirements: *70183758676260
|
|
47
47
|
- !ruby/object:Gem::Dependency
|
|
48
48
|
name: trollop
|
|
49
|
-
requirement: &
|
|
49
|
+
requirement: &70183758674400 !ruby/object:Gem::Requirement
|
|
50
50
|
none: false
|
|
51
51
|
requirements:
|
|
52
52
|
- - ! '>='
|
|
@@ -54,10 +54,10 @@ dependencies:
|
|
|
54
54
|
version: '0'
|
|
55
55
|
type: :runtime
|
|
56
56
|
prerelease: false
|
|
57
|
-
version_requirements: *
|
|
57
|
+
version_requirements: *70183758674400
|
|
58
58
|
- !ruby/object:Gem::Dependency
|
|
59
59
|
name: rack-test
|
|
60
|
-
requirement: &
|
|
60
|
+
requirement: &70183758673380 !ruby/object:Gem::Requirement
|
|
61
61
|
none: false
|
|
62
62
|
requirements:
|
|
63
63
|
- - ! '>='
|
|
@@ -65,10 +65,10 @@ dependencies:
|
|
|
65
65
|
version: '0'
|
|
66
66
|
type: :development
|
|
67
67
|
prerelease: false
|
|
68
|
-
version_requirements: *
|
|
68
|
+
version_requirements: *70183758673380
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: sham_rack
|
|
71
|
-
requirement: &
|
|
71
|
+
requirement: &70183758672900 !ruby/object:Gem::Requirement
|
|
72
72
|
none: false
|
|
73
73
|
requirements:
|
|
74
74
|
- - ! '>='
|
|
@@ -76,10 +76,10 @@ dependencies:
|
|
|
76
76
|
version: '0'
|
|
77
77
|
type: :development
|
|
78
78
|
prerelease: false
|
|
79
|
-
version_requirements: *
|
|
79
|
+
version_requirements: *70183758672900
|
|
80
80
|
- !ruby/object:Gem::Dependency
|
|
81
81
|
name: rspec
|
|
82
|
-
requirement: &
|
|
82
|
+
requirement: &70183758672340 !ruby/object:Gem::Requirement
|
|
83
83
|
none: false
|
|
84
84
|
requirements:
|
|
85
85
|
- - ! '>='
|
|
@@ -87,10 +87,10 @@ dependencies:
|
|
|
87
87
|
version: '0'
|
|
88
88
|
type: :development
|
|
89
89
|
prerelease: false
|
|
90
|
-
version_requirements: *
|
|
90
|
+
version_requirements: *70183758672340
|
|
91
91
|
- !ruby/object:Gem::Dependency
|
|
92
92
|
name: cucumber
|
|
93
|
-
requirement: &
|
|
93
|
+
requirement: &70183758671800 !ruby/object:Gem::Requirement
|
|
94
94
|
none: false
|
|
95
95
|
requirements:
|
|
96
96
|
- - ! '>='
|
|
@@ -98,10 +98,10 @@ dependencies:
|
|
|
98
98
|
version: '0.10'
|
|
99
99
|
type: :development
|
|
100
100
|
prerelease: false
|
|
101
|
-
version_requirements: *
|
|
101
|
+
version_requirements: *70183758671800
|
|
102
102
|
- !ruby/object:Gem::Dependency
|
|
103
103
|
name: cucumber-sinatra
|
|
104
|
-
requirement: &
|
|
104
|
+
requirement: &70183758670580 !ruby/object:Gem::Requirement
|
|
105
105
|
none: false
|
|
106
106
|
requirements:
|
|
107
107
|
- - ! '>='
|
|
@@ -109,7 +109,7 @@ dependencies:
|
|
|
109
109
|
version: '0'
|
|
110
110
|
type: :development
|
|
111
111
|
prerelease: false
|
|
112
|
-
version_requirements: *
|
|
112
|
+
version_requirements: *70183758670580
|
|
113
113
|
description: Dead simple mocking for HTTP services, a Sinatra app
|
|
114
114
|
email:
|
|
115
115
|
- giles.alexander@gmail.com
|