pact 1.0.20 → 1.0.21
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +8 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +3 -16
- data/README.md +135 -64
- data/lib/pact/consumer/mock_service/rack_request_helper.rb +1 -1
- data/lib/pact/provider/client_project_pact_helper.rb +3 -1
- data/lib/pact/provider/pact_spec_runner.rb +2 -3
- data/lib/pact/provider/request.rb +16 -9
- data/lib/pact/version.rb +1 -1
- data/pact.gemspec +3 -5
- data/spec/integration/consumer_spec.rb +34 -0
- data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +7 -1
- data/spec/lib/pact/provider/request_spec.rb +6 -6
- metadata +13 -40
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,28 @@ Do this to generate your change history
|
|
2
2
|
|
3
3
|
git log --date=relative --pretty=format:' * %h - %s (%an, %ad)'
|
4
4
|
|
5
|
+
### 1.0.21 (25 November 2013)
|
6
|
+
|
7
|
+
* f810795 - add jruby 2.0 to travis (Ronald Holshausen, 4 days ago)
|
8
|
+
* 65e0ea2 - dropped rbx as it was failing in a crazy way (Ronald Holshausen, 4 days ago)
|
9
|
+
* 1403594 - added ruby 2 to travis (Ronald Holshausen, 4 days ago)
|
10
|
+
* c72662e - rbx requires the rubysl-thwait gem (Ronald Holshausen, 4 days ago)
|
11
|
+
* 70745dc - require webrick (Ronald Holshausen, 4 days ago)
|
12
|
+
* 43110ad - removed thin as a runtime dependancy as it is not supported on all rubies (Ronald Holshausen, 4 days ago)
|
13
|
+
* d4eea58 - dropped all rubies < 1.9.3 (Ronald Holshausen, 4 days ago)
|
14
|
+
* cb312b5 - removed debugger as a development dependancy as it will not build on all rubies (Ronald Holshausen, 4 days ago)
|
15
|
+
* 872c649 - removed ruby 1.9.2 as active support does not active support it (Ronald Holshausen, 4 days ago)
|
16
|
+
* 1930269 - added travis CI for the uglyog repo (Ronald Holshausen, 4 days ago)
|
17
|
+
* 7750ee1 - added travis build status image (Ronald Holshausen, 5 days ago)
|
18
|
+
* 9f72b31 - added travis build status image (Ronald Holshausen, 5 days ago)
|
19
|
+
* d9be65b - Added .travis.yml (Beth, 6 days ago)
|
20
|
+
* e7a7e7b - Refactoring pact_helper loading. (Beth, 6 days ago)
|
21
|
+
* 0224d36 - Only log loading of pact_helper once https://github.com/uglyog/pact/issues/8 (Beth, 6 days ago)
|
22
|
+
* 0123207 - Updating gemspec description (Beth, 7 days ago)
|
23
|
+
* 697cbdc - Updating README.md (Beth, 4 weeks ago)
|
24
|
+
* ca79968 - Investigating Rack and HTTP headers in response to https://github.com/uglyog/pact/issues/6. Updated tests and README with info on multiple headers with the same name. (B
|
25
|
+
* 01f0b9a - Updating README (Beth, 4 weeks ago)
|
26
|
+
|
5
27
|
### 1.0.20 (29 October 2013)
|
6
28
|
|
7
29
|
* c03f34f - Fixed the pretty generation of JSON when active support is loaded. It is both a sad and a happy moment. (Beth, 7 minutes ago)
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pact (1.0.
|
4
|
+
pact (1.0.21)
|
5
5
|
awesome_print (~> 1.1.0)
|
6
6
|
find_a_port (~> 1.0.1)
|
7
7
|
json
|
8
8
|
rack-test (~> 0.6.2)
|
9
9
|
randexp (~> 0.1.7)
|
10
10
|
rspec (~> 2.12)
|
11
|
-
thin
|
12
11
|
thor
|
12
|
+
webrick
|
13
13
|
|
14
14
|
GEM
|
15
15
|
remote: https://rubygems.org/
|
@@ -24,18 +24,9 @@ GEM
|
|
24
24
|
atomic (1.1.14)
|
25
25
|
awesome_print (1.1.0)
|
26
26
|
coderay (1.0.9)
|
27
|
-
columnize (0.3.6)
|
28
27
|
crack (0.4.1)
|
29
28
|
safe_yaml (~> 0.9.0)
|
30
|
-
daemons (1.1.9)
|
31
|
-
debugger (1.6.2)
|
32
|
-
columnize (>= 0.3.1)
|
33
|
-
debugger-linecache (~> 1.2.0)
|
34
|
-
debugger-ruby_core_source (~> 1.2.3)
|
35
|
-
debugger-linecache (1.2.0)
|
36
|
-
debugger-ruby_core_source (1.2.3)
|
37
29
|
diff-lcs (1.2.4)
|
38
|
-
eventmachine (1.0.3)
|
39
30
|
fakefs (0.4.2)
|
40
31
|
find_a_port (1.0.1)
|
41
32
|
geminabox-client (0.0.2)
|
@@ -68,10 +59,6 @@ GEM
|
|
68
59
|
rspec-mocks (2.14.3)
|
69
60
|
safe_yaml (0.9.5)
|
70
61
|
slop (3.4.6)
|
71
|
-
thin (1.6.0)
|
72
|
-
daemons (>= 1.0.9)
|
73
|
-
eventmachine (>= 1.0.0)
|
74
|
-
rack (>= 1.5.0)
|
75
62
|
thor (0.18.1)
|
76
63
|
thread_safe (0.1.3)
|
77
64
|
atomic
|
@@ -79,13 +66,13 @@ GEM
|
|
79
66
|
webmock (1.9.3)
|
80
67
|
addressable (>= 2.2.7)
|
81
68
|
crack (>= 0.3.2)
|
69
|
+
webrick (1.3.1)
|
82
70
|
|
83
71
|
PLATFORMS
|
84
72
|
ruby
|
85
73
|
|
86
74
|
DEPENDENCIES
|
87
75
|
activesupport
|
88
|
-
debugger
|
89
76
|
fakefs (~> 0.4)
|
90
77
|
geminabox-client
|
91
78
|
hashie (~> 2.0)
|
data/README.md
CHANGED
@@ -9,6 +9,8 @@ This allows you to test both sides of an integration point using fast unit tests
|
|
9
9
|
|
10
10
|
This gem is inspired by the concept of "Consumer driven contracts". See http://martinfowler.com/articles/consumerDrivenContracts.html for more information.
|
11
11
|
|
12
|
+
Travis CI Status: [![travis-ci.org Build Status](https://travis-ci.org/uglyog/pact.png)](https://travis-ci.org/uglyog/pact)
|
13
|
+
|
12
14
|
## Features
|
13
15
|
* A services is mocked using an actual process running on a specified port, so javascript clients can be tested as easily as backend clients.
|
14
16
|
* "Provider states" (similar to fixtures) allow the same request to be made with a different expected response.
|
@@ -23,53 +25,49 @@ Put it in your Gemfile. You know how.
|
|
23
25
|
|
24
26
|
## Usage
|
25
27
|
|
26
|
-
###
|
28
|
+
### Service Consumer project
|
29
|
+
|
30
|
+
#### 1. Start with you model
|
31
|
+
|
32
|
+
Imagine a model class that looks something like this. The attributes for a SomethingModel live on a remote server, and will need to be retrieved by an HTTP call.
|
27
33
|
|
28
34
|
```ruby
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
+
class SomethingModel
|
36
|
+
attr_reader :name
|
37
|
+
|
38
|
+
def intialize name
|
39
|
+
@name = name
|
40
|
+
end
|
41
|
+
|
42
|
+
def == other
|
43
|
+
other.is_a?(Something) && other.name == name
|
44
|
+
end
|
35
45
|
end
|
36
46
|
```
|
37
47
|
|
38
|
-
|
48
|
+
#### 2. Create a skeleton client class
|
39
49
|
|
40
|
-
|
50
|
+
Imagine a service provider client class that looks something like this.
|
41
51
|
|
42
52
|
```ruby
|
43
53
|
|
44
|
-
# Imagine a service provider client class that looks something like this
|
45
|
-
|
46
54
|
class MyServiceProviderClient
|
47
55
|
include HTTParty
|
48
56
|
base_uri 'http://my-service'
|
49
57
|
|
50
58
|
def get_something
|
51
|
-
|
52
|
-
Something.new(name)
|
59
|
+
# Yet to be implemented because we're doing Test First Development...
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def intialize name
|
60
|
-
@name = name
|
61
|
-
end
|
63
|
+
```
|
64
|
+
#### 3. Configure the mock server
|
62
65
|
|
63
|
-
|
64
|
-
other.is_a?(Something) && other.name == name
|
65
|
-
end
|
66
|
-
end
|
66
|
+
The following code will create a mock service on localhost:1234 which will respond to your application's queries over HTTP as if it were the real "My Service Provider" app. It also creats a mock service provider object which you will use to set up your expectations. The method name to access the mock service provider will be what ever name you give as the service argument - in this case "my_service_provider"
|
67
67
|
|
68
68
|
|
69
|
-
|
70
|
-
#
|
71
|
-
# which you will use to set up your expectations. The method name to access the mock service provider
|
72
|
-
# will be what ever name you give as the service argument - in this case "my_service_provider"
|
69
|
+
```ruby
|
70
|
+
# In /spec/service_providers/pact_helper.rb
|
73
71
|
|
74
72
|
require 'pact/consumer/rspec'
|
75
73
|
|
@@ -80,9 +78,14 @@ Pact.service_consumer "My Service Consumer" do
|
|
80
78
|
end
|
81
79
|
end
|
82
80
|
end
|
81
|
+
```
|
83
82
|
|
84
|
-
|
83
|
+
#### 4. Write a failing spec for the client
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
# In /spec/service_providers/my_service_provider_client_spec.rb
|
85
87
|
|
88
|
+
# Use the :pact => true describe metadata to include all the pact generation functionality in your spec.
|
86
89
|
describe MyServiceProviderClient, :pact => true do
|
87
90
|
|
88
91
|
before do
|
@@ -104,7 +107,7 @@ describe MyServiceProviderClient, :pact => true do
|
|
104
107
|
end
|
105
108
|
|
106
109
|
it "returns a Something" do
|
107
|
-
expect(MyServiceProviderClient.get_something).to eq(
|
110
|
+
expect(MyServiceProviderClient.get_something).to eq(SomethingModel.new('A small something'))
|
108
111
|
end
|
109
112
|
|
110
113
|
end
|
@@ -113,27 +116,45 @@ end
|
|
113
116
|
|
114
117
|
```
|
115
118
|
|
119
|
+
#### 5. Run the specs
|
120
|
+
|
116
121
|
Running the consumer spec will generate a pact file in the configured pact dir (spec/pacts by default).
|
117
122
|
Logs will be output to the configured log dir that can be useful when diagnosing problems.
|
118
123
|
|
119
|
-
|
124
|
+
Of course, the above specs will fail because the client method is not implemented, so next, implement your client methods.
|
125
|
+
|
126
|
+
#### 6. Implement the client methods
|
120
127
|
|
121
128
|
```ruby
|
122
|
-
|
123
|
-
|
124
|
-
|
129
|
+
|
130
|
+
class MyServiceProviderClient
|
131
|
+
include HTTParty
|
132
|
+
base_uri 'http://my-service'
|
133
|
+
|
134
|
+
def get_something
|
135
|
+
name = JSON.parse(self.class.get("/something").body)['name']
|
136
|
+
SomethingModel.new(name)
|
137
|
+
end
|
125
138
|
end
|
139
|
+
|
126
140
|
```
|
127
141
|
|
142
|
+
#### 7. Run the specs again.
|
143
|
+
|
144
|
+
Green! You now have a pact file that can be used to verify your expectations in the provider project.
|
145
|
+
|
128
146
|
### Service Provider project
|
129
147
|
|
130
|
-
####
|
148
|
+
#### 1. Create the skeleton API classes
|
149
|
+
|
150
|
+
Create your API class using the framework of your choice (e.g. Sinatra, Grape) - leave the methods unimplemented, we're doing Test First Develoment, remember?
|
131
151
|
|
132
|
-
|
152
|
+
#### 2. Tell your provider that it needs to honour the pact file you made earlier
|
153
|
+
|
154
|
+
Create a `pact_helper.rb` in your service provider project. The file must be called pact_helper.rb, however there is some flexibility in where it can be stored. The recommended place is `specs/service_consumers/pact_helper.rb`.
|
133
155
|
|
134
156
|
```ruby
|
135
157
|
require 'my_app' # Require the boot files for your app
|
136
|
-
require 'provider_states_for_my_consumer' # See next section on setting up provider states
|
137
158
|
|
138
159
|
Pact.service_provider "My Service Provider" do
|
139
160
|
app { MyApp.new }
|
@@ -147,8 +168,31 @@ Pact.service_provider "My Service Provider" do
|
|
147
168
|
end
|
148
169
|
|
149
170
|
```
|
171
|
+
Require "pact/tasks" in your Rakefile. If the pact gem is in the test/development section of your Gemfile, you may want to put an env check around this so it doesn't load the pact tasks in prod.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
# In Rakefile
|
175
|
+
|
176
|
+
require 'pact/tasks'
|
177
|
+
```
|
178
|
+
|
179
|
+
#### 3. Run your failing specs
|
180
|
+
|
181
|
+
$ rake pact:verify
|
182
|
+
|
183
|
+
Congratulations! You now have a failing spec to develop against.
|
184
|
+
|
185
|
+
#### 4. Implement your service provider
|
186
|
+
|
187
|
+
At this stage, you'll probably want to be able to run your specs one at a time while you implement. Define the environment variables PACT_DESCRIPTION and/or PACT_PROVIDER_STATE as so:
|
188
|
+
|
189
|
+
$ PACT_DESCRIPTION="a request for something" PACT_PROVIDER_STATE="something exists" rake pact:verify
|
150
190
|
|
151
|
-
####
|
191
|
+
#### 5. Keep going til you're green
|
192
|
+
|
193
|
+
Yay! Your provider now honours the pact it has with your consumer. You can now have confidence that your consumer and provider will play nicely together.
|
194
|
+
|
195
|
+
### Using provider states
|
152
196
|
|
153
197
|
Having different service provider states allows you to test the same request with different expected responses.
|
154
198
|
|
@@ -161,6 +205,8 @@ my_service.
|
|
161
205
|
with(method: 'get', path: '/thing').
|
162
206
|
will_respond_with(status: 200, :body => {thing: "yay!"} )
|
163
207
|
|
208
|
+
...
|
209
|
+
|
164
210
|
my_service.
|
165
211
|
given("a thing does not exist").
|
166
212
|
upon_receiving("a request for a thing").
|
@@ -168,13 +214,11 @@ my_service.
|
|
168
214
|
will_respond_with(status: 404, :body => {error: "There is no thing :("} )
|
169
215
|
```
|
170
216
|
|
171
|
-
To define service provider states that create the right data for "a thing exists" and "a thing does not exist", write the following in the service provider project.
|
217
|
+
To define service provider states that create the right data for "a thing exists" and "a thing does not exist", write the following in the service provider project. (The consumer name here must match the name of the consumer configured in your consumer project for it to correctly find these provider states.)
|
172
218
|
|
173
219
|
|
174
220
|
```ruby
|
175
|
-
#
|
176
|
-
# for it to correctly find these provider states.
|
177
|
-
# Make sure the provider states are included in or required by your pact_helper.rb file.
|
221
|
+
# In /spec/service_consumers/provider_states_for_my_service_consumer.rb
|
178
222
|
|
179
223
|
Pact.provider_states_for 'My Service Consumer' do
|
180
224
|
provider_state "a thing exists" do
|
@@ -194,45 +238,46 @@ end
|
|
194
238
|
|
195
239
|
```
|
196
240
|
|
197
|
-
If a state should be used for all consumers, the top level Pact.with_consumer can be skipped, and a global Pact.provider_state can be defined on its own.
|
198
|
-
|
199
|
-
#### Verify that the service provider honours the pact
|
200
|
-
|
201
241
|
```ruby
|
202
|
-
|
203
|
-
# If the pact gem is in the test/development section of your Gemfile, you may want to put an env check around this so it doesn't load the pact tasks in prod.
|
204
|
-
require 'pact/tasks'
|
205
|
-
```
|
242
|
+
# In /spec/service_consumers/pact_helper.rb
|
206
243
|
|
207
|
-
|
208
|
-
$ rake -T
|
209
|
-
rake pact:verify # Verifies the pact files configured in the pact_helper.rb against this service provider.
|
210
|
-
rake pact:verify:at[pact_uri] # Verifies the pact at the given URI against this service provider.
|
211
|
-
$ rake pact:verify
|
244
|
+
require_relative 'provider_states_for_my_service_consumer.rb'
|
212
245
|
```
|
213
246
|
|
214
|
-
|
247
|
+
If a state should be used for all consumers, the top level Pact.with_consumer can be skipped, and a global Pact.provider_state can be defined on its own.
|
215
248
|
|
216
|
-
```
|
217
|
-
# Local URI
|
218
|
-
$ rake pact:verify:at[../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json]
|
219
249
|
|
220
|
-
|
221
|
-
|
222
|
-
|
250
|
+
### Verifying pacts
|
251
|
+
|
252
|
+
You can verify a pact at an arbitrary local or remote URL
|
253
|
+
|
254
|
+
$ rake pact:verify:at[../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json]
|
255
|
+
$ rake pact:verify:at[http://build-box/MyConsumerBuild/latestSuccessful/artifacts/my_consumer-my_provider.json]
|
223
256
|
|
224
|
-
To make a shortcut task for pact at an arbitrary
|
257
|
+
To make a shortcut task for pact at an arbitrary URL, add the following to your Rakefile. The pact.uri may be a local file system path or a remote URL.
|
225
258
|
|
226
259
|
```ruby
|
260
|
+
# In Rakefile or /tasks/pact.rake
|
261
|
+
|
227
262
|
# This creates a rake task that can be executed by running
|
228
|
-
# $rake pact:verify:dev
|
263
|
+
# $ rake pact:verify:dev
|
264
|
+
|
229
265
|
Pact::VerificationTask.new(:dev) do | pact |
|
230
266
|
pact.uri '../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json'
|
231
267
|
end
|
232
268
|
```
|
233
269
|
|
234
|
-
|
270
|
+
### Configuration
|
235
271
|
|
272
|
+
```ruby
|
273
|
+
Pact.configure do | config |
|
274
|
+
config.pact_dir = "???" # Optional, default is ./spec/pacts
|
275
|
+
config.log_dir = "???" # Optional, default is ./log
|
276
|
+
config.logger = "??" # Optional, defaults to a file logger to the configured log_dir.
|
277
|
+
config.logger.level = Logger::DEBUG #By default this is INFO, bump this up to debug for more detailed logs
|
278
|
+
config.pactfile_write_mode = :ovewrite / :update / :smart # Optional. The default pactfile_write_mode is :overwrite. See notes in Advanced section for further information.
|
279
|
+
end
|
280
|
+
```
|
236
281
|
|
237
282
|
## Pact best practices
|
238
283
|
|
@@ -305,6 +350,32 @@ A pact service can be run locally and is really useful for debugging purposes.
|
|
305
350
|
The service prints messages it recieves to stdout which can be really useful
|
306
351
|
when diagnosing issues with pacts.
|
307
352
|
|
353
|
+
### To run your consumer app as a process during your consumer specs
|
354
|
+
|
355
|
+
Eg. for Capybara tests
|
356
|
+
|
357
|
+
```ruby
|
358
|
+
Pact.service_consumer "My Consumer" do
|
359
|
+
app my_consumer_rack_app
|
360
|
+
port 4321
|
361
|
+
end
|
362
|
+
```
|
363
|
+
|
364
|
+
### Handling multiple headers with the same name
|
365
|
+
|
366
|
+
RFC 2616 states that two headers with the same name can interpreted as a single header with two comma-separated values. This is the safest way to specify multiple headers with the same name, as Rack will only pass the last value through when they are defined separately (see https://github.com/rack/rack/issues/436).
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
my_service_provider.
|
370
|
+
.given("it is RFC 2616 compliant")
|
371
|
+
.upon_receiving("a request with a header with commas separated values")
|
372
|
+
.with( method: :get, path: '/', headers: {'X-Request-Multival' => "A, B"} )
|
373
|
+
.will_respond_with(
|
374
|
+
status: 200, headers: {'X-Response-Multival' => "C, D"}
|
375
|
+
)
|
376
|
+
|
377
|
+
```
|
378
|
+
|
308
379
|
### Pact file write mode
|
309
380
|
|
310
381
|
By default, the pact file will be overwritten (started from scratch) every time any rspec runs any spec using pacts. This means that if there are interactions that haven't been executed in the most recent rspec run, they are effectively removed from the pact file. If you have long running pact specs (e.g. they are generated using the browser with Capybara) and you are developing both consumer and provider in parallel, or trying to fix a broken interaction, it can be tedius to run all the specs at once. In this scenario, you can set the pactfile_write_mode to :update. This will keep all existing interactions, and update only the changed ones, identified by description and provider state. The down side of this is that if either of those fields change, the old interactions will not be removed from the pact file. As a middle path, you can set pactfile_write_mode to :smart. This will use :overwrite mode when running rake (as determined by a call to system using 'ps') and :update when running an individual spec.
|
@@ -36,7 +36,7 @@ module Pact
|
|
36
36
|
private
|
37
37
|
|
38
38
|
def headers_from env
|
39
|
-
headers = env.reject{ |key, value| !(key.start_with?("HTTP") || key == 'CONTENT_TYPE')}
|
39
|
+
headers = env.reject{ |key, value| !(key.start_with?("HTTP") || key == 'CONTENT_TYPE' || key == 'CONTENT_LENGTH')}
|
40
40
|
headers.inject({}) do | hash, header |
|
41
41
|
hash[standardise_header(header.first)] = header.last
|
42
42
|
hash
|
@@ -42,14 +42,13 @@ module Pact
|
|
42
42
|
|
43
43
|
def require_pact_helper spec_definition
|
44
44
|
if spec_definition[:pact_helper]
|
45
|
-
puts "
|
45
|
+
puts "Using #{spec_definition[:pact_helper]}"
|
46
46
|
require spec_definition[:pact_helper]
|
47
47
|
elsif spec_definition[:support_file]
|
48
|
-
puts "
|
48
|
+
puts "Using #{spec_definition[:support_file]}"
|
49
49
|
$stderr.puts SUPPORT_FILE_DEPRECATION_MESSAGE
|
50
50
|
require spec_definition[:support_file]
|
51
51
|
else
|
52
|
-
puts "Requiring #{Pact::Provider::PactHelperLocater.pact_helper_path}"
|
53
52
|
require 'pact/provider/client_project_pact_helper'
|
54
53
|
end
|
55
54
|
end
|
@@ -7,6 +7,9 @@ module Pact
|
|
7
7
|
module Request
|
8
8
|
class Replayable
|
9
9
|
|
10
|
+
# See https://github.com/rack/rack/blob/e7d741c6282ca4cf4e01506f5681e6e6b14c0b32/SPEC#L87-89
|
11
|
+
NO_HTTP_PREFIX = ["CONTENT-TYPE", "CONTENT-LENGTH"]
|
12
|
+
|
10
13
|
def initialize expected_request
|
11
14
|
@expected_request = expected_request
|
12
15
|
end
|
@@ -32,12 +35,7 @@ module Pact
|
|
32
35
|
request_headers = {}
|
33
36
|
return request_headers if expected_request.headers.is_a?(Pact::NullExpectation)
|
34
37
|
expected_request.headers.each do |key, value|
|
35
|
-
key =
|
36
|
-
if key.match(/CONTENT.TYPE/)
|
37
|
-
request_headers['CONTENT_TYPE'] = value
|
38
|
-
else
|
39
|
-
request_headers[formatted_request_header_key(key)] = value
|
40
|
-
end
|
38
|
+
request_headers[rack_request_header_for(key)] = value
|
41
39
|
end
|
42
40
|
request_headers
|
43
41
|
end
|
@@ -54,10 +52,19 @@ module Pact
|
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
57
|
-
def
|
58
|
-
|
55
|
+
def rack_request_header_for header
|
56
|
+
with_http_prefix(header.to_s.upcase).gsub('-', '_')
|
57
|
+
end
|
58
|
+
|
59
|
+
def rack_request_value_for value
|
60
|
+
Array(value).join("\n")
|
59
61
|
end
|
60
|
-
|
62
|
+
|
63
|
+
def with_http_prefix header
|
64
|
+
NO_HTTP_PREFIX.include?(header) ? header : "HTTP_#{header}"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
61
68
|
end
|
62
69
|
end
|
63
70
|
end
|
data/lib/pact/version.rb
CHANGED
data/pact.gemspec
CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |gem|
|
|
8
8
|
gem.version = Pact::VERSION
|
9
9
|
gem.authors = ["James Fraser", "Sergei Matheson", "Brent Snook", "Ronald Holshausen", "Bethany Skurrie"]
|
10
10
|
gem.email = ["james.fraser@alumni.swinburne.edu", "sergei.matheson@gmail.com", "brent@fuglylogic.com", "uglyog@gmail.com", "bskurrie@dius.com.au"]
|
11
|
-
gem.description = %q{
|
12
|
-
gem.summary = %q{
|
11
|
+
gem.description = %q{Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.}
|
12
|
+
gem.summary = %q{Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.}
|
13
13
|
gem.homepage = "https://github.com/uglyog/pact.git"
|
14
14
|
|
15
15
|
gem.files = `git ls-files`.split($/)
|
@@ -24,8 +24,8 @@ Gem::Specification.new do |gem|
|
|
24
24
|
gem.add_runtime_dependency 'rack-test', '~> 0.6.2'
|
25
25
|
gem.add_runtime_dependency 'awesome_print', '~> 1.1.0'
|
26
26
|
gem.add_runtime_dependency 'thor'
|
27
|
-
gem.add_runtime_dependency 'thin'
|
28
27
|
gem.add_runtime_dependency 'json' #Not locking down a version because buncher gem requires 1.6, while other projects use 1.7.
|
28
|
+
gem.add_runtime_dependency 'webrick'
|
29
29
|
|
30
30
|
gem.add_development_dependency 'rake', '~> 10.0.3'
|
31
31
|
gem.add_development_dependency 'webmock', '~> 1.9.3'
|
@@ -34,6 +34,4 @@ Gem::Specification.new do |gem|
|
|
34
34
|
gem.add_development_dependency 'hashie', '~> 2.0'
|
35
35
|
gem.add_development_dependency 'rspec-fire'
|
36
36
|
gem.add_development_dependency 'activesupport'
|
37
|
-
gem.add_development_dependency 'debugger'
|
38
|
-
gem.add_development_dependency 'geminabox-client'
|
39
37
|
end
|
@@ -129,6 +129,40 @@ describe "A service consumer side of a pact", :pact => true do
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
+
context "with multiple headers" do
|
133
|
+
before do
|
134
|
+
Pact.clear_configuration
|
135
|
+
Pact.service_consumer "Consumer" do
|
136
|
+
has_pact_with "Multi Headers Service" do
|
137
|
+
mock_service :multi_headers_service do
|
138
|
+
verify true
|
139
|
+
port 1240
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
multi_headers_service.
|
145
|
+
given("there are multiple headers").
|
146
|
+
upon_receiving("a request with multiple headers").
|
147
|
+
with(method: :get, path: '/something', headers: {'X-Something' => "1, 2"}).
|
148
|
+
will_respond_with(status: 200)
|
149
|
+
end
|
150
|
+
|
151
|
+
it "handles multiple headers with the same name in a comma separated list" do
|
152
|
+
interactions = Pact::ConsumerContract.from_json(File.read(multi_headers_service.consumer_contract.pactfile_path)).interactions
|
153
|
+
expect(interactions.first.request.headers['X-Something']).to eq("1, 2")
|
154
|
+
|
155
|
+
uri = URI('http://localhost:1240/something')
|
156
|
+
post_req = Net::HTTP::Get.new(uri.path)
|
157
|
+
post_req.add_field('X-Something', '1')
|
158
|
+
post_req.add_field('X-Something', '2')
|
159
|
+
response = Net::HTTP.start(uri.hostname, uri.port) do |http|
|
160
|
+
http.request post_req
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
132
166
|
context "with a async interaction with provider" do
|
133
167
|
before do
|
134
168
|
Pact.clear_configuration
|
@@ -27,6 +27,7 @@ module Pact::Consumer
|
|
27
27
|
"HTTP_ACCEPT" => "text/plain",
|
28
28
|
"HTTP_USER_AGENT" => "Ruby",
|
29
29
|
"HTTP_HOST" => "localhost:4321",
|
30
|
+
"HTTP_X_SOMETHING" => "1, 2",
|
30
31
|
"rack.version" => [1, 2 ],
|
31
32
|
"rack.input" => StringIO.new(body),
|
32
33
|
"rack.errors" => nil,
|
@@ -39,6 +40,9 @@ module Pact::Consumer
|
|
39
40
|
}
|
40
41
|
}
|
41
42
|
|
43
|
+
let(:content_type) { "" }
|
44
|
+
let(:body) { '' }
|
45
|
+
|
42
46
|
subject { TestSubject.new }
|
43
47
|
|
44
48
|
let(:expected_request) {
|
@@ -49,10 +53,12 @@ module Pact::Consumer
|
|
49
53
|
:path => "/donuts",
|
50
54
|
:headers => {
|
51
55
|
"Content-Type" => content_type,
|
56
|
+
"Content-Length" => "16",
|
52
57
|
"Accept" => "text/plain",
|
53
58
|
"User-Agent" => "Ruby",
|
54
59
|
"Host" => "localhost:4321",
|
55
|
-
"Version" => "HTTP/1.1"
|
60
|
+
"Version" => "HTTP/1.1",
|
61
|
+
"X-Something" => "1, 2"
|
56
62
|
}
|
57
63
|
}
|
58
64
|
}
|
@@ -6,10 +6,10 @@ describe Pact::Provider::Request::Replayable do
|
|
6
6
|
let(:path) { '/path?something' }
|
7
7
|
let(:body) { {a: 'body'} }
|
8
8
|
let(:headers) { {} }
|
9
|
-
let(:expected_request) do
|
10
|
-
instance_double('Pact::Request::Expected',
|
11
|
-
:method => 'post',
|
12
|
-
:full_path => path,
|
9
|
+
let(:expected_request) do
|
10
|
+
instance_double('Pact::Request::Expected',
|
11
|
+
:method => 'post',
|
12
|
+
:full_path => path,
|
13
13
|
:body => body,
|
14
14
|
:headers => headers)
|
15
15
|
end
|
@@ -62,8 +62,8 @@ describe Pact::Provider::Request::Replayable do
|
|
62
62
|
|
63
63
|
describe "headers" do
|
64
64
|
context "when headers are expected" do
|
65
|
-
let(:headers) { {"Content-Type" => "text/plain", "
|
66
|
-
let(:expected_headers) { {"CONTENT_TYPE" => "text/plain", "
|
65
|
+
let(:headers) { {"Content-Type" => "text/plain", "Content-Length" => "123", "X-Content-Type" => "special", "Access-Control-Request-Method" => "POST"} }
|
66
|
+
let(:expected_headers) { {"CONTENT_TYPE" => "text/plain", "CONTENT_LENGTH" => "123", "HTTP_ACCESS_CONTROL_REQUEST_METHOD" => "POST", "HTTP_X_CONTENT_TYPE" => "special"} }
|
67
67
|
it "transforms the headers into Rack format" do
|
68
68
|
expect(subject.headers).to eq( expected_headers )
|
69
69
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pact
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.21
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2013-
|
16
|
+
date: 2013-11-25 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: randexp
|
@@ -112,7 +112,7 @@ dependencies:
|
|
112
112
|
- !ruby/object:Gem::Version
|
113
113
|
version: '0'
|
114
114
|
- !ruby/object:Gem::Dependency
|
115
|
-
name:
|
115
|
+
name: json
|
116
116
|
requirement: !ruby/object:Gem::Requirement
|
117
117
|
none: false
|
118
118
|
requirements:
|
@@ -128,7 +128,7 @@ dependencies:
|
|
128
128
|
- !ruby/object:Gem::Version
|
129
129
|
version: '0'
|
130
130
|
- !ruby/object:Gem::Dependency
|
131
|
-
name:
|
131
|
+
name: webrick
|
132
132
|
requirement: !ruby/object:Gem::Requirement
|
133
133
|
none: false
|
134
134
|
requirements:
|
@@ -255,39 +255,9 @@ dependencies:
|
|
255
255
|
- - ! '>='
|
256
256
|
- !ruby/object:Gem::Version
|
257
257
|
version: '0'
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
none: false
|
262
|
-
requirements:
|
263
|
-
- - ! '>='
|
264
|
-
- !ruby/object:Gem::Version
|
265
|
-
version: '0'
|
266
|
-
type: :development
|
267
|
-
prerelease: false
|
268
|
-
version_requirements: !ruby/object:Gem::Requirement
|
269
|
-
none: false
|
270
|
-
requirements:
|
271
|
-
- - ! '>='
|
272
|
-
- !ruby/object:Gem::Version
|
273
|
-
version: '0'
|
274
|
-
- !ruby/object:Gem::Dependency
|
275
|
-
name: geminabox-client
|
276
|
-
requirement: !ruby/object:Gem::Requirement
|
277
|
-
none: false
|
278
|
-
requirements:
|
279
|
-
- - ! '>='
|
280
|
-
- !ruby/object:Gem::Version
|
281
|
-
version: '0'
|
282
|
-
type: :development
|
283
|
-
prerelease: false
|
284
|
-
version_requirements: !ruby/object:Gem::Requirement
|
285
|
-
none: false
|
286
|
-
requirements:
|
287
|
-
- - ! '>='
|
288
|
-
- !ruby/object:Gem::Version
|
289
|
-
version: '0'
|
290
|
-
description: Define a pact between service consumers and providers
|
258
|
+
description: Enables consumer driven contract testing, providing a mock service and
|
259
|
+
DSL for the consumer project, and interaction playback and verification for the
|
260
|
+
service provider project.
|
291
261
|
email:
|
292
262
|
- james.fraser@alumni.swinburne.edu
|
293
263
|
- sergei.matheson@gmail.com
|
@@ -301,6 +271,7 @@ extra_rdoc_files: []
|
|
301
271
|
files:
|
302
272
|
- .gitignore
|
303
273
|
- .rspec
|
274
|
+
- .travis.yml
|
304
275
|
- CHANGELOG.md
|
305
276
|
- Gemfile
|
306
277
|
- Gemfile.lock
|
@@ -448,7 +419,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
448
419
|
version: '0'
|
449
420
|
segments:
|
450
421
|
- 0
|
451
|
-
hash: -
|
422
|
+
hash: -2335732393810580306
|
452
423
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
453
424
|
none: false
|
454
425
|
requirements:
|
@@ -457,13 +428,15 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
457
428
|
version: '0'
|
458
429
|
segments:
|
459
430
|
- 0
|
460
|
-
hash: -
|
431
|
+
hash: -2335732393810580306
|
461
432
|
requirements: []
|
462
433
|
rubyforge_project:
|
463
434
|
rubygems_version: 1.8.23
|
464
435
|
signing_key:
|
465
436
|
specification_version: 3
|
466
|
-
summary:
|
437
|
+
summary: Enables consumer driven contract testing, providing a mock service and DSL
|
438
|
+
for the consumer project, and interaction playback and verification for the service
|
439
|
+
provider project.
|
467
440
|
test_files:
|
468
441
|
- spec/features/consumption_spec.rb
|
469
442
|
- spec/features/production_spec.rb
|