pact-support 1.4.0 → 1.8.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 +143 -0
- data/lib/pact/configuration.rb +4 -0
- data/lib/pact/consumer_contract/consumer_contract.rb +19 -8
- data/lib/pact/consumer_contract/http_consumer_contract_parser.rb +37 -0
- data/lib/pact/consumer_contract/interaction.rb +57 -56
- data/lib/pact/consumer_contract/interaction_parser.rb +23 -0
- data/lib/pact/consumer_contract/interaction_v2_parser.rb +34 -0
- data/lib/pact/consumer_contract/interaction_v3_parser.rb +73 -0
- data/lib/pact/consumer_contract/pact_file.rb +24 -24
- data/lib/pact/consumer_contract/provider_state.rb +34 -0
- data/lib/pact/consumer_contract/query.rb +0 -2
- data/lib/pact/consumer_contract/query_hash.rb +6 -0
- data/lib/pact/consumer_contract/query_string.rb +2 -2
- data/lib/pact/consumer_contract/request.rb +1 -5
- data/lib/pact/consumer_contract/string_with_matching_rules.rb +17 -0
- data/lib/pact/matchers/multipart_form_diff_formatter.rb +41 -0
- data/lib/pact/matching_rules/merge.rb +43 -28
- data/lib/pact/matching_rules/v3/extract.rb +94 -0
- data/lib/pact/matching_rules/v3/merge.rb +135 -0
- data/lib/pact/matching_rules.rb +19 -6
- data/lib/pact/reification.rb +6 -3
- data/lib/pact/shared/multipart_form_differ.rb +14 -0
- data/lib/pact/specification_version.rb +18 -0
- data/lib/pact/support/version.rb +1 -1
- data/script/release.sh +1 -1
- data/spec/fixtures/multipart-form-diff.txt +9 -0
- data/spec/fixtures/not-a-pact.json +3 -0
- data/spec/fixtures/pact-http-v2.json +36 -0
- data/spec/fixtures/pact-http-v3.json +36 -0
- data/spec/integration/matching_rules_extract_and_merge_spec.rb +41 -4
- data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +54 -31
- data/spec/lib/pact/consumer_contract/http_consumer_contract_parser_spec.rb +25 -0
- data/spec/lib/pact/consumer_contract/interaction_parser_spec.rb +62 -0
- data/spec/lib/pact/consumer_contract/interaction_spec.rb +2 -26
- data/spec/lib/pact/consumer_contract/interaction_v2_parser_spec.rb +54 -0
- data/spec/lib/pact/consumer_contract/interaction_v3_parser_spec.rb +48 -0
- data/spec/lib/pact/consumer_contract/pact_file_spec.rb +9 -0
- data/spec/lib/pact/consumer_contract/query_hash_spec.rb +23 -0
- data/spec/lib/pact/matchers/multipart_form_diff_formatter_spec.rb +36 -0
- data/spec/lib/pact/matching_rules/merge_spec.rb +198 -110
- data/spec/lib/pact/matching_rules/v3/extract_spec.rb +238 -0
- data/spec/lib/pact/matching_rules/v3/merge_spec.rb +462 -0
- data/spec/lib/pact/matching_rules_spec.rb +82 -0
- data/spec/lib/pact/reification_spec.rb +18 -5
- data/spec/lib/pact/shared/multipart_form_differ_spec.rb +39 -0
- data/spec/support/factories.rb +5 -0
- data/tasks/spec.rake +0 -1
- metadata +40 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2e798766a40ccdcb43c8aed0bfbb4ee222c266db
|
|
4
|
+
data.tar.gz: fb9ab7ab4bac90b552b8c993ff582f035b5a9e00
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d0363e0c4f19d960c4301cb96918d0eed195bd149eef1f4ce0ea4707c7629abf24a7c496e1918fb73f7b806bfea730e5a28397b972ea69a947e332bd2cd7cff
|
|
7
|
+
data.tar.gz: 0ec07b703a4abe989d0507d2f91f0aa96a76b16142e2115abca1ca355d1b0e4333e733c9d0f9142c33674116995a907ca58781d0b07ba970625253a4eebe1df6
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,146 @@
|
|
|
1
|
+
<a name="v1.8.0"></a>
|
|
2
|
+
### v1.8.0 (2018-10-01)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
#### Features
|
|
6
|
+
|
|
7
|
+
* **v3**
|
|
8
|
+
* parse array of provider states with params ([4471df3](/../../commit/4471df3))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
<a name="v1.7.2"></a>
|
|
12
|
+
### v1.7.2 (2018-08-09)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
#### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* correctly handle Pact.like and Pact.each_like at top level of body ([f37c283](/../../commit/f37c283))
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
<a name="v1.7.1"></a>
|
|
21
|
+
### v1.7.1 (2018-08-09)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
#### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* remove incorrect warning messages about matching rules being ignored ([30328e0](/../../commit/30328e0))
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
<a name="v1.7.0"></a>
|
|
30
|
+
### v1.7.0 (2018-08-07)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
#### Features
|
|
34
|
+
|
|
35
|
+
* add support for multipart/form ([8ed4332](/../../commit/8ed4332))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
<a name="v1.6.6"></a>
|
|
39
|
+
### v1.6.6 (2018-07-25)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
#### Bug Fixes
|
|
43
|
+
|
|
44
|
+
* correctly handle an 'each like' inside a 'like' ([7dc76dc](/../../commit/7dc76dc))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
<a name="v1.6.5"></a>
|
|
48
|
+
### v1.6.5 (2018-07-23)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
#### Features
|
|
52
|
+
|
|
53
|
+
* use 0 as the nil pact specification version ([88e4750](/../../commit/88e4750))
|
|
54
|
+
* reify StringWithMatchingRules to a String ([a025dd3](/../../commit/a025dd3))
|
|
55
|
+
* parse String response and request bodies to StringWithMatchingRules to support pact-xml ([a9fbb58](/../../commit/a9fbb58))
|
|
56
|
+
* add custom contract parsers to front of pact parsers list so that customised parsers are tried first ([babc319](/../../commit/babc319))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
#### Bug Fixes
|
|
60
|
+
|
|
61
|
+
* show a more helpful error when attempting to parse a URI that is not a pact ([a8ba1ed](/../../commit/a8ba1ed))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
<a name="v1.6.4"></a>
|
|
65
|
+
### v1.6.4 (2018-07-14)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
#### Bug Fixes
|
|
69
|
+
|
|
70
|
+
* correctly serialize query params that use a Pact.each_like in pact file ([b3414dd](/../../commit/b3414dd))
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
<a name="v1.6.3"></a>
|
|
74
|
+
### v1.6.3 (2018-07-12)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
#### Bug Fixes
|
|
78
|
+
|
|
79
|
+
* remove incorrect warning about ignoring unsupported matching rules for min => x ([50d5f6d](/../../commit/50d5f6d))
|
|
80
|
+
* serialize ArrayLike in query params without wrapping another array around it ([b4a9ec7](/../../commit/b4a9ec7))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
<a name="v1.6.2"></a>
|
|
84
|
+
### v1.6.2 (2018-05-31)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
#### Bug Fixes
|
|
88
|
+
|
|
89
|
+
* **windows-path**
|
|
90
|
+
* prevent locale file paths to be parsed by URI to stop errors in windows paths like spaces in paths ([ecf64d6](/../../commit/ecf64d6))
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
<a name="v1.6.1"></a>
|
|
94
|
+
### v1.6.1 (2018-05-21)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
#### Bug Fixes
|
|
98
|
+
|
|
99
|
+
* correctly read local windows pact file paths with backslashes ([e27bd38](/../../commit/e27bd38))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
<a name="v1.6.0"></a>
|
|
103
|
+
### v1.6.0 (2018-04-03)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
#### Features
|
|
107
|
+
|
|
108
|
+
* add support for writing v3 matching rules ([fc89696](/../../commit/fc89696))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
<a name="v1.5.2"></a>
|
|
112
|
+
### v1.5.2 (2018-03-23)
|
|
113
|
+
|
|
114
|
+
#### Bug Fixes
|
|
115
|
+
|
|
116
|
+
* remove include of pact matchers in query string ([c478dff](/../../commit/c478dff))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
<a name="v1.5.1"></a>
|
|
120
|
+
### v1.5.1 (2018-03-23)
|
|
121
|
+
|
|
122
|
+
#### Bug Fixes
|
|
123
|
+
|
|
124
|
+
* add missing require for pact matchers in query ([927d3b9](/../../commit/927d3b9))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
<a name="v1.5.0"></a>
|
|
128
|
+
### v1.5.0 (2018-03-23)
|
|
129
|
+
|
|
130
|
+
#### Features
|
|
131
|
+
|
|
132
|
+
* parse pacts without a specification version as v2 ([a69b5e6](/../../commit/a69b5e6))
|
|
133
|
+
* locate matching rules correctly for v3 pacts ([0f22db2](/../../commit/0f22db2))
|
|
134
|
+
* read matching rules from v3 format ([07013de](/../../commit/07013de))
|
|
135
|
+
* allow different consumer contract parsers to be registered ([531ab3a](/../../commit/531ab3a))
|
|
136
|
+
* update message classes to support pact-message ([2e48892](/../../commit/2e48892))
|
|
137
|
+
* add request and response to message ([93839cf](/../../commit/93839cf))
|
|
138
|
+
|
|
139
|
+
* **message contracts**
|
|
140
|
+
* dynamically mix in new and from_hash into Pact::Message ([c0c3ad5](/../../commit/c0c3ad5))
|
|
141
|
+
* read message pact into Ruby object ([6573bd4](/../../commit/6573bd4))
|
|
142
|
+
|
|
143
|
+
|
|
1
144
|
<a name="v1.3.1"></a>
|
|
2
145
|
### v1.3.1 (2018-03-19)
|
|
3
146
|
|
data/lib/pact/configuration.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
require 'pact/matchers/embedded_diff_formatter'
|
|
2
2
|
require 'pact/matchers/unix_diff_formatter'
|
|
3
3
|
require 'pact/matchers/list_diff_formatter'
|
|
4
|
+
require 'pact/matchers/multipart_form_diff_formatter'
|
|
4
5
|
require 'pact/shared/json_differ'
|
|
5
6
|
require 'pact/shared/text_differ'
|
|
6
7
|
require 'pact/shared/form_differ'
|
|
8
|
+
require 'pact/shared/multipart_form_differ'
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
module Pact
|
|
@@ -24,6 +26,7 @@ module Pact
|
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
DIFF_FORMATTER_REGISTRATIONS = [
|
|
29
|
+
[/multipart\/form-data/, Pact::Matchers::MultipartFormDiffFormatter],
|
|
27
30
|
[/.*/, Pact::Matchers::UnixDiffFormatter],
|
|
28
31
|
[NilMatcher, Pact::Matchers::UnixDiffFormatter]
|
|
29
32
|
]
|
|
@@ -31,6 +34,7 @@ module Pact
|
|
|
31
34
|
DIFFERS = [
|
|
32
35
|
[/json/, Pact::JsonDiffer],
|
|
33
36
|
[/application\/x\-www\-form\-urlencoded/, Pact::FormDiffer],
|
|
37
|
+
[/multipart\/form-data/, Pact::MultipartFormDiffer],
|
|
34
38
|
[NilMatcher, Pact::TextDiffer],
|
|
35
39
|
[/.*/, Pact::TextDiffer]
|
|
36
40
|
]
|
|
@@ -10,9 +10,12 @@ require 'pact/consumer_contract/service_consumer'
|
|
|
10
10
|
require 'pact/consumer_contract/service_provider'
|
|
11
11
|
require 'pact/consumer_contract/interaction'
|
|
12
12
|
require 'pact/consumer_contract/pact_file'
|
|
13
|
+
require 'pact/consumer_contract/http_consumer_contract_parser'
|
|
13
14
|
|
|
14
15
|
module Pact
|
|
15
16
|
|
|
17
|
+
class UnrecognizePactFormatError < ::Pact::Error; end
|
|
18
|
+
|
|
16
19
|
class ConsumerContract
|
|
17
20
|
|
|
18
21
|
include SymbolizeKeys
|
|
@@ -29,13 +32,19 @@ module Pact
|
|
|
29
32
|
@provider = attributes[:provider]
|
|
30
33
|
end
|
|
31
34
|
|
|
35
|
+
def self.add_parser consumer_contract_parser
|
|
36
|
+
parsers.unshift(consumer_contract_parser)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.parsers
|
|
40
|
+
@parsers ||= [Pact::HttpConsumerContractParser.new]
|
|
41
|
+
end
|
|
42
|
+
|
|
32
43
|
def self.from_hash(hash)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
:interactions => hash[:interactions].collect { |h| Interaction.from_hash(h)}
|
|
38
|
-
)
|
|
44
|
+
parsers.each do | parser |
|
|
45
|
+
return parser.call(hash) if parser.can_parse?(hash)
|
|
46
|
+
end
|
|
47
|
+
raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact format: #{hash}")
|
|
39
48
|
end
|
|
40
49
|
|
|
41
50
|
def self.from_json string
|
|
@@ -45,6 +54,8 @@ module Pact
|
|
|
45
54
|
|
|
46
55
|
def self.from_uri uri, options = {}
|
|
47
56
|
from_json(Pact::PactFile.read(uri, options))
|
|
57
|
+
rescue UnrecognizePactFormatError
|
|
58
|
+
raise Pact::UnrecognizePactFormatError.new("This document does not use a recognised Pact format. Please check that #{uri} is a valid pact file.")
|
|
48
59
|
end
|
|
49
60
|
|
|
50
61
|
def self.maintain_backwards_compatiblity_with_producer_keys string
|
|
@@ -54,9 +65,9 @@ module Pact
|
|
|
54
65
|
def find_interaction criteria
|
|
55
66
|
interactions = find_interactions criteria
|
|
56
67
|
if interactions.size == 0
|
|
57
|
-
raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
|
|
68
|
+
raise Pact::Error.new("Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}.")
|
|
58
69
|
elsif interactions.size > 1
|
|
59
|
-
raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
|
|
70
|
+
raise Pact::Error.new("Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}.")
|
|
60
71
|
end
|
|
61
72
|
interactions.first
|
|
62
73
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'pact/specification_version'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
class HttpConsumerContractParser
|
|
5
|
+
include SymbolizeKeys
|
|
6
|
+
|
|
7
|
+
def call(hash)
|
|
8
|
+
hash = symbolize_keys(hash)
|
|
9
|
+
v = pact_specification_version(hash)
|
|
10
|
+
options = { pact_specification_version: v }
|
|
11
|
+
|
|
12
|
+
if v.after? 3
|
|
13
|
+
Pact.configuration.error_stream.puts "WARN: This code only knows how to parse v3 pacts, attempting to parse v#{options[:pact_specification_version]} pact using v3 code."
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
interactions = hash[:interactions].collect { |hash| Interaction.from_hash(hash, options) }
|
|
17
|
+
ConsumerContract.new(
|
|
18
|
+
:consumer => ServiceConsumer.from_hash(hash[:consumer]),
|
|
19
|
+
:provider => ServiceProvider.from_hash(hash[:provider]),
|
|
20
|
+
:interactions => interactions
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def pact_specification_version hash
|
|
25
|
+
# TODO handle all 3 ways of defining this...
|
|
26
|
+
# metadata.pactSpecificationVersion
|
|
27
|
+
maybe_pact_specification_version_1 = hash[:metadata] && hash[:metadata]['pactSpecification'] && hash[:metadata]['pactSpecification']['version']
|
|
28
|
+
maybe_pact_specification_version_2 = hash[:metadata] && hash[:metadata]['pactSpecificationVersion']
|
|
29
|
+
pact_specification_version = maybe_pact_specification_version_1 || maybe_pact_specification_version_2
|
|
30
|
+
pact_specification_version ? Pact::SpecificationVersion.new(pact_specification_version) : Pact::SpecificationVersion::NIL_VERSION
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def can_parse?(hash)
|
|
34
|
+
hash.key?('interactions') || hash.key?(:interactions)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -1,76 +1,77 @@
|
|
|
1
|
-
require 'pact/consumer_contract/request'
|
|
2
|
-
require 'pact/consumer_contract/response'
|
|
3
|
-
require 'pact/symbolize_keys'
|
|
4
1
|
require 'pact/shared/active_support_support'
|
|
5
|
-
require 'pact/
|
|
6
|
-
require 'pact/errors'
|
|
2
|
+
require 'pact/consumer_contract/interaction_parser'
|
|
7
3
|
|
|
8
4
|
module Pact
|
|
9
|
-
|
|
5
|
+
class Interaction
|
|
10
6
|
include ActiveSupportSupport
|
|
11
|
-
include SymbolizeKeys
|
|
12
7
|
|
|
13
|
-
|
|
8
|
+
attr_accessor :description, :request, :response, :provider_state, :provider_states
|
|
14
9
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
10
|
+
def initialize attributes = {}
|
|
11
|
+
@description = attributes[:description]
|
|
12
|
+
@request = attributes[:request]
|
|
13
|
+
@response = attributes[:response]
|
|
14
|
+
@provider_state = attributes[:provider_state] || attributes[:providerState]
|
|
15
|
+
@provider_states = attributes[:provider_states]
|
|
16
|
+
end
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
response_hash = Pact::MatchingRules.merge(hash['response'], hash['response']['matchingRules'])
|
|
26
|
-
response = Pact::Response.from_hash(response_hash)
|
|
27
|
-
new(symbolize_keys(hash).merge(request: request, response: response))
|
|
28
|
-
end
|
|
18
|
+
def self.from_hash hash, options = {}
|
|
19
|
+
InteractionParser.call(hash, options)
|
|
20
|
+
end
|
|
29
21
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
description: description,
|
|
33
|
-
provider_state: provider_state,
|
|
34
|
-
request: request.to_hash,
|
|
35
|
-
response: response.to_hash
|
|
36
|
-
}
|
|
37
|
-
end
|
|
22
|
+
def to_hash
|
|
23
|
+
h = { description: description }
|
|
38
24
|
|
|
39
|
-
|
|
40
|
-
|
|
25
|
+
if provider_states
|
|
26
|
+
h[:provider_states] = provider_states.collect(&:to_hash)
|
|
27
|
+
else
|
|
28
|
+
h[:provider_state] = provider_state
|
|
41
29
|
end
|
|
42
30
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
31
|
+
h[:request] = request.to_hash
|
|
32
|
+
h[:response] = response.to_hash
|
|
33
|
+
h
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def http?
|
|
37
|
+
true
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def validate!
|
|
41
|
+
raise Pact::InvalidInteractionError.new(self) unless description && request && response
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def matches_criteria? criteria
|
|
45
|
+
criteria.each do | key, value |
|
|
46
|
+
unless match_criterion self.send(key.to_s), value
|
|
47
|
+
return false
|
|
48
48
|
end
|
|
49
|
-
true
|
|
50
49
|
end
|
|
50
|
+
true
|
|
51
|
+
end
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
def match_criterion target, criterion
|
|
54
|
+
target == criterion || (criterion.is_a?(Regexp) && criterion.match(target))
|
|
55
|
+
end
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
def == other
|
|
58
|
+
other.is_a?(Interaction) && to_hash == other.to_hash
|
|
59
|
+
end
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
def eq? other
|
|
62
|
+
self == other
|
|
63
|
+
end
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
def description_with_provider_state_quoted
|
|
66
|
+
provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\""
|
|
67
|
+
end
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
def request_modifies_resource_without_checking_response_body?
|
|
70
|
+
request.modifies_resource? && response.body_allows_any_value?
|
|
71
|
+
end
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
def to_s
|
|
74
|
+
to_hash.to_s
|
|
75
|
+
end
|
|
76
|
+
end
|
|
76
77
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'pact/specification_version'
|
|
2
|
+
require 'pact/consumer_contract/interaction_v2_parser'
|
|
3
|
+
require 'pact/consumer_contract/interaction_v3_parser'
|
|
4
|
+
|
|
5
|
+
module Pact
|
|
6
|
+
class InteractionParser
|
|
7
|
+
def self.call hash, options = {}
|
|
8
|
+
pact_specification_version = options[:pact_specification_version] || Pact::SpecificationVersion::NIL_VERSION
|
|
9
|
+
case pact_specification_version.major
|
|
10
|
+
when nil, 0, 1, 2 then parse_v2_interaction(hash, pact_specification_version: pact_specification_version)
|
|
11
|
+
else parse_v3_interaction(hash, pact_specification_version: pact_specification_version)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.parse_v2_interaction hash, options
|
|
16
|
+
InteractionV2Parser.call(hash, options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.parse_v3_interaction hash, options
|
|
20
|
+
InteractionV3Parser.call(hash, options)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
require 'pact/consumer_contract/request'
|
|
2
|
+
require 'pact/consumer_contract/response'
|
|
3
|
+
require 'pact/consumer_contract/provider_state'
|
|
4
|
+
require 'pact/symbolize_keys'
|
|
5
|
+
require 'pact/matching_rules'
|
|
6
|
+
require 'pact/errors'
|
|
7
|
+
|
|
8
|
+
module Pact
|
|
9
|
+
class InteractionV2Parser
|
|
10
|
+
|
|
11
|
+
include SymbolizeKeys
|
|
12
|
+
|
|
13
|
+
def self.call hash, options
|
|
14
|
+
request = parse_request(hash['request'], options)
|
|
15
|
+
response = parse_response(hash['response'], options)
|
|
16
|
+
provider_states = parse_provider_states(hash['providerState'] || hash['provider_state'])
|
|
17
|
+
Interaction.new(symbolize_keys(hash).merge(request: request, response: response, provider_states: provider_states))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.parse_request request_hash, options
|
|
21
|
+
request_hash = Pact::MatchingRules.merge(request_hash, request_hash['matchingRules'], options)
|
|
22
|
+
Pact::Request::Expected.from_hash(request_hash)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.parse_response response_hash, options
|
|
26
|
+
response_hash = Pact::MatchingRules.merge(response_hash, response_hash['matchingRules'], options)
|
|
27
|
+
Pact::Response.from_hash(response_hash)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.parse_provider_states provider_state_name
|
|
31
|
+
provider_state_name ? [Pact::ProviderState.new(provider_state_name)] : []
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require 'pact/consumer_contract/request'
|
|
2
|
+
require 'pact/consumer_contract/response'
|
|
3
|
+
require 'pact/consumer_contract/provider_state'
|
|
4
|
+
require 'pact/symbolize_keys'
|
|
5
|
+
require 'pact/matching_rules'
|
|
6
|
+
require 'pact/errors'
|
|
7
|
+
require 'pact/consumer_contract/string_with_matching_rules'
|
|
8
|
+
|
|
9
|
+
module Pact
|
|
10
|
+
class InteractionV3Parser
|
|
11
|
+
|
|
12
|
+
include SymbolizeKeys
|
|
13
|
+
|
|
14
|
+
def self.call hash, options
|
|
15
|
+
request = parse_request(hash['request'], options)
|
|
16
|
+
response = parse_response(hash['response'], options)
|
|
17
|
+
provider_states = parse_provider_states(hash['providerStates'])
|
|
18
|
+
provider_state = provider_states.any? ? provider_states.first.name : nil
|
|
19
|
+
if provider_states && provider_states.size > 1
|
|
20
|
+
Pact.configuration.error_stream.puts("WARN: Currently only 1 provider state is supported. Ignoring ")
|
|
21
|
+
end
|
|
22
|
+
Interaction.new(symbolize_keys(hash).merge(request: request, response: response, provider_states: provider_states, provider_state: provider_state))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.parse_request request_hash, options
|
|
26
|
+
request_matching_rules = request_hash['matchingRules'] || {}
|
|
27
|
+
if request_hash['body'].is_a?(String)
|
|
28
|
+
parse_request_with_string_body(request_hash, request_matching_rules['body'] || {}, options)
|
|
29
|
+
else
|
|
30
|
+
parse_request_with_non_string_body(request_hash, request_matching_rules, options)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.parse_response response_hash, options
|
|
35
|
+
response_matching_rules = response_hash['matchingRules'] || {}
|
|
36
|
+
if response_hash['body'].is_a?(String)
|
|
37
|
+
parse_response_with_string_body(response_hash, response_matching_rules['body'] || {}, options)
|
|
38
|
+
else
|
|
39
|
+
parse_response_with_non_string_body(response_hash, response_matching_rules, options)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def self.parse_request_with_non_string_body request_hash, request_matching_rules, options
|
|
44
|
+
request_hash = request_hash.keys.each_with_object({}) do | key, new_hash |
|
|
45
|
+
new_hash[key] = Pact::MatchingRules.merge(request_hash[key], request_matching_rules[key], options)
|
|
46
|
+
end
|
|
47
|
+
Pact::Request::Expected.from_hash(request_hash)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.parse_response_with_non_string_body response_hash, response_matching_rules, options
|
|
51
|
+
response_hash = response_hash.keys.each_with_object({}) do | key, new_hash |
|
|
52
|
+
new_hash[key] = Pact::MatchingRules.merge(response_hash[key], response_matching_rules[key], options)
|
|
53
|
+
end
|
|
54
|
+
Pact::Response.from_hash(response_hash)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def self.parse_request_with_string_body request_hash, request_matching_rules, options
|
|
58
|
+
string_with_matching_rules = StringWithMatchingRules.new(request_hash['body'], options[:pact_specification_version], request_matching_rules)
|
|
59
|
+
Pact::Request::Expected.from_hash(request_hash.merge('body' => string_with_matching_rules))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.parse_response_with_string_body response_hash, response_matching_rules, options
|
|
63
|
+
string_with_matching_rules = StringWithMatchingRules.new(response_hash['body'], options[:pact_specification_version], response_matching_rules)
|
|
64
|
+
Pact::Response.from_hash(response_hash.merge('body' => string_with_matching_rules))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.parse_provider_states provider_states
|
|
68
|
+
(provider_states || []).collect do | provider_state_hash |
|
|
69
|
+
Pact::ProviderState.new(provider_state_hash['name'], provider_state_hash['params'])
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -36,39 +36,25 @@ module Pact
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def render_pact(uri_string, options)
|
|
39
|
-
|
|
40
|
-
if uri_obj.userinfo
|
|
41
|
-
options[:username] = uri_obj.user unless options[:username]
|
|
42
|
-
options[:password] = uri_obj.password unless options[:password]
|
|
43
|
-
end
|
|
44
|
-
get(uri_obj, options)
|
|
39
|
+
local?(uri_string) ? get_local(uri_string, options) : get_remote_with_retry(uri_string, options)
|
|
45
40
|
end
|
|
46
41
|
|
|
47
42
|
private
|
|
48
43
|
|
|
49
|
-
def get(uri, options)
|
|
50
|
-
local?(uri) ? get_local(uri, options) : get_remote_with_retry(uri, options)
|
|
51
|
-
end
|
|
52
|
-
|
|
53
44
|
def local? uri
|
|
54
|
-
!uri.
|
|
45
|
+
!uri.start_with?("http://", "https://")
|
|
55
46
|
end
|
|
56
47
|
|
|
57
|
-
def get_local(
|
|
58
|
-
File.read
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def get_remote(uri, options)
|
|
62
|
-
request = Net::HTTP::Get.new(uri)
|
|
63
|
-
request.basic_auth(options[:username], options[:password]) if options[:username]
|
|
64
|
-
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
65
|
-
http.open_timeout = options[:open_timeout] || OPEN_TIMEOUT
|
|
66
|
-
http.read_timeout = options[:read_timeout] || READ_TIMEOUT
|
|
67
|
-
http.request(request)
|
|
68
|
-
end
|
|
48
|
+
def get_local(filepath, _)
|
|
49
|
+
File.read windows_safe(filepath)
|
|
69
50
|
end
|
|
70
51
|
|
|
71
|
-
def get_remote_with_retry(
|
|
52
|
+
def get_remote_with_retry(uri_string, options)
|
|
53
|
+
uri = URI(uri_string)
|
|
54
|
+
if uri.userinfo
|
|
55
|
+
options[:username] = uri.user unless options[:username]
|
|
56
|
+
options[:password] = uri.password unless options[:password]
|
|
57
|
+
end
|
|
72
58
|
((options[:retry_limit] || RETRY_LIMIT) + 1).times do |i|
|
|
73
59
|
begin
|
|
74
60
|
response = get_remote(uri, options)
|
|
@@ -89,6 +75,16 @@ module Pact
|
|
|
89
75
|
end
|
|
90
76
|
end
|
|
91
77
|
|
|
78
|
+
def get_remote(uri, options)
|
|
79
|
+
request = Net::HTTP::Get.new(uri)
|
|
80
|
+
request.basic_auth(options[:username], options[:password]) if options[:username]
|
|
81
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
82
|
+
http.open_timeout = options[:open_timeout] || OPEN_TIMEOUT
|
|
83
|
+
http.read_timeout = options[:read_timeout] || READ_TIMEOUT
|
|
84
|
+
http.request(request)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
92
88
|
def success?(response)
|
|
93
89
|
response.code.to_i == 200
|
|
94
90
|
end
|
|
@@ -104,5 +100,9 @@ module Pact
|
|
|
104
100
|
def delay_retry(count)
|
|
105
101
|
Kernel.sleep(2 ** count * 0.3)
|
|
106
102
|
end
|
|
103
|
+
|
|
104
|
+
def windows_safe(uri)
|
|
105
|
+
uri.start_with?("http") ? uri : uri.gsub("\\", File::SEPARATOR)
|
|
106
|
+
end
|
|
107
107
|
end
|
|
108
108
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
class ProviderState
|
|
3
|
+
|
|
4
|
+
attr_reader :name, :params
|
|
5
|
+
|
|
6
|
+
def initialize name, params = {}
|
|
7
|
+
@name = name
|
|
8
|
+
@params = params
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.from_hash(hash)
|
|
12
|
+
new(hash["name"], hash["params"])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ==(other)
|
|
16
|
+
other.is_a?(Pact::ProviderState) && other.name == self.name && other.params == self.params
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_hash
|
|
20
|
+
{
|
|
21
|
+
"name" => name,
|
|
22
|
+
"params" => params
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_json(opts = {})
|
|
27
|
+
as_json(opts).to_json(opts)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def as_json(opts = {})
|
|
31
|
+
to_hash
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|