pact 1.28.0 → 1.53.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 +5 -5
- data/CHANGELOG.md +408 -0
- data/lib/pact/cli/run_pact_verification.rb +26 -7
- data/lib/pact/cli/spec_criteria.rb +3 -0
- data/lib/pact/cli.rb +7 -1
- data/lib/pact/consumer/configuration/dsl.rb +0 -1
- data/lib/pact/consumer/configuration/mock_service.rb +9 -3
- data/lib/pact/consumer/consumer_contract_builder.rb +6 -2
- data/lib/pact/consumer/interaction_builder.rb +6 -0
- data/lib/pact/consumer/spec_hooks.rb +4 -4
- data/lib/pact/doc/sort_interactions.rb +2 -3
- data/lib/pact/hal/authorization_header_redactor.rb +32 -0
- data/lib/pact/hal/entity.rb +27 -5
- data/lib/pact/hal/http_client.rb +25 -7
- data/lib/pact/hal/link.rb +67 -20
- data/lib/pact/hal/non_json_entity.rb +28 -0
- data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +93 -0
- data/lib/pact/pact_broker/fetch_pacts.rb +20 -18
- data/lib/pact/pact_broker/notices.rb +34 -0
- data/lib/pact/pact_broker/pact_selection_description.rb +24 -0
- data/lib/pact/pact_broker.rb +25 -0
- data/lib/pact/provider/configuration/dsl.rb +6 -1
- data/lib/pact/provider/configuration/message_provider_dsl.rb +59 -0
- data/lib/pact/provider/configuration/pact_verification_from_broker.rb +29 -4
- data/lib/pact/provider/configuration/service_provider_dsl.rb +1 -1
- data/lib/pact/provider/help/content.rb +4 -4
- data/lib/pact/provider/help/pact_diff.rb +10 -34
- data/lib/pact/provider/help/write.rb +6 -7
- data/lib/pact/provider/pact_helper_locator.rb +13 -11
- data/lib/pact/provider/pact_source.rb +10 -1
- data/lib/pact/provider/pact_spec_runner.rb +26 -13
- data/lib/pact/provider/pact_uri.rb +8 -6
- data/lib/pact/provider/request.rb +1 -1
- data/lib/pact/provider/rspec/formatter_rspec_3.rb +39 -8
- data/lib/pact/provider/rspec/json_formatter.rb +100 -0
- data/lib/pact/provider/rspec/pact_broker_formatter.rb +7 -17
- data/lib/pact/provider/rspec.rb +24 -11
- data/lib/pact/provider/state/provider_state.rb +6 -7
- data/lib/pact/provider/state/provider_state_manager.rb +10 -10
- data/lib/pact/provider/state/set_up.rb +1 -1
- data/lib/pact/provider/state/tear_down.rb +1 -1
- data/lib/pact/provider/test_methods.rb +22 -7
- data/lib/pact/provider/verification_results/create.rb +6 -0
- data/lib/pact/provider/verification_results/publish.rb +5 -8
- data/lib/pact/provider/verification_results/publish_all.rb +14 -1
- data/lib/pact/provider/verification_results/verification_result.rb +2 -5
- data/lib/pact/provider/world.rb +1 -1
- data/lib/pact/retry.rb +2 -0
- data/lib/pact/tasks/task_helper.rb +10 -4
- data/lib/pact/tasks/verification_task.rb +3 -1
- data/lib/pact/version.rb +1 -1
- data/pact.gemspec +19 -15
- metadata +75 -80
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'pact/provider/configuration/service_provider_dsl'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module Provider
|
|
5
|
+
module Configuration
|
|
6
|
+
class MessageProviderDSL < ServiceProviderDSL
|
|
7
|
+
class RackToMessageAdapter
|
|
8
|
+
def initialize(message_builder)
|
|
9
|
+
@message_builder = message_builder
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def call(env)
|
|
13
|
+
request_body_json = JSON.parse(env['rack.input'].read)
|
|
14
|
+
contents = @message_builder.call(request_body_json['description'])
|
|
15
|
+
[200, {"Content-Type" => "application/json"}, [{ contents: contents }.to_json]]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize name
|
|
20
|
+
super
|
|
21
|
+
@mapper_block = lambda { |args| }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
dsl do
|
|
25
|
+
def app &block
|
|
26
|
+
self.app_block = block
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def app_version application_version
|
|
30
|
+
self.application_version = application_version
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def app_version_tags tags
|
|
34
|
+
self.tags = tags
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def publish_verification_results publish_verification_results
|
|
38
|
+
self.publish_verification_results = publish_verification_results
|
|
39
|
+
Pact::RSpec.with_rspec_2 do
|
|
40
|
+
Pact.configuration.error_stream.puts "WARN: Publishing of verification results is currently not supported with rspec 2. If you would like this functionality, please feel free to submit a PR!"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def honours_pact_with consumer_name, options = {}, &block
|
|
45
|
+
create_pact_verification consumer_name, options, &block
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def honours_pacts_from_pact_broker &block
|
|
49
|
+
create_pact_verification_from_broker &block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def builder &block
|
|
53
|
+
self.app_block = lambda { RackToMessageAdapter.new(block) }
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'pact/shared/dsl'
|
|
2
2
|
require 'pact/provider/world'
|
|
3
|
-
require 'pact/pact_broker/
|
|
3
|
+
require 'pact/pact_broker/fetch_pact_uris_for_verification'
|
|
4
|
+
require 'pact/errors'
|
|
4
5
|
|
|
5
6
|
module Pact
|
|
6
7
|
module Provider
|
|
@@ -13,11 +14,13 @@ module Pact
|
|
|
13
14
|
# in parent scope, it will clash with these ones,
|
|
14
15
|
# so put an underscore in front of the name to be safer.
|
|
15
16
|
|
|
16
|
-
attr_accessor :_provider_name, :_pact_broker_base_url, :_consumer_version_tags, :_basic_auth_options, :_verbose
|
|
17
|
+
attr_accessor :_provider_name, :_pact_broker_base_url, :_consumer_version_tags, :_provider_version_tags, :_basic_auth_options, :_enable_pending, :_verbose
|
|
17
18
|
|
|
18
|
-
def initialize(provider_name)
|
|
19
|
+
def initialize(provider_name, provider_version_tags)
|
|
19
20
|
@_provider_name = provider_name
|
|
21
|
+
@_provider_version_tags = provider_version_tags
|
|
20
22
|
@_consumer_version_tags = []
|
|
23
|
+
@_enable_pending = false
|
|
21
24
|
@_verbose = false
|
|
22
25
|
end
|
|
23
26
|
|
|
@@ -31,6 +34,10 @@ module Pact
|
|
|
31
34
|
self._consumer_version_tags = *consumer_version_tags
|
|
32
35
|
end
|
|
33
36
|
|
|
37
|
+
def enable_pending enable_pending
|
|
38
|
+
self._enable_pending = enable_pending
|
|
39
|
+
end
|
|
40
|
+
|
|
34
41
|
def verbose verbose
|
|
35
42
|
self._verbose = verbose
|
|
36
43
|
end
|
|
@@ -44,10 +51,28 @@ module Pact
|
|
|
44
51
|
private
|
|
45
52
|
|
|
46
53
|
def create_pact_verification
|
|
47
|
-
fetch_pacts = Pact::PactBroker::
|
|
54
|
+
fetch_pacts = Pact::PactBroker::FetchPactURIsForVerification.new(
|
|
55
|
+
_provider_name,
|
|
56
|
+
consumer_version_selectors,
|
|
57
|
+
_provider_version_tags,
|
|
58
|
+
_pact_broker_base_url,
|
|
59
|
+
_basic_auth_options.merge(verbose: _verbose),
|
|
60
|
+
{ include_pending_status: _enable_pending }
|
|
61
|
+
)
|
|
62
|
+
|
|
48
63
|
Pact.provider_world.add_pact_uri_source fetch_pacts
|
|
49
64
|
end
|
|
50
65
|
|
|
66
|
+
def consumer_version_selectors
|
|
67
|
+
# TODO support "all"
|
|
68
|
+
_consumer_version_tags.collect do | tag |
|
|
69
|
+
{
|
|
70
|
+
tag: tag,
|
|
71
|
+
latest: true
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
51
76
|
def validate
|
|
52
77
|
raise Pact::Error.new("Please provide a pact_broker_base_url from which to retrieve the pacts") unless _pact_broker_base_url
|
|
53
78
|
end
|
|
@@ -5,8 +5,8 @@ module Pact
|
|
|
5
5
|
module Help
|
|
6
6
|
class Content
|
|
7
7
|
|
|
8
|
-
def initialize
|
|
9
|
-
@
|
|
8
|
+
def initialize pact_sources
|
|
9
|
+
@pact_sources = pact_sources
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def text
|
|
@@ -15,7 +15,7 @@ module Pact
|
|
|
15
15
|
|
|
16
16
|
private
|
|
17
17
|
|
|
18
|
-
attr_reader :
|
|
18
|
+
attr_reader :pact_sources
|
|
19
19
|
|
|
20
20
|
def help_text
|
|
21
21
|
temp_dir = Pact.configuration.tmp_dir
|
|
@@ -28,7 +28,7 @@ module Pact
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def pact_diffs
|
|
31
|
-
|
|
31
|
+
pact_sources.collect do | pact_json |
|
|
32
32
|
PactDiff.call(pact_json)
|
|
33
33
|
end.compact.join("\n")
|
|
34
34
|
end
|
|
@@ -1,26 +1,24 @@
|
|
|
1
|
+
require 'pact/hal/entity'
|
|
2
|
+
|
|
1
3
|
module Pact
|
|
2
4
|
module Provider
|
|
3
5
|
module Help
|
|
4
|
-
|
|
5
6
|
class PactDiff
|
|
6
|
-
|
|
7
7
|
class PrintPactDiffError < StandardError; end
|
|
8
8
|
|
|
9
|
-
attr_reader :
|
|
9
|
+
attr_reader :pact_source, :output
|
|
10
10
|
|
|
11
|
-
def initialize
|
|
12
|
-
@
|
|
11
|
+
def initialize pact_source
|
|
12
|
+
@pact_source = pact_source
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def self.call
|
|
16
|
-
new(
|
|
15
|
+
def self.call pact_source
|
|
16
|
+
new(pact_source).call
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def call
|
|
20
20
|
begin
|
|
21
|
-
|
|
22
|
-
header + "\n" + get_diff
|
|
23
|
-
end
|
|
21
|
+
header + "\n" + get_diff
|
|
24
22
|
rescue PrintPactDiffError => e
|
|
25
23
|
return e.message
|
|
26
24
|
end
|
|
@@ -32,35 +30,13 @@ module Pact
|
|
|
32
30
|
"The following changes have been made since the previous distinct version of this pact, and may be responsible for verification failure:\n"
|
|
33
31
|
end
|
|
34
32
|
|
|
35
|
-
def pact_hash
|
|
36
|
-
@pact_hash ||= json_load(pact_json)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def links
|
|
40
|
-
pact_hash['_links'] || pact_hash['links']
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def diff_rel
|
|
44
|
-
return nil unless links
|
|
45
|
-
key = links.keys.find { | key | key =~ /diff/ && key =~ /distinct/ && key =~ /previous/}
|
|
46
|
-
key ? links[key] : nil
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def diff_url
|
|
50
|
-
diff_rel['href']
|
|
51
|
-
end
|
|
52
|
-
|
|
53
33
|
def get_diff
|
|
54
34
|
begin
|
|
55
|
-
|
|
35
|
+
pact_source.hal_entity._link!("pb:diff-previous-distinct").get!(nil, "Accept" => "text/plain").body
|
|
56
36
|
rescue StandardError => e
|
|
57
|
-
raise PrintPactDiffError.new("Tried to retrieve diff with previous pact
|
|
37
|
+
raise PrintPactDiffError.new("Tried to retrieve diff with previous pact, but received error #{e.class} #{e.message}.")
|
|
58
38
|
end
|
|
59
39
|
end
|
|
60
|
-
|
|
61
|
-
def json_load json
|
|
62
|
-
JSON.load(json, nil, { max_nesting: 50 })
|
|
63
|
-
end
|
|
64
40
|
end
|
|
65
41
|
end
|
|
66
42
|
end
|
|
@@ -9,12 +9,12 @@ module Pact
|
|
|
9
9
|
|
|
10
10
|
HELP_FILE_NAME = 'help.md'
|
|
11
11
|
|
|
12
|
-
def self.call
|
|
13
|
-
new(
|
|
12
|
+
def self.call pact_sources, reports_dir = Pact.configuration.reports_dir
|
|
13
|
+
new(pact_sources, reports_dir).call
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def initialize
|
|
17
|
-
@
|
|
16
|
+
def initialize pact_sources, reports_dir
|
|
17
|
+
@pact_sources = pact_sources
|
|
18
18
|
@reports_dir = File.expand_path(reports_dir)
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ module Pact
|
|
|
25
25
|
|
|
26
26
|
private
|
|
27
27
|
|
|
28
|
-
attr_reader :reports_dir, :
|
|
28
|
+
attr_reader :reports_dir, :pact_sources
|
|
29
29
|
|
|
30
30
|
def clean_reports_dir
|
|
31
31
|
raise "Cleaning report dir #{reports_dir} would delete project!" if reports_dir_contains_pwd
|
|
@@ -46,9 +46,8 @@ module Pact
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def help_text
|
|
49
|
-
Content.new(
|
|
49
|
+
Content.new(pact_sources).text
|
|
50
50
|
end
|
|
51
|
-
|
|
52
51
|
end
|
|
53
52
|
end
|
|
54
53
|
end
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
module Pact
|
|
2
2
|
module Provider
|
|
3
3
|
module PactHelperLocater
|
|
4
|
-
|
|
4
|
+
PACT_HELPER_FILE_PATTERNS = [
|
|
5
5
|
"spec/**/*service*consumer*/pact_helper.rb",
|
|
6
6
|
"spec/**/*consumer*/pact_helper.rb",
|
|
7
7
|
"spec/**/pact_helper.rb",
|
|
8
|
-
"
|
|
8
|
+
"test/**/*service*consumer*/pact_helper.rb",
|
|
9
|
+
"test/**/*consumer*/pact_helper.rb",
|
|
10
|
+
"test/**/pact_helper.rb",
|
|
11
|
+
"**/pact_helper.rb"
|
|
12
|
+
]
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def self.pact_helper_path
|
|
13
|
-
pact_helper_search_results = []
|
|
14
|
-
PACT_HELPER_FILE_PATTERNS.find { | pattern | (pact_helper_search_results.concat(Dir.glob(pattern))).any? }
|
|
15
|
-
raise NO_PACT_HELPER_FOUND_MSG if pact_helper_search_results.empty?
|
|
16
|
-
File.join(Dir.pwd, pact_helper_search_results[0])
|
|
17
|
-
end
|
|
14
|
+
NO_PACT_HELPER_FOUND_MSG = "Please create a pact_helper.rb file that can be found using one of the following patterns: #{PACT_HELPER_FILE_PATTERNS.join(", ")}"
|
|
18
15
|
|
|
16
|
+
def self.pact_helper_path
|
|
17
|
+
pact_helper_search_results = []
|
|
18
|
+
PACT_HELPER_FILE_PATTERNS.find { | pattern | (pact_helper_search_results.concat(Dir.glob(pattern))).any? }
|
|
19
|
+
raise NO_PACT_HELPER_FOUND_MSG if pact_helper_search_results.empty?
|
|
20
|
+
File.join(Dir.pwd, pact_helper_search_results[0])
|
|
21
|
+
end
|
|
19
22
|
end
|
|
20
23
|
end
|
|
21
24
|
end
|
|
22
|
-
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
require 'pact/consumer_contract/pact_file'
|
|
2
|
+
require 'pact/hal/http_client'
|
|
3
|
+
require 'pact/hal/entity'
|
|
2
4
|
|
|
3
5
|
module Pact
|
|
4
6
|
module Provider
|
|
5
7
|
class PactSource
|
|
6
8
|
|
|
7
|
-
attr_reader :uri
|
|
9
|
+
attr_reader :uri # PactURI class
|
|
8
10
|
|
|
9
11
|
def initialize uri
|
|
10
12
|
@uri = uri
|
|
@@ -17,6 +19,13 @@ module Pact
|
|
|
17
19
|
def pact_hash
|
|
18
20
|
@pact_hash ||= JSON.load(pact_json, nil, { max_nesting: 50 })
|
|
19
21
|
end
|
|
22
|
+
|
|
23
|
+
def hal_entity
|
|
24
|
+
http_client_keys = [:username, :password, :token]
|
|
25
|
+
http_client_options = uri.options.reject{ |k, _| !http_client_keys.include?(k) }
|
|
26
|
+
http_client = Pact::Hal::HttpClient.new(http_client_options)
|
|
27
|
+
Pact::Hal::Entity.new(uri, pact_hash, http_client)
|
|
28
|
+
end
|
|
20
29
|
end
|
|
21
30
|
end
|
|
22
31
|
end
|
|
@@ -9,9 +9,8 @@ require 'pact/provider/pact_source'
|
|
|
9
9
|
require 'pact/provider/help/write'
|
|
10
10
|
require 'pact/provider/verification_results/publish_all'
|
|
11
11
|
require 'pact/provider/rspec/pact_broker_formatter'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
require 'pact/provider/rspec/json_formatter'
|
|
13
|
+
require 'pact/provider/rspec'
|
|
15
14
|
|
|
16
15
|
module Pact
|
|
17
16
|
module Provider
|
|
@@ -36,6 +35,7 @@ module Pact
|
|
|
36
35
|
ensure
|
|
37
36
|
::RSpec.reset
|
|
38
37
|
Pact.clear_provider_world
|
|
38
|
+
Pact.clear_consumer_world
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -79,7 +79,7 @@ module Pact
|
|
|
79
79
|
executing_with_ruby = executing_with_ruby?
|
|
80
80
|
|
|
81
81
|
config.after(:suite) do | suite |
|
|
82
|
-
Pact::Provider::Help::Write.call(
|
|
82
|
+
Pact::Provider::Help::Write.call(Pact.provider_world.pact_sources) if executing_with_ruby
|
|
83
83
|
end
|
|
84
84
|
end
|
|
85
85
|
|
|
@@ -90,7 +90,7 @@ module Pact
|
|
|
90
90
|
::RSpec::Core::CommandLine.new(NoConfigurationOptions.new)
|
|
91
91
|
.run(::RSpec.configuration.output_stream, ::RSpec.configuration.error_stream)
|
|
92
92
|
end
|
|
93
|
-
exit_code
|
|
93
|
+
pending_mode? ? 0 : exit_code
|
|
94
94
|
end
|
|
95
95
|
|
|
96
96
|
def rspec_runner_options
|
|
@@ -117,10 +117,12 @@ module Pact
|
|
|
117
117
|
|
|
118
118
|
def initialize_specs
|
|
119
119
|
pact_sources.each do | pact_source |
|
|
120
|
-
|
|
121
|
-
criteria:
|
|
120
|
+
spec_options = {
|
|
121
|
+
criteria: options[:criteria],
|
|
122
|
+
ignore_failures: options[:ignore_failures],
|
|
123
|
+
request_customizer: options[:request_customizer]
|
|
122
124
|
}
|
|
123
|
-
honour_pactfile pact_source
|
|
125
|
+
honour_pactfile pact_source, ordered_pact_json(pact_source.pact_json), spec_options
|
|
124
126
|
end
|
|
125
127
|
end
|
|
126
128
|
|
|
@@ -131,11 +133,12 @@ module Pact
|
|
|
131
133
|
|
|
132
134
|
output = options[:out] || Pact.configuration.output_stream
|
|
133
135
|
if options[:format]
|
|
134
|
-
::RSpec
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
formatter = options[:format] == 'json' ? Pact::Provider::RSpec::JsonFormatter : options[:format]
|
|
137
|
+
# Send formatted output to $stdout for parsing, unless a file is specified
|
|
138
|
+
output = options[:out] || $stdout
|
|
139
|
+
::RSpec.configuration.add_formatter formatter, output
|
|
140
|
+
# Don't want to mess up the JSON parsing with INFO and DEBUG messages to stdout, so send it to stderr
|
|
141
|
+
Pact.configuration.output_stream = Pact.configuration.error_stream if !options[:out]
|
|
139
142
|
else
|
|
140
143
|
# Sometimes the formatter set in the cli.rb get set with an output of StringIO.. don't know why
|
|
141
144
|
formatter_class = Pact::RSpec.formatter_class
|
|
@@ -144,6 +147,8 @@ module Pact
|
|
|
144
147
|
end
|
|
145
148
|
|
|
146
149
|
::RSpec.configuration.full_backtrace = @options[:full_backtrace]
|
|
150
|
+
|
|
151
|
+
::RSpec.configuration.failure_color = :yellow if pending_mode?
|
|
147
152
|
end
|
|
148
153
|
|
|
149
154
|
def ordered_pact_json(pact_json)
|
|
@@ -154,12 +159,20 @@ module Pact
|
|
|
154
159
|
consumer_contract.to_json
|
|
155
160
|
end
|
|
156
161
|
|
|
162
|
+
def all_pacts_pending?
|
|
163
|
+
pact_urls.all?{ | pact_url| pact_url.metadata[:pending] }
|
|
164
|
+
end
|
|
165
|
+
|
|
157
166
|
def class_exists? name
|
|
158
167
|
Kernel.const_get name
|
|
159
168
|
rescue NameError
|
|
160
169
|
false
|
|
161
170
|
end
|
|
162
171
|
|
|
172
|
+
def pending_mode?
|
|
173
|
+
(all_pacts_pending? || options[:ignore_failures])
|
|
174
|
+
end
|
|
175
|
+
|
|
163
176
|
def executing_with_ruby?
|
|
164
177
|
ENV['PACT_EXECUTING_LANGUAGE'] == 'ruby'
|
|
165
178
|
end
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
module Pact
|
|
2
2
|
module Provider
|
|
3
3
|
class PactURI
|
|
4
|
-
attr_reader :uri, :options
|
|
4
|
+
attr_reader :uri, :options, :metadata
|
|
5
5
|
|
|
6
|
-
def initialize (uri, options=
|
|
6
|
+
def initialize (uri, options = nil, metadata = nil)
|
|
7
7
|
@uri = uri
|
|
8
|
-
@options = options
|
|
8
|
+
@options = options || {}
|
|
9
|
+
@metadata = metadata || {} # make sure it's not nil if nil is passed in
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
def == other
|
|
12
13
|
other.is_a?(PactURI) &&
|
|
13
14
|
uri == other.uri &&
|
|
14
|
-
options == other.options
|
|
15
|
+
options == other.options &&
|
|
16
|
+
metadata == other.metadata
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def basic_auth?
|
|
@@ -27,10 +29,10 @@ module Pact
|
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def to_s
|
|
30
|
-
if
|
|
32
|
+
if basic_auth? && uri.start_with?('http://', 'https://')
|
|
31
33
|
URI(@uri).tap { |x| x.userinfo="#{username}:*****"}.to_s
|
|
32
34
|
else
|
|
33
|
-
|
|
35
|
+
uri
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
@@ -6,13 +6,13 @@ require 'pact/provider/help/prompt_text'
|
|
|
6
6
|
module Pact
|
|
7
7
|
module Provider
|
|
8
8
|
module RSpec
|
|
9
|
-
|
|
10
9
|
class Formatter < ::RSpec::Core::Formatters::DocumentationFormatter
|
|
11
10
|
|
|
12
11
|
class NilFormatter < ::RSpec::Core::Formatters::BaseFormatter
|
|
13
12
|
Pact::RSpec.with_rspec_3 do
|
|
14
13
|
::RSpec::Core::Formatters.register self, :start, :example_group_started, :close
|
|
15
14
|
end
|
|
15
|
+
|
|
16
16
|
def dump_summary(summary)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
@@ -42,9 +42,21 @@ module Pact
|
|
|
42
42
|
summary.failed_examples.collect{ |e| e.metadata[:pact_interaction_example_description] }.uniq.size
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def ignore_failures?(summary)
|
|
46
|
+
summary.failed_examples.any?{ |e| e.metadata[:pact_ignore_failures] }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def failure_title summary
|
|
50
|
+
if ignore_failures?(summary)
|
|
51
|
+
"#{failed_interactions_count(summary)} pending"
|
|
52
|
+
else
|
|
53
|
+
::RSpec::Core::Formatters::Helpers.pluralize(failed_interactions_count(summary), "failure")
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
45
57
|
def totals_line summary
|
|
46
58
|
line = ::RSpec::Core::Formatters::Helpers.pluralize(interactions_count(summary), "interaction")
|
|
47
|
-
line << ", " <<
|
|
59
|
+
line << ", " << failure_title(summary)
|
|
48
60
|
line
|
|
49
61
|
end
|
|
50
62
|
|
|
@@ -57,7 +69,11 @@ module Pact
|
|
|
57
69
|
end
|
|
58
70
|
|
|
59
71
|
def print_rerun_commands summary
|
|
60
|
-
|
|
72
|
+
if ignore_failures?(summary)
|
|
73
|
+
output.puts("\nPending interactions: (Failures listed here are expected and do not affect your suite's status)\n\n")
|
|
74
|
+
else
|
|
75
|
+
output.puts("\nFailed interactions:\n\n")
|
|
76
|
+
end
|
|
61
77
|
interaction_rerun_commands(summary).each do | message |
|
|
62
78
|
output.puts(message)
|
|
63
79
|
end
|
|
@@ -77,17 +93,32 @@ module Pact
|
|
|
77
93
|
|
|
78
94
|
def interaction_rerun_command_for example
|
|
79
95
|
example_description = example.metadata[:pact_interaction_example_description]
|
|
80
|
-
|
|
96
|
+
|
|
97
|
+
_id = example.metadata[:pact_interaction]._id
|
|
98
|
+
index = example.metadata[:pact_interaction].index
|
|
99
|
+
provider_state = example.metadata[:pact_interaction].provider_state
|
|
100
|
+
description = example.metadata[:pact_interaction].description
|
|
101
|
+
pactfile_uri = example.metadata[:pactfile_uri]
|
|
102
|
+
|
|
103
|
+
if _id && ENV['PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER']
|
|
104
|
+
cmd = String.new(ENV['PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER'])
|
|
105
|
+
cmd.gsub!("<PACT_URI>", example.metadata[:pactfile_uri].to_s)
|
|
106
|
+
cmd.gsub!("<PACT_BROKER_INTERACTION_ID>", "#{_id}")
|
|
107
|
+
colorizer.wrap("#{cmd} ", ::RSpec.configuration.failure_color) + colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
|
|
108
|
+
elsif ENV['PACT_INTERACTION_RERUN_COMMAND']
|
|
81
109
|
cmd = String.new(ENV['PACT_INTERACTION_RERUN_COMMAND'])
|
|
82
|
-
provider_state = example.metadata[:pact_interaction].provider_state
|
|
83
|
-
description = example.metadata[:pact_interaction].description
|
|
84
|
-
pactfile_uri = example.metadata[:pactfile_uri]
|
|
85
110
|
cmd.gsub!("<PACT_URI>", pactfile_uri.to_s)
|
|
86
111
|
cmd.gsub!("<PACT_DESCRIPTION>", description)
|
|
87
112
|
cmd.gsub!("<PACT_PROVIDER_STATE>", "#{provider_state}")
|
|
113
|
+
cmd.gsub!("<PACT_INTERACTION_INDEX>", "#{index}")
|
|
88
114
|
colorizer.wrap("#{cmd} ", ::RSpec.configuration.failure_color) + colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
|
|
89
115
|
else
|
|
90
|
-
|
|
116
|
+
message = if _id
|
|
117
|
+
"* #{example_description} (to re-run just this interaction, set environment variable PACT_BROKER_INTERACTION_ID=\"#{_id}\")"
|
|
118
|
+
else
|
|
119
|
+
"* #{example_description} (to re-run just this interaction, set environment variables PACT_DESCRIPTION=\"#{description}\" PACT_PROVIDER_STATE=\"#{provider_state}\")"
|
|
120
|
+
end
|
|
121
|
+
colorizer.wrap(message, ::RSpec.configuration.failure_color)
|
|
91
122
|
end
|
|
92
123
|
end
|
|
93
124
|
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
require 'rspec/core/formatters/json_formatter'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module Provider
|
|
5
|
+
module RSpec
|
|
6
|
+
class JsonFormatter < ::RSpec::Core::Formatters::JsonFormatter
|
|
7
|
+
::RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :seed, :close
|
|
8
|
+
|
|
9
|
+
def dump_summary(summary)
|
|
10
|
+
super(create_custom_summary(summary))
|
|
11
|
+
output_hash[:summary][:pacts] = pacts(summary)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def format_example(example)
|
|
15
|
+
{
|
|
16
|
+
:id => example.id,
|
|
17
|
+
:interaction_index => example.metadata[:pact_interaction].index,
|
|
18
|
+
:description => example.description,
|
|
19
|
+
:full_description => example.full_description,
|
|
20
|
+
:status => calculate_status(example),
|
|
21
|
+
:file_path => example.metadata[:file_path],
|
|
22
|
+
:line_number => example.metadata[:line_number],
|
|
23
|
+
:run_time => example.execution_result.run_time,
|
|
24
|
+
:mismatches => extract_differences(example),
|
|
25
|
+
:pact_url => example.metadata[:pact_uri].uri
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def stop(notification)
|
|
30
|
+
output_hash[:examples] = notification.examples.map do |example|
|
|
31
|
+
format_example(example).tap do |hash|
|
|
32
|
+
e = example.exception
|
|
33
|
+
if e
|
|
34
|
+
hash[:exception] = {
|
|
35
|
+
class: e.class.name,
|
|
36
|
+
message: e.message,
|
|
37
|
+
}
|
|
38
|
+
# No point providing a backtrace for a mismatch, too much noise
|
|
39
|
+
if !e.is_a?(::RSpec::Expectations::ExpectationNotMetError)
|
|
40
|
+
hash[:exception][:backtrace]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def calculate_status(example)
|
|
48
|
+
if example.execution_result.status == :failed && example.metadata[:pact_ignore_failures]
|
|
49
|
+
'pending'
|
|
50
|
+
else
|
|
51
|
+
example.execution_result.status.to_s
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# There will most likely be only one pact associated with this RSpec execution, because
|
|
56
|
+
# the most likely user of this formatter is the Go implementation that parses the JSON
|
|
57
|
+
# and builds Go tests from them.
|
|
58
|
+
# If the JSON formatter is used by someone else and they have multiple pacts, all the notices
|
|
59
|
+
# for the pacts will be mushed together in one collection, so it will be hard to know which notice
|
|
60
|
+
# belongs to which pact.
|
|
61
|
+
def pacts(summary)
|
|
62
|
+
unique_pact_metadatas(summary).collect do | example_metadata |
|
|
63
|
+
pact_uri = example_metadata[:pact_uri]
|
|
64
|
+
notices = (pact_uri.metadata[:notices] && pact_uri.metadata[:notices].before_verification_notices) || []
|
|
65
|
+
{
|
|
66
|
+
notices: notices,
|
|
67
|
+
url: pact_uri.uri,
|
|
68
|
+
consumer_name: example_metadata[:pact_consumer_contract].consumer.name,
|
|
69
|
+
provider_name: example_metadata[:pact_consumer_contract].provider.name,
|
|
70
|
+
short_description: pact_uri.metadata[:short_description]
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def unique_pact_metadatas(summary)
|
|
76
|
+
summary.examples.collect(&:metadata).group_by{ | metadata | metadata[:pact_uri].uri }.values.collect(&:first)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def create_custom_summary(summary)
|
|
80
|
+
::RSpec::Core::Notifications::SummaryNotification.new(
|
|
81
|
+
summary.duration,
|
|
82
|
+
summary.examples,
|
|
83
|
+
summary.examples.select{ | example | example.execution_result.status == :failed && !example.metadata[:pact_ignore_failures] },
|
|
84
|
+
summary.examples.select{ | example | example.execution_result.status == :failed && example.metadata[:pact_ignore_failures] },
|
|
85
|
+
summary.load_time,
|
|
86
|
+
summary.errors_outside_of_examples_count
|
|
87
|
+
)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def extract_differences(example)
|
|
91
|
+
if example.metadata[:pact_diff]
|
|
92
|
+
Pact::Matchers::ExtractDiffMessages.call(example.metadata[:pact_diff]).to_a
|
|
93
|
+
else
|
|
94
|
+
[]
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|