pact-v2 2.0.0.pre.preview1
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 +7 -0
- data/CHANGELOG.md +1321 -0
- data/LICENSE.txt +23 -0
- data/bin/pact +4 -0
- data/lib/pact/cli/generate_pact_docs.rb +4 -0
- data/lib/pact/cli/run_pact_verification.rb +99 -0
- data/lib/pact/cli/spec_criteria.rb +26 -0
- data/lib/pact/cli.rb +45 -0
- data/lib/pact/consumer/configuration/configuration_extensions.rb +90 -0
- data/lib/pact/consumer/configuration/dsl.rb +11 -0
- data/lib/pact/consumer/configuration/mock_service.rb +112 -0
- data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
- data/lib/pact/consumer/configuration/service_provider.rb +40 -0
- data/lib/pact/consumer/configuration.rb +10 -0
- data/lib/pact/consumer/consumer_contract_builder.rb +82 -0
- data/lib/pact/consumer/consumer_contract_builders.rb +10 -0
- data/lib/pact/consumer/interaction_builder.rb +45 -0
- data/lib/pact/consumer/rspec.rb +35 -0
- data/lib/pact/consumer/spec_hooks.rb +40 -0
- data/lib/pact/consumer/world.rb +37 -0
- data/lib/pact/consumer.rb +7 -0
- data/lib/pact/doc/README.md +13 -0
- data/lib/pact/doc/doc_file.rb +40 -0
- data/lib/pact/doc/generate.rb +11 -0
- data/lib/pact/doc/generator.rb +82 -0
- data/lib/pact/doc/interaction_view_model.rb +124 -0
- data/lib/pact/doc/markdown/consumer_contract_renderer.rb +68 -0
- data/lib/pact/doc/markdown/generator.rb +26 -0
- data/lib/pact/doc/markdown/index_renderer.rb +43 -0
- data/lib/pact/doc/markdown/interaction.erb +14 -0
- data/lib/pact/doc/markdown/interaction_renderer.rb +43 -0
- data/lib/pact/doc/sort_interactions.rb +16 -0
- data/lib/pact/hal/authorization_header_redactor.rb +32 -0
- data/lib/pact/hal/entity.rb +110 -0
- data/lib/pact/hal/http_client.rb +128 -0
- data/lib/pact/hal/link.rb +112 -0
- data/lib/pact/hal/non_json_entity.rb +28 -0
- data/lib/pact/hash_refinements.rb +17 -0
- data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +112 -0
- data/lib/pact/pact_broker/fetch_pacts.rb +103 -0
- data/lib/pact/pact_broker/notices.rb +34 -0
- data/lib/pact/pact_broker/pact_selection_description.rb +66 -0
- data/lib/pact/pact_broker.rb +25 -0
- data/lib/pact/project_root.rb +7 -0
- data/lib/pact/provider/configuration/configuration_extension.rb +69 -0
- data/lib/pact/provider/configuration/dsl.rb +18 -0
- data/lib/pact/provider/configuration/message_provider_dsl.rb +63 -0
- data/lib/pact/provider/configuration/pact_verification.rb +48 -0
- data/lib/pact/provider/configuration/pact_verification_from_broker.rb +126 -0
- data/lib/pact/provider/configuration/service_provider_config.rb +32 -0
- data/lib/pact/provider/configuration/service_provider_dsl.rb +107 -0
- data/lib/pact/provider/configuration.rb +7 -0
- data/lib/pact/provider/context.rb +0 -0
- data/lib/pact/provider/help/console_text.rb +76 -0
- data/lib/pact/provider/help/content.rb +38 -0
- data/lib/pact/provider/help/pact_diff.rb +43 -0
- data/lib/pact/provider/help/prompt_text.rb +49 -0
- data/lib/pact/provider/help/write.rb +56 -0
- data/lib/pact/provider/matchers/messages.rb +66 -0
- data/lib/pact/provider/pact_helper_locator.rb +24 -0
- data/lib/pact/provider/pact_source.rb +40 -0
- data/lib/pact/provider/pact_spec_runner.rb +188 -0
- data/lib/pact/provider/pact_uri.rb +55 -0
- data/lib/pact/provider/pact_verification.rb +17 -0
- data/lib/pact/provider/print_missing_provider_states.rb +35 -0
- data/lib/pact/provider/request.rb +77 -0
- data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
- data/lib/pact/provider/rspec/calculate_exit_code.rb +18 -0
- data/lib/pact/provider/rspec/custom_options_file +0 -0
- data/lib/pact/provider/rspec/formatter_rspec_2.rb +76 -0
- data/lib/pact/provider/rspec/formatter_rspec_3.rb +195 -0
- data/lib/pact/provider/rspec/json_formatter.rb +100 -0
- data/lib/pact/provider/rspec/matchers.rb +80 -0
- data/lib/pact/provider/rspec/pact_broker_formatter.rb +76 -0
- data/lib/pact/provider/rspec.rb +234 -0
- data/lib/pact/provider/state/provider_state.rb +180 -0
- data/lib/pact/provider/state/provider_state_configured_modules.rb +15 -0
- data/lib/pact/provider/state/provider_state_manager.rb +42 -0
- data/lib/pact/provider/state/provider_state_proxy.rb +39 -0
- data/lib/pact/provider/state/set_up.rb +13 -0
- data/lib/pact/provider/state/tear_down.rb +13 -0
- data/lib/pact/provider/test_methods.rb +77 -0
- data/lib/pact/provider/verification_report.rb +36 -0
- data/lib/pact/provider/verification_results/create.rb +88 -0
- data/lib/pact/provider/verification_results/publish.rb +143 -0
- data/lib/pact/provider/verification_results/publish_all.rb +50 -0
- data/lib/pact/provider/verification_results/verification_result.rb +40 -0
- data/lib/pact/provider/world.rb +50 -0
- data/lib/pact/provider.rb +3 -0
- data/lib/pact/retry.rb +37 -0
- data/lib/pact/tasks/task_helper.rb +62 -0
- data/lib/pact/tasks/verification_task.rb +95 -0
- data/lib/pact/tasks.rb +2 -0
- data/lib/pact/templates/help.erb +22 -0
- data/lib/pact/templates/provider_state.erb +14 -0
- data/lib/pact/utils/metrics.rb +100 -0
- data/lib/pact/utils/string.rb +35 -0
- data/lib/pact/v2/configuration.rb +23 -0
- data/lib/pact/v2/consumer/grpc_interaction_builder.rb +187 -0
- data/lib/pact/v2/consumer/http_interaction_builder.rb +163 -0
- data/lib/pact/v2/consumer/interaction_contents.rb +54 -0
- data/lib/pact/v2/consumer/message_interaction_builder.rb +280 -0
- data/lib/pact/v2/consumer/mock_server.rb +99 -0
- data/lib/pact/v2/consumer/pact_config/base.rb +24 -0
- data/lib/pact/v2/consumer/pact_config/grpc.rb +26 -0
- data/lib/pact/v2/consumer/pact_config/http.rb +55 -0
- data/lib/pact/v2/consumer/pact_config/message.rb +17 -0
- data/lib/pact/v2/consumer/pact_config.rb +24 -0
- data/lib/pact/v2/consumer.rb +8 -0
- data/lib/pact/v2/matchers/base.rb +67 -0
- data/lib/pact/v2/matchers/v1/equality.rb +19 -0
- data/lib/pact/v2/matchers/v2/regex.rb +19 -0
- data/lib/pact/v2/matchers/v2/type.rb +17 -0
- data/lib/pact/v2/matchers/v3/boolean.rb +17 -0
- data/lib/pact/v2/matchers/v3/date.rb +18 -0
- data/lib/pact/v2/matchers/v3/date_time.rb +18 -0
- data/lib/pact/v2/matchers/v3/decimal.rb +17 -0
- data/lib/pact/v2/matchers/v3/each.rb +42 -0
- data/lib/pact/v2/matchers/v3/include.rb +17 -0
- data/lib/pact/v2/matchers/v3/integer.rb +17 -0
- data/lib/pact/v2/matchers/v3/number.rb +17 -0
- data/lib/pact/v2/matchers/v3/time.rb +18 -0
- data/lib/pact/v2/matchers/v4/each_key.rb +26 -0
- data/lib/pact/v2/matchers/v4/each_key_value.rb +32 -0
- data/lib/pact/v2/matchers/v4/each_value.rb +33 -0
- data/lib/pact/v2/matchers/v4/not_empty.rb +17 -0
- data/lib/pact/v2/matchers.rb +94 -0
- data/lib/pact/v2/native/blocking_verifier.rb +17 -0
- data/lib/pact/v2/native/logger.rb +25 -0
- data/lib/pact/v2/provider/async_message_verifier.rb +28 -0
- data/lib/pact/v2/provider/base_verifier.rb +242 -0
- data/lib/pact/v2/provider/grpc_verifier.rb +38 -0
- data/lib/pact/v2/provider/gruf_server.rb +75 -0
- data/lib/pact/v2/provider/http_server.rb +79 -0
- data/lib/pact/v2/provider/http_verifier.rb +43 -0
- data/lib/pact/v2/provider/message_provider_servlet.rb +79 -0
- data/lib/pact/v2/provider/mixed_verifier.rb +22 -0
- data/lib/pact/v2/provider/pact_broker_proxy.rb +71 -0
- data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +77 -0
- data/lib/pact/v2/provider/pact_config/async.rb +29 -0
- data/lib/pact/v2/provider/pact_config/base.rb +101 -0
- data/lib/pact/v2/provider/pact_config/grpc.rb +26 -0
- data/lib/pact/v2/provider/pact_config/http.rb +27 -0
- data/lib/pact/v2/provider/pact_config/mixed.rb +39 -0
- data/lib/pact/v2/provider/pact_config.rb +26 -0
- data/lib/pact/v2/provider/provider_server_runner.rb +89 -0
- data/lib/pact/v2/provider/provider_state_configuration.rb +32 -0
- data/lib/pact/v2/provider/provider_state_servlet.rb +86 -0
- data/lib/pact/v2/provider.rb +8 -0
- data/lib/pact/v2/railtie.rb +13 -0
- data/lib/pact/v2/rspec/support/pact_consumer_helpers.rb +80 -0
- data/lib/pact/v2/rspec/support/pact_message_helpers.rb +42 -0
- data/lib/pact/v2/rspec/support/pact_provider_helpers.rb +129 -0
- data/lib/pact/v2/rspec/support/waterdrop/pact_waterdrop_client.rb +27 -0
- data/lib/pact/v2/rspec/support/webmock/webmock_helpers.rb +30 -0
- data/lib/pact/v2/rspec.rb +17 -0
- data/lib/pact/v2/tasks/pact.rake +13 -0
- data/lib/pact/v2/version.rb +8 -0
- data/lib/pact/v2.rb +71 -0
- data/lib/pact/version.rb +4 -0
- data/lib/pact.rb +13 -0
- data/lib/tasks/pact.rake +34 -0
- data/pact.gemspec +106 -0
- metadata +529 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'pact/provider/verification_results/verification_result'
|
2
|
+
module Pact
|
3
|
+
module Provider
|
4
|
+
module VerificationResults
|
5
|
+
class Create
|
6
|
+
|
7
|
+
def self.call pact_source, test_results_hash
|
8
|
+
new(pact_source, test_results_hash).call
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize pact_source, test_results_hash
|
12
|
+
@pact_source = pact_source
|
13
|
+
@test_results_hash = test_results_hash
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
VerificationResult.new(
|
18
|
+
publishable?,
|
19
|
+
!any_failures?,
|
20
|
+
Pact.configuration.provider.application_version,
|
21
|
+
test_results_hash_for_pact_uri,
|
22
|
+
Pact.configuration.provider.build_url
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def pact_uri
|
29
|
+
@pact_uri ||= pact_source.uri
|
30
|
+
end
|
31
|
+
|
32
|
+
def any_failures?
|
33
|
+
count_failures_for_pact_uri > 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def publishable?
|
37
|
+
if defined?(@publishable)
|
38
|
+
@publishable
|
39
|
+
else
|
40
|
+
@publishable = pact_source.consumer_contract.interactions.all? do | interaction |
|
41
|
+
examples_for_pact_uri.any?{ |e| example_is_for_interaction?(e, interaction) }
|
42
|
+
end && examples_for_pact_uri.count > 0
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def example_is_for_interaction?(example, interaction)
|
47
|
+
# Use the Pact Broker id if supported
|
48
|
+
if interaction._id
|
49
|
+
example[:pact_interaction]._id == interaction._id
|
50
|
+
else
|
51
|
+
# fall back to object equality (based on the field values of the interaction)
|
52
|
+
example[:pact_interaction] == interaction
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def examples_for_pact_uri
|
57
|
+
@examples_for_pact_uri ||= test_results_hash[:tests].select{ |e| e[:pact_uri] == pact_uri }
|
58
|
+
end
|
59
|
+
|
60
|
+
def count_failures_for_pact_uri
|
61
|
+
examples_for_pact_uri.count{ |e| e[:status] != 'passed' }
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_results_hash_for_pact_uri
|
65
|
+
{
|
66
|
+
tests: examples_for_pact_uri.collect{ |e| clean_example(e) },
|
67
|
+
summary: {
|
68
|
+
testCount: examples_for_pact_uri.size,
|
69
|
+
failureCount: count_failures_for_pact_uri
|
70
|
+
},
|
71
|
+
metadata: {
|
72
|
+
warning: "These test results use a beta format. Do not rely on it, as it will definitely change.",
|
73
|
+
pactVerificationResultsSpecification: {
|
74
|
+
version: "1.0.0-beta.1"
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def clean_example(example)
|
81
|
+
example.reject{ |k, v| k == :pact_uri || k == :pact_interaction }
|
82
|
+
end
|
83
|
+
|
84
|
+
attr_reader :pact_source, :test_results_hash
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'pact/errors'
|
3
|
+
require 'pact/retry'
|
4
|
+
require 'pact/hal/entity'
|
5
|
+
require 'pact/hal/http_client'
|
6
|
+
|
7
|
+
# TODO move this to the pact broker client
|
8
|
+
|
9
|
+
module Pact
|
10
|
+
module Provider
|
11
|
+
module VerificationResults
|
12
|
+
class PublicationError < Pact::Error; end
|
13
|
+
|
14
|
+
class Publish
|
15
|
+
|
16
|
+
PUBLISH_RELATION = 'pb:publish-verification-results'.freeze
|
17
|
+
PROVIDER_RELATION = 'pb:provider'.freeze
|
18
|
+
VERSION_TAG_RELATION = 'pb:version-tag'.freeze
|
19
|
+
BRANCH_VERSION_RELATION = 'pb:branch-version'.freeze
|
20
|
+
|
21
|
+
def self.call pact_source, verification_result, options = {}
|
22
|
+
new(pact_source, verification_result, options).call
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize pact_source, verification_result, options = {}
|
26
|
+
@pact_source = pact_source
|
27
|
+
@verification_result = verification_result
|
28
|
+
http_client_options = pact_source.uri.options.reject{ |k, v| ![:username, :password, :token].include?(k) }
|
29
|
+
@http_client = Pact::Hal::HttpClient.new(http_client_options.merge(verbose: options[:verbose]))
|
30
|
+
@pact_entity = Pact::Hal::Entity.new(pact_source.uri, pact_source.pact_hash, http_client)
|
31
|
+
end
|
32
|
+
|
33
|
+
def call
|
34
|
+
if can_publish_verification_results?
|
35
|
+
create_branch_version_if_configured
|
36
|
+
tag_versions_if_configured
|
37
|
+
publish_verification_results
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
attr_reader :pact_source, :verification_result, :pact_entity, :http_client
|
46
|
+
|
47
|
+
def can_publish_verification_results?
|
48
|
+
return false unless Pact.configuration.provider.publish_verification_results?
|
49
|
+
|
50
|
+
if !pact_entity.can?(PUBLISH_RELATION)
|
51
|
+
Pact.configuration.error_stream.puts "WARN: Cannot publish verification for #{consumer_name} as there is no link named pb:publish-verification-results in the pact JSON. If you are using a pact broker, please upgrade to version 2.0.0 or later."
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
|
55
|
+
if !verification_result.publishable?
|
56
|
+
Pact.configuration.error_stream.puts "WARN: Cannot publish verification for #{consumer_name} as not all interactions have been verified. Re-run the verification without the filter parameters or environment variables to publish the verification."
|
57
|
+
return false
|
58
|
+
end
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
def hacky_tag_url provider_entity
|
63
|
+
hacky_tag_url = provider_entity._link('self').href + "/versions/{version}/tags/{tag}"
|
64
|
+
Pact::Hal::Link.new('href' => hacky_tag_url)
|
65
|
+
end
|
66
|
+
|
67
|
+
def tag_versions_if_configured
|
68
|
+
if Pact.configuration.provider.tags.any?
|
69
|
+
if pact_entity.can?(PROVIDER_RELATION)
|
70
|
+
tag_versions
|
71
|
+
else
|
72
|
+
Pact.configuration.error_stream.puts "WARN: Could not tag provider version as the pb:provider link cannot be found"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_branch_version_if_configured
|
78
|
+
if Pact.configuration.provider.branch
|
79
|
+
branch_version_link = provider_entity._link(BRANCH_VERSION_RELATION)
|
80
|
+
if branch_version_link
|
81
|
+
version_number = Pact.configuration.provider.application_version
|
82
|
+
branch = Pact.configuration.provider.branch
|
83
|
+
|
84
|
+
Pact.configuration.output_stream.puts "INFO: Creating #{provider_name} version #{version_number} with branch \"#{branch}\""
|
85
|
+
branch_entity = branch_version_link.expand(
|
86
|
+
version: version_number,
|
87
|
+
branch: branch
|
88
|
+
).put
|
89
|
+
unless branch_entity.success?
|
90
|
+
raise PublicationError.new("Error returned from tagging request: status=#{branch_entity.response.code} body=#{branch_entity.response.body}")
|
91
|
+
end
|
92
|
+
else
|
93
|
+
raise PublicationError.new("This version of the Pact Broker does not support version branches. Please update to version 2.58.0 or later.")
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def tag_versions
|
99
|
+
tag_link = provider_entity._link(VERSION_TAG_RELATION) || hacky_tag_url(provider_entity)
|
100
|
+
provider_application_version = Pact.configuration.provider.application_version
|
101
|
+
|
102
|
+
Pact.configuration.provider.tags.each do | tag |
|
103
|
+
Pact.configuration.output_stream.puts "INFO: Tagging version #{provider_application_version} of #{provider_name} as #{tag.inspect}"
|
104
|
+
tag_entity = tag_link.expand(version: provider_application_version, tag: tag).put
|
105
|
+
unless tag_entity.success?
|
106
|
+
raise PublicationError.new("Error returned from tagging request: status=#{tag_entity.response.code} body=#{tag_entity.response.body}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def publish_verification_results
|
112
|
+
verification_entity = nil
|
113
|
+
begin
|
114
|
+
# The verifications resource didn't have the content_types_provided set correctly, so publishing fails if we don't have */*
|
115
|
+
verification_entity = pact_entity.post(PUBLISH_RELATION, verification_result, { "Accept" => "application/hal+json, */*" })
|
116
|
+
rescue StandardError => e
|
117
|
+
error_message = "Failed to publish verification results due to: #{e.class} #{e.message} #{e.backtrace.join("\n")}"
|
118
|
+
raise PublicationError.new(error_message)
|
119
|
+
end
|
120
|
+
|
121
|
+
if verification_entity.success?
|
122
|
+
new_resource_url = verification_entity._link('self').href
|
123
|
+
Pact.configuration.output_stream.puts "INFO: Verification results published to #{new_resource_url}"
|
124
|
+
else
|
125
|
+
raise PublicationError.new("Error returned from verification results publication #{verification_entity.response.code} #{verification_entity.response.body}")
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def consumer_name
|
130
|
+
pact_source.pact_hash['consumer']['name']
|
131
|
+
end
|
132
|
+
|
133
|
+
def provider_name
|
134
|
+
pact_source.pact_hash['provider']['name']
|
135
|
+
end
|
136
|
+
|
137
|
+
def provider_entity
|
138
|
+
@provider_entity ||= pact_entity.get!(PROVIDER_RELATION)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require'pact/provider/verification_results/create'
|
2
|
+
require'pact/provider/verification_results/publish'
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
module Provider
|
6
|
+
module VerificationResults
|
7
|
+
class PublishAll
|
8
|
+
|
9
|
+
def self.call pact_sources, test_results_hash, options = {}
|
10
|
+
new(pact_sources, test_results_hash, options).call
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize pact_sources, test_results_hash, options = {}
|
14
|
+
@pact_sources = pact_sources
|
15
|
+
@test_results_hash = test_results_hash
|
16
|
+
@options = options
|
17
|
+
end
|
18
|
+
|
19
|
+
def call
|
20
|
+
verification_results.collect do | (pact_source, verification_result) |
|
21
|
+
published = false
|
22
|
+
begin
|
23
|
+
published = Publish.call(pact_source, verification_result, { verbose: options[:verbose] })
|
24
|
+
ensure
|
25
|
+
print_after_verification_notices(pact_source, verification_result, published)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def verification_results
|
33
|
+
pact_sources.collect do | pact_source |
|
34
|
+
[pact_source, Create.call(pact_source, test_results_hash)]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def print_after_verification_notices(pact_source, verification_result, published)
|
39
|
+
if pact_source.uri.metadata[:notices]
|
40
|
+
pact_source.uri.metadata[:notices].after_verification_notices_text(verification_result.success, published).each do | text |
|
41
|
+
Pact.configuration.output_stream.puts "DEBUG: #{text}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
attr_reader :pact_sources, :test_results_hash, :options
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
module Provider
|
5
|
+
module VerificationResults
|
6
|
+
class VerificationResult
|
7
|
+
attr_reader :success, :provider_application_version, :test_results_hash
|
8
|
+
|
9
|
+
def initialize publishable, success, provider_application_version, test_results_hash, build_url
|
10
|
+
@publishable = publishable
|
11
|
+
@success = success
|
12
|
+
@provider_application_version = provider_application_version
|
13
|
+
@test_results_hash = test_results_hash
|
14
|
+
@build_url = build_url
|
15
|
+
end
|
16
|
+
|
17
|
+
def publishable?
|
18
|
+
@publishable
|
19
|
+
end
|
20
|
+
|
21
|
+
def provider_application_version_set?
|
22
|
+
!!provider_application_version
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_json(options = {})
|
26
|
+
{
|
27
|
+
success: success,
|
28
|
+
providerApplicationVersion: provider_application_version,
|
29
|
+
testResults: test_results_hash,
|
30
|
+
buildUrl: @build_url
|
31
|
+
}.to_json(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
"[success: #{success}, providerApplicationVersion: #{provider_application_version}]"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'pact/provider/state/provider_state_proxy'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
|
5
|
+
def self.provider_world
|
6
|
+
@world ||= Pact::Provider::World.new
|
7
|
+
end
|
8
|
+
|
9
|
+
# internal api, for testing only
|
10
|
+
def self.clear_provider_world
|
11
|
+
@world = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
module Provider
|
15
|
+
class World
|
16
|
+
|
17
|
+
attr_accessor :pact_sources, :failed_examples, :verbose
|
18
|
+
|
19
|
+
def provider_states
|
20
|
+
@provider_states_proxy ||= Pact::Provider::State::ProviderStateProxy.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_pact_verification verification
|
24
|
+
pact_verifications << verification
|
25
|
+
end
|
26
|
+
|
27
|
+
def pact_verifications
|
28
|
+
@pact_verifications ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def pact_urls
|
32
|
+
(pact_verifications.collect(&:uri) + pact_uris_from_pact_uri_sources).compact
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_pact_uri_source pact_uri_source
|
36
|
+
pact_uri_sources << pact_uri_source
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def pact_uri_sources
|
42
|
+
@pact_uri_sources ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
def pact_uris_from_pact_uri_sources
|
46
|
+
pact_uri_sources.collect(&:call).flatten
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/pact/retry.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'pact/errors'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
class Retry
|
5
|
+
class RescuableError
|
6
|
+
UNRESCUEABLE = [Pact::Error]
|
7
|
+
|
8
|
+
def self.===(e)
|
9
|
+
case e
|
10
|
+
when *UNRESCUEABLE then
|
11
|
+
false
|
12
|
+
else
|
13
|
+
true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.until_true options = {}
|
19
|
+
max_tries = options.fetch(:times, 3)
|
20
|
+
tries = 0
|
21
|
+
while true
|
22
|
+
begin
|
23
|
+
return yield
|
24
|
+
rescue RescuableError => e
|
25
|
+
tries += 1
|
26
|
+
$stderr.puts "Error making request - #{e.class} #{e.message} #{e.backtrace.find {|l| l.include?('pact_provider')}}, attempt #{tries} of #{max_tries}"
|
27
|
+
raise e if max_tries == tries
|
28
|
+
sleep options
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.sleep options
|
34
|
+
Kernel.sleep options.fetch(:sleep, 5)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'pact/configuration'
|
2
|
+
require 'pact/provider/pact_helper_locator'
|
3
|
+
require 'rake/file_utils'
|
4
|
+
require 'shellwords'
|
5
|
+
|
6
|
+
module Pact
|
7
|
+
module TaskHelper
|
8
|
+
|
9
|
+
PACT_INTERACTION_RERUN_COMMAND = "bundle exec rake pact:verify:at[<PACT_URI>] PACT_DESCRIPTION=\"<PACT_DESCRIPTION>\" PACT_PROVIDER_STATE=\"<PACT_PROVIDER_STATE>\""
|
10
|
+
PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER = "bundle exec rake pact:verify:at[<PACT_URI>] PACT_BROKER_INTERACTION_ID=\"<PACT_BROKER_INTERACTION_ID>\""
|
11
|
+
|
12
|
+
extend self
|
13
|
+
|
14
|
+
def execute_pact_verify pact_uri = nil, pact_helper = nil, rspec_opts = nil, verification_opts = {}
|
15
|
+
execute_cmd verify_command(pact_helper || Pact::Provider::PactHelperLocater.pact_helper_path, pact_uri, rspec_opts, verification_opts)
|
16
|
+
end
|
17
|
+
|
18
|
+
def handle_verification_failure
|
19
|
+
exit_status = yield
|
20
|
+
abort if exit_status != 0
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify_command pact_helper, pact_uri, rspec_opts, verification_opts
|
24
|
+
command_parts = []
|
25
|
+
# Clear SPEC_OPTS, otherwise we can get extra formatters, creating duplicate output eg. CI Reporting.
|
26
|
+
# Allow deliberate configuration using rspec_opts in VerificationTask.
|
27
|
+
command_parts << "SPEC_OPTS=#{Shellwords.escape(rspec_opts || '')}"
|
28
|
+
command_parts << FileUtils::RUBY
|
29
|
+
command_parts << "-S pact verify"
|
30
|
+
command_parts << "--pact-helper" << Shellwords.escape(pact_helper.end_with?(".rb") ? pact_helper : pact_helper + ".rb")
|
31
|
+
(command_parts << "--pact-uri" << pact_uri) if pact_uri
|
32
|
+
command_parts << "--ignore-failures" if verification_opts[:ignore_failures]
|
33
|
+
command_parts << "--pact-broker-username" << ENV['PACT_BROKER_USERNAME'] if ENV['PACT_BROKER_USERNAME']
|
34
|
+
command_parts << "--pact-broker-password" << ENV['PACT_BROKER_PASSWORD'] if ENV['PACT_BROKER_PASSWORD']
|
35
|
+
command_parts << "--backtrace" if ENV['BACKTRACE'] == 'true'
|
36
|
+
command_parts << "--description #{Shellwords.escape(ENV['PACT_DESCRIPTION'])}" if ENV['PACT_DESCRIPTION']
|
37
|
+
command_parts << "--provider-state #{Shellwords.escape(ENV['PACT_PROVIDER_STATE'])}" if ENV['PACT_PROVIDER_STATE']
|
38
|
+
command_parts << "--pact-broker-interaction-id #{Shellwords.escape(ENV['PACT_BROKER_INTERACTION_ID'])}" if ENV['PACT_BROKER_INTERACTION_ID']
|
39
|
+
command_parts << "--interaction-index #{Shellwords.escape(ENV['PACT_INTERACTION_INDEX'])}" if ENV['PACT_INTERACTION_INDEX']
|
40
|
+
command_parts.flatten.join(" ")
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute_cmd command
|
44
|
+
Pact.configuration.output_stream.puts command
|
45
|
+
temporarily_set_env_var 'PACT_EXECUTING_LANGUAGE', 'ruby' do
|
46
|
+
temporarily_set_env_var 'PACT_INTERACTION_RERUN_COMMAND', PACT_INTERACTION_RERUN_COMMAND do
|
47
|
+
temporarily_set_env_var 'PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER', PACT_INTERACTION_RERUN_COMMAND_FOR_BROKER do
|
48
|
+
exit_status = system(command) ? 0 : 1
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def temporarily_set_env_var name, value
|
55
|
+
original_value = ENV[name]
|
56
|
+
ENV[name] ||= value
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
ENV[name] = original_value
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
To create a rake pact:verify:<something> task
|
5
|
+
|
6
|
+
Pact::VerificationTask.new(:head) do | pact |
|
7
|
+
pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-MAS/latestSuccessful/artifact/JOB2/Pacts/mas-contract_transaction_service.json'
|
8
|
+
pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-IMAGINARY-CONSUMER/latestSuccessful/artifact/JOB2/Pacts/imaginary_consumer-contract_transaction_service.json'
|
9
|
+
end
|
10
|
+
|
11
|
+
The pact.uri may be a local file system path or a remote URL.
|
12
|
+
|
13
|
+
To run a pact:verify:xxx task you need to define a pact_helper.rb, ideally in spec/service_consumers.
|
14
|
+
It should contain your service_provider definition, and load any provider state definition files.
|
15
|
+
It should also load all your app's dependencies (eg by calling out to spec_helper)
|
16
|
+
|
17
|
+
Eg.
|
18
|
+
|
19
|
+
require 'spec_helper'
|
20
|
+
require 'provider_states_for_my_consumer'
|
21
|
+
|
22
|
+
Pact.service_provider "My Provider" do
|
23
|
+
app { TestApp.new }
|
24
|
+
end
|
25
|
+
|
26
|
+
=end
|
27
|
+
|
28
|
+
module Pact
|
29
|
+
class VerificationTask < ::Rake::TaskLib
|
30
|
+
|
31
|
+
attr_reader :pact_spec_configs
|
32
|
+
attr_accessor :rspec_opts
|
33
|
+
attr_accessor :ignore_failures
|
34
|
+
attr_accessor :_pact_helper
|
35
|
+
|
36
|
+
def initialize(name)
|
37
|
+
@rspec_opts = nil
|
38
|
+
@ignore_failures = false
|
39
|
+
@pact_spec_configs = []
|
40
|
+
@name = name
|
41
|
+
yield self
|
42
|
+
rake_task
|
43
|
+
end
|
44
|
+
|
45
|
+
def pact_helper(pact_helper)
|
46
|
+
@pact_spec_configs << { pact_helper: pact_helper }
|
47
|
+
end
|
48
|
+
|
49
|
+
def uri(uri, options = {})
|
50
|
+
@pact_spec_configs << {uri: uri, pact_helper: options[:pact_helper]}
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_reader :name
|
56
|
+
|
57
|
+
# def parse_pactfile config
|
58
|
+
# Pact::ConsumerContract.from_uri config[:uri]
|
59
|
+
# end
|
60
|
+
|
61
|
+
# def publish_report config, output, result, provider_ref, reports_dir
|
62
|
+
# consumer_contract = parse_pactfile config
|
63
|
+
# #TODO - when checking out a historical version, provider ref will be prod, however it will think it is head. Fix this!!!!
|
64
|
+
# report = Provider::VerificationReport.new(
|
65
|
+
# :result => result,
|
66
|
+
# :output => output,
|
67
|
+
# :consumer => {:name => consumer_contract.consumer.name, :ref => name},
|
68
|
+
# :provider => {:name => consumer_contract.provider.name, :ref => provider_ref}
|
69
|
+
# )
|
70
|
+
|
71
|
+
# FileUtils.mkdir_p reports_dir
|
72
|
+
# File.open("#{reports_dir}/#{report.report_file_name}", "w") { |file| file << JSON.pretty_generate(report) }
|
73
|
+
# end
|
74
|
+
|
75
|
+
def rake_task
|
76
|
+
namespace :pact do
|
77
|
+
|
78
|
+
desc "Verify provider against the consumer pacts for #{name}"
|
79
|
+
task "verify:#{name}" do |t, args|
|
80
|
+
|
81
|
+
require 'pact/tasks/task_helper'
|
82
|
+
|
83
|
+
exit_statuses = pact_spec_configs.collect do | config |
|
84
|
+
Pact::TaskHelper.execute_pact_verify config[:uri], config[:pact_helper], rspec_opts, { ignore_failures: ignore_failures }
|
85
|
+
end
|
86
|
+
|
87
|
+
Pact::TaskHelper.handle_verification_failure do
|
88
|
+
exit_statuses.count{ | status | status != 0 }
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/pact/tasks.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# For assistance debugging failures
|
2
|
+
|
3
|
+
* The pact files have been stored locally in the following temp directory:
|
4
|
+
<%= temp_dir %>
|
5
|
+
|
6
|
+
* The requests and responses are logged in the following log file:
|
7
|
+
<%= log_path %>
|
8
|
+
|
9
|
+
* Add BACKTRACE=true to the `rake pact:verify` command to see the full backtrace
|
10
|
+
|
11
|
+
* If the diff output is confusing, try using another diff formatter.
|
12
|
+
The options are :unix, :embedded and :list
|
13
|
+
|
14
|
+
Pact.configure do | config |
|
15
|
+
config.diff_formatter = :embedded
|
16
|
+
end
|
17
|
+
|
18
|
+
See https://github.com/pact-foundation/pact-ruby/blob/master/documentation/configuration.md#diff_formatter for examples and more information.
|
19
|
+
|
20
|
+
* Check out https://github.com/pact-foundation/pact-ruby/wiki/Troubleshooting
|
21
|
+
|
22
|
+
* Ask a question on stackoverflow and tag it `pact-ruby`
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Could not find one or more provider states.
|
2
|
+
Have you required the provider states file for this consumer in your pact_helper.rb?
|
3
|
+
If you have not yet defined these states, here is a template:
|
4
|
+
<% consumers.keys.each do | consumer_name | %>
|
5
|
+
Pact.provider_states_for "<%= consumer_name %>" do
|
6
|
+
<% consumers[consumer_name].each do | provider_state | %>
|
7
|
+
provider_state "<%= provider_state %>" do
|
8
|
+
set_up do
|
9
|
+
# Your set up code goes here
|
10
|
+
end
|
11
|
+
end
|
12
|
+
<% end %>
|
13
|
+
end
|
14
|
+
<% end %>
|