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,181 @@
1
+ require 'open-uri'
2
+ require 'pact/consumer_contract'
3
+ require 'pact/provider/rspec/matchers'
4
+ require 'pact/provider/test_methods'
5
+ require 'pact/provider/configuration'
6
+
7
+ module Pact
8
+ module Provider
9
+ module RSpec
10
+
11
+ module InstanceMethods
12
+ def app
13
+ Pact.configuration.provider.app
14
+ end
15
+ end
16
+
17
+ module ClassMethods
18
+
19
+ include ::RSpec::Core::DSL
20
+
21
+ def honour_pactfile pactfile_uri, options = {}
22
+ #TODO change puts to use output stream
23
+ puts "Reading pact at #{pactfile_uri}"
24
+ puts "Filtering interactions by: #{options[:criteria]}" if options[:criteria] && options[:criteria].any?
25
+ consumer_contract = Pact::ConsumerContract.from_json(read_pact_from(pactfile_uri, options))
26
+ ::RSpec.describe "Verifying a pact between #{consumer_contract.consumer.name} and #{consumer_contract.provider.name}", :pactfile_uri => pactfile_uri do
27
+ honour_consumer_contract consumer_contract, options
28
+ end
29
+ end
30
+
31
+ def honour_consumer_contract consumer_contract, options = {}
32
+ describe_consumer_contract consumer_contract, options.merge({:consumer => consumer_contract.consumer.name})
33
+ end
34
+
35
+ private
36
+
37
+ def describe_consumer_contract consumer_contract, options
38
+ consumer_interactions(consumer_contract, options).each do |interaction|
39
+ describe_interaction_with_provider_state interaction, options
40
+ end
41
+ end
42
+
43
+ def consumer_interactions(consumer_contract, options)
44
+ if options[:criteria].nil?
45
+ consumer_contract.interactions
46
+ else
47
+ consumer_contract.find_interactions options[:criteria]
48
+ end
49
+ end
50
+
51
+ def describe_interaction_with_provider_state interaction, options
52
+ if interaction.provider_state
53
+ describe "Given #{interaction.provider_state}" do
54
+ describe_interaction interaction, options
55
+ end
56
+ else
57
+ describe_interaction interaction, options
58
+ end
59
+ end
60
+
61
+ def describe_interaction interaction, options
62
+
63
+ metadata = {
64
+ :pact => :verify,
65
+ :pact_interaction => interaction,
66
+ :pact_interaction_example_description => interaction_description_for_rerun_command(interaction)
67
+ }
68
+
69
+ describe description_for(interaction), metadata do
70
+
71
+ describe "with #{interaction.request.method_and_path}" do
72
+
73
+ interaction_context = InteractionContext.new
74
+
75
+ before do | example |
76
+ interaction_context.run_once :before do
77
+ Pact.configuration.logger.info "Running example '#{Pact::RSpec.full_description(example)}'"
78
+ set_up_provider_state interaction.provider_state, options[:consumer]
79
+ replay_interaction interaction
80
+ interaction_context.last_response = last_response
81
+ end
82
+ end
83
+
84
+ after do
85
+ interaction_context.run_once :after do
86
+ tear_down_provider_state interaction.provider_state, options[:consumer]
87
+ end
88
+ end
89
+
90
+ describe_response Pact::Term.unpack_regexps(interaction.response), interaction_context
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+
98
+ def describe_response expected_response, interaction_context
99
+
100
+ describe "returns a response which" do
101
+
102
+ include Pact::RSpec::Matchers
103
+
104
+ let(:expected_response_status) { expected_response['status'] }
105
+ let(:expected_response_body) { expected_response['body'] }
106
+ let(:response) { interaction_context.last_response }
107
+ let(:response_status) { response.status }
108
+ let(:response_body) { parse_body_from_response(response) }
109
+ let(:differ) { Pact.configuration.body_differ_for_content_type diff_content_type }
110
+ let(:diff_formatter) { Pact.configuration.diff_formatter_for_content_type diff_content_type }
111
+ let(:expected_content_type) { Pact::Headers.new(expected_response['headers'] || {})['Content-Type'] }
112
+ let(:actual_content_type) { response.headers['Content-Type']}
113
+ let(:diff_content_type) { String === expected_content_type ? expected_content_type : actual_content_type } # expected_content_type may be a Regexp
114
+ let(:diff_options) { { with: differ, diff_formatter: diff_formatter } }
115
+
116
+ if expected_response['status']
117
+ it "has status code #{expected_response['status']}" do
118
+ expect(response_status).to eql expected_response_status
119
+ end
120
+ end
121
+
122
+ if expected_response['headers']
123
+ describe "includes headers" do
124
+ expected_response['headers'].each do |name, expected_header_value|
125
+ it "\"#{name}\" with value #{expected_header_value.inspect}" do
126
+ header_value = response.headers[name]
127
+ expect(header_value).to match_header(name, expected_header_value)
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ if expected_response['body']
134
+ it "has a matching body" do
135
+ expect(response_body).to match_term expected_response_body, diff_options
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ def description_for interaction
142
+ interaction.provider_state ? interaction.description : interaction.description.capitalize
143
+ end
144
+
145
+ def interaction_description_for_rerun_command interaction
146
+ description_for(interaction).capitalize + ( interaction.provider_state ? " given #{interaction.provider_state}" : "")
147
+ end
148
+
149
+ def read_pact_from uri, options = {}
150
+ Pact::PactFile.read(uri, options)
151
+ end
152
+
153
+ end
154
+
155
+ # The "arrange" and "act" parts of the test really only need to be run once,
156
+ # however, stubbing is not supported in before :all, so this is a
157
+ # wee hack to enable before :all like functionality using before :each.
158
+ # In an ideal world, the test setup and execution should be quick enough for
159
+ # the difference between :all and :each to be unnoticable, but the annoying
160
+ # reality is, sometimes it does make a difference. This is for you, V!
161
+
162
+ class InteractionContext
163
+
164
+ attr_accessor :last_response
165
+
166
+ def initialize
167
+ @already_run = []
168
+ end
169
+
170
+ def run_once hook
171
+ unless @already_run.include?(hook)
172
+ yield
173
+ @already_run << hook
174
+ end
175
+ end
176
+
177
+ end
178
+ end
179
+ end
180
+ end
181
+
@@ -0,0 +1,181 @@
1
+ require 'pact/shared/dsl'
2
+ require 'pact/provider/state/provider_state_configured_modules'
3
+
4
+ module Pact
5
+ module Provider::State
6
+
7
+ BASE_PROVIDER_STATE_NAME = "__base_provider_state__"
8
+
9
+ module DSL
10
+ def provider_state name, &block
11
+ ProviderStates.provider_state(name, &block).register
12
+ end
13
+
14
+ def set_up &block
15
+ ProviderStates.base_provider_state.register.register_set_up &block
16
+ end
17
+
18
+ def tear_down &block
19
+ ProviderStates.base_provider_state.register_tear_down &block
20
+ end
21
+
22
+ def provider_states_for name, &block
23
+ ProviderStates.current_namespaces << name
24
+ instance_eval(&block)
25
+ ProviderStates.current_namespaces.pop
26
+ end
27
+ end
28
+
29
+ class ProviderStates
30
+ def self.provider_state name, &block
31
+ ProviderState.build(name, current_namespaces.join('.'), &block)
32
+ end
33
+
34
+ def self.base_provider_state
35
+ fullname = namespaced_name BASE_PROVIDER_STATE_NAME, {:for => current_namespaces.first }
36
+ provider_states[fullname] ||
37
+ ProviderState.new(BASE_PROVIDER_STATE_NAME, current_namespaces.join('.'))
38
+ end
39
+
40
+ def self.register name, provider_state
41
+ provider_states[name] = provider_state
42
+ end
43
+
44
+ def self.provider_states
45
+ @@provider_states ||= {}
46
+ end
47
+
48
+ def self.current_namespaces
49
+ @@current_namespaces ||= []
50
+ end
51
+
52
+ def self.get name, options = {}
53
+ fullname = namespaced_name name, options
54
+ (provider_states[fullname] || provider_states[fullname.to_sym] || provider_states[name])
55
+ end
56
+
57
+ def self.get_base opts = {}
58
+ fullname = namespaced_name BASE_PROVIDER_STATE_NAME, opts
59
+ provider_states[fullname] || NoOpProviderState
60
+ end
61
+
62
+ def self.namespaced_name name, options = {}
63
+ fullname = options[:for] ? "#{options[:for]}.#{name}" : name
64
+ end
65
+ end
66
+
67
+ class ProviderState
68
+
69
+ attr_accessor :name
70
+ attr_accessor :namespace
71
+
72
+ extend Pact::DSL
73
+
74
+ def initialize name, namespace, &block
75
+ @name = name
76
+ @namespace = namespace
77
+ @set_up_defined = false
78
+ @tear_down_defined = false
79
+ @no_op_defined = false
80
+ end
81
+
82
+ dsl do
83
+ def set_up &block
84
+ self.register_set_up &block
85
+ end
86
+
87
+ def tear_down &block
88
+ self.register_tear_down &block
89
+ end
90
+
91
+ def no_op
92
+ self.register_no_op
93
+ end
94
+ end
95
+
96
+ def register
97
+ ProviderStates.register(namespaced(name), self)
98
+ self
99
+ end
100
+
101
+ def finalize
102
+ validate
103
+ end
104
+
105
+ def register_set_up &block
106
+ @set_up_block = block
107
+ @set_up_defined = true
108
+ end
109
+
110
+ def register_tear_down &block
111
+ @tear_down_block = block
112
+ @tear_down_defined = true
113
+ end
114
+
115
+ def register_no_op
116
+ @no_op_defined = true
117
+ end
118
+
119
+ def set_up
120
+ if @set_up_block
121
+ include_provider_state_configured_modules
122
+ instance_eval &@set_up_block
123
+ end
124
+ end
125
+
126
+ def tear_down
127
+ if @tear_down_block
128
+ include_provider_state_configured_modules
129
+ instance_eval &@tear_down_block
130
+ end
131
+ end
132
+
133
+ private
134
+
135
+ attr_accessor :no_op_defined, :set_up_defined, :tear_down_defined
136
+
137
+ def validate
138
+ if no_op_defined && set_up_defined
139
+ raise error_message_for_extra_block 'set_up'
140
+ elsif no_op_defined && tear_down_defined
141
+ raise error_message_for_extra_block 'tear_down'
142
+ elsif !(no_op_defined || set_up_defined || tear_down_defined)
143
+ raise "Please provide a set_up or tear_down block for provider state \"#{name}\". If there is no data to set up or tear down, you can use \"no_op\" instead."
144
+ end
145
+ end
146
+
147
+ def error_message_for_extra_block block_name
148
+ "Provider state \"#{name}\" has been defined as a no_op but it also has a #{block_name} block. Please remove one or the other."
149
+ end
150
+
151
+ def namespaced(name)
152
+ if namespace.empty?
153
+ name
154
+ else
155
+ "#{namespace}.#{name}"
156
+ end
157
+ end
158
+
159
+ def include_provider_state_configured_modules
160
+ # Doing this at runtime means the order of the Pact configuration block
161
+ # and the provider state declarations doesn't matter.
162
+ # Using include ProviderStateConfiguredModules on the class doesn't seem to work -
163
+ # modules dynamically added to ProviderStateConfiguredModules don't seem to be
164
+ # included in the including class.
165
+ self.extend(ProviderStateConfiguredModules) unless self.singleton_class.ancestors.include?(ProviderStateConfiguredModules)
166
+ end
167
+ end
168
+
169
+ class NoOpProviderState
170
+
171
+ def self.set_up
172
+
173
+ end
174
+
175
+ def self.tear_down
176
+
177
+ end
178
+
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,13 @@
1
+ require 'rspec/mocks'
2
+
3
+ module Pact
4
+ module Provider::State
5
+ module ProviderStateConfiguredModules
6
+
7
+ include ::RSpec::Mocks::ExampleMethods
8
+
9
+ # Placeholder for modules configured using config.include
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,42 @@
1
+ module Pact
2
+ module Provider::State
3
+ class ProviderStateManager
4
+
5
+ attr_reader :provider_state_name, :consumer
6
+
7
+ def initialize provider_state_name, consumer
8
+ @provider_state_name = provider_state_name
9
+ @consumer = consumer
10
+ end
11
+
12
+ def set_up_provider_state
13
+ get_global_base_provider_state.set_up
14
+ get_consumer_base_provider_state.set_up
15
+ if provider_state_name
16
+ get_provider_state.set_up
17
+ end
18
+ end
19
+
20
+ def tear_down_provider_state
21
+ if provider_state_name
22
+ get_provider_state.tear_down
23
+ end
24
+ get_consumer_base_provider_state.tear_down
25
+ get_global_base_provider_state.tear_down
26
+ end
27
+
28
+ def get_provider_state
29
+ Pact.provider_world.provider_states.get(provider_state_name, :for => consumer)
30
+ end
31
+
32
+ def get_consumer_base_provider_state
33
+ Pact.provider_world.provider_states.get_base(:for => consumer)
34
+ end
35
+
36
+ def get_global_base_provider_state
37
+ Pact.provider_world.provider_states.get_base
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ module Pact
2
+ module Provider::State
3
+ class ProviderStateProxy
4
+
5
+ attr_reader :missing_provider_states
6
+
7
+ def initialize
8
+ @missing_provider_states = {}
9
+ end
10
+
11
+ def get name, options = {}
12
+ unless provider_state = ProviderStates.get(name, options)
13
+ register_missing_provider_state name, options[:for]
14
+ raise error_message name, options[:for]
15
+ end
16
+ provider_state
17
+ end
18
+
19
+ def get_base options = {}
20
+ ProviderStates.get_base options
21
+ end
22
+
23
+ private
24
+
25
+ def error_message name, consumer
26
+ "Could not find provider state \"#{name}\" for consumer #{consumer}"
27
+ end
28
+
29
+ def register_missing_provider_state name, consumer
30
+ missing_states_for(consumer) << name unless missing_states_for(consumer).include?(name)
31
+ end
32
+
33
+ def missing_states_for consumer
34
+ @missing_provider_states[consumer] ||= []
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,47 @@
1
+ require 'pact/logging'
2
+ require 'rack/test'
3
+ require 'pact/consumer_contract/interaction'
4
+ require 'pact/provider/state/provider_state'
5
+ require 'pact/provider/state/provider_state_proxy'
6
+ require 'pact/provider/request'
7
+ require 'pact/provider/world'
8
+ require 'pact/provider/state/provider_state_manager'
9
+
10
+ module Pact
11
+ module Provider
12
+ module TestMethods
13
+
14
+ include Pact::Logging
15
+ include Rack::Test::Methods
16
+
17
+ def replay_interaction interaction
18
+ request = Request::Replayable.new(interaction.request)
19
+ args = [request.path, request.body, request.headers]
20
+
21
+ logger.info "Sending #{request.method.upcase} request to path: \"#{request.path}\" with headers: #{request.headers}, see debug logs for body"
22
+ logger.debug "body :#{request.body}"
23
+ response = self.send(request.method.downcase, *args)
24
+ logger.info "Received response with status: #{response.status}, headers: #{response.headers}, see debug logs for body"
25
+ logger.debug "body: #{response.body}"
26
+ end
27
+
28
+ def parse_body_from_response rack_response
29
+ case rack_response.headers['Content-Type']
30
+ when /json/
31
+ JSON.load(rack_response.body)
32
+ else
33
+ rack_response.body
34
+ end
35
+ end
36
+
37
+ def set_up_provider_state provider_state_name, consumer
38
+ State::ProviderStateManager.new(provider_state_name, consumer).set_up_provider_state
39
+ end
40
+
41
+ def tear_down_provider_state provider_state_name, consumer
42
+ State::ProviderStateManager.new(provider_state_name, consumer).tear_down_provider_state
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ require 'pact/consumer_contract'
2
+
3
+ module Pact::Provider
4
+ class VerificationReport
5
+
6
+ include Pact::FileName
7
+
8
+ def initialize (options)
9
+ @consumer = options[:consumer]
10
+ @provider = options[:provider]
11
+ @result = options[:result]
12
+ @output = options[:output]
13
+ end
14
+
15
+ def to_hash
16
+ {
17
+ :consumer => @consumer,
18
+ :provider => @provider,
19
+ :result => @result,
20
+ :output => @output
21
+ }
22
+ end
23
+
24
+ def as_json options = {}
25
+ to_hash
26
+ end
27
+
28
+ def to_json(options = {})
29
+ as_json.to_json(options)
30
+ end
31
+
32
+ def report_file_name
33
+ file_name("#{@consumer[:name]}_#{@consumer[:ref]}", "#{@provider[:name]}_#{@provider[:ref]}")
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,23 @@
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
+ def provider_states
18
+ @provider_states_proxy ||= Pact::Provider::State::ProviderStateProxy.new
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,3 @@
1
+ require 'pact/configuration'
2
+ require 'pact/provider/configuration'
3
+ require 'pact/provider/world'
@@ -4,19 +4,21 @@ module Pact
4
4
  module Reification
5
5
 
6
6
  def self.from_term(term)
7
- case
8
- when term.respond_to?(:generate)
7
+ case term
8
+ when Pact::Term, Regexp, Pact::SomethingLike
9
9
  term.generate
10
- when term.is_a?(Hash)
10
+ when Hash
11
11
  term.inject({}) do |mem, (key,term)|
12
12
  mem[key] = from_term(term)
13
13
  mem
14
14
  end
15
- when term.is_a?(Array)
15
+ when Array
16
16
  term.inject([]) do |mem, term|
17
17
  mem << from_term(term)
18
18
  mem
19
19
  end
20
+ when Pact::Request::Base
21
+ from_term(term.to_hash)
20
22
  else
21
23
  term
22
24
  end
data/lib/pact/rspec.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'rspec'
2
+ # This is horrible, must work out a better way of doing this
3
+ module Pact
4
+ module RSpec
5
+
6
+ def self.color_enabled?
7
+ if ::RSpec.configuration.respond_to?(:color_enabled?)
8
+ ::RSpec.configuration.color_enabled?(::RSpec.configuration.output_stream)
9
+ else
10
+ ::RSpec.configuration.color_enabled?
11
+ end
12
+ end
13
+
14
+ def self.formatter_class
15
+ if ::RSpec::Core::Formatters.respond_to?(:register)
16
+ require 'pact/provider/rspec/formatter_rspec_3'
17
+ Pact::Provider::RSpec::Formatter
18
+ else
19
+ require 'pact/provider/rspec/formatter_rspec_2'
20
+ Pact::Provider::RSpec::Formatter2
21
+ end
22
+
23
+ end
24
+
25
+ def self.full_description example
26
+ example.respond_to?(:full_description) ? example.full_description : example.example.full_description
27
+ end
28
+
29
+ def self.runner_defined?
30
+ defined?(::RSpec::Core::Runner)
31
+ end
32
+
33
+ def self.is_rspec_3
34
+ ::RSpec::Core::Formatters.respond_to?(:register)
35
+ end
36
+
37
+ def self.is_rspec_2
38
+ !is_rspec_3
39
+ end
40
+
41
+ def self.with_rspec_3
42
+ if is_rspec_3
43
+ yield
44
+ end
45
+ end
46
+
47
+ def self.with_rspec_2
48
+ if is_rspec_2
49
+ yield
50
+ end
51
+ end
52
+ end
53
+ end