pact 1.3.0 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -1
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +5 -5
- data/README.md +25 -29
- data/documentation/README.md +9 -7
- data/lib/pact/configuration.rb +83 -12
- data/lib/pact/consumer/mock_service/interaction_mismatch.rb +5 -1
- data/lib/pact/consumer/spec_hooks.rb +7 -4
- data/lib/pact/consumer/world.rb +12 -0
- data/lib/pact/consumer_contract/headers.rb +51 -0
- data/lib/pact/consumer_contract/request.rb +6 -1
- data/lib/pact/provider/configuration/service_provider_dsl.rb +6 -1
- data/lib/pact/provider/matchers/messages.rb +2 -2
- data/lib/pact/provider/rspec.rb +7 -1
- data/lib/pact/provider/rspec/backtrace_formatter.rb +30 -6
- data/lib/pact/provider/rspec/matchers.rb +9 -6
- data/lib/pact/shared/json_differ.rb +15 -0
- data/lib/pact/shared/request.rb +11 -2
- data/lib/pact/shared/text_differ.rb +14 -0
- data/lib/pact/version.rb +1 -1
- data/spec/lib/pact/configuration_spec.rb +127 -5
- data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +8 -5
- data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
- data/spec/lib/pact/consumer_contract/request_spec.rb +9 -0
- data/spec/lib/pact/matchers/matchers_spec.rb +35 -0
- data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +16 -0
- data/spec/lib/pact/provider/matchers/messages_spec.rb +5 -4
- data/spec/lib/pact/provider/rspec_spec.rb +15 -11
- data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
- data/spec/lib/pact/shared/request_spec.rb +25 -1
- data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
- data/spec/support/pact_helper.rb +2 -0
- data/spec/support/test_app_with_right_content_type_differ.json +23 -0
- data/tasks/pact-test.rake +6 -0
- metadata +72 -30
- checksums.yaml +0 -7
- data/documentation/Testing with pact.png +0 -0
- data/documentation/best-practices.md +0 -33
- data/documentation/development-workflow.md +0 -22
- data/documentation/faq.md +0 -81
- data/documentation/provider-states.md +0 -178
- data/documentation/raq.md +0 -39
- data/documentation/terminology.md +0 -25
- data/documentation/troubleshooting.md +0 -4
- data/documentation/verifying-pacts.md +0 -106
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,20 @@ Do this to generate your change history
|
|
2
2
|
|
3
3
|
git log --pretty=format:' * %h - %s (%an, %ad)'
|
4
4
|
|
5
|
+
### 1.3.1 (11 August 2014)
|
6
|
+
|
7
|
+
* 3432259 - Fixed 'pact:verify broken with rspec-core 3.0.3' https://github.com/realestate-com-au/pact/issues/44 (bethesque, Mon Aug 11 10:14:42 2014 +1000)
|
8
|
+
* e2e8eff - Deleted documentation that has been moved to the wiki (bethesque, Thu Jul 24 15:20:07 2014 +1000)
|
9
|
+
* bcc3143 - Fixing bug 'Method case should not matter when matching requests' https://github.com/realestate-com-au/pact/issues/41 (bethesque, Tue Jul 22 16:51:48 2014 +1000)
|
10
|
+
* d4bfab9 - Adding ability to configure DiffFormatter based on content-type (bethesque, Mon Jun 23 21:22:47 2014 +1000)
|
11
|
+
* eb330ea - Ensured content-type header works in a case insensitive way when looking up the right differ (bethesque, Mon Jun 23 17:23:04 2014 +1000)
|
12
|
+
* 2733e8e - Made header matching case insensitive for requests. Fixing issue https://github.com/realestate-com-au/pact/issues/20 (bethesque, Mon May 26 19:15:48 2014 +1000)
|
13
|
+
* 2b8355d - Added nicer error message for scenario when a service provider app has not been configured, and there is no config.ru (bethesque, Mon Jun 23 09:42:18 2014 +1000)
|
14
|
+
* 1e774bb - Defaulting to TextDiffer if response has no content-type (bethesque, Sat Jun 21 10:34:44 2014 +1000)
|
15
|
+
* 863b093 - Added support for documents without content types (bethesque, Sat Jun 21 10:32:08 2014 +1000)
|
16
|
+
* b21900b - Enabling differs to be configured based on Content-Type (bethesque, Sat Jun 21 10:21:37 2014 +1000)
|
17
|
+
* 527b5d5 - Modified after hook to only write pacts when one or more 'pact => true' examples have run (bethesque, Wed Jun 18 15:15:55 2014 +1000)
|
18
|
+
|
5
19
|
### 1.3.0 (18 June 2014)
|
6
20
|
|
7
21
|
* ea79190 - Modifying (cough*monkeypatching*cough) RSpec::Core::BacktraceFormatter as RSpec3 has some hardcoded exclusion patterns that result in *all* the backtrace lines being shown when pact:verify fails. (bethesque, Wed Jun 18 13:02:38 2014 +1000)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pact (1.3.
|
4
|
+
pact (1.3.1)
|
5
5
|
awesome_print (~> 1.1)
|
6
6
|
find_a_port (~> 1.0.1)
|
7
7
|
json
|
@@ -50,14 +50,14 @@ GEM
|
|
50
50
|
rspec-core (~> 3.0.0)
|
51
51
|
rspec-expectations (~> 3.0.0)
|
52
52
|
rspec-mocks (~> 3.0.0)
|
53
|
-
rspec-core (3.0.
|
53
|
+
rspec-core (3.0.3)
|
54
54
|
rspec-support (~> 3.0.0)
|
55
|
-
rspec-expectations (3.0.
|
55
|
+
rspec-expectations (3.0.3)
|
56
56
|
diff-lcs (>= 1.2.0, < 2.0)
|
57
57
|
rspec-support (~> 3.0.0)
|
58
|
-
rspec-mocks (3.0.
|
58
|
+
rspec-mocks (3.0.3)
|
59
59
|
rspec-support (~> 3.0.0)
|
60
|
-
rspec-support (3.0.
|
60
|
+
rspec-support (3.0.3)
|
61
61
|
safe_yaml (1.0.3)
|
62
62
|
slop (3.5.0)
|
63
63
|
term-ansicolor (1.3.0)
|
data/README.md
CHANGED
@@ -14,6 +14,12 @@ Travis CI Status: [![travis-ci.org Build Status](https://travis-ci.org/realestat
|
|
14
14
|
|
15
15
|
Pact is most valuable for designing and testing integrations where you (or your team/organisation/partner organisation) control the development of both the consumer and the provider. It is fantastic tool for intra-organsation microservices.
|
16
16
|
|
17
|
+
## What is it not good for?
|
18
|
+
|
19
|
+
* Performance and load testing.
|
20
|
+
* Functional testing of the provider - that is what the provider's own tests should do. Pact is about checking the contents and format of requests and responses.
|
21
|
+
* Situations where you cannot load data into the provider without using the API that you're actually testing.
|
22
|
+
|
17
23
|
## Features
|
18
24
|
|
19
25
|
* A service is mocked using an actual process running on a specified port, so javascript clients can be tested as easily as backend clients.
|
@@ -27,7 +33,7 @@ Pact is most valuable for designing and testing integrations where you (or your
|
|
27
33
|
## How does it work?
|
28
34
|
|
29
35
|
1. In the specs for the provider facing code in the consumer project, expectations are set up on a mock service provider.
|
30
|
-
1. When the specs are run, the requests, and their expected responses, are written to a "pact" file.
|
36
|
+
1. When the specs are run, the mock service returns the expected responses. The requests, and their expected responses, are then written to a "pact" file.
|
31
37
|
1. The requests in the pact file are later replayed against the provider, and the actual responses are checked to make sure they match the expected responses.
|
32
38
|
|
33
39
|
## Why is developing and testing with Pact better than using traditional system integration tests?
|
@@ -37,6 +43,7 @@ Pact is most valuable for designing and testing integrations where you (or your
|
|
37
43
|
* Causes of failure are easier to identify as only one component is being tested at a time.
|
38
44
|
* Design of service provider is improved by considering first how the data is actually going to be used, rather than how it is most easily retrieved and serialised.
|
39
45
|
* No separate integration environment required for automated integration tests - pact tests run in standalone CI builds.
|
46
|
+
* Integration flows that would traditionally require running multiple services at the same time can be broken down and each integration point tested separately.
|
40
47
|
|
41
48
|
## Contact
|
42
49
|
|
@@ -180,7 +187,7 @@ require 'pact/tasks'
|
|
180
187
|
|
181
188
|
Create a `pact_helper.rb` in your service provider project. The recommended place is `spec/service_consumers/pact_helper.rb`.
|
182
189
|
|
183
|
-
See [Verifying Pacts](
|
190
|
+
See [Verifying Pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts) and the [Provider](documentation/configuration.md#provider) section of the Configuration documentation for more information.
|
184
191
|
|
185
192
|
```ruby
|
186
193
|
# In specs/service_consumers/pact_helper.rb
|
@@ -220,29 +227,8 @@ Yay! Your provider now honours the pact it has with your consumer. You can now h
|
|
220
227
|
|
221
228
|
### Using provider states
|
222
229
|
|
223
|
-
|
224
|
-
|
225
|
-
Read more about provider states [here](/documentation/provider-states.md).
|
226
|
-
|
227
|
-
### Verifying pacts
|
230
|
+
Each interaction in a pact is verified in isolation, with no context maintained from the previous interactions. So how do you test a request that requires data to already exist on the provider? Read about provider states [here](https://github.com/realestate-com-au/pact/wiki/Provider-states).
|
228
231
|
|
229
|
-
You can verify a pact at an arbitrary local or remote URL
|
230
|
-
|
231
|
-
$ rake pact:verify:at[../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json]
|
232
|
-
$ rake pact:verify:at[http://build-box/MyConsumerBuild/latestSuccessful/artifacts/my_consumer-my_provider.json]
|
233
|
-
|
234
|
-
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.
|
235
|
-
|
236
|
-
```ruby
|
237
|
-
# In Rakefile or /tasks/pact.rake
|
238
|
-
|
239
|
-
# This creates a rake task that can be executed by running
|
240
|
-
# $ rake pact:verify:dev
|
241
|
-
|
242
|
-
Pact::VerificationTask.new(:dev) do | pact |
|
243
|
-
pact.uri '../path-to-your-consumer-project/specs/pacts/my_consumer-my_provider.json'
|
244
|
-
end
|
245
|
-
```
|
246
232
|
|
247
233
|
## Configuration
|
248
234
|
|
@@ -250,15 +236,20 @@ See the [Configuration](/documentation/configuration.md) section of the document
|
|
250
236
|
|
251
237
|
## Pact best practices
|
252
238
|
|
253
|
-
As in all things, there are good ways to implement Pacts, and there are not so good ways. Check out the [Best practices](/
|
239
|
+
As in all things, there are good ways to implement Pacts, and there are not so good ways. Check out the [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices) section of the documentation to make sure you're not Pacting it Wrong.
|
254
240
|
|
255
241
|
## Docs
|
256
242
|
|
257
243
|
* [Example](example)
|
258
|
-
* [
|
259
|
-
* [
|
260
|
-
* [
|
261
|
-
* [
|
244
|
+
* [Configuration](documentation/configuration.md)
|
245
|
+
* [Terminology](https://github.com/realestate-com-au/pact/wiki/Terminology)
|
246
|
+
* [Provider States](https://github.com/realestate-com-au/pact/wiki/Provider-states)
|
247
|
+
* [Verifying pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts)
|
248
|
+
* [Frequently asked questions](https://github.com/realestate-com-au/pact/wiki/FAQ)
|
249
|
+
* [Rarely asked questions](https://github.com/realestate-com-au/pact/wiki/RAQ)
|
250
|
+
* [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices)
|
251
|
+
* [Troubleshooting](https://github.com/realestate-com-au/pact/wiki/Troubleshooting)
|
252
|
+
* [Testing with pact diagram](https://github.com/realestate-com-au/pact/wiki/Testing with pact.png)
|
262
253
|
|
263
254
|
## Related libraries
|
264
255
|
|
@@ -270,6 +261,8 @@ As in all things, there are good ways to implement Pacts, and there are not so g
|
|
270
261
|
|
271
262
|
[Pact JVM](https://github.com/DiUS/pact-jvm) - A Pact implementation for the JVM (Java and Scala). It generates pact files that are compatible with the Ruby implementation.
|
272
263
|
|
264
|
+
[Pact .NET](https://github.com/SEEK-Jobs/pact-net) - A Pact implementation for .NET.
|
265
|
+
|
273
266
|
[Shokkenki](https://github.com/brentsnook/shokkenki) - Another Consumer Driven Contract gem written by one of Pact's original authors, Brent Snook. Shokkenki allows matchers to be composed using jsonpath expressions and allows auto-generation of mock response values based on regular expressions.
|
274
267
|
|
275
268
|
## Links
|
@@ -301,3 +294,6 @@ Long term:
|
|
301
294
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
302
295
|
4. Push to the branch (`git push origin my-new-feature`)
|
303
296
|
5. Create new Pull Request
|
297
|
+
|
298
|
+
If you would like to implement pact in another language, please check out the [Pact specification](https://github.com/bethesque/pact-specification) and have a chat to one of us on the [pact-dev Google group](https://groups.google.com/forum/#!forum/pact-dev). The vision is to have a compatible pact implementation in all the commonly used languages, your help would be greatly appreciated!
|
299
|
+
|
data/documentation/README.md
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
### Pact Documentation
|
2
2
|
|
3
3
|
* Step by step instructions for getting started with pacts can be found in the project [README.md](/README.md#usage)
|
4
|
-
* [Terminology](
|
4
|
+
* [Terminology](https://github.com/realestate-com-au/pact/wiki/Terminology)
|
5
5
|
* [Configuration](configuration.md)
|
6
|
-
* [Provider States](
|
7
|
-
* [
|
8
|
-
* [
|
9
|
-
* [
|
10
|
-
* [
|
11
|
-
* [
|
6
|
+
* [Provider States](https://github.com/realestate-com-au/pact/wiki/Provider-states)
|
7
|
+
* [Verifying pacts](https://github.com/realestate-com-au/pact/wiki/Verifying-pacts)
|
8
|
+
* [Frequently asked questions](https://github.com/realestate-com-au/pact/wiki/FAQ)
|
9
|
+
* [Rarely asked questions](https://github.com/realestate-com-au/pact/wiki/RAQ)
|
10
|
+
* [Best practices](https://github.com/realestate-com-au/pact/wiki/Best-practices)
|
11
|
+
* [Troubleshooting](https://github.com/realestate-com-au/pact/wiki/Troubleshooting)
|
12
|
+
* [Testing with pact diagram](https://github.com/realestate-com-au/pact/wiki/Testing with pact.png)
|
13
|
+
* [Development workflow](https://github.com/realestate-com-au/pact/wiki/Development-workflow)
|
data/lib/pact/configuration.rb
CHANGED
@@ -4,6 +4,8 @@ require 'pact/doc/markdown/generator'
|
|
4
4
|
require 'pact/matchers/unix_diff_formatter'
|
5
5
|
require 'pact/matchers/embedded_diff_formatter'
|
6
6
|
require 'pact/matchers/list_diff_formatter'
|
7
|
+
require 'pact/shared/json_differ'
|
8
|
+
require 'pact/shared/text_differ'
|
7
9
|
|
8
10
|
module Pact
|
9
11
|
|
@@ -17,6 +19,27 @@ module Pact
|
|
17
19
|
:list => Pact::Matchers::ListDiffFormatter
|
18
20
|
}
|
19
21
|
|
22
|
+
|
23
|
+
class NilMatcher
|
24
|
+
def self.=~ other
|
25
|
+
other == nil ? 0 : nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
DIFF_FORMATTER_REGISTRATIONS = [
|
30
|
+
[/.*/, Pact::Matchers::UnixDiffFormatter],
|
31
|
+
[NilMatcher, Pact::Matchers::UnixDiffFormatter]
|
32
|
+
]
|
33
|
+
|
34
|
+
DIFFERS = [
|
35
|
+
[/json/, Pact::JsonDiffer],
|
36
|
+
[NilMatcher, Pact::TextDiffer],
|
37
|
+
[/.*/, Pact::TextDiffer]
|
38
|
+
]
|
39
|
+
|
40
|
+
|
41
|
+
DEFAULT_DIFFER = Pact::TextDiffer
|
42
|
+
|
20
43
|
attr_accessor :pact_dir
|
21
44
|
attr_accessor :log_dir
|
22
45
|
attr_accessor :doc_dir
|
@@ -41,6 +64,11 @@ module Pact
|
|
41
64
|
c
|
42
65
|
end
|
43
66
|
|
67
|
+
def initialize
|
68
|
+
@differ_registrations = []
|
69
|
+
@diff_formatter_registrations = []
|
70
|
+
end
|
71
|
+
|
44
72
|
def logger
|
45
73
|
@logger ||= create_logger
|
46
74
|
end
|
@@ -61,20 +89,29 @@ module Pact
|
|
61
89
|
@doc_generators ||= []
|
62
90
|
end
|
63
91
|
|
64
|
-
|
65
|
-
|
92
|
+
# Should this be deprecated in favour of register_diff_formatter???
|
93
|
+
def diff_formatter= diff_formatter
|
94
|
+
register_diff_formatter /.*/, diff_formatter
|
95
|
+
register_diff_formatter nil, diff_formatter
|
66
96
|
end
|
67
97
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
98
|
+
def register_diff_formatter content_type, diff_formatter
|
99
|
+
key = content_type_regexp_for content_type
|
100
|
+
@diff_formatter_registrations << [key, diff_formatter_for(diff_formatter)]
|
101
|
+
end
|
102
|
+
|
103
|
+
def diff_formatter_for_content_type content_type
|
104
|
+
diff_formatter_registrations.find{ | registration | registration.first =~ content_type }.last
|
105
|
+
end
|
106
|
+
|
107
|
+
def register_body_differ content_type, differ
|
108
|
+
key = content_type_regexp_for content_type
|
109
|
+
validate_differ differ
|
110
|
+
@differ_registrations << [key, differ]
|
111
|
+
end
|
112
|
+
|
113
|
+
def body_differ_for_content_type content_type
|
114
|
+
differ_registrations.find{ | registration | registration.first =~ content_type }.last
|
78
115
|
end
|
79
116
|
|
80
117
|
def log_path
|
@@ -91,6 +128,40 @@ module Pact
|
|
91
128
|
|
92
129
|
private
|
93
130
|
|
131
|
+
def diff_formatter_for input
|
132
|
+
if DIFF_FORMATTERS[input]
|
133
|
+
DIFF_FORMATTERS[input]
|
134
|
+
elsif input.respond_to?(:call)
|
135
|
+
input
|
136
|
+
else
|
137
|
+
raise "Pact diff_formatter needs to respond to call, or be in the preconfigured list: #{DIFF_FORMATTERS.keys}"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def validate_differ differ
|
142
|
+
if !differ.respond_to?(:call)
|
143
|
+
raise "Pact.configuration.register_body_differ expects a differ that is a lamda or a class/object that responds to call."
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def content_type_regexp_for content_type
|
148
|
+
case content_type
|
149
|
+
when String then Regexp.new(/^#{Regexp.escape(content_type)}$/)
|
150
|
+
when Regexp then content_type
|
151
|
+
when nil then NilMatcher
|
152
|
+
else
|
153
|
+
raise "Invalid content type used to register a differ (#{content_type.inspect}). Please use a Regexp or a String."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def differ_registrations
|
158
|
+
@differ_registrations + DIFFERS
|
159
|
+
end
|
160
|
+
|
161
|
+
def diff_formatter_registrations
|
162
|
+
@diff_formatter_registrations + DIFF_FORMATTER_REGISTRATIONS
|
163
|
+
end
|
164
|
+
|
94
165
|
def self.default_log_dir
|
95
166
|
File.expand_path("./log")
|
96
167
|
end
|
@@ -56,10 +56,14 @@ module Pact
|
|
56
56
|
def to_s
|
57
57
|
[
|
58
58
|
"Diff with interaction: #{candidate_interaction.description_with_provider_state_quoted}",
|
59
|
-
|
59
|
+
diff_formatter.call(diff, {colour: false})
|
60
60
|
].join("\n")
|
61
61
|
end
|
62
62
|
|
63
|
+
def diff_formatter
|
64
|
+
Pact.configuration.diff_formatter_for_content_type(candidate_interaction.request.content_type)
|
65
|
+
end
|
66
|
+
|
63
67
|
def diff
|
64
68
|
@diff ||= candidate_interaction.request.difference(actual_request)
|
65
69
|
end
|
@@ -11,6 +11,7 @@ module Pact
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def before_each example_description
|
14
|
+
Pact.consumer_world.register_pact_example_ran
|
14
15
|
Pact.configuration.logger.info "Clearing all expectations"
|
15
16
|
Pact::Consumer::AppManager.instance.ports_of_mock_services.each do | port |
|
16
17
|
Pact::Consumer::MockServiceClient.clear_interactions port, example_description
|
@@ -25,10 +26,12 @@ module Pact
|
|
25
26
|
end
|
26
27
|
|
27
28
|
def after_suite
|
28
|
-
Pact.consumer_world.
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
if Pact.consumer_world.any_pact_examples_ran?
|
30
|
+
Pact.consumer_world.consumer_contract_builders.each { | c | c.write_pact }
|
31
|
+
Pact::Doc::Generate.call
|
32
|
+
Pact::Consumer::AppManager.instance.kill_all
|
33
|
+
Pact::Consumer::AppManager.instance.clear_all
|
34
|
+
end
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
data/lib/pact/consumer/world.rb
CHANGED
@@ -12,6 +12,10 @@ module Pact
|
|
12
12
|
module Consumer
|
13
13
|
class World
|
14
14
|
|
15
|
+
def initialize
|
16
|
+
@any_pact_examples_ran = false
|
17
|
+
end
|
18
|
+
|
15
19
|
def consumer_contract_builders
|
16
20
|
@consumer_contract_builders ||= []
|
17
21
|
end
|
@@ -20,6 +24,14 @@ module Pact
|
|
20
24
|
consumer_contract_builders << consumer_contract_builder
|
21
25
|
end
|
22
26
|
|
27
|
+
def register_pact_example_ran
|
28
|
+
@any_pact_examples_ran = true
|
29
|
+
end
|
30
|
+
|
31
|
+
def any_pact_examples_ran?
|
32
|
+
@any_pact_examples_ran
|
33
|
+
end
|
34
|
+
|
23
35
|
end
|
24
36
|
end
|
25
37
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Pact
|
2
|
+
|
3
|
+
class DuplicateHeaderError < StandardError; end
|
4
|
+
class InvalidHeaderNameTypeError < StandardError; end
|
5
|
+
|
6
|
+
class Headers < Hash
|
7
|
+
|
8
|
+
def initialize hash = {}
|
9
|
+
hash.each_pair do | key, value |
|
10
|
+
check_for_invalid key
|
11
|
+
self[find_matching_key(key)] = value
|
12
|
+
end
|
13
|
+
self.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
def [] key
|
17
|
+
super(find_matching_key(key))
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch *args, &block
|
21
|
+
args[0] = find_matching_key(args[0]) if args.first
|
22
|
+
super(*args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def key? key
|
26
|
+
super(find_matching_key(key))
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :has_key?, :key?
|
30
|
+
alias_method :include?, :key?
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def find_matching_key key
|
35
|
+
key = key.to_s
|
36
|
+
match = keys.find { |k| k.downcase == key.downcase }
|
37
|
+
match.nil? ? key : match
|
38
|
+
end
|
39
|
+
|
40
|
+
def check_for_invalid key
|
41
|
+
unless (String === key || Symbol === key)
|
42
|
+
raise InvalidHeaderNameTypeError.new "Header name (#{key}) must be a String or a Symbol."
|
43
|
+
end
|
44
|
+
if key? key
|
45
|
+
raise DuplicateHeaderError.new "Duplicate header found (#{find_matching_key(key)} and #{key}. Please use a comma separated single value when multiple headers with the same name are required."
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -60,8 +60,13 @@ module Pact
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def body_difference(actual_body)
|
63
|
-
|
63
|
+
body_differ.call({:body => body}, {body: actual_body}, allow_unexpected_keys: runtime_options[:allow_unexpected_keys_in_body])
|
64
64
|
end
|
65
|
+
|
66
|
+
def body_differ
|
67
|
+
Pact.configuration.body_differ_for_content_type content_type
|
68
|
+
end
|
69
|
+
|
65
70
|
end
|
66
71
|
|
67
72
|
end
|