pact 0.1.28 → 1.3.3

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 (285) hide show
  1. data/.gitignore +2 -1
  2. data/.ruby-version +1 -0
  3. data/.travis.yml +8 -0
  4. data/CHANGELOG.md +374 -0
  5. data/Gemfile +0 -1
  6. data/Gemfile.lock +58 -55
  7. data/README.md +214 -139
  8. data/Rakefile +4 -28
  9. data/bin/pact +2 -2
  10. data/config.ru +3 -0
  11. data/documentation/README.md +13 -0
  12. data/documentation/configuration.md +166 -0
  13. data/documentation/diff_formatter_embedded.png +0 -0
  14. data/documentation/diff_formatter_list.png +0 -0
  15. data/documentation/diff_formatter_unix.png +0 -0
  16. data/example/animal-service/Gemfile +14 -0
  17. data/example/animal-service/Gemfile.lock +69 -0
  18. data/example/animal-service/Rakefile +5 -0
  19. data/example/animal-service/config.ru +3 -0
  20. data/example/animal-service/db/animal_db.sqlite3 +0 -0
  21. data/example/animal-service/lib/animal_service/animal_repository.rb +12 -0
  22. data/example/animal-service/lib/animal_service/api.rb +28 -0
  23. data/example/animal-service/lib/animal_service/db.rb +5 -0
  24. data/example/animal-service/spec/service_consumers/pact_helper.rb +11 -0
  25. data/example/animal-service/spec/service_consumers/provider_states_for_zoo_app.rb +26 -0
  26. data/example/zoo-app/Gemfile +12 -0
  27. data/example/zoo-app/Gemfile.lock +63 -0
  28. data/example/zoo-app/Rakefile +5 -0
  29. data/example/zoo-app/doc/pacts/markdown/README.md +3 -0
  30. data/example/zoo-app/doc/pacts/markdown/Zoo App - Animal Service.md +75 -0
  31. data/example/zoo-app/lib/zoo_app/animal_service_client.rb +40 -0
  32. data/example/zoo-app/lib/zoo_app/models/alligator.rb +15 -0
  33. data/example/zoo-app/spec/pacts/zoo_app-animal_service.json +67 -0
  34. data/example/zoo-app/spec/service_providers/animal_service_client_spec.rb +71 -0
  35. data/example/zoo-app/spec/service_providers/pact_helper.rb +15 -0
  36. data/example/zoo-app/spec/spec_helper.rb +6 -0
  37. data/lib/pact/cli.rb +138 -0
  38. data/lib/pact/configuration.rb +169 -28
  39. data/lib/pact/consumer/app_manager.rb +10 -29
  40. data/lib/pact/consumer/configuration/configuration_extensions.rb +15 -0
  41. data/lib/pact/consumer/configuration/dsl.rb +12 -0
  42. data/lib/pact/consumer/configuration/mock_service.rb +91 -0
  43. data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
  44. data/lib/pact/consumer/configuration/service_provider.rb +40 -0
  45. data/lib/pact/consumer/configuration.rb +11 -0
  46. data/lib/pact/consumer/consumer_contract_builder.rb +44 -47
  47. data/lib/pact/consumer/interaction_builder.rb +41 -0
  48. data/lib/pact/consumer/interactions_filter.rb +48 -0
  49. data/lib/pact/consumer/mock_service/app.rb +82 -0
  50. data/lib/pact/consumer/mock_service/interaction_delete.rb +33 -0
  51. data/lib/pact/consumer/mock_service/interaction_list.rb +76 -0
  52. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +73 -0
  53. data/lib/pact/consumer/mock_service/interaction_post.rb +31 -0
  54. data/lib/pact/consumer/mock_service/interaction_replay.rb +139 -0
  55. data/lib/pact/consumer/mock_service/log_get.rb +28 -0
  56. data/lib/pact/consumer/mock_service/missing_interactions_get.rb +30 -0
  57. data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +31 -0
  58. data/lib/pact/consumer/mock_service/pact_post.rb +33 -0
  59. data/lib/pact/consumer/mock_service/rack_request_helper.rb +51 -0
  60. data/lib/pact/consumer/mock_service/verification_get.rb +68 -0
  61. data/lib/pact/consumer/mock_service.rb +1 -339
  62. data/lib/pact/consumer/mock_service_client.rb +65 -0
  63. data/lib/pact/consumer/mock_service_interaction_expectation.rb +37 -0
  64. data/lib/pact/consumer/request.rb +27 -0
  65. data/lib/pact/consumer/rspec.rb +9 -19
  66. data/lib/pact/consumer/server.rb +90 -0
  67. data/lib/pact/consumer/spec_hooks.rb +38 -0
  68. data/lib/pact/consumer/world.rb +37 -0
  69. data/lib/pact/consumer.rb +10 -7
  70. data/lib/pact/consumer_contract/consumer_contract.rb +115 -0
  71. data/lib/pact/consumer_contract/consumer_contract_writer.rb +84 -0
  72. data/lib/pact/consumer_contract/file_name.rb +19 -0
  73. data/lib/pact/consumer_contract/headers.rb +51 -0
  74. data/lib/pact/consumer_contract/interaction.rb +67 -0
  75. data/lib/pact/consumer_contract/pact_file.rb +24 -0
  76. data/lib/pact/consumer_contract/request.rb +73 -0
  77. data/lib/pact/consumer_contract/service_consumer.rb +28 -0
  78. data/lib/pact/consumer_contract/service_provider.rb +28 -0
  79. data/lib/pact/consumer_contract.rb +1 -110
  80. data/lib/pact/doc/doc_file.rb +40 -0
  81. data/lib/pact/doc/generate.rb +11 -0
  82. data/lib/pact/doc/generator.rb +82 -0
  83. data/lib/pact/doc/interaction_view_model.rb +114 -0
  84. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +56 -0
  85. data/lib/pact/doc/markdown/generator.rb +26 -0
  86. data/lib/pact/doc/markdown/index_renderer.rb +41 -0
  87. data/lib/pact/doc/markdown/interaction.erb +14 -0
  88. data/lib/pact/doc/markdown/interaction_renderer.rb +38 -0
  89. data/lib/pact/doc/sort_interactions.rb +17 -0
  90. data/lib/pact/matchers/actual_type.rb +16 -0
  91. data/lib/pact/matchers/base_difference.rb +37 -0
  92. data/lib/pact/matchers/differ.rb +153 -0
  93. data/lib/pact/matchers/difference.rb +13 -0
  94. data/lib/pact/matchers/difference_indicator.rb +26 -0
  95. data/lib/pact/matchers/embedded_diff_formatter.rb +62 -0
  96. data/lib/pact/matchers/expected_type.rb +35 -0
  97. data/lib/pact/matchers/index_not_found.rb +15 -0
  98. data/lib/pact/matchers/list_diff_formatter.rb +101 -0
  99. data/lib/pact/matchers/matchers.rb +91 -37
  100. data/lib/pact/matchers/no_diff_indicator.rb +18 -0
  101. data/lib/pact/matchers/regexp_difference.rb +13 -0
  102. data/lib/pact/matchers/type_difference.rb +16 -0
  103. data/lib/pact/matchers/unexpected_index.rb +11 -0
  104. data/lib/pact/matchers/unexpected_key.rb +11 -0
  105. data/lib/pact/matchers/unix_diff_formatter.rb +114 -0
  106. data/lib/pact/project_root.rb +7 -0
  107. data/lib/pact/provider/configuration/configuration_extension.rb +58 -0
  108. data/lib/pact/provider/configuration/dsl.rb +13 -0
  109. data/lib/pact/provider/configuration/pact_verification.rb +46 -0
  110. data/lib/pact/provider/configuration/service_provider_config.rb +16 -0
  111. data/lib/pact/provider/configuration/service_provider_dsl.rb +59 -0
  112. data/lib/pact/provider/configuration.rb +7 -0
  113. data/lib/pact/provider/context.rb +0 -0
  114. data/lib/pact/provider/matchers/messages.rb +45 -0
  115. data/lib/pact/provider/pact_helper_locator.rb +22 -0
  116. data/lib/pact/provider/pact_spec_runner.rb +123 -0
  117. data/lib/pact/provider/pact_verification.rb +17 -0
  118. data/lib/pact/provider/print_missing_provider_states.rb +30 -0
  119. data/lib/pact/provider/request.rb +70 -0
  120. data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
  121. data/lib/pact/provider/rspec/custom_options_file +0 -0
  122. data/lib/pact/provider/rspec/formatter_rspec_2.rb +65 -0
  123. data/lib/pact/provider/rspec/formatter_rspec_3.rb +96 -0
  124. data/lib/pact/provider/rspec/matchers.rb +97 -0
  125. data/lib/pact/provider/rspec.rb +181 -0
  126. data/lib/pact/provider/state/provider_state.rb +181 -0
  127. data/lib/pact/provider/state/provider_state_configured_modules.rb +13 -0
  128. data/lib/pact/provider/state/provider_state_manager.rb +42 -0
  129. data/lib/pact/provider/state/provider_state_proxy.rb +39 -0
  130. data/lib/pact/provider/test_methods.rb +47 -0
  131. data/lib/pact/provider/verification_report.rb +36 -0
  132. data/lib/pact/provider/world.rb +23 -0
  133. data/lib/pact/provider.rb +3 -0
  134. data/lib/pact/reification.rb +6 -4
  135. data/lib/pact/rspec.rb +53 -0
  136. data/lib/pact/shared/active_support_support.rb +51 -0
  137. data/lib/pact/shared/dsl.rb +76 -0
  138. data/lib/pact/shared/jruby_support.rb +18 -0
  139. data/lib/pact/shared/json_differ.rb +15 -0
  140. data/lib/pact/shared/key_not_found.rb +15 -0
  141. data/lib/pact/shared/null_expectation.rb +31 -0
  142. data/lib/pact/shared/request.rb +80 -0
  143. data/lib/pact/shared/text_differ.rb +14 -0
  144. data/lib/pact/something_like.rb +49 -0
  145. data/lib/pact/symbolize_keys.rb +12 -0
  146. data/lib/pact/tasks/task_helper.rb +36 -0
  147. data/lib/pact/tasks/verification_task.rb +87 -0
  148. data/lib/pact/tasks.rb +2 -0
  149. data/lib/pact/templates/provider_state.erb +14 -0
  150. data/lib/pact/term.rb +47 -2
  151. data/lib/pact/version.rb +1 -1
  152. data/lib/pact.rb +1 -1
  153. data/lib/tasks/pact.rake +25 -2
  154. data/pact.gemspec +13 -11
  155. data/{scratchpad.txt → scratchpad.rb} +18 -2
  156. data/spec/features/consumption_spec.rb +24 -56
  157. data/spec/features/production_spec.rb +19 -24
  158. data/spec/features/{producer_states → provider_states}/zebras.rb +3 -3
  159. data/spec/integration/consumer_spec.rb +212 -0
  160. data/spec/integration/pact/consumer_configuration_spec.rb +66 -0
  161. data/spec/integration/pact/provider_configuration_spec.rb +25 -0
  162. data/spec/lib/pact/cli_spec.rb +47 -0
  163. data/spec/lib/pact/configuration_spec.rb +269 -7
  164. data/spec/lib/pact/consumer/app_manager_spec.rb +3 -3
  165. data/spec/lib/pact/consumer/configuration_spec.rb +57 -0
  166. data/spec/lib/pact/consumer/consumer_contract_builder_spec.rb +52 -70
  167. data/spec/lib/pact/consumer/interaction_builder_spec.rb +91 -0
  168. data/spec/lib/pact/consumer/interactions_spec.rb +64 -0
  169. data/spec/lib/pact/consumer/mock_service/app_spec.rb +52 -0
  170. data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +78 -0
  171. data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +70 -0
  172. data/spec/lib/pact/consumer/mock_service/interaction_replay_spec.rb +12 -0
  173. data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +88 -0
  174. data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +142 -0
  175. data/spec/lib/pact/consumer/mock_service_client_spec.rb +88 -0
  176. data/spec/lib/pact/consumer/mock_service_interaction_expectation_spec.rb +54 -0
  177. data/spec/lib/pact/consumer/request_spec.rb +24 -0
  178. data/spec/lib/pact/consumer/service_consumer_spec.rb +1 -1
  179. data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +58 -0
  180. data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +180 -0
  181. data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +111 -0
  182. data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
  183. data/spec/lib/pact/consumer_contract/interaction_spec.rb +107 -0
  184. data/spec/lib/pact/{request_spec.rb → consumer_contract/request_spec.rb} +80 -67
  185. data/spec/lib/pact/doc/generator_spec.rb +84 -0
  186. data/spec/lib/pact/doc/interaction_view_model_spec.rb +132 -0
  187. data/spec/lib/pact/doc/markdown/consumer_contract_renderer_spec.rb +29 -0
  188. data/spec/lib/pact/doc/markdown/index_renderer_spec.rb +29 -0
  189. data/spec/lib/pact/matchers/differ_spec.rb +214 -0
  190. data/spec/lib/pact/matchers/difference_spec.rb +22 -0
  191. data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +90 -0
  192. data/spec/lib/pact/matchers/index_not_found_spec.rb +21 -0
  193. data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +114 -0
  194. data/spec/lib/pact/matchers/matchers_spec.rb +400 -254
  195. data/spec/lib/pact/matchers/regexp_difference_spec.rb +20 -0
  196. data/spec/lib/pact/matchers/type_difference_spec.rb +34 -0
  197. data/spec/lib/pact/matchers/unexpected_index_spec.rb +20 -0
  198. data/spec/lib/pact/matchers/unexpected_key_spec.rb +20 -0
  199. data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +216 -0
  200. data/spec/lib/pact/provider/configuration/configuration_extension_spec.rb +30 -0
  201. data/spec/lib/pact/provider/configuration/pact_verification_spec.rb +43 -0
  202. data/spec/lib/pact/provider/configuration/service_provider_config_spec.rb +21 -0
  203. data/spec/lib/pact/provider/configuration/service_provider_dsl_spec.rb +108 -0
  204. data/spec/lib/pact/provider/configuration_spec.rb +50 -0
  205. data/spec/lib/pact/provider/matchers/messages_spec.rb +116 -0
  206. data/spec/lib/pact/provider/pact_helper_locator_spec.rb +54 -0
  207. data/spec/lib/pact/provider/print_missing_provider_states_spec.rb +19 -0
  208. data/spec/lib/pact/provider/request_spec.rb +78 -0
  209. data/spec/lib/pact/provider/rspec/formatter_rspec_2_spec.rb +68 -0
  210. data/spec/lib/pact/provider/rspec/formatter_rspec_3_spec.rb +72 -0
  211. data/spec/lib/pact/provider/rspec_spec.rb +55 -0
  212. data/spec/lib/pact/provider/state/provider_state_manager_spec.rb +89 -0
  213. data/spec/lib/pact/provider/state/provider_state_proxy_spec.rb +80 -0
  214. data/spec/lib/pact/provider/state/provider_state_spec.rb +213 -0
  215. data/spec/lib/pact/provider/world_spec.rb +41 -0
  216. data/spec/lib/pact/reification_spec.rb +24 -0
  217. data/spec/lib/pact/shared/dsl_spec.rb +86 -0
  218. data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
  219. data/spec/lib/pact/shared/key_not_found_spec.rb +20 -0
  220. data/spec/lib/pact/shared/request_spec.rb +111 -0
  221. data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
  222. data/spec/lib/pact/something_like_spec.rb +21 -0
  223. data/spec/lib/pact/tasks/task_helper_spec.rb +74 -0
  224. data/spec/lib/pact/tasks/verification_task_spec.rb +75 -0
  225. data/spec/lib/pact/term_spec.rb +57 -4
  226. data/spec/pact_specification/compliance-1.0.0.rb +47 -0
  227. data/spec/spec_helper.rb +17 -0
  228. data/spec/standalone/consumer_fail_test.rb +55 -0
  229. data/spec/standalone/consumer_pass_test.rb +51 -0
  230. data/spec/support/a_consumer-a_producer.json +2 -4
  231. data/spec/support/a_consumer-a_provider.json +32 -0
  232. data/spec/support/active_support_if_configured.rb +6 -0
  233. data/spec/support/app_for_config_ru.rb +4 -0
  234. data/spec/support/consumer_contract_template.json +24 -0
  235. data/spec/support/dsl_spec_support.rb +7 -0
  236. data/spec/support/factories.rb +82 -0
  237. data/spec/support/generated_index.md +4 -0
  238. data/spec/support/generated_markdown.md +55 -0
  239. data/spec/support/interaction_view_model.json +63 -0
  240. data/spec/support/interaction_view_model_with_terms.json +50 -0
  241. data/spec/support/markdown_pact.json +48 -0
  242. data/spec/support/missing_provider_states_output.txt +25 -0
  243. data/spec/support/options.json +21 -0
  244. data/spec/support/options_app.rb +15 -0
  245. data/spec/support/pact_helper.rb +57 -0
  246. data/spec/support/shared_examples_for_request.rb +94 -0
  247. data/spec/support/spec_support.rb +20 -0
  248. data/spec/support/stubbing.json +22 -0
  249. data/spec/support/stubbing_using_allow.rb +29 -0
  250. data/spec/support/term.json +48 -0
  251. data/spec/support/test_app_fail.json +41 -2
  252. data/spec/support/test_app_pass.json +19 -2
  253. data/spec/support/test_app_with_right_content_type_differ.json +23 -0
  254. data/tasks/pact-test.rake +98 -8
  255. data/tasks/spec.rake +8 -0
  256. metadata +393 -95
  257. data/lib/pact/app.rb +0 -32
  258. data/lib/pact/consumer/configuration_dsl.rb +0 -71
  259. data/lib/pact/consumer/dsl.rb +0 -98
  260. data/lib/pact/consumer/interaction.rb +0 -70
  261. data/lib/pact/consumer/run_condor.rb +0 -4
  262. data/lib/pact/consumer/run_mock_contract_service.rb +0 -13
  263. data/lib/pact/consumer/service_consumer.rb +0 -22
  264. data/lib/pact/consumer/service_producer.rb +0 -23
  265. data/lib/pact/json_warning.rb +0 -23
  266. data/lib/pact/producer/configuration_dsl.rb +0 -62
  267. data/lib/pact/producer/matchers.rb +0 -22
  268. data/lib/pact/producer/pact_spec_runner.rb +0 -57
  269. data/lib/pact/producer/producer_state.rb +0 -81
  270. data/lib/pact/producer/rspec.rb +0 -127
  271. data/lib/pact/producer/test_methods.rb +0 -89
  272. data/lib/pact/producer.rb +0 -1
  273. data/lib/pact/rake_task.rb +0 -64
  274. data/lib/pact/request.rb +0 -109
  275. data/lib/pact/verification_task.rb +0 -57
  276. data/spec/integration/pact/configuration_spec.rb +0 -65
  277. data/spec/lib/pact/consumer/dsl_spec.rb +0 -52
  278. data/spec/lib/pact/consumer/interaction_spec.rb +0 -108
  279. data/spec/lib/pact/consumer/mock_service_spec.rb +0 -147
  280. data/spec/lib/pact/consumer_contract_spec.rb +0 -125
  281. data/spec/lib/pact/producer/configuration_dsl_spec.rb +0 -101
  282. data/spec/lib/pact/producer/producer_state_spec.rb +0 -103
  283. data/spec/lib/pact/producer/rspec_spec.rb +0 -48
  284. data/spec/lib/pact/verification_task_spec.rb +0 -64
  285. data/spec/support/pact_rake_support.rb +0 -41
@@ -0,0 +1,59 @@
1
+ require 'pact/provider/configuration/pact_verification'
2
+ require 'pact/provider/configuration/service_provider_config'
3
+
4
+ module Pact
5
+
6
+ module Provider
7
+
8
+ module Configuration
9
+
10
+ class ServiceProviderDSL
11
+
12
+ extend Pact::DSL
13
+
14
+ attr_accessor :name, :app_block
15
+
16
+ CONFIG_RU_APP = lambda {
17
+ unless File.exist? Pact.configuration.config_ru_path
18
+ raise "Could not find config.ru file at #{Pact.configuration.config_ru_path} Please configure the service provider app or create a config.ru file in the root directory of the project. See https://github.com/realestate-com-au/pact/blob/master/documentation/verifying-pacts.md for more information."
19
+ end
20
+ Rack::Builder.parse_file(Pact.configuration.config_ru_path).first
21
+ }
22
+
23
+ def initialize name
24
+ @name = name
25
+ @app_block = CONFIG_RU_APP
26
+ end
27
+
28
+ dsl do
29
+ def app &block
30
+ self.app_block = block
31
+ end
32
+
33
+ def honours_pact_with consumer_name, options = {}, &block
34
+ create_pact_verification consumer_name, options, &block
35
+ end
36
+ end
37
+
38
+ def create_pact_verification consumer_name, options, &block
39
+ PactVerification.build(consumer_name, options, &block)
40
+ end
41
+
42
+ def finalize
43
+ validate
44
+ create_service_provider
45
+ end
46
+
47
+ private
48
+
49
+ def validate
50
+ raise "Please provide a name for the Provider" unless name && !name.strip.empty?
51
+ end
52
+
53
+ def create_service_provider
54
+ Pact.configuration.provider = ServiceProviderConfig.new(&@app_block)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,7 @@
1
+ require 'pact/provider/configuration/dsl'
2
+ require 'pact/provider/configuration/configuration_extension'
3
+ require 'pact/provider/state/provider_state'
4
+
5
+ Pact.send(:extend, Pact::Provider::DSL)
6
+ Pact.send(:extend, Pact::Provider::State::DSL)
7
+ Pact::Configuration.send(:include, Pact::Provider::Configuration::ConfigurationExtension)
File without changes
@@ -0,0 +1,45 @@
1
+ require 'term/ansicolor'
2
+ require 'pact/term'
3
+
4
+ module Pact
5
+ module Matchers
6
+ module Messages
7
+
8
+ def match_term_failure_message diff, actual, diff_formatter, color_enabled
9
+ message = "Actual: #{(String === actual ? actual : actual.to_json)}\n\n"
10
+ formatted_diff = diff_formatter.call(diff)
11
+ message + colorize_if_enabled(formatted_diff, color_enabled)
12
+ end
13
+
14
+ def match_header_failure_message header_name, expected, actual
15
+ "Expected header \"#{header_name}\" to #{expected_desc(expected)}, but was #{actual_desc(actual)}"
16
+ end
17
+
18
+ private
19
+
20
+ def colorize_if_enabled formatted_diff, color_enabled
21
+ if color_enabled
22
+ # RSpec wraps each line in the failure message with failure_color, turning it red.
23
+ # To ensure the lines in the diff that should be white, stay white, put an
24
+ # ANSI reset at the start of each line.
25
+ formatted_diff.split("\n").collect{ |line| ::Term::ANSIColor.reset + line }.join("\n")
26
+ else
27
+ formatted_diff
28
+ end
29
+ end
30
+
31
+ def expected_desc expected
32
+ case expected
33
+ when NilClass then "be nil"
34
+ else
35
+ "match #{expected.inspect}"
36
+ end
37
+ end
38
+
39
+ def actual_desc actual
40
+ actual.nil? ? 'nil' : '"' + actual + '"'
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module Pact
2
+ module Provider
3
+ module PactHelperLocater
4
+ PACT_HELPER_FILE_PATTERNS = [
5
+ "spec/**/*service*consumer*/pact_helper.rb",
6
+ "spec/**/*consumer*/pact_helper.rb",
7
+ "spec/**/pact_helper.rb",
8
+ "**/pact_helper.rb"]
9
+
10
+ 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(", ")}"
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
18
+
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,123 @@
1
+ require 'open-uri'
2
+ require 'rspec'
3
+ require 'rspec/core'
4
+ require 'rspec/core/formatters/documentation_formatter'
5
+ require 'pact/provider/pact_helper_locator'
6
+ require 'pact/project_root'
7
+ require 'pact/rspec'
8
+
9
+ require_relative 'rspec'
10
+
11
+
12
+ module Pact
13
+ module Provider
14
+ class PactSpecRunner
15
+
16
+ include Pact::Provider::RSpec::ClassMethods
17
+
18
+ attr_reader :spec_definitions
19
+ attr_reader :options
20
+ attr_reader :output
21
+
22
+ def initialize spec_definitions, options = {}
23
+ @spec_definitions = spec_definitions
24
+ @options = options
25
+ @results = nil
26
+ end
27
+
28
+ def run
29
+ begin
30
+ configure_rspec
31
+ initialize_specs
32
+ run_specs
33
+ ensure
34
+ ::RSpec.reset
35
+ Pact.clear_provider_world
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def initialize_specs
42
+ spec_definitions.each do | spec_definition |
43
+ options = {
44
+ consumer: spec_definition[:consumer],
45
+ save_pactfile_to_tmp: true,
46
+ criteria: @options[:criteria]
47
+ }
48
+ honour_pactfile spec_definition[:uri], options
49
+ end
50
+ end
51
+
52
+ def configure_rspec
53
+ monkey_patch_backtrace_formatter
54
+
55
+ config = ::RSpec.configuration
56
+
57
+ config.color = true
58
+ config.pattern = "pattern which doesn't match any files"
59
+ config.backtrace_inclusion_patterns = [Regexp.new(Dir.getwd), /pact.*main/]
60
+
61
+ config.extend Pact::Provider::RSpec::ClassMethods
62
+ config.include Pact::Provider::RSpec::InstanceMethods
63
+ config.include Pact::Provider::TestMethods
64
+
65
+ if options[:silent]
66
+ config.output_stream = StringIO.new
67
+ config.error_stream = StringIO.new
68
+ else
69
+ config.error_stream = Pact.configuration.error_stream
70
+ config.output_stream = Pact.configuration.output_stream
71
+ end
72
+
73
+ # Sometimes the formatter set in the cli.rb get set with an output of StringIO.. don't know why
74
+ formatter_class = Pact::RSpec.formatter_class
75
+ pact_formatter = ::RSpec.configuration.formatters.find {|f| f.class == formatter_class && f.output == ::RSpec.configuration.output_stream}
76
+ ::RSpec.configuration.add_formatter formatter_class unless pact_formatter
77
+
78
+ config.before(:suite) do
79
+ # Preload app before suite so the classes loaded in memory are consistent for
80
+ # before :each and after :each hooks.
81
+ # Otherwise the app and all its dependencies are loaded between the first before :each
82
+ # and the first after :each, leading to inconsistent behaviour
83
+ # (eg. with database_cleaner transactions)
84
+ Pact.configuration.provider.app
85
+ end
86
+
87
+ end
88
+
89
+ def run_specs
90
+ exit_code = if Pact::RSpec.runner_defined?
91
+ ::RSpec::Core::Runner.run(rspec_runner_options)
92
+ else
93
+ ::RSpec::Core::CommandLine.new(NoConfigurationOptions.new)
94
+ .run(::RSpec.configuration.output_stream, ::RSpec.configuration.error_stream)
95
+ end
96
+ exit_code
97
+ end
98
+
99
+ def rspec_runner_options
100
+ ["--options", Pact.project_root.join("lib/pact/provider/rspec/custom_options_file").to_s]
101
+ end
102
+
103
+ def monkey_patch_backtrace_formatter
104
+ Pact::RSpec.with_rspec_3 do
105
+ require 'pact/provider/rspec/backtrace_formatter'
106
+ end
107
+ end
108
+
109
+ def class_exists? name
110
+ Kernel.const_get name
111
+ rescue NameError
112
+ false
113
+ end
114
+
115
+ class NoConfigurationOptions
116
+ def method_missing(method, *args, &block)
117
+ # Do nothing!
118
+ end
119
+ end
120
+
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,17 @@
1
+ module Pact::Provider
2
+ class PactVerification
3
+ attr_reader :consumer_name, :uri, :ref
4
+ def initialize consumer_name, uri, ref
5
+ @consumer_name = consumer_name
6
+ @uri = uri
7
+ @ref = ref
8
+ end
9
+
10
+ def == other
11
+ other.is_a?(PactVerification) &&
12
+ consumer_name == other.consumer_name &&
13
+ uri == other.uri &&
14
+ ref == other.ref
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,30 @@
1
+ module Pact
2
+ module Provider
3
+ class PrintMissingProviderStates
4
+
5
+ # Hash of consumer names to array of names of missing provider states
6
+ def self.call missing_provider_states, output
7
+ if missing_provider_states.any?
8
+ output.puts orangeify(text(missing_provider_states))
9
+ end
10
+ end
11
+
12
+ def self.orangeify string
13
+ "\e[33m#{string}\e[m"
14
+ end
15
+
16
+ def self.text missing_provider_states
17
+ create_provider_states_for(missing_provider_states)
18
+ end
19
+
20
+ def self.create_provider_states_for consumers
21
+ ERB.new(template_string).result(binding)
22
+ end
23
+
24
+ def self.template_string
25
+ File.read(File.expand_path( '../../templates/provider_state.erb', __FILE__))
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,70 @@
1
+ require 'json'
2
+ require 'pact/reification'
3
+ require 'pact/shared/null_expectation'
4
+
5
+ module Pact
6
+ module Provider
7
+ module Request
8
+ class Replayable
9
+
10
+ # See https://github.com/rack/rack/blob/e7d741c6282ca4cf4e01506f5681e6e6b14c0b32/SPEC#L87-89
11
+ NO_HTTP_PREFIX = ["CONTENT-TYPE", "CONTENT-LENGTH"]
12
+
13
+ def initialize expected_request
14
+ @expected_request = expected_request
15
+ end
16
+
17
+ def method
18
+ expected_request.method
19
+ end
20
+
21
+ def path
22
+ expected_request.full_path
23
+ end
24
+
25
+ def body
26
+ case expected_request.body
27
+ when String then expected_request.body
28
+ when NullExpectation then ''
29
+ else
30
+ reified_body
31
+ end
32
+ end
33
+
34
+ def headers
35
+ request_headers = {}
36
+ return request_headers if expected_request.headers.is_a?(Pact::NullExpectation)
37
+ expected_request.headers.each do |key, value|
38
+ request_headers[rack_request_header_for(key)] = value
39
+ end
40
+ request_headers
41
+ end
42
+
43
+ private
44
+ attr_reader :expected_request
45
+
46
+ def reified_body
47
+ rb = Pact::Reification.from_term(expected_request.body)
48
+ if rb.is_a?(String)
49
+ rb
50
+ else
51
+ JSON.dump(rb)
52
+ end
53
+ end
54
+
55
+ def rack_request_header_for header
56
+ with_http_prefix(header.to_s.upcase).gsub('-', '_')
57
+ end
58
+
59
+ def rack_request_value_for value
60
+ Array(value).join("\n")
61
+ end
62
+
63
+ def with_http_prefix header
64
+ NO_HTTP_PREFIX.include?(header) ? header : "HTTP_#{header}"
65
+ end
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,43 @@
1
+ module RSpec
2
+ module Core
3
+
4
+ # RSpec 3 has a hardwired @system_exclusion_patterns which removes everything matching /bin\//
5
+ # This causes *all* the backtrace lines to be cleaned, as rake pact:verify now shells out
6
+ # to the executable `pact verify ...`
7
+ # which then causes *all* the lines to be included as the BacktraceFormatter will
8
+ # include all lines of the backtrace if all lines were filtered out.
9
+ # This monkey patch only shows lines including bin/pact and removes the
10
+ # "show all lines if no lines would otherwise be shown" logic.
11
+
12
+ class BacktraceFormatter
13
+
14
+
15
+ def format_backtrace(backtrace, options = {})
16
+ return backtrace if options[:full_backtrace]
17
+ backtrace.map { |l| backtrace_line(l) }.compact
18
+ end
19
+
20
+ def backtrace_line(line)
21
+ relative_path(line) unless exclude?(line)
22
+ rescue SecurityError
23
+ nil
24
+ end
25
+
26
+ def exclude?(line)
27
+ return false if @full_backtrace
28
+ relative_line = relative_path(line)
29
+ return true unless /bin\/pact/ =~ relative_line
30
+ end
31
+
32
+ # Copied from Metadata so a refactor can't break this overridden class
33
+ def relative_path(line)
34
+ line = line.sub(File.expand_path("."), ".")
35
+ line = line.sub(/\A([^:]+:\d+)$/, '\\1')
36
+ return nil if line == '-e:1'
37
+ line
38
+ rescue SecurityError
39
+ nil
40
+ end
41
+ end
42
+ end
43
+ end
File without changes
@@ -0,0 +1,65 @@
1
+ require 'pact/provider/print_missing_provider_states'
2
+ require 'rspec/core/formatters/documentation_formatter'
3
+ require 'term/ansicolor'
4
+
5
+ module Pact
6
+ module Provider
7
+ module RSpec
8
+ class Formatter2 < ::RSpec::Core::Formatters::DocumentationFormatter
9
+
10
+ C = ::Term::ANSIColor
11
+
12
+ def dump_commands_to_rerun_failed_examples
13
+ return if failed_examples.empty?
14
+
15
+ print_rerun_commands
16
+ print_failure_message
17
+ print_missing_provider_states
18
+
19
+ end
20
+
21
+ private
22
+
23
+ def print_rerun_commands
24
+ output.puts("\n")
25
+ interaction_rerun_commands.each do | message |
26
+ output.puts(message)
27
+ end
28
+ end
29
+
30
+ def print_missing_provider_states
31
+ PrintMissingProviderStates.call Pact.provider_world.provider_states.missing_provider_states, output
32
+ end
33
+
34
+ def interaction_rerun_commands
35
+ failed_examples.collect do |example|
36
+ interaction_rerun_command_for example
37
+ end.uniq
38
+ end
39
+
40
+ def interaction_rerun_command_for example
41
+ provider_state = example.metadata[:pact_interaction].provider_state
42
+ description = example.metadata[:pact_interaction].description
43
+ pactfile_uri = example.metadata[:pactfile_uri]
44
+ example_description = example.metadata[:pact_interaction_example_description]
45
+ failure_color("rake pact:verify:at[#{pactfile_uri}] PACT_DESCRIPTION=\"#{description}\" PACT_PROVIDER_STATE=\"#{provider_state}\"") + " " + detail_color("# #{example_description}")
46
+ end
47
+
48
+ def print_failure_message
49
+ output.puts failure_message
50
+ end
51
+
52
+ def failure_message
53
+ "\n" + C.underline(C.yellow("For assistance debugging failures, please note:")) + "\n\n" +
54
+ "The pact files have been stored locally in the following temp directory:\n #{Pact.configuration.tmp_dir}\n\n" +
55
+ "The requests and responses are logged in the following log file:\n #{Pact.configuration.log_path}\n\n"
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+
@@ -0,0 +1,96 @@
1
+ require 'pact/provider/print_missing_provider_states'
2
+ require 'rspec/core/formatters'
3
+ require 'term/ansicolor'
4
+
5
+ module Pact
6
+ module Provider
7
+ module RSpec
8
+ class Formatter < ::RSpec::Core::Formatters::DocumentationFormatter
9
+
10
+ Pact::RSpec.with_rspec_3 do
11
+ ::RSpec::Core::Formatters.register self, :example_group_started, :example_group_finished,
12
+ :example_passed, :example_pending, :example_failed
13
+ end
14
+
15
+ C = ::Term::ANSIColor
16
+
17
+ def dump_summary(summary)
18
+ output.puts "\n" + colorized_totals_line(summary)
19
+ return if summary.failure_count == 0
20
+ print_rerun_commands summary
21
+ print_failure_message
22
+ print_missing_provider_states
23
+ end
24
+
25
+ private
26
+
27
+ def interactions_count(summary)
28
+ summary.examples.collect{ |e|e.metadata[:pact_interaction_example_description]}.uniq.size
29
+ end
30
+
31
+ def failed_interactions_count(summary)
32
+ summary.failed_examples.collect{ |e|e.metadata[:pact_interaction_example_description]}.uniq.size
33
+ end
34
+
35
+ def totals_line summary
36
+ line = ::RSpec::Core::Formatters::Helpers.pluralize(interactions_count(summary), "interaction")
37
+ line << ", " << ::RSpec::Core::Formatters::Helpers.pluralize(failed_interactions_count(summary), "failure")
38
+ line
39
+ end
40
+
41
+ def colorized_totals_line(summary)
42
+ if summary.failure_count > 0
43
+ colorizer.wrap(totals_line(summary), ::RSpec.configuration.failure_color)
44
+ else
45
+ colorizer.wrap(totals_line(summary), ::RSpec.configuration.success_color)
46
+ end
47
+ end
48
+
49
+ def print_rerun_commands summary
50
+ output.puts("\nFailed interactions:\n\n")
51
+ interaction_rerun_commands(summary).each do | message |
52
+ output.puts(message)
53
+ end
54
+ end
55
+
56
+ def print_missing_provider_states
57
+ PrintMissingProviderStates.call Pact.provider_world.provider_states.missing_provider_states, output
58
+ end
59
+
60
+ def interaction_rerun_commands summary
61
+ summary.failed_examples.collect do |example|
62
+ interaction_rerun_command_for example
63
+ end.uniq
64
+ end
65
+
66
+ def interaction_rerun_command_for example
67
+ provider_state = example.metadata[:pact_interaction].provider_state
68
+ description = example.metadata[:pact_interaction].description
69
+ pactfile_uri = example.metadata[:pactfile_uri]
70
+ example_description = example.metadata[:pact_interaction_example_description]
71
+ colorizer.wrap("rake pact:verify:at[#{pactfile_uri}] PACT_DESCRIPTION=\"#{description}\" PACT_PROVIDER_STATE=\"#{provider_state}\" ", ::RSpec.configuration.failure_color) +
72
+ colorizer.wrap("# #{example_description}", ::RSpec.configuration.detail_color)
73
+ end
74
+
75
+ def print_failure_message
76
+ output.puts failure_message
77
+ end
78
+
79
+ def failure_message
80
+ "\n" + C.underline(C.yellow("For assistance debugging failures, please note:")) + "\n\n" +
81
+ "The pact files have been stored locally in the following temp directory:\n #{Pact.configuration.tmp_dir}\n\n" +
82
+ "The requests and responses are logged in the following log file:\n #{Pact.configuration.log_path}\n\n"
83
+ end
84
+
85
+ def colorizer
86
+ @colorizer ||= ::RSpec::Core::Formatters::ConsoleCodes
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
95
+
96
+
@@ -0,0 +1,97 @@
1
+ require 'rspec'
2
+ require 'pact/matchers'
3
+ require 'pact/provider/matchers/messages'
4
+ require 'pact/rspec'
5
+ require 'pact/shared/json_differ'
6
+
7
+
8
+ module Pact
9
+ module RSpec
10
+ module Matchers
11
+
12
+ module RSpec2Delegator
13
+ # For backwards compatiblity with rspec-2
14
+ def method_missing(method, *args, &block)
15
+ if method_name == :failure_message_for_should
16
+ failure_message method, *args, &block
17
+ else
18
+ super
19
+ end
20
+ end
21
+ end
22
+
23
+ class MatchTerm
24
+
25
+ include Pact::Matchers::Messages
26
+ include RSpec2Delegator
27
+
28
+ def initialize expected, differ, diff_formatter
29
+ @expected = expected
30
+ @differ = differ
31
+ @diff_formatter = diff_formatter
32
+ end
33
+
34
+ def matches? actual
35
+ @actual = actual
36
+ (@difference = @differ.call(@expected, @actual)).empty?
37
+ end
38
+
39
+ def failure_message
40
+ match_term_failure_message @difference, @actual, @diff_formatter, Pact::RSpec.color_enabled?
41
+ end
42
+
43
+ end
44
+
45
+ def match_term expected, options
46
+ MatchTerm.new(expected, options.fetch(:with), options.fetch(:diff_formatter))
47
+ end
48
+
49
+ class MatchHeader
50
+
51
+ include Pact::Matchers
52
+ include Pact::Matchers::Messages
53
+ include RSpec2Delegator
54
+
55
+ def initialize header_name, expected
56
+ @header_name = header_name
57
+ @expected = expected
58
+ end
59
+
60
+ def matches? actual
61
+ @actual = actual
62
+ diff(@expected, @actual).empty?
63
+ end
64
+
65
+ def failure_message
66
+ match_header_failure_message @header_name, @expected, @actual
67
+ end
68
+
69
+ end
70
+
71
+ def match_header header_name, expected
72
+ MatchHeader.new(header_name, expected)
73
+ end
74
+
75
+ end
76
+ end
77
+ end
78
+
79
+
80
+ # RSpec::Matchers.define :match_header do |header_name, expected|
81
+
82
+ # include Pact::Matchers
83
+ # include Pact::Matchers::Messages
84
+
85
+ # match do |actual|
86
+ # diff(expected, actual).empty?
87
+ # end
88
+
89
+ # def failure_message_for_should(actual)
90
+ # match_header_failure_message header_name, expected, actual
91
+ # end
92
+
93
+ # # failure_message_for_should do | actual |
94
+ # # match_header_failure_message header_name, expected, actual
95
+ # # end
96
+
97
+ # end