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,71 @@
1
+ require_relative 'pact_helper'
2
+ require 'zoo_app/animal_service_client'
3
+
4
+ module ZooApp
5
+ describe AnimalServiceClient, :pact => true do
6
+
7
+ before do
8
+ AnimalServiceClient.base_uri animal_service.mock_service_base_url
9
+ end
10
+
11
+ describe ".find_alligator_by_name" do
12
+ context "when an alligator by the given name exists" do
13
+
14
+ before do
15
+ animal_service.given("there is an alligator named Mary").
16
+ upon_receiving("a request for an alligator").with(
17
+ method: :get,
18
+ path: '/alligators/Mary',
19
+ headers: {'Accept' => 'application/json'} ).
20
+ will_respond_with(
21
+ status: 200,
22
+ headers: {'Content-Type' => 'application/json;charset=utf-8'},
23
+ body: {name: 'Mary'}
24
+ )
25
+ end
26
+
27
+ it "returns the alligator" do
28
+ expect(AnimalServiceClient.find_alligator_by_name("Mary")).to eq ZooApp::Animals::Alligator.new(name: 'Mary')
29
+ end
30
+
31
+ end
32
+
33
+ context "when an alligator by the given name does not exist" do
34
+
35
+ before do
36
+ animal_service.given("there is not an alligator named Mary").
37
+ upon_receiving("a request for an alligator").with(
38
+ method: :get,
39
+ path: '/alligators/Mary',
40
+ headers: {'Accept' => 'application/json'} ).
41
+ will_respond_with(status: 404)
42
+ end
43
+
44
+ it "returns nil" do
45
+ expect(AnimalServiceClient.find_alligator_by_name("Mary")).to be_nil
46
+ end
47
+
48
+ end
49
+
50
+ context "when an error occurs retrieving the alligator" do
51
+
52
+ before do
53
+ animal_service.given("an error occurs retrieving an alligator").
54
+ upon_receiving("a request for an alligator").with(
55
+ method: :get,
56
+ path: '/alligators/Mary',
57
+ headers: {'Accept' => 'application/json'}).
58
+ will_respond_with(
59
+ status: 500,
60
+ headers: { 'Content-Type' => 'application/json;charset=utf-8'},
61
+ body: {error: 'Argh!!!'})
62
+ end
63
+
64
+ it "raises an error" do
65
+ expect{ AnimalServiceClient.find_alligator_by_name("Mary") }.to raise_error /Argh/
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,15 @@
1
+ require_relative '../spec_helper'
2
+ require 'pact/consumer/rspec'
3
+
4
+ Pact.configure do | config |
5
+ config.doc_generator = :markdown
6
+ end
7
+
8
+ Pact.service_consumer 'Zoo App' do
9
+ has_pact_with "Animal Service" do
10
+ mock_service :animal_service do
11
+ port 1234
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,6 @@
1
+ $: << File.expand_path("../../lib", __FILE__)
2
+
3
+ RSpec.configure do | config |
4
+ config.color = true
5
+ config.formatter = :documentation
6
+ end
data/lib/pact/cli.rb ADDED
@@ -0,0 +1,138 @@
1
+ require 'find_a_port'
2
+ require 'thor'
3
+ require 'thwait'
4
+ require 'pact/consumer'
5
+ require 'rack/handler/webrick'
6
+
7
+ module Pact
8
+ class CLI < Thor
9
+
10
+ desc 'verify', "Verify a pact"
11
+ method_option :pact_helper, aliases: "-h", desc: "Pact helper file", :required => true
12
+ method_option :pact_uri, aliases: "-p", desc: "Pact URI"
13
+
14
+ def verify
15
+ RunPactVerification.call(options)
16
+ end
17
+
18
+ desc 'service', "Start a mock service"
19
+ method_option :port, aliases: "-p", desc: "Port on which to run the service"
20
+ method_option :log, aliases: "-l", desc: "File to which to log output"
21
+ method_option :quiet, aliases: "-q", desc: "If true, no admin messages will be shown"
22
+
23
+ def service
24
+ RunStandaloneMockService.call(options)
25
+ end
26
+
27
+ private
28
+
29
+ def log message
30
+ puts message unless options[:quiet]
31
+ end
32
+ end
33
+
34
+ class RunPactVerification
35
+
36
+ attr_reader :options
37
+
38
+ def initialize options
39
+ @options = options
40
+ end
41
+
42
+ def self.call options
43
+ new(options).call
44
+ end
45
+
46
+
47
+ def call
48
+ initialize_rspec
49
+ setup_load_path
50
+ load_pact_helper
51
+ run_specs
52
+ end
53
+
54
+ private
55
+
56
+ def initialize_rspec
57
+ # With RSpec3, if the pact_helper loads a library that adds its own formatter before we set one,
58
+ # we will get a ProgressFormatter too, and get little dots sprinkled throughout our output.
59
+ require 'pact/rspec'
60
+ ::RSpec.configuration.add_formatter Pact::RSpec.formatter_class
61
+ end
62
+
63
+ def setup_load_path
64
+ require 'pact/provider/pact_spec_runner'
65
+ lib = File.join(Dir.pwd, "lib") # Assume we are running from within the project root. RSpec is smarter about this.
66
+ spec = File.join(Dir.pwd, "spec")
67
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
68
+ $LOAD_PATH.unshift(spec) if Dir.exist?(spec) && !$LOAD_PATH.include?(spec)
69
+ end
70
+
71
+ def load_pact_helper
72
+ load options[:pact_helper]
73
+ end
74
+
75
+ def run_specs
76
+ exit_code = if options[:pact_uri]
77
+ run_with_pact_uri
78
+ else
79
+ run_with_configured_pacts
80
+ end
81
+ exit 1 unless exit_code == 0
82
+ end
83
+
84
+ def run_with_pact_uri
85
+ Pact::Provider::PactSpecRunner.new([{uri: options[:pact_uri]}], pact_spec_options).run
86
+ end
87
+
88
+ def run_with_configured_pacts
89
+ pact_verifications = Pact.configuration.pact_verifications
90
+ verification_configs = pact_verifications.collect { | pact_verification | { :uri => pact_verification.uri }}
91
+ raise "Please configure a pact to verify" if verification_configs.empty?
92
+ Pact::Provider::PactSpecRunner.new(verification_configs, options).run
93
+ end
94
+
95
+ def pact_spec_options
96
+ {criteria: SpecCriteria.call}
97
+ end
98
+
99
+ end
100
+
101
+ class RunStandaloneMockService
102
+
103
+ def self.call options
104
+ service_options = {}
105
+ if options[:log]
106
+ log = File.open(options[:log], 'w')
107
+ log.sync = true
108
+ service_options[:log_file] = log
109
+ end
110
+
111
+ port = options[:port] || FindAPort.available_port
112
+ mock_service = Consumer::MockService.new(service_options)
113
+ trap(:INT) { Rack::Handler::WEBrick.shutdown }
114
+ Rack::Handler::WEBrick.run(mock_service, :Port => port, :AccessLog => [])
115
+ end
116
+ end
117
+
118
+ class SpecCriteria
119
+
120
+ def self.call
121
+ criteria = {}
122
+
123
+ description = ENV["PACT_DESCRIPTION"]
124
+ criteria[:description] = Regexp.new(description) if description
125
+
126
+ provider_state = ENV["PACT_PROVIDER_STATE"]
127
+ if provider_state
128
+ if provider_state.length == 0
129
+ criteria[:provider_state] = nil #Allow PACT_PROVIDER_STATE="" to mean no provider state
130
+ else
131
+ criteria[:provider_state] = Regexp.new(provider_state)
132
+ end
133
+ end
134
+
135
+ criteria
136
+ end
137
+ end
138
+ end
@@ -1,22 +1,186 @@
1
1
  require 'ostruct'
2
2
  require 'logger'
3
+ require 'pact/doc/markdown/generator'
4
+ require 'pact/matchers/unix_diff_formatter'
5
+ require 'pact/matchers/embedded_diff_formatter'
6
+ require 'pact/matchers/list_diff_formatter'
7
+ require 'pact/shared/json_differ'
8
+ require 'pact/shared/text_differ'
3
9
 
4
10
  module Pact
5
11
 
6
12
  class Configuration
13
+
14
+ DOC_GENERATORS = { markdown: Pact::Doc::Markdown::Generator }
15
+
16
+ DIFF_FORMATTERS = {
17
+ :embedded => Pact::Matchers::EmbeddedDiffFormatter,
18
+ :unix => Pact::Matchers::UnixDiffFormatter,
19
+ :list => Pact::Matchers::ListDiffFormatter
20
+ }
21
+
22
+
23
+ class NilMatcher
24
+ def self.=~ other
25
+ other == nil ? 0 : nil
26
+ end
27
+ end
28
+
29
+ DIFF_FORMATTER_REGISTRATIONS = [
30
+ [/.*/, Pact::Matchers::UnixDiffFormatter],
31
+ [NilMatcher, Pact::Matchers::UnixDiffFormatter]
32
+ ]
33
+
34
+ DIFFERS = [
35
+ [/json/, Pact::JsonDiffer],
36
+ [NilMatcher, Pact::TextDiffer],
37
+ [/.*/, Pact::TextDiffer]
38
+ ]
39
+
40
+
41
+ DEFAULT_DIFFER = Pact::TextDiffer
42
+
7
43
  attr_accessor :pact_dir
8
44
  attr_accessor :log_dir
9
- attr_accessor :logger
45
+ attr_accessor :doc_dir
46
+ attr_accessor :reports_dir
47
+ attr_writer :logger
10
48
  attr_accessor :tmp_dir
11
- attr_accessor :pactfile_write_mode
49
+ attr_writer :pactfile_write_mode
50
+
51
+ attr_accessor :error_stream
52
+ attr_accessor :output_stream
53
+
54
+ def self.default_configuration
55
+ c = Configuration.new
56
+ c.pact_dir = File.expand_path('./spec/pacts')
57
+ c.tmp_dir = File.expand_path('./tmp/pacts')
58
+ c.log_dir = default_log_dir
59
+ c.pactfile_write_mode = :overwrite
60
+ c.reports_dir = File.expand_path('./reports/pacts')
61
+ c.doc_dir = File.expand_path("./doc/pacts")
62
+ c.output_stream = $stdout
63
+ c.error_stream = $stderr
64
+ c
65
+ end
66
+
67
+ def initialize
68
+ @differ_registrations = []
69
+ @diff_formatter_registrations = []
70
+ end
71
+
72
+ def logger
73
+ @logger ||= create_logger
74
+ end
75
+
76
+ def doc_generator= doc_generator
77
+ doc_generators << begin
78
+ if DOC_GENERATORS[doc_generator]
79
+ DOC_GENERATORS[doc_generator]
80
+ elsif doc_generator.respond_to?(:call)
81
+ doc_generator
82
+ else
83
+ raise "Pact.configuration.doc_generator needs to respond to call, or be in the preconfigured list: #{DOC_GENERATORS.keys}"
84
+ end
85
+ end
86
+ end
87
+
88
+ def doc_generators
89
+ @doc_generators ||= []
90
+ end
91
+
92
+ # Should this be deprecated in favour of register_diff_formatter???
93
+ def diff_formatter= diff_formatter
94
+ register_diff_formatter /.*/, diff_formatter
95
+ register_diff_formatter nil, diff_formatter
96
+ end
97
+
98
+ def register_diff_formatter content_type, diff_formatter
99
+ key = content_type_regexp_for content_type
100
+ @diff_formatter_registrations << [key, diff_formatter_for(diff_formatter)]
101
+ end
102
+
103
+ def diff_formatter_for_content_type content_type
104
+ diff_formatter_registrations.find{ | registration | registration.first =~ content_type }.last
105
+ end
106
+
107
+ def register_body_differ content_type, differ
108
+ key = content_type_regexp_for content_type
109
+ validate_differ differ
110
+ @differ_registrations << [key, differ]
111
+ end
112
+
113
+ def body_differ_for_content_type content_type
114
+ differ_registrations.find{ | registration | registration.first =~ content_type }.last
115
+ end
12
116
 
13
117
  def log_path
14
- log_dir + "/pact_gem.log"
118
+ log_dir + "/pact.log"
119
+ end
120
+
121
+ def pactfile_write_mode
122
+ if @pactfile_write_mode == :smart
123
+ is_rake_running? ? :overwrite : :update
124
+ else
125
+ @pactfile_write_mode
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def diff_formatter_for input
132
+ if DIFF_FORMATTERS[input]
133
+ DIFF_FORMATTERS[input]
134
+ elsif input.respond_to?(:call)
135
+ input
136
+ else
137
+ raise "Pact diff_formatter needs to respond to call, or be in the preconfigured list: #{DIFF_FORMATTERS.keys}"
138
+ end
139
+ end
140
+
141
+ def validate_differ differ
142
+ if !differ.respond_to?(:call)
143
+ raise "Pact.configuration.register_body_differ expects a differ that is a lamda or a class/object that responds to call."
144
+ end
145
+ end
146
+
147
+ def content_type_regexp_for content_type
148
+ case content_type
149
+ when String then Regexp.new(/^#{Regexp.escape(content_type)}$/)
150
+ when Regexp then content_type
151
+ when nil then NilMatcher
152
+ else
153
+ raise "Invalid content type used to register a differ (#{content_type.inspect}). Please use a Regexp or a String."
154
+ end
155
+ end
156
+
157
+ def differ_registrations
158
+ @differ_registrations + DIFFERS
159
+ end
160
+
161
+ def diff_formatter_registrations
162
+ @diff_formatter_registrations + DIFF_FORMATTER_REGISTRATIONS
163
+ end
164
+
165
+ def self.default_log_dir
166
+ File.expand_path("./log")
167
+ end
168
+
169
+ #Would love a better way of determining this! It sure won't work on windows.
170
+ def is_rake_running?
171
+ `ps -ef | grep rake | grep #{Process.ppid} | grep -v 'grep'`.size > 0
172
+ end
173
+
174
+ def create_logger
175
+ FileUtils::mkdir_p log_dir
176
+ logger = Logger.new(log_path)
177
+ logger.level = Logger::DEBUG
178
+ logger
15
179
  end
16
180
  end
17
181
 
18
182
  def self.configuration
19
- @configuration ||= default_configuration
183
+ @configuration ||= Configuration.default_configuration
20
184
  end
21
185
 
22
186
  def self.configure
@@ -25,30 +189,7 @@ module Pact
25
189
  end
26
190
 
27
191
  def self.clear_configuration
28
- @configuration = default_configuration
29
- end
30
-
31
- private
32
-
33
- def self.default_configuration
34
- c = Configuration.new
35
- c.pact_dir = File.expand_path('./spec/pacts')
36
- c.tmp_dir = File.expand_path('./tmp/pacts')
37
- c.log_dir = default_log_dir
38
- c.logger = default_logger c.log_path
39
- c.pactfile_write_mode = defined?(Rake) ? :overwrite : :update
40
- c
41
- end
42
-
43
- def self.default_log_dir
44
- File.expand_path("./log")
45
- end
46
-
47
- def self.default_logger path
48
- FileUtils::mkdir_p File.dirname(path)
49
- logger = Logger.new(path)
50
- logger.level = Logger::INFO
51
- logger
192
+ @configuration = nil
52
193
  end
53
194
 
54
195
  end
@@ -1,10 +1,11 @@
1
1
  require 'thwait'
2
- require 'capybara'
3
2
 
4
3
  require 'net/http'
5
4
  require 'uri'
6
5
  require 'find_a_port'
7
6
  require 'pact/logging'
7
+ require 'pact/consumer/server'
8
+ require 'singleton'
8
9
 
9
10
  module Pact
10
11
  module Consumer
@@ -82,7 +83,7 @@ module Pact
82
83
  lower_case_name.gsub('_service', '_mock_service')
83
84
  else
84
85
  lower_case_name + '_mock_service'
85
- end
86
+ end
86
87
  end
87
88
 
88
89
  def app_registrations
@@ -110,9 +111,11 @@ module Pact
110
111
  end
111
112
 
112
113
  def kill
113
- logger.info "Killing #{self}"
114
- Process.kill(9, pid)
115
- Process.wait(pid)
114
+ # TODO: need to work out how to kill
115
+ # logger.info "Killing #{self}"
116
+ # Process.kill(9, pid)
117
+ # Process.wait(pid)
118
+ # self.pid = nil
116
119
  self.pid = nil
117
120
  end
118
121
 
@@ -133,31 +136,9 @@ module Pact
133
136
  end
134
137
 
135
138
  def spawn
136
- # following stolen from https://github.com/jwilger/kookaburra
137
139
  logger.info "Starting app #{self}..."
138
- self.pid = fork do
139
- begin
140
- Capybara.server_port = port
141
- Capybara::Server.new(app).boot
142
-
143
- # This ensures that this forked process keeps running, because the
144
- # actual server is started in a thread by Capybara.
145
- ThreadsWait.all_waits(Thread.list)
146
- rescue Exception => e
147
- logger.error "Error starting up #{self}"
148
- $stderr.puts "Error starting up #{self}"
149
- raise e
150
- end
151
- end
152
-
153
-
154
- wait_until do
155
- begin
156
- Net::HTTP.get_response(URI.parse("http://localhost:#{port}/index.html"))
157
- rescue Errno::ECONNREFUSED
158
- false
159
- end
160
- end
140
+ Pact::Server.new(app, port).boot
141
+ self.pid = 'unknown'
161
142
  logger.info "Started with pid #{pid}"
162
143
  end
163
144
 
@@ -0,0 +1,15 @@
1
+ module Pact
2
+ module Consumer
3
+ module Configuration
4
+
5
+ module ConfigurationExtensions
6
+ def add_provider_verification &block
7
+ provider_verifications << block
8
+ end
9
+ def provider_verifications
10
+ @provider_verifications ||= []
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ require 'pact/consumer/configuration/service_consumer'
2
+
3
+ module Pact
4
+
5
+ module Consumer
6
+ module DSL
7
+ def service_consumer name, &block
8
+ Configuration::ServiceConsumer.build(name, &block)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,91 @@
1
+ require 'pact/consumer/app_manager'
2
+ require 'pact/consumer/consumer_contract_builder'
3
+ require 'pact/consumer/consumer_contract_builders'
4
+ require 'pact/consumer/world'
5
+
6
+ module Pact
7
+ module Consumer
8
+ module Configuration
9
+ class MockService
10
+
11
+ extend Pact::DSL
12
+
13
+ attr_accessor :port, :standalone, :verify, :provider_name, :consumer_name
14
+
15
+ def initialize name, consumer_name, provider_name
16
+ @name = name
17
+ @consumer_name = consumer_name
18
+ @provider_name = provider_name
19
+ @port = nil
20
+ @standalone = false
21
+ @verify = true
22
+ end
23
+
24
+ dsl do
25
+ def port port
26
+ self.port = port
27
+ end
28
+
29
+ def standalone standalone
30
+ self.standalone = standalone
31
+ end
32
+
33
+ def verify verify
34
+ self.verify = verify
35
+ end
36
+ end
37
+
38
+ def finalize
39
+ validate
40
+ register_mock_service
41
+ configure_consumer_contract_builder
42
+ end
43
+
44
+ private
45
+
46
+ def register_mock_service
47
+ unless standalone
48
+ AppManager.instance.register_mock_service_for provider_name, "http://localhost:#{port}"
49
+ end
50
+ end
51
+
52
+ def configure_consumer_contract_builder
53
+ consumer_contract_builder = create_consumer_contract_builder
54
+ create_consumer_contract_builders_method consumer_contract_builder
55
+ setup_verification(consumer_contract_builder) if verify
56
+ consumer_contract_builder
57
+ end
58
+
59
+ def create_consumer_contract_builder
60
+ consumer_contract_builder_fields = {
61
+ :consumer_name => consumer_name,
62
+ :provider_name => provider_name,
63
+ :pactfile_write_mode => Pact.configuration.pactfile_write_mode,
64
+ :port => port
65
+ }
66
+ Pact::Consumer::ConsumerContractBuilder.new consumer_contract_builder_fields
67
+ end
68
+
69
+ def setup_verification consumer_contract_builder
70
+ Pact.configuration.add_provider_verification do | example_description |
71
+ consumer_contract_builder.verify example_description
72
+ end
73
+ end
74
+
75
+ # This makes the consumer_contract_builder available via a module method with the given identifier
76
+ # as the method name.
77
+ # I feel this should be defined somewhere else, but I'm not sure where
78
+ def create_consumer_contract_builders_method consumer_contract_builder
79
+ Pact::Consumer::ConsumerContractBuilders.send(:define_method, @name.to_sym) do
80
+ consumer_contract_builder
81
+ end
82
+ Pact.consumer_world.add_consumer_contract_builder consumer_contract_builder
83
+ end
84
+
85
+ def validate
86
+ raise "Please provide a port for service #{name}" unless port
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,51 @@
1
+ require 'pact/shared/dsl'
2
+
3
+ module Pact
4
+ module Consumer
5
+ module Configuration
6
+ class ServiceConsumer
7
+
8
+ extend Pact::DSL
9
+
10
+ attr_accessor :app, :port, :name
11
+
12
+ def initialize name
13
+ @name = name
14
+ @app = nil
15
+ @port = nil
16
+ end
17
+
18
+ dsl do
19
+ def app app
20
+ self.app = app
21
+ end
22
+
23
+ def port port
24
+ self.port = port
25
+ end
26
+
27
+ def has_pact_with service_provider_name, &block
28
+ ServiceProvider.build(service_provider_name, name, &block)
29
+ end
30
+ end
31
+
32
+ def finalize
33
+ validate
34
+ register_consumer_app if @app
35
+ end
36
+
37
+ private
38
+
39
+ def validate
40
+ raise "Please provide a consumer name" unless (name && !name.empty?)
41
+ raise "Please provide a port for the consumer app" if app && !port
42
+ end
43
+
44
+
45
+ def register_consumer_app
46
+ Pact::Consumer::AppManager.instance.register app, port
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end