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,180 @@
1
+ require 'spec_helper'
2
+ require 'pact/consumer_contract'
3
+
4
+ module Pact
5
+ describe ConsumerContract do
6
+ describe "as_json" do
7
+
8
+ class MockInteraction
9
+ def as_json(options ={})
10
+ {:mock => "interaction"}
11
+ end
12
+ end
13
+
14
+ before do
15
+ allow(DateTime).to receive(:now).and_return(DateTime.strptime("2013-08-15T13:27:13+10:00"))
16
+ end
17
+
18
+ let(:service_consumer) { double('ServiceConsumer', :as_json => {:a => 'consumer'}) }
19
+ let(:service_provider) { double('ServiceProvider', :as_json => {:a => 'provider'}) }
20
+ let(:pact) { ConsumerContract.new({:interactions => [MockInteraction.new], :consumer => service_consumer, :provider => service_provider }) }
21
+ let(:expected_as_json) { {:provider=>{:a=>"provider"}, :consumer=>{:a=>"consumer"}, :interactions=>[{:mock=>"interaction"}], :metadata=>{:pactSpecificationVersion=> "1.0.0" }} }
22
+
23
+ it "should return a hash representation of the Pact" do
24
+ expect(pact.as_json).to eq expected_as_json
25
+ end
26
+
27
+ end
28
+
29
+ describe ".from_json" do
30
+ let(:loaded_pact) { ConsumerContract.from_json(string) }
31
+ context "when the top level object is a ConsumerContract" do
32
+ let(:string) { '{"interactions":[{"request": {"path":"/path", "method" : "get"}}], "consumer": {"name" : "Bob"} , "provider": {"name" : "Mary"} }' }
33
+
34
+ it "should create a Pact" do
35
+ expect(loaded_pact).to be_instance_of ConsumerContract
36
+ end
37
+
38
+ it "should have interactions" do
39
+ expect(loaded_pact.interactions).to be_instance_of Array
40
+ end
41
+
42
+ it "should have a consumer" do
43
+ expect(loaded_pact.consumer).to be_instance_of Pact::ServiceConsumer
44
+ end
45
+
46
+ it "should have a provider" do
47
+ expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider
48
+ end
49
+ end
50
+
51
+ context "with old 'producer' key" do
52
+ let(:string) { File.read('./spec/support/a_consumer-a_producer.json')}
53
+ it "should create a Pact" do
54
+ expect(loaded_pact).to be_instance_of ConsumerContract
55
+ end
56
+
57
+ it "should have interactions" do
58
+ expect(loaded_pact.interactions).to be_instance_of Array
59
+ end
60
+
61
+ it "should have a consumer" do
62
+ expect(loaded_pact.consumer).to be_instance_of Pact::ServiceConsumer
63
+ end
64
+
65
+ it "should have a provider" do
66
+ expect(loaded_pact.provider).to be_instance_of Pact::ServiceProvider
67
+ expect(loaded_pact.provider.name).to eq "an old producer"
68
+ end
69
+
70
+ it "should have a provider_state" do
71
+ expect(loaded_pact.interactions.first.provider_state).to eq 'state one'
72
+ end
73
+ end
74
+ end
75
+
76
+ describe "find_interactions" do
77
+ let(:consumer) { double('Pact::ServiceConsumer', :name => 'Consumer')}
78
+ let(:provider) { double('Pact::ServiceProvider', :name => 'Provider')}
79
+ let(:interaction) { double('Pact::Interaction') }
80
+ subject { ConsumerContract.new(:interactions => [interaction], :consumer => consumer, :provider => provider) }
81
+ let(:criteria) { {:description => /blah/} }
82
+ before do
83
+ expect(interaction).to receive(:matches_criteria?).with(criteria).and_return(matches)
84
+ end
85
+ context "by description" do
86
+ context "when no interactions are found" do
87
+ let(:matches) { false }
88
+ it "returns an empty array" do
89
+ expect(subject.find_interactions(criteria)).to eql []
90
+ end
91
+ end
92
+ context "when interactions are found" do
93
+ let(:matches) { true }
94
+ it "returns an array of the matching interactions" do
95
+ expect(subject.find_interactions(criteria)).to eql [interaction]
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ describe "find_interaction" do
102
+ let(:consumer) { double('Pact::ServiceConsumer', :name => 'Consumer')}
103
+ let(:provider) { double('Pact::ServiceProvider', :name => 'Provider')}
104
+ let(:interaction1) { double('Pact::Interaction') }
105
+ let(:interaction2) { double('Pact::Interaction') }
106
+ let(:criteria) { {:description => /blah/} }
107
+
108
+ before do
109
+ expect(interaction1).to receive(:matches_criteria?).with(criteria).and_return(matches1)
110
+ expect(interaction2).to receive(:matches_criteria?).with(criteria).and_return(matches2)
111
+ end
112
+
113
+ subject { ConsumerContract.new(:interactions => [interaction1, interaction2], :consumer => consumer, :provider => provider) }
114
+ context "by description" do
115
+ context "when a match is found" do
116
+ let(:matches1) { true }
117
+ let(:matches2) { false }
118
+
119
+ it "returns the interaction" do
120
+ expect(subject.find_interaction criteria).to eql interaction1
121
+ end
122
+ end
123
+ context "when more than one match is found" do
124
+ let(:matches1) { true }
125
+ let(:matches2) { true }
126
+ it "raises an error" do
127
+ expect{ subject.find_interaction(criteria) }.to raise_error "Found more than 1 interaction matching {:description=>/blah/} in pact file between Consumer and Provider."
128
+ end
129
+ end
130
+ context "when a match is not found" do
131
+ let(:matches1) { false }
132
+ let(:matches2) { false }
133
+ it "raises an error" do
134
+ expect{ subject.find_interaction(criteria) }.to raise_error "Could not find interaction matching {:description=>/blah/} in pact file between Consumer and Provider."
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ describe "update_pactfile" do
141
+ let(:pacts_dir) { Pathname.new("./tmp/pactfiles") }
142
+ let(:expected_pact_path) { pacts_dir + "test_consumer-test_service.json" }
143
+ let(:expected_pact_string) do <<-eos
144
+ {
145
+ "provider": {
146
+ "name": "test_service"
147
+ },
148
+ "consumer": {
149
+ "name": "test_consumer"
150
+ },
151
+ "interactions": [
152
+ "something"
153
+ ],
154
+ "metadata": {
155
+ "pactSpecificationVersion": "1.0.0"
156
+ }
157
+ }
158
+ eos
159
+ end
160
+ let(:consumer) { Pact::ServiceConsumer.new(:name => 'test_consumer')}
161
+ let(:provider) { Pact::ServiceProvider.new(:name => 'test_service')}
162
+ let(:interactions) { [double("interaction", as_json: "something")]}
163
+ subject { ConsumerContract.new(:consumer => consumer, :provider => provider, :interactions => interactions) }
164
+ before do
165
+ allow(Pact.configuration).to receive(:pact_dir).and_return(Pathname.new("./tmp/pactfiles"))
166
+ FileUtils.rm_rf pacts_dir
167
+ FileUtils.mkdir_p pacts_dir
168
+ subject.update_pactfile
169
+ end
170
+
171
+ it "should write to a file specified by the consumer and provider name" do
172
+ expect(File.exist?(expected_pact_path)).to eq true
173
+ end
174
+
175
+ it "should write the interactions to the file" do
176
+ expect(File.read(expected_pact_path)).to eql expected_pact_string.strip
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+ require 'pact/consumer_contract/consumer_contract_writer'
3
+
4
+ module Pact
5
+
6
+ describe ConsumerContractWriter do
7
+
8
+ let(:support_pact_file) { './spec/support/a_consumer-a_provider.json' }
9
+ let(:consumer_name) { 'a consumer' }
10
+ let(:provider_name) { 'a provider' }
11
+
12
+ before do
13
+ Pact.clear_configuration
14
+ allow(Pact.configuration).to receive(:pact_dir).and_return(File.expand_path(tmp_pact_dir))
15
+ FileUtils.rm_rf tmp_pact_dir
16
+ FileUtils.mkdir_p tmp_pact_dir
17
+ FileUtils.cp support_pact_file, "#{tmp_pact_dir}/a_consumer-a_provider.json"
18
+ end
19
+
20
+ let(:existing_interactions) { ConsumerContract.from_json(File.read(support_pact_file)).interactions }
21
+ let(:new_interactions) { [InteractionFactory.create] }
22
+ let(:tmp_pact_dir) {"./tmp/pacts"}
23
+ let(:logger) { double("logger").as_null_object }
24
+ let(:pactfile_write_mode) {:overwrite}
25
+ let(:consumer_contract_details) {
26
+ {
27
+ consumer: {name: consumer_name},
28
+ provider: {name: provider_name},
29
+ pactfile_write_mode: pactfile_write_mode,
30
+ interactions: new_interactions
31
+ }
32
+ }
33
+
34
+ let(:consumer_contract_writer) { ConsumerContractWriter.new(consumer_contract_details, logger) }
35
+
36
+ describe "consumer_contract" do
37
+
38
+ let(:subject) { consumer_contract_writer.consumer_contract }
39
+
40
+ context "when overwriting pact" do
41
+
42
+ it "it uses only the interactions from the current test run" do
43
+ expect(consumer_contract_writer.consumer_contract.interactions).to eq new_interactions
44
+ end
45
+
46
+ end
47
+
48
+ context "when updating pact" do
49
+
50
+ let(:pactfile_write_mode) {:update}
51
+
52
+ it "merges the interactions from the current test run with the interactions from the existing file" do
53
+ allow_any_instance_of(ConsumerContractWriter).to receive(:info_and_puts)
54
+ expect(consumer_contract_writer.consumer_contract.interactions).to eq existing_interactions + new_interactions
55
+ end
56
+
57
+ let(:line0) { /\*/ }
58
+ let(:line1) { /Updating existing file/ }
59
+ let(:line2) { /Only interactions defined in this test run will be updated/ }
60
+ let(:line3) { /As interactions are identified by description and provider state/ }
61
+
62
+ it "logs a description message" do
63
+ expect($stdout).to receive(:puts).with(line0).twice
64
+ expect($stdout).to receive(:puts).with(line1)
65
+ expect($stdout).to receive(:puts).with(line2)
66
+ expect($stdout).to receive(:puts).with(line3)
67
+ expect(logger).to receive(:info).with(line0).twice
68
+ expect(logger).to receive(:info).with(line1)
69
+ expect(logger).to receive(:info).with(line2)
70
+ expect(logger).to receive(:info).with(line3)
71
+ consumer_contract_writer.consumer_contract
72
+ end
73
+ end
74
+
75
+ context "when an error occurs deserializing the existing pactfile" do
76
+
77
+ let(:pactfile_write_mode) {:update}
78
+ let(:error) { RuntimeError.new('some error')}
79
+ let(:line1) { /Could not load existing consumer contract from .* due to some error/ }
80
+ let(:line2) {'Creating a new file.'}
81
+
82
+ before do
83
+ allow(ConsumerContract).to receive(:from_json).and_raise(error)
84
+ allow($stderr).to receive(:puts)
85
+ allow(logger).to receive(:puts)
86
+ end
87
+
88
+ it "logs the error" do
89
+ expect($stderr).to receive(:puts).with(line1)
90
+ expect($stderr).to receive(:puts).with(line2)
91
+ expect(logger).to receive(:warn).with(line1)
92
+ expect(logger).to receive(:warn).with(line2)
93
+ consumer_contract_writer.consumer_contract
94
+ end
95
+
96
+ it "uses the new interactions" do
97
+ expect(consumer_contract_writer.consumer_contract.interactions).to eq new_interactions
98
+ end
99
+ end
100
+ end
101
+
102
+ describe "write" do
103
+ it "writes the pact file" do
104
+ expect_any_instance_of(ConsumerContract).to receive(:update_pactfile)
105
+ consumer_contract_writer.write
106
+ end
107
+ end
108
+
109
+ end
110
+
111
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'pact/consumer_contract/headers'
3
+
4
+ module Pact
5
+ describe Headers do
6
+
7
+ describe "initialize" do
8
+
9
+ context "with duplicate headers" do
10
+
11
+ subject { Headers.new('Content-Type' => 'application/hippo', 'CONTENT-TYPE' => 'application/giraffe') }
12
+
13
+ it "raises an error" do
14
+ expect { subject }.to raise_error DuplicateHeaderError, /Content\-Type.*CONTENT\-TYPE/
15
+ end
16
+
17
+ end
18
+
19
+ context "with a symbol as a header name" do
20
+
21
+ subject { Headers.new(:'content-type' => 'application/hippo') }
22
+
23
+ it "converts the header name to a String" do
24
+ expect( subject.to_hash ).to eq 'content-type' => 'application/hippo'
25
+ end
26
+
27
+ end
28
+
29
+ context "with a nil header name" do
30
+
31
+ subject { Headers.new(nil => 'application/hippo') }
32
+
33
+ it "raises an error" do
34
+ expect{ subject }.to raise_error InvalidHeaderNameTypeError
35
+ end
36
+
37
+ end
38
+
39
+ context "with a boolean header name" do
40
+
41
+ subject { Headers.new(false => 'application/hippo') }
42
+
43
+ it "raises an error" do
44
+ expect{ subject }.to raise_error InvalidHeaderNameTypeError
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ describe "[]" do
52
+
53
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
54
+
55
+ it "is case insensitive as HTTP headers are case insensitive" do
56
+ expect(subject['Content-Type']).to eq('application/hippo')
57
+ expect(subject['CONTENT-TYPE']).to eq('application/hippo')
58
+ expect(subject['content-type']).to eq('application/hippo')
59
+ end
60
+
61
+ end
62
+
63
+ describe "fetch" do
64
+
65
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
66
+
67
+ it "is case insensitive as HTTP headers are case insensitive" do
68
+ expect(subject.fetch('Content-Type')).to eq('application/hippo')
69
+ expect(subject.fetch('CONTENT-TYPE')).to eq('application/hippo')
70
+ expect(subject.fetch('content-type')).to eq('application/hippo')
71
+ expect(subject.fetch('Content-Length','1')).to eq('1')
72
+ expect { subject.fetch('Content-Length')}.to raise_error KeyError
73
+ end
74
+
75
+ end
76
+
77
+ describe "key?" do
78
+
79
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
80
+
81
+ it "is case insensitive as HTTP headers are case insensitive" do
82
+ expect(subject.key?('CONTENT-TYPE')).to be true
83
+ expect(subject.key?('CONTENT-LENGTH')).to be false
84
+ end
85
+ end
86
+
87
+ describe "has_key?" do
88
+
89
+ subject { Headers.new 'Content-Type' => 'application/hippo' }
90
+
91
+ it "is case insensitive as HTTP headers are case insensitive" do
92
+ expect(subject.has_key?('CONTENT-TYPE')).to be true
93
+ expect(subject.has_key?('CONTENT-LENGTH')).to be false
94
+ end
95
+ end
96
+
97
+ describe "[]=" do
98
+
99
+ subject { Headers.new }
100
+
101
+ it "does not allow modification" do
102
+ expect{ subject['Content-Type'] = 'application/hippo' }.to raise_error /frozen/
103
+ end
104
+
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+ require 'pact/reification'
3
+ require 'pact/consumer_contract/interaction'
4
+
5
+ module Pact
6
+ module Consumer
7
+
8
+ describe Interaction do
9
+
10
+ let(:request) { {method: 'get', path: 'path'} }
11
+ let(:response) { {} }
12
+
13
+ describe "==" do
14
+ subject { InteractionFactory.create }
15
+ context "when other is the same" do
16
+ let(:other) { InteractionFactory.create }
17
+ it "returns true" do
18
+ expect(subject == other).to be true
19
+ end
20
+ end
21
+ context "when other is not the same" do
22
+ let(:other) { InteractionFactory.create(:request => {:path => '/a_different_path'}) }
23
+ it "returns false" do
24
+ expect(subject == other).to be false
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "matches_criteria?" do
30
+ subject { InteractionFactory.create(:description => 'a request for food') }
31
+ context "by description" do
32
+ context "when the interaction matches" do
33
+ it "returns true" do
34
+ expect(subject.matches_criteria?(:description => /request.*food/)).to be true
35
+ end
36
+ end
37
+ context "when the interaction does not match" do
38
+ it "returns false" do
39
+ expect(subject.matches_criteria?(:description => /blah/)).to be false
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "from_hash" do
46
+ context "when providerState has been used instead of provider_state" do
47
+
48
+ subject { Interaction.from_hash('response' => response, 'request' => request, 'providerState' => 'some state') }
49
+
50
+ it "recognises the provider state" do
51
+ expect(subject.provider_state).to eq 'some state'
52
+ end
53
+ end
54
+ end
55
+
56
+ describe "to JSON" do
57
+ let(:request) do
58
+ {
59
+ method: 'post',
60
+ path: '/foo',
61
+ body: Term.new(generate: 'waffle', matcher: /ffl/),
62
+ headers: { 'Content-Type' => 'application/json' },
63
+ query: '',
64
+ }
65
+ end
66
+
67
+ let(:response) do
68
+ { baz: /qux/, wiffle: Term.new(generate: 'wiffle', matcher: /iff/) }
69
+ end
70
+
71
+ let(:parsed_result) do
72
+ JSON.load(subject.to_json)
73
+ end
74
+
75
+ subject { Interaction.from_hash('response' => response, 'request' => request) }
76
+
77
+ it "contains the request" do
78
+ expect(parsed_result['request']).to eq({
79
+ 'method' => 'post',
80
+ 'path' => '/foo',
81
+ 'headers' => {
82
+ 'Content-Type' => 'application/json'
83
+ },
84
+ 'body' => Term.new(generate: 'waffle', matcher: /ffl/),
85
+ 'query' => ''
86
+ })
87
+ end
88
+
89
+ describe "response" do
90
+
91
+ it "serialises regexes" do
92
+ expect(parsed_result['response']['baz']).to eql /qux/
93
+ end
94
+
95
+ it "serialises terms" do
96
+ term = Term.new(generate:'wiffle', matcher: /iff/)
97
+ parsed_term = parsed_result['response']['wiffle']
98
+ expect(term.matcher).to eql parsed_term.matcher
99
+ expect(term.generate).to eql parsed_term.generate
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+ end