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.
Files changed (164) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1321 -0
  3. data/LICENSE.txt +23 -0
  4. data/bin/pact +4 -0
  5. data/lib/pact/cli/generate_pact_docs.rb +4 -0
  6. data/lib/pact/cli/run_pact_verification.rb +99 -0
  7. data/lib/pact/cli/spec_criteria.rb +26 -0
  8. data/lib/pact/cli.rb +45 -0
  9. data/lib/pact/consumer/configuration/configuration_extensions.rb +90 -0
  10. data/lib/pact/consumer/configuration/dsl.rb +11 -0
  11. data/lib/pact/consumer/configuration/mock_service.rb +112 -0
  12. data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
  13. data/lib/pact/consumer/configuration/service_provider.rb +40 -0
  14. data/lib/pact/consumer/configuration.rb +10 -0
  15. data/lib/pact/consumer/consumer_contract_builder.rb +82 -0
  16. data/lib/pact/consumer/consumer_contract_builders.rb +10 -0
  17. data/lib/pact/consumer/interaction_builder.rb +45 -0
  18. data/lib/pact/consumer/rspec.rb +35 -0
  19. data/lib/pact/consumer/spec_hooks.rb +40 -0
  20. data/lib/pact/consumer/world.rb +37 -0
  21. data/lib/pact/consumer.rb +7 -0
  22. data/lib/pact/doc/README.md +13 -0
  23. data/lib/pact/doc/doc_file.rb +40 -0
  24. data/lib/pact/doc/generate.rb +11 -0
  25. data/lib/pact/doc/generator.rb +82 -0
  26. data/lib/pact/doc/interaction_view_model.rb +124 -0
  27. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +68 -0
  28. data/lib/pact/doc/markdown/generator.rb +26 -0
  29. data/lib/pact/doc/markdown/index_renderer.rb +43 -0
  30. data/lib/pact/doc/markdown/interaction.erb +14 -0
  31. data/lib/pact/doc/markdown/interaction_renderer.rb +43 -0
  32. data/lib/pact/doc/sort_interactions.rb +16 -0
  33. data/lib/pact/hal/authorization_header_redactor.rb +32 -0
  34. data/lib/pact/hal/entity.rb +110 -0
  35. data/lib/pact/hal/http_client.rb +128 -0
  36. data/lib/pact/hal/link.rb +112 -0
  37. data/lib/pact/hal/non_json_entity.rb +28 -0
  38. data/lib/pact/hash_refinements.rb +17 -0
  39. data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +112 -0
  40. data/lib/pact/pact_broker/fetch_pacts.rb +103 -0
  41. data/lib/pact/pact_broker/notices.rb +34 -0
  42. data/lib/pact/pact_broker/pact_selection_description.rb +66 -0
  43. data/lib/pact/pact_broker.rb +25 -0
  44. data/lib/pact/project_root.rb +7 -0
  45. data/lib/pact/provider/configuration/configuration_extension.rb +69 -0
  46. data/lib/pact/provider/configuration/dsl.rb +18 -0
  47. data/lib/pact/provider/configuration/message_provider_dsl.rb +63 -0
  48. data/lib/pact/provider/configuration/pact_verification.rb +48 -0
  49. data/lib/pact/provider/configuration/pact_verification_from_broker.rb +126 -0
  50. data/lib/pact/provider/configuration/service_provider_config.rb +32 -0
  51. data/lib/pact/provider/configuration/service_provider_dsl.rb +107 -0
  52. data/lib/pact/provider/configuration.rb +7 -0
  53. data/lib/pact/provider/context.rb +0 -0
  54. data/lib/pact/provider/help/console_text.rb +76 -0
  55. data/lib/pact/provider/help/content.rb +38 -0
  56. data/lib/pact/provider/help/pact_diff.rb +43 -0
  57. data/lib/pact/provider/help/prompt_text.rb +49 -0
  58. data/lib/pact/provider/help/write.rb +56 -0
  59. data/lib/pact/provider/matchers/messages.rb +66 -0
  60. data/lib/pact/provider/pact_helper_locator.rb +24 -0
  61. data/lib/pact/provider/pact_source.rb +40 -0
  62. data/lib/pact/provider/pact_spec_runner.rb +188 -0
  63. data/lib/pact/provider/pact_uri.rb +55 -0
  64. data/lib/pact/provider/pact_verification.rb +17 -0
  65. data/lib/pact/provider/print_missing_provider_states.rb +35 -0
  66. data/lib/pact/provider/request.rb +77 -0
  67. data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
  68. data/lib/pact/provider/rspec/calculate_exit_code.rb +18 -0
  69. data/lib/pact/provider/rspec/custom_options_file +0 -0
  70. data/lib/pact/provider/rspec/formatter_rspec_2.rb +76 -0
  71. data/lib/pact/provider/rspec/formatter_rspec_3.rb +195 -0
  72. data/lib/pact/provider/rspec/json_formatter.rb +100 -0
  73. data/lib/pact/provider/rspec/matchers.rb +80 -0
  74. data/lib/pact/provider/rspec/pact_broker_formatter.rb +76 -0
  75. data/lib/pact/provider/rspec.rb +234 -0
  76. data/lib/pact/provider/state/provider_state.rb +180 -0
  77. data/lib/pact/provider/state/provider_state_configured_modules.rb +15 -0
  78. data/lib/pact/provider/state/provider_state_manager.rb +42 -0
  79. data/lib/pact/provider/state/provider_state_proxy.rb +39 -0
  80. data/lib/pact/provider/state/set_up.rb +13 -0
  81. data/lib/pact/provider/state/tear_down.rb +13 -0
  82. data/lib/pact/provider/test_methods.rb +77 -0
  83. data/lib/pact/provider/verification_report.rb +36 -0
  84. data/lib/pact/provider/verification_results/create.rb +88 -0
  85. data/lib/pact/provider/verification_results/publish.rb +143 -0
  86. data/lib/pact/provider/verification_results/publish_all.rb +50 -0
  87. data/lib/pact/provider/verification_results/verification_result.rb +40 -0
  88. data/lib/pact/provider/world.rb +50 -0
  89. data/lib/pact/provider.rb +3 -0
  90. data/lib/pact/retry.rb +37 -0
  91. data/lib/pact/tasks/task_helper.rb +62 -0
  92. data/lib/pact/tasks/verification_task.rb +95 -0
  93. data/lib/pact/tasks.rb +2 -0
  94. data/lib/pact/templates/help.erb +22 -0
  95. data/lib/pact/templates/provider_state.erb +14 -0
  96. data/lib/pact/utils/metrics.rb +100 -0
  97. data/lib/pact/utils/string.rb +35 -0
  98. data/lib/pact/v2/configuration.rb +23 -0
  99. data/lib/pact/v2/consumer/grpc_interaction_builder.rb +187 -0
  100. data/lib/pact/v2/consumer/http_interaction_builder.rb +163 -0
  101. data/lib/pact/v2/consumer/interaction_contents.rb +54 -0
  102. data/lib/pact/v2/consumer/message_interaction_builder.rb +280 -0
  103. data/lib/pact/v2/consumer/mock_server.rb +99 -0
  104. data/lib/pact/v2/consumer/pact_config/base.rb +24 -0
  105. data/lib/pact/v2/consumer/pact_config/grpc.rb +26 -0
  106. data/lib/pact/v2/consumer/pact_config/http.rb +55 -0
  107. data/lib/pact/v2/consumer/pact_config/message.rb +17 -0
  108. data/lib/pact/v2/consumer/pact_config.rb +24 -0
  109. data/lib/pact/v2/consumer.rb +8 -0
  110. data/lib/pact/v2/matchers/base.rb +67 -0
  111. data/lib/pact/v2/matchers/v1/equality.rb +19 -0
  112. data/lib/pact/v2/matchers/v2/regex.rb +19 -0
  113. data/lib/pact/v2/matchers/v2/type.rb +17 -0
  114. data/lib/pact/v2/matchers/v3/boolean.rb +17 -0
  115. data/lib/pact/v2/matchers/v3/date.rb +18 -0
  116. data/lib/pact/v2/matchers/v3/date_time.rb +18 -0
  117. data/lib/pact/v2/matchers/v3/decimal.rb +17 -0
  118. data/lib/pact/v2/matchers/v3/each.rb +42 -0
  119. data/lib/pact/v2/matchers/v3/include.rb +17 -0
  120. data/lib/pact/v2/matchers/v3/integer.rb +17 -0
  121. data/lib/pact/v2/matchers/v3/number.rb +17 -0
  122. data/lib/pact/v2/matchers/v3/time.rb +18 -0
  123. data/lib/pact/v2/matchers/v4/each_key.rb +26 -0
  124. data/lib/pact/v2/matchers/v4/each_key_value.rb +32 -0
  125. data/lib/pact/v2/matchers/v4/each_value.rb +33 -0
  126. data/lib/pact/v2/matchers/v4/not_empty.rb +17 -0
  127. data/lib/pact/v2/matchers.rb +94 -0
  128. data/lib/pact/v2/native/blocking_verifier.rb +17 -0
  129. data/lib/pact/v2/native/logger.rb +25 -0
  130. data/lib/pact/v2/provider/async_message_verifier.rb +28 -0
  131. data/lib/pact/v2/provider/base_verifier.rb +242 -0
  132. data/lib/pact/v2/provider/grpc_verifier.rb +38 -0
  133. data/lib/pact/v2/provider/gruf_server.rb +75 -0
  134. data/lib/pact/v2/provider/http_server.rb +79 -0
  135. data/lib/pact/v2/provider/http_verifier.rb +43 -0
  136. data/lib/pact/v2/provider/message_provider_servlet.rb +79 -0
  137. data/lib/pact/v2/provider/mixed_verifier.rb +22 -0
  138. data/lib/pact/v2/provider/pact_broker_proxy.rb +71 -0
  139. data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +77 -0
  140. data/lib/pact/v2/provider/pact_config/async.rb +29 -0
  141. data/lib/pact/v2/provider/pact_config/base.rb +101 -0
  142. data/lib/pact/v2/provider/pact_config/grpc.rb +26 -0
  143. data/lib/pact/v2/provider/pact_config/http.rb +27 -0
  144. data/lib/pact/v2/provider/pact_config/mixed.rb +39 -0
  145. data/lib/pact/v2/provider/pact_config.rb +26 -0
  146. data/lib/pact/v2/provider/provider_server_runner.rb +89 -0
  147. data/lib/pact/v2/provider/provider_state_configuration.rb +32 -0
  148. data/lib/pact/v2/provider/provider_state_servlet.rb +86 -0
  149. data/lib/pact/v2/provider.rb +8 -0
  150. data/lib/pact/v2/railtie.rb +13 -0
  151. data/lib/pact/v2/rspec/support/pact_consumer_helpers.rb +80 -0
  152. data/lib/pact/v2/rspec/support/pact_message_helpers.rb +42 -0
  153. data/lib/pact/v2/rspec/support/pact_provider_helpers.rb +129 -0
  154. data/lib/pact/v2/rspec/support/waterdrop/pact_waterdrop_client.rb +27 -0
  155. data/lib/pact/v2/rspec/support/webmock/webmock_helpers.rb +30 -0
  156. data/lib/pact/v2/rspec.rb +17 -0
  157. data/lib/pact/v2/tasks/pact.rake +13 -0
  158. data/lib/pact/v2/version.rb +8 -0
  159. data/lib/pact/v2.rb +71 -0
  160. data/lib/pact/version.rb +4 -0
  161. data/lib/pact.rb +13 -0
  162. data/lib/tasks/pact.rake +34 -0
  163. data/pact.gemspec +106 -0
  164. 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
@@ -0,0 +1,3 @@
1
+ require 'pact/configuration'
2
+ require 'pact/provider/configuration'
3
+ require 'pact/provider/world'
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,2 @@
1
+ load File.expand_path('../tasks/pact.rake', File.dirname(__FILE__))
2
+ require 'pact/tasks/verification_task'
@@ -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 %>