pact-support 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/lib/pact/consumer_contract/interaction.rb +6 -3
- data/lib/pact/matchers/index_not_found.rb +1 -1
- data/lib/pact/matchers/list_diff_formatter.rb +2 -2
- data/lib/pact/matchers/matchers.rb +42 -34
- data/lib/pact/matchers/{no_diff_indicator.rb → no_diff_at_index.rb} +3 -3
- data/lib/pact/matchers/unexpected_index.rb +1 -1
- data/lib/pact/matchers/unix_diff_formatter.rb +23 -5
- data/lib/pact/matching_rules.rb +17 -0
- data/lib/pact/matching_rules/extract.rb +64 -0
- data/lib/pact/matching_rules/merge.rb +80 -0
- data/lib/pact/support/version.rb +1 -1
- data/lib/pact/term.rb +1 -1
- data/spec/fixtures/interaction-with-matching-rules.json +27 -0
- data/spec/integration/matching_rules_extract_and_merge_spec.rb +98 -0
- data/spec/lib/pact/consumer_contract/interaction_spec.rb +14 -0
- data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +2 -2
- data/spec/lib/pact/matchers/matchers_spec.rb +9 -8
- data/spec/lib/pact/matchers/no_diff_at_index_spec.rb +15 -0
- data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +15 -14
- data/spec/lib/pact/matching_rules/extract_spec.rb +105 -0
- data/spec/lib/pact/matching_rules/merge_spec.rb +148 -0
- data/spec/support/spec_support.rb +9 -0
- metadata +51 -39
- data/spec/lib/pact/matchers/no_diff_indicator_spec.rb +0 -15
data/lib/pact/support/version.rb
CHANGED
data/lib/pact/term.rb
CHANGED
@@ -27,7 +27,7 @@ module Pact
|
|
27
27
|
@matcher = attributes[:matcher]
|
28
28
|
raise "Please specify a matcher for the Term" unless @matcher != nil
|
29
29
|
raise "Please specify a value to generate for the Term" unless @generate != nil
|
30
|
-
raise "Value to generate \"#{@generate}\" does not match regular expression #{@matcher}" unless @generate =~ @matcher
|
30
|
+
raise "Value to generate \"#{@generate}\" does not match regular expression #{@matcher.inspect}" unless @generate =~ @matcher
|
31
31
|
end
|
32
32
|
|
33
33
|
def to_hash
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"description": "a request to make something",
|
3
|
+
"providerState" : null,
|
4
|
+
"request" : {
|
5
|
+
"method" : "POST",
|
6
|
+
"path" : "/something",
|
7
|
+
"body": {
|
8
|
+
"name": "Mary"
|
9
|
+
},
|
10
|
+
"requestMatchingRules": {
|
11
|
+
"$.body.name" : {"regex" : ".+"}
|
12
|
+
}
|
13
|
+
},
|
14
|
+
"response" : {
|
15
|
+
"status" : 200,
|
16
|
+
"body" : {
|
17
|
+
"_links" : {
|
18
|
+
"self" : {
|
19
|
+
"href" : "http://localhost:1234/things/1"
|
20
|
+
}
|
21
|
+
}
|
22
|
+
},
|
23
|
+
"responseMatchingRules" : {
|
24
|
+
"$.body._links.self.href": {"regex": "http:\\/\\/.*\\/\\d+"}
|
25
|
+
}
|
26
|
+
}
|
27
|
+
}
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'pact/term'
|
2
|
+
require 'pact/something_like'
|
3
|
+
require 'pact/matching_rules/extract'
|
4
|
+
require 'pact/matching_rules/merge'
|
5
|
+
require 'pact/reification'
|
6
|
+
|
7
|
+
describe "converting Pact::Term and Pact::SomethingLike to matching rules and back again" do
|
8
|
+
|
9
|
+
let(:example) { Pact::Reification.from_term expected }
|
10
|
+
let(:matching_rules) { Pact::MatchingRules::Extract.(expected) }
|
11
|
+
let(:recreated_expected) { Pact::MatchingRules::Merge.(example, matching_rules)}
|
12
|
+
|
13
|
+
context "with a Pact::Term" do
|
14
|
+
let(:expected) do
|
15
|
+
{
|
16
|
+
body: {
|
17
|
+
alligator: {
|
18
|
+
name: Pact::Term.new(generate: 'Mary', matcher: /M/)
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "recreates the same object hierarchy" do
|
25
|
+
expect(recreated_expected).to eq expected
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with a Pact::SomethingLike" do
|
30
|
+
let(:expected) do
|
31
|
+
{
|
32
|
+
body: {
|
33
|
+
alligator: {
|
34
|
+
name: Pact::SomethingLike.new("Mary")
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
it "recreates the same object hierarchy" do
|
41
|
+
expect(recreated_expected).to eq expected
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with a Pact::SomethingLike containing a Hash" do
|
46
|
+
let(:expected) do
|
47
|
+
{
|
48
|
+
body: {
|
49
|
+
alligator: Pact::SomethingLike.new(name: 'Mary')
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
let(:similar) do
|
55
|
+
{
|
56
|
+
body: {
|
57
|
+
alligator: {
|
58
|
+
name: Pact::SomethingLike.new('Mary')
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
it "recreates the same object hierarchy", pending: 'Waiting for Pact JVM to implement nested type matching' do
|
65
|
+
expect(recreated_expected).to eq expected
|
66
|
+
end
|
67
|
+
|
68
|
+
it "recreates a similar object hierarchy that does the same thing" do
|
69
|
+
expect(recreated_expected).to eq similar
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "with a Pact::SomethingLike containing an Array" do
|
74
|
+
let(:expected) do
|
75
|
+
{
|
76
|
+
body: {
|
77
|
+
alligators: Pact::SomethingLike.new(["Mary", "Betty"])
|
78
|
+
}
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
let(:similar) do
|
83
|
+
{
|
84
|
+
body: {
|
85
|
+
alligators: [Pact::SomethingLike.new("Mary"), Pact::SomethingLike.new("Betty")]
|
86
|
+
}
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
it "recreates the same object hierarchy", pending: 'Waiting for Pact JVM to implement nested type matching' do
|
91
|
+
expect(recreated_expected).to eq expected
|
92
|
+
end
|
93
|
+
|
94
|
+
it "recreates a similar object hierarchy that does the same thing" do
|
95
|
+
expect(recreated_expected).to eq similar
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -61,6 +61,20 @@ module Pact
|
|
61
61
|
expect(subject.provider_state).to eq 'some state'
|
62
62
|
end
|
63
63
|
end
|
64
|
+
|
65
|
+
context "when there are matching rules" do
|
66
|
+
let(:hash) { load_json_fixture 'interaction-with-matching-rules.json' }
|
67
|
+
|
68
|
+
subject { Interaction.from_hash hash }
|
69
|
+
|
70
|
+
it "merges the rules with the example for the request" do
|
71
|
+
expect(subject.request.body['name']).to be_instance_of(Pact::Term)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "merges the rules with the example for the response" do
|
75
|
+
expect(subject.response.body['_links']['self']['href']).to be_instance_of(Pact::Term)
|
76
|
+
end
|
77
|
+
end
|
64
78
|
end
|
65
79
|
|
66
80
|
describe "request_modifies_resource_without_checking_response_body?" do
|
@@ -80,7 +80,7 @@ EOS
|
|
80
80
|
end
|
81
81
|
|
82
82
|
context "when there is a missing index" do
|
83
|
-
let(:diff) { [
|
83
|
+
let(:diff) { [NoDiffAtIndex.new, Difference.new(1, IndexNotFound.new )]}
|
84
84
|
it "includes the expected value" do
|
85
85
|
expect(subject).to match(/Missing.*1/m)
|
86
86
|
end
|
@@ -91,7 +91,7 @@ EOS
|
|
91
91
|
end
|
92
92
|
|
93
93
|
context "when there is an unexpected index" do
|
94
|
-
let(:diff) { [
|
94
|
+
let(:diff) { [NoDiffAtIndex.new, Difference.new(UnexpectedIndex.new, 2), Difference.new(UnexpectedIndex.new, "b")]}
|
95
95
|
it "includes the unexpected value" do
|
96
96
|
expect(subject).to include("Array contained unexpected item:")
|
97
97
|
end
|
@@ -143,10 +143,11 @@ module Pact::Matchers
|
|
143
143
|
let(:expected) { [{name: 'Fred'}, {name: 'Mary'}] }
|
144
144
|
context "when an item with differing class values is found" do
|
145
145
|
let(:actual) { [{name: 'Fred'}, {name: 1}] }
|
146
|
-
let(:difference) {
|
147
|
-
|
148
|
-
|
149
|
-
|
146
|
+
let(:difference) {
|
147
|
+
[
|
148
|
+
NoDiffAtIndex.new,
|
149
|
+
{
|
150
|
+
:name => TypeDifference.new(Pact::ExpectedType.new("Mary"), Pact::ActualType.new(1))
|
150
151
|
}
|
151
152
|
]
|
152
153
|
}
|
@@ -214,7 +215,7 @@ module Pact::Matchers
|
|
214
215
|
context "when expected is longer than the actual" do
|
215
216
|
subject { [1,2,3] }
|
216
217
|
let(:actual) { [1,2]}
|
217
|
-
let(:difference) { [
|
218
|
+
let(:difference) { [NoDiffAtIndex.new, NoDiffAtIndex.new, Difference.new(3, Pact::IndexNotFound.new)] }
|
218
219
|
it 'returns the diff' do
|
219
220
|
expect(diff(subject, actual)).to eq(difference)
|
220
221
|
end
|
@@ -223,7 +224,7 @@ module Pact::Matchers
|
|
223
224
|
context "when the different index is in the middle of an array" do
|
224
225
|
subject { [1,2,3] }
|
225
226
|
let(:actual) { [1,7,3]}
|
226
|
-
let(:difference) { [
|
227
|
+
let(:difference) { [NoDiffAtIndex.new, Difference.new(2, 7), NoDiffAtIndex.new] }
|
227
228
|
it 'returns the diff' do
|
228
229
|
expect(diff(subject, actual)).to eq(difference)
|
229
230
|
end
|
@@ -232,7 +233,7 @@ module Pact::Matchers
|
|
232
233
|
context "when actual array is longer than the expected" do
|
233
234
|
subject { [1] }
|
234
235
|
let(:actual) { [1,2]}
|
235
|
-
let(:difference) { [
|
236
|
+
let(:difference) { [NoDiffAtIndex.new, Difference.new(Pact::UnexpectedIndex.new, 2)] }
|
236
237
|
it 'returns the diff' do
|
237
238
|
expect(diff(subject, actual)).to eq(difference)
|
238
239
|
end
|
@@ -478,7 +479,7 @@ module Pact::Matchers
|
|
478
479
|
context "when two different arrays are found" do
|
479
480
|
subject { [4,5,6] }
|
480
481
|
let(:actual) { [4,6,7] }
|
481
|
-
let(:difference) { [
|
482
|
+
let(:difference) { [NoDiffAtIndex.new, Difference.new(5, 6), Difference.new(6, 7)] }
|
482
483
|
|
483
484
|
it 'includes this in the diff' do
|
484
485
|
expect(diff(subject, actual)).to eq(difference)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pact/matchers/no_diff_at_index'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
module Matchers
|
5
|
+
describe NoDiffAtIndex do
|
6
|
+
|
7
|
+
describe "#to_json" do
|
8
|
+
it "returns a json string" do
|
9
|
+
expect(NoDiffAtIndex.new.to_json).to eq '"<no difference at this index>"'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -61,19 +61,19 @@ EOF
|
|
61
61
|
end
|
62
62
|
|
63
63
|
context "with an incorrect value in an array" do
|
64
|
-
let(:diff) { [
|
64
|
+
let(:diff) { [NoDiffAtIndex.new, Difference.new({name: 'Mary'}, "Joe"), NoDiffAtIndex.new] }
|
65
65
|
|
66
66
|
it "displays '+' next to the incorrect values and '-' next to the missing ones" do
|
67
|
-
expect(subject).to
|
67
|
+
expect(subject).to include "... ,"
|
68
68
|
expect(subject).to match /\-.*{/
|
69
69
|
expect(subject).to match /\-.*}/
|
70
70
|
expect(subject).to match /\-.*Mary/
|
71
71
|
expect(subject).to match /\+.*Joe/
|
72
|
-
expect(subject).to match
|
72
|
+
expect(subject).to match /\.\.\..*Mary.*Joe.*\.\.\./m
|
73
73
|
end
|
74
74
|
|
75
75
|
it "doesn't display the no difference indicator as a change" do
|
76
|
-
expect(subject).to match(/^\s
|
76
|
+
expect(subject).to match(/^\s+... ,$/)
|
77
77
|
end
|
78
78
|
|
79
79
|
it "generates the right number of lines, even with ActiveSupport loaded" do
|
@@ -147,7 +147,7 @@ EOF
|
|
147
147
|
end
|
148
148
|
|
149
149
|
context "with a missing index" do
|
150
|
-
let(:diff) { [
|
150
|
+
let(:diff) { [NoDiffAtIndex.new, Difference.new({name: 'Mary'}, IndexNotFound.new)] }
|
151
151
|
|
152
152
|
it "displays '-' next to the missing items" do
|
153
153
|
expect(subject).to match /\-.*Mary/
|
@@ -160,13 +160,14 @@ EOF
|
|
160
160
|
end
|
161
161
|
|
162
162
|
it "generates the right number of lines, even with ActiveSupport loaded" do
|
163
|
-
expect(line_count).to eq
|
163
|
+
expect(line_count).to eq 7 + key_lines_count
|
164
164
|
end
|
165
165
|
|
166
166
|
end
|
167
167
|
|
168
168
|
context "with an unexpected index" do
|
169
|
-
let(:diff) { [
|
169
|
+
let(:diff) { [NoDiffAtIndex.new, Difference.new(UnexpectedIndex.new, {name: 'Mary'})] }
|
170
|
+
let(:diff) { { some_array: [NoDiffAtIndex.new, Difference.new(UnexpectedIndex.new, {name: 'Mary'})]} }
|
170
171
|
|
171
172
|
it "displays '+' next to the unexpected item" do
|
172
173
|
expect(subject).to match /\+.*{/
|
@@ -175,10 +176,10 @@ EOF
|
|
175
176
|
expect(subject).to match /\+.*Mary/
|
176
177
|
end
|
177
178
|
|
178
|
-
|
179
|
-
expect(subject).to
|
180
|
-
expect(subject).to_not match
|
181
|
-
expect(subject).to_not match
|
179
|
+
it "doesn't mark the 'no difference' as a change" do
|
180
|
+
expect(subject).to include "... ,"
|
181
|
+
expect(subject).to_not match /\-.*\.\.\./
|
182
|
+
expect(subject).to_not match /\+.*\.\.\./
|
182
183
|
end
|
183
184
|
|
184
185
|
it "does not display the UnexpectedIndex" do
|
@@ -186,13 +187,13 @@ EOF
|
|
186
187
|
end
|
187
188
|
|
188
189
|
it "generates the right number of lines, even with ActiveSupport loaded" do
|
189
|
-
expect(line_count).to eq
|
190
|
+
expect(line_count).to eq 9 + key_lines_count
|
190
191
|
end
|
191
192
|
|
192
193
|
end
|
193
194
|
|
194
195
|
context "with 2 unexpected indexes" do
|
195
|
-
let(:diff) { [
|
196
|
+
let(:diff) { [NoDiffAtIndex.new, Difference.new(UnexpectedIndex.new, {name: 'Mary'}), Difference.new(UnexpectedIndex.new, {name: 'Joe'})] }
|
196
197
|
|
197
198
|
it "displays '+' next to the unexpected item" do
|
198
199
|
expect(subject).to match /\+.*Mary/
|
@@ -204,7 +205,7 @@ EOF
|
|
204
205
|
end
|
205
206
|
|
206
207
|
it "generates the right number of lines, even with ActiveSupport loaded" do
|
207
|
-
expect(line_count).to eq
|
208
|
+
expect(line_count).to eq 10 + key_lines_count
|
208
209
|
end
|
209
210
|
|
210
211
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'pact/matching_rules/extract'
|
2
|
+
require 'pact/something_like'
|
3
|
+
require 'pact/term'
|
4
|
+
|
5
|
+
module Pact
|
6
|
+
module MatchingRules
|
7
|
+
describe Extract do
|
8
|
+
|
9
|
+
describe ".call" do
|
10
|
+
|
11
|
+
subject { Extract.call(matchable) }
|
12
|
+
|
13
|
+
context "with a Pact::SomethingLike" do
|
14
|
+
let(:matchable) do
|
15
|
+
{
|
16
|
+
body: Pact::SomethingLike.new(foo: 'bar', alligator: { name: 'Mary' })
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:rules) do
|
21
|
+
{
|
22
|
+
"$.body.foo" => {"match" => "type"},
|
23
|
+
"$.body.alligator.name" => {"match" => "type"},
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
it "creates a rule that matches by type" do
|
29
|
+
expect(subject).to eq rules
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context "with a Pact::Term" do
|
34
|
+
let(:matchable) do
|
35
|
+
{
|
36
|
+
body: {
|
37
|
+
alligator: {
|
38
|
+
name: Pact::Term.new(generate: 'Mary', matcher: /.*a/)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
let(:rules) do
|
45
|
+
{
|
46
|
+
"$.body.alligator.name" => {"match" => "regex", "regex" => ".*a"}
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
it "creates a rule that matches by regex" do
|
52
|
+
expect(subject).to eq rules
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with a Pact::SomethingLike containing a Term" do
|
57
|
+
let(:matchable) do
|
58
|
+
{
|
59
|
+
body: Pact::SomethingLike.new(
|
60
|
+
foo: 'bar',
|
61
|
+
alligator: { name: Pact::Term.new(generate: 'Mary', matcher: /.*a/) }
|
62
|
+
)
|
63
|
+
}
|
64
|
+
end
|
65
|
+
|
66
|
+
let(:rules) do
|
67
|
+
{
|
68
|
+
"$.body.foo" => {"match" => "type"},
|
69
|
+
"$.body.alligator.name" => {"match" => "regex", "regex"=>".*a"},
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
it "the match:regex overrides the match:type" do
|
75
|
+
expect(subject).to eq rules
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "with a Pact::SomethingLike containing an array" do
|
80
|
+
let(:matchable) do
|
81
|
+
{
|
82
|
+
body: Pact::SomethingLike.new(
|
83
|
+
alligators: [
|
84
|
+
{name: 'Mary'},
|
85
|
+
{name: 'Betty'}
|
86
|
+
]
|
87
|
+
)
|
88
|
+
}
|
89
|
+
end
|
90
|
+
|
91
|
+
let(:rules) do
|
92
|
+
{
|
93
|
+
"$.body.alligators[0].name" => {"match" => "type"},
|
94
|
+
"$.body.alligators[1].name" => {"match" => "type"}
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
it "lists a rule for each item" do
|
99
|
+
expect(subject).to eq rules
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|