pact-support 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +29 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +80 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +4 -0
  10. data/Rakefile +4 -0
  11. data/lib/pact/configuration.rb +164 -0
  12. data/lib/pact/consumer/request.rb +27 -0
  13. data/lib/pact/consumer_contract.rb +1 -0
  14. data/lib/pact/consumer_contract/consumer_contract.rb +114 -0
  15. data/lib/pact/consumer_contract/file_name.rb +19 -0
  16. data/lib/pact/consumer_contract/headers.rb +51 -0
  17. data/lib/pact/consumer_contract/interaction.rb +73 -0
  18. data/lib/pact/consumer_contract/pact_file.rb +24 -0
  19. data/lib/pact/consumer_contract/request.rb +73 -0
  20. data/lib/pact/consumer_contract/response.rb +51 -0
  21. data/lib/pact/consumer_contract/service_consumer.rb +28 -0
  22. data/lib/pact/consumer_contract/service_provider.rb +28 -0
  23. data/lib/pact/logging.rb +14 -0
  24. data/lib/pact/matchers.rb +1 -0
  25. data/lib/pact/matchers/actual_type.rb +16 -0
  26. data/lib/pact/matchers/base_difference.rb +37 -0
  27. data/lib/pact/matchers/differ.rb +153 -0
  28. data/lib/pact/matchers/difference.rb +13 -0
  29. data/lib/pact/matchers/difference_indicator.rb +26 -0
  30. data/lib/pact/matchers/embedded_diff_formatter.rb +62 -0
  31. data/lib/pact/matchers/expected_type.rb +35 -0
  32. data/lib/pact/matchers/index_not_found.rb +15 -0
  33. data/lib/pact/matchers/list_diff_formatter.rb +101 -0
  34. data/lib/pact/matchers/matchers.rb +139 -0
  35. data/lib/pact/matchers/no_diff_indicator.rb +18 -0
  36. data/lib/pact/matchers/regexp_difference.rb +13 -0
  37. data/lib/pact/matchers/type_difference.rb +16 -0
  38. data/lib/pact/matchers/unexpected_index.rb +11 -0
  39. data/lib/pact/matchers/unexpected_key.rb +11 -0
  40. data/lib/pact/matchers/unix_diff_formatter.rb +114 -0
  41. data/lib/pact/reification.rb +28 -0
  42. data/lib/pact/rspec.rb +53 -0
  43. data/lib/pact/shared/active_support_support.rb +51 -0
  44. data/lib/pact/shared/dsl.rb +76 -0
  45. data/lib/pact/shared/jruby_support.rb +18 -0
  46. data/lib/pact/shared/json_differ.rb +15 -0
  47. data/lib/pact/shared/key_not_found.rb +15 -0
  48. data/lib/pact/shared/null_expectation.rb +31 -0
  49. data/lib/pact/shared/request.rb +97 -0
  50. data/lib/pact/shared/text_differ.rb +14 -0
  51. data/lib/pact/something_like.rb +49 -0
  52. data/lib/pact/support.rb +9 -0
  53. data/lib/pact/support/version.rb +5 -0
  54. data/lib/pact/symbolize_keys.rb +12 -0
  55. data/lib/pact/term.rb +85 -0
  56. data/lib/tasks/pact.rake +29 -0
  57. data/pact-support.gemspec +35 -0
  58. data/spec/lib/pact/consumer/request_spec.rb +25 -0
  59. data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +58 -0
  60. data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +141 -0
  61. data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
  62. data/spec/lib/pact/consumer_contract/interaction_spec.rb +151 -0
  63. data/spec/lib/pact/consumer_contract/request_spec.rb +329 -0
  64. data/spec/lib/pact/consumer_contract/response_spec.rb +73 -0
  65. data/spec/lib/pact/matchers/differ_spec.rb +215 -0
  66. data/spec/lib/pact/matchers/difference_spec.rb +22 -0
  67. data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +90 -0
  68. data/spec/lib/pact/matchers/index_not_found_spec.rb +21 -0
  69. data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +120 -0
  70. data/spec/lib/pact/matchers/matchers_spec.rb +500 -0
  71. data/spec/lib/pact/matchers/regexp_difference_spec.rb +20 -0
  72. data/spec/lib/pact/matchers/type_difference_spec.rb +34 -0
  73. data/spec/lib/pact/matchers/unexpected_index_spec.rb +20 -0
  74. data/spec/lib/pact/matchers/unexpected_key_spec.rb +20 -0
  75. data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +216 -0
  76. data/spec/lib/pact/reification_spec.rb +67 -0
  77. data/spec/lib/pact/shared/dsl_spec.rb +86 -0
  78. data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
  79. data/spec/lib/pact/shared/key_not_found_spec.rb +20 -0
  80. data/spec/lib/pact/shared/request_spec.rb +196 -0
  81. data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
  82. data/spec/lib/pact/something_like_spec.rb +21 -0
  83. data/spec/lib/pact/term_spec.rb +89 -0
  84. data/spec/spec_helper.rb +19 -0
  85. data/spec/support/a_consumer-a_producer.json +32 -0
  86. data/spec/support/a_consumer-a_provider.json +32 -0
  87. data/spec/support/active_support_if_configured.rb +6 -0
  88. data/spec/support/case-insensitive-response-header-matching.json +21 -0
  89. data/spec/support/consumer_contract_template.json +24 -0
  90. data/spec/support/dsl_spec_support.rb +7 -0
  91. data/spec/support/factories.rb +82 -0
  92. data/spec/support/generated_index.md +4 -0
  93. data/spec/support/generated_markdown.md +55 -0
  94. data/spec/support/interaction_view_model.json +63 -0
  95. data/spec/support/interaction_view_model_with_terms.json +50 -0
  96. data/spec/support/markdown_pact.json +48 -0
  97. data/spec/support/missing_provider_states_output.txt +25 -0
  98. data/spec/support/options.json +21 -0
  99. data/spec/support/shared_examples_for_request.rb +94 -0
  100. data/spec/support/spec_support.rb +20 -0
  101. data/spec/support/stubbing.json +22 -0
  102. data/spec/support/term.json +48 -0
  103. data/spec/support/test_app_fail.json +61 -0
  104. data/spec/support/test_app_pass.json +38 -0
  105. data/spec/support/test_app_with_right_content_type_differ.json +23 -0
  106. data/tasks/spec.rake +6 -0
  107. metadata +401 -0
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'pact/consumer_contract/headers'
3
+
4
+ module Pact
5
+ describe Headers do
6
+
7
+ describe "initialize" do
8
+
9
+ context "with duplicate headers" do
10
+
11
+ subject { Headers.new('Content-Type' => 'application/hippo', 'CONTENT-TYPE' => 'application/giraffe') }
12
+
13
+ it "raises an error" do
14
+ expect { subject }.to raise_error DuplicateHeaderError, /Content\-Type.*CONTENT\-TYPE/
15
+ end
16
+
17
+ end
18
+
19
+ context "with a symbol as a header name" do
20
+
21
+ subject { Headers.new(:'content-type' => 'application/hippo') }
22
+
23
+ it "converts the header name to a String" do
24
+ expect( subject.to_hash ).to eq 'content-type' => 'application/hippo'
25
+ end
26
+
27
+ end
28
+
29
+ context "with a nil header name" do
30
+
31
+ subject { Headers.new(nil => 'application/hippo') }
32
+
33
+ it "raises an error" do
34
+ expect{ subject }.to raise_error InvalidHeaderNameTypeError
35
+ end
36
+
37
+ end
38
+
39
+ context "with a boolean header name" do
40
+
41
+ subject { Headers.new(false => 'application/hippo') }
42
+
43
+ it "raises an error" do
44
+ expect{ subject }.to raise_error InvalidHeaderNameTypeError
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ describe "[]" do
52
+
53
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
54
+
55
+ it "is case insensitive as HTTP headers are case insensitive" do
56
+ expect(subject['Content-Type']).to eq('application/hippo')
57
+ expect(subject['CONTENT-TYPE']).to eq('application/hippo')
58
+ expect(subject['content-type']).to eq('application/hippo')
59
+ end
60
+
61
+ end
62
+
63
+ describe "fetch" do
64
+
65
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
66
+
67
+ it "is case insensitive as HTTP headers are case insensitive" do
68
+ expect(subject.fetch('Content-Type')).to eq('application/hippo')
69
+ expect(subject.fetch('CONTENT-TYPE')).to eq('application/hippo')
70
+ expect(subject.fetch('content-type')).to eq('application/hippo')
71
+ expect(subject.fetch('Content-Length','1')).to eq('1')
72
+ expect { subject.fetch('Content-Length')}.to raise_error KeyError
73
+ end
74
+
75
+ end
76
+
77
+ describe "key?" do
78
+
79
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
80
+
81
+ it "is case insensitive as HTTP headers are case insensitive" do
82
+ expect(subject.key?('CONTENT-TYPE')).to be true
83
+ expect(subject.key?('CONTENT-LENGTH')).to be false
84
+ end
85
+ end
86
+
87
+ describe "has_key?" do
88
+
89
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
90
+
91
+ it "is case insensitive as HTTP headers are case insensitive" do
92
+ expect(subject.has_key?('CONTENT-TYPE')).to be true
93
+ expect(subject.has_key?('CONTENT-LENGTH')).to be false
94
+ end
95
+ end
96
+
97
+ describe "[]=" do
98
+
99
+ subject { Headers.new }
100
+
101
+ it "does not allow modification" do
102
+ expect{ subject['Content-Type'] = 'application/hippo' }.to raise_error /frozen/
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,151 @@
1
+ require 'spec_helper'
2
+ require 'pact/reification'
3
+ require 'pact/consumer_contract/interaction'
4
+
5
+ module Pact
6
+ module Consumer
7
+
8
+ describe Interaction do
9
+
10
+ let(:request) { {method: 'get', path: 'path'} }
11
+ let(:response) { {} }
12
+
13
+ describe "==" do
14
+ subject { InteractionFactory.create }
15
+ context "when other is the same" do
16
+ let(:other) { InteractionFactory.create }
17
+ it "returns true" do
18
+ expect(subject == other).to be true
19
+ end
20
+ end
21
+ context "when other is not the same" do
22
+ let(:other) { InteractionFactory.create(:request => {:path => '/a_different_path'}) }
23
+ it "returns false" do
24
+ expect(subject == other).to be false
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "matches_criteria?" do
30
+ subject { InteractionFactory.create(:description => 'a request for food') }
31
+ context "by description" do
32
+ context "when the interaction matches" do
33
+ it "returns true" do
34
+ expect(subject.matches_criteria?(:description => /request.*food/)).to be true
35
+ end
36
+ end
37
+ context "when the interaction does not match" do
38
+ it "returns false" do
39
+ expect(subject.matches_criteria?(:description => /blah/)).to be false
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "from_hash" do
46
+ context "when providerState has been used instead of provider_state" do
47
+
48
+ subject { Interaction.from_hash('response' => response, 'request' => request, 'providerState' => 'some state') }
49
+
50
+ it "recognises the provider state" do
51
+ expect(subject.provider_state).to eq 'some state'
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "to JSON" do
57
+ let(:request) do
58
+ {
59
+ method: 'post',
60
+ path: '/foo',
61
+ body: Term.new(generate: 'waffle', matcher: /ffl/),
62
+ headers: { 'Content-Type' => 'application/json' },
63
+ query: '',
64
+ }
65
+ end
66
+
67
+ let(:response) do
68
+ { baz: /qux/, wiffle: Term.new(generate: 'wiffle', matcher: /iff/) }
69
+ end
70
+
71
+ let(:parsed_result) do
72
+ JSON.load(subject.to_json)
73
+ end
74
+
75
+ subject { Interaction.from_hash('response' => response, 'request' => request) }
76
+
77
+ it "contains the request" do
78
+ expect(parsed_result['request']).to eq({
79
+ 'method' => 'post',
80
+ 'path' => '/foo',
81
+ 'headers' => {
82
+ 'Content-Type' => 'application/json'
83
+ },
84
+ 'body' => Term.new(generate: 'waffle', matcher: /ffl/),
85
+ 'query' => ''
86
+ })
87
+ end
88
+
89
+ describe "response" do
90
+
91
+ it "serialises regexes" do
92
+ expect(parsed_result['response']['baz']).to eql /qux/
93
+ end
94
+
95
+ it "serialises terms" do
96
+ term = Term.new(generate:'wiffle', matcher: /iff/)
97
+ parsed_term = parsed_result['response']['wiffle']
98
+ expect(term.matcher).to eql parsed_term.matcher
99
+ expect(term.generate).to eql parsed_term.generate
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
106
+ describe "request_modifies_resource_without_checking_response_body?" do
107
+
108
+ let(:interaction) { Interaction.new(request: request, response: response)}
109
+
110
+ subject { interaction.request_modifies_resource_without_checking_response_body?}
111
+
112
+ context "when the request modifies the resource and the response allows any value in body" do
113
+ let(:request) { instance_double(Pact::Request::Expected, modifies_resource?: true) }
114
+ let(:response) { instance_double(Pact::Response, body_allows_any_value?: true) }
115
+
116
+ it "returns true" do
117
+ expect(subject).to be true
118
+ end
119
+ end
120
+
121
+ context "when the request modifies the resource and the response does not allow any value in body" do
122
+ let(:request) { instance_double(Pact::Request::Expected, modifies_resource?: true) }
123
+ let(:response) { instance_double(Pact::Response, body_allows_any_value?: false) }
124
+
125
+ it "returns false" do
126
+ expect(subject).to be false
127
+ end
128
+ end
129
+
130
+ context "when the request does not modifies the resource and the response does not allow any value in body" do
131
+ let(:request) { instance_double(Pact::Request::Expected, modifies_resource?: false) }
132
+ let(:response) { instance_double(Pact::Response, body_allows_any_value?: false) }
133
+
134
+ it "returns false" do
135
+ expect(subject).to be false
136
+ end
137
+ end
138
+
139
+ context "when the request does not modifies the resource and the response allows any value in body" do
140
+ let(:request) { instance_double(Pact::Request::Expected, modifies_resource?: false) }
141
+ let(:response) { instance_double(Pact::Response, body_allows_any_value?: true) }
142
+
143
+ it "returns false" do
144
+ expect(subject).to be false
145
+ end
146
+ end
147
+
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,329 @@
1
+ require 'spec_helper'
2
+ require 'pact/consumer_contract/request'
3
+ require 'support/shared_examples_for_request'
4
+ require 'pact/consumer/request'
5
+
6
+ module Pact
7
+
8
+ describe Request::Expected do
9
+ it_behaves_like "a request"
10
+
11
+ let(:raw_request) do
12
+ {
13
+ 'method' => 'get',
14
+ 'path' => '/mallory'
15
+ }
16
+ end
17
+
18
+ describe "from_hash" do
19
+ context "when optional field are not defined" do
20
+ subject { described_class.from_hash(raw_request) }
21
+ it "sets their values to NullExpectation" do
22
+ expect(subject.body).to be_instance_of(Pact::NullExpectation)
23
+ expect(subject.query).to be_instance_of(Pact::NullExpectation)
24
+ expect(subject.headers).to be_instance_of(Pact::NullExpectation)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "as_json" do
30
+ subject { Request::Expected.new(:get, '/path', {:header => 'value'}, {:body => 'yeah'}, "query", {some: 'options'}) }
31
+ context "with options" do
32
+ it "does not include the options because they are a temporary hack and should leave no trace of themselves in the pact file" do
33
+ expect(subject.as_json.key?(:options)).to be false
34
+ end
35
+ end
36
+ end
37
+
38
+ describe "matching to actual requests" do
39
+
40
+ subject { Request::Expected.new(expected_method, expected_path, expected_headers, expected_body, expected_query, options) }
41
+ let(:options) { {} }
42
+
43
+ let(:expected_method) { 'get' }
44
+ let(:expected_path) { '/foo' }
45
+ let(:expected_headers) { Pact::NullExpectation.new }
46
+ let(:expected_body) { Pact::NullExpectation.new }
47
+ let(:expected_query) { '' }
48
+
49
+ let(:actual_request) { Consumer::Request::Actual.new(actual_method, actual_path, actual_headers, actual_body, actual_query) }
50
+
51
+ let(:actual_method) { 'get' }
52
+ let(:actual_path) { '/foo' }
53
+ let(:actual_headers) { {} }
54
+ let(:actual_body) { '' }
55
+ let(:actual_query) { '' }
56
+
57
+ it "matches identical requests" do
58
+ expect(subject.matches? actual_request).to be true
59
+ end
60
+
61
+ context "when the methods are the same but one is symbolized" do
62
+ let(:expected_method) { :get }
63
+ let(:actual_method) { 'get' }
64
+
65
+ it "matches" do
66
+ expect(subject.matches? actual_request).to be true
67
+ end
68
+ end
69
+
70
+ context "when the methods are different" do
71
+ let(:expected_method) { 'get' }
72
+ let(:actual_method) { 'post' }
73
+
74
+ it "does not match" do
75
+ expect(subject.matches? actual_request).to be false
76
+ end
77
+ end
78
+
79
+ context "when the methods are the same but different case" do
80
+ let(:expected_method) { 'get' }
81
+ let(:actual_method) { 'GET' }
82
+
83
+ it "matches" do
84
+ expect(subject.matches? actual_request).to be true
85
+ end
86
+ end
87
+
88
+ context "when the paths are different" do
89
+ let(:expected_path) { '/foo' }
90
+ let(:actual_path) { '/bar' }
91
+
92
+ it "does not match" do
93
+ expect(subject.matches? actual_request).to be false
94
+ end
95
+ end
96
+
97
+ context "when the paths vary only by a trailing slash" do
98
+ let(:expected_path) { '/foo' }
99
+ let(:actual_path) { '/foo/' }
100
+
101
+ it "matches" do
102
+ expect(subject.matches? actual_request).to be true
103
+ end
104
+ end
105
+
106
+ context "when the expected body is nil and the actual body is empty" do
107
+ let(:expected_body) { nil }
108
+ let(:actual_body) { '' }
109
+
110
+ it "does not match" do
111
+ expect(subject.matches? actual_request).to be false
112
+ end
113
+ end
114
+
115
+ context "when the expected body has no expectation and the actual body is empty" do
116
+ let(:expected_body) { Pact::NullExpectation.new }
117
+ let(:actual_body) { '' }
118
+
119
+ it "matches" do
120
+ expect(subject.matches? actual_request).to be true
121
+ end
122
+ end
123
+
124
+ context "when the expected body is nested and the actual body is nil" do
125
+ let(:expected_body) do
126
+ {
127
+ a: 'a'
128
+ }
129
+ end
130
+
131
+ let(:actual_body) { nil }
132
+
133
+ it "does not match" do
134
+ expect(subject.matches? actual_request).to be false
135
+ end
136
+ end
137
+
138
+ context "when the bodies are different" do
139
+ let(:expected_body) { 'foo' }
140
+ let(:actual_body) { 'bar' }
141
+
142
+ it "does not match" do
143
+ expect(subject.matches? actual_request).to be false
144
+ end
145
+ end
146
+
147
+ context "when the expected body contains matching regexes" do
148
+ let(:expected_body) do
149
+ {
150
+ name: 'Bob',
151
+ customer_id: /CN.*/
152
+ }
153
+ end
154
+
155
+ let(:actual_body) do
156
+ {
157
+ name: 'Bob',
158
+ customer_id: 'CN1234'
159
+ }
160
+ end
161
+
162
+ it "matches" do
163
+ expect(subject.matches? actual_request).to be true
164
+ end
165
+ end
166
+
167
+ context "when the expected body contains non-matching regexes" do
168
+ let(:expected_body) do
169
+ {
170
+ name: 'Bob',
171
+ customer_id: /foo/
172
+ }
173
+ end
174
+
175
+ let(:actual_body) do
176
+ {
177
+ name: 'Bob',
178
+ customer_id: 'CN1234'
179
+ }
180
+ end
181
+
182
+ it "does not match" do
183
+ expect(subject.matches? actual_request).to be false
184
+ end
185
+ end
186
+
187
+ context "when the expected body contains matching terms" do
188
+ let(:expected_body) do
189
+ {
190
+ name: 'Bob',
191
+ customer_id: Term.new(matcher: /CN.*/, generate: 'CN789')
192
+ }
193
+ end
194
+
195
+ let(:actual_body) do
196
+ {
197
+ name: 'Bob',
198
+ customer_id: 'CN1234'
199
+ }
200
+ end
201
+
202
+ it "matches" do
203
+ expect(subject.matches? actual_request).to be true
204
+ end
205
+ end
206
+
207
+ context "when the expected body contains non-matching terms" do
208
+ let(:expected_body) do
209
+ {
210
+ name: 'Bob',
211
+ customer_id: Term.new(matcher: /foo/, generate: 'fooool')
212
+ }
213
+ end
214
+
215
+ let(:actual_body) do
216
+ {
217
+ name: 'Bob',
218
+ customer_id: 'CN1234'
219
+ }
220
+ end
221
+
222
+ it "does not match" do
223
+ expect(subject.matches? actual_request).to be false
224
+ end
225
+ end
226
+
227
+ context "when the expected body contains non-matching arrays" do
228
+ let(:expected_body) do
229
+ {
230
+ name: 'Robert',
231
+ nicknames: ['Bob', 'Bobert']
232
+ }
233
+ end
234
+
235
+ let(:actual_body) do
236
+ {
237
+ name: 'Bob',
238
+ nicknames: ['Bob']
239
+ }
240
+ end
241
+
242
+ it "does not match" do
243
+ expect(subject.matches? actual_request).to be false
244
+ end
245
+ end
246
+ context "when the expected body contains non-matching hash where one field contains a substring of the other" do
247
+ let(:expected_body) do
248
+ {
249
+ name: 'Robert',
250
+ }
251
+ end
252
+
253
+ let(:actual_body) do
254
+ {
255
+ name: 'Rob'
256
+ }
257
+ end
258
+
259
+ it "does not match" do
260
+ expect(subject.matches? actual_request).to be false
261
+ end
262
+ end
263
+
264
+ context "when the expected body contains matching arrays" do
265
+ let(:expected_body) do
266
+ {
267
+ name: 'Robert',
268
+ nicknames: ['Bob', 'Bobert']
269
+ }
270
+ end
271
+
272
+ let(:actual_body) do
273
+ {
274
+ name: 'Robert',
275
+ nicknames: ['Bob', 'Bobert']
276
+ }
277
+ end
278
+
279
+ it "does not match" do
280
+ expect(subject.matches? actual_request).to be true
281
+ end
282
+ end
283
+
284
+ context "when the queries are different" do
285
+ let(:expected_query) { 'foo' }
286
+ let(:actual_query) { 'bar' }
287
+
288
+ it "does not match" do
289
+ expect(subject.matches? actual_request).to be false
290
+ end
291
+ end
292
+
293
+ context 'when there is no query expectation' do
294
+ let(:expected_query) { Pact::NullExpectation.new }
295
+ let(:actual_query) { 'bar' }
296
+
297
+ it 'matches' do
298
+ expect(subject.matches? actual_request).to be true
299
+ end
300
+ end
301
+
302
+ context "when a string is expected, but a number is found" do
303
+ let(:actual_body) { { thing: 123} }
304
+ let(:expected_body) { { thing: "123" } }
305
+
306
+ it 'does not match' do
307
+ expect(subject.matches? actual_request).to be false
308
+ end
309
+ end
310
+
311
+ context "when unexpected keys are found in the body" do
312
+ let(:expected_body) { {a: 1} }
313
+ let(:actual_body) { {a: 1, b: 2} }
314
+ context "when allowing unexpected keys" do
315
+ let(:options) { {'allow_unexpected_keys_in_body' => true} } #From json, these will be strings
316
+ it "matches" do
317
+ expect(subject.matches? actual_request).to be true
318
+ end
319
+ end
320
+ context "when not allowing unexpected keys" do
321
+ let(:options) { {'allow_unexpected_keys_in_body' => false} }
322
+ it "does not match" do
323
+ expect(subject.matches? actual_request).to be false
324
+ end
325
+ end
326
+ end
327
+ end
328
+ end
329
+ end