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
data/lib/pact/consumer.rb CHANGED
@@ -1,7 +1,10 @@
1
- require_relative 'consumer/interaction'
2
- require_relative 'consumer/consumer_contract_builder'
3
- require_relative 'consumer/mock_service'
4
- require_relative 'consumer/app_manager'
5
- require_relative 'term'
6
- require_relative 'request'
7
- require_relative 'consumer_contract'
1
+ require 'pact/consumer_contract'
2
+ require 'pact/consumer/configuration'
3
+ require 'pact/consumer/consumer_contract_builder'
4
+ require 'pact/consumer/consumer_contract_builders'
5
+ require 'pact/consumer/interaction_builder'
6
+ require 'pact/consumer/mock_service'
7
+ require 'pact/consumer/mock_service_client'
8
+ require 'pact/consumer/app_manager'
9
+ require 'pact/term'
10
+ require 'pact/something_like'
@@ -0,0 +1,115 @@
1
+ require 'pact/logging'
2
+ require 'pact/something_like'
3
+ require 'pact/symbolize_keys'
4
+ require 'pact/term'
5
+ require 'pact/version'
6
+ require 'pact/shared/active_support_support'
7
+ require 'date'
8
+ require 'json/add/regexp'
9
+ require 'open-uri'
10
+ require_relative 'service_consumer'
11
+ require_relative 'service_provider'
12
+ require_relative 'interaction'
13
+ require_relative 'request'
14
+ require_relative 'pact_file'
15
+ require_relative 'file_name'
16
+
17
+
18
+
19
+ module Pact
20
+
21
+ class ConsumerContract
22
+
23
+ include SymbolizeKeys
24
+ include Logging
25
+ include FileName
26
+ include ActiveSupportSupport
27
+
28
+ attr_accessor :interactions
29
+ attr_accessor :consumer
30
+ attr_accessor :provider
31
+
32
+ def initialize(attributes = {})
33
+ @interactions = attributes[:interactions] || []
34
+ @consumer = attributes[:consumer]
35
+ @provider = attributes[:provider]
36
+ end
37
+
38
+ def to_hash
39
+ {
40
+ provider: @provider.as_json,
41
+ consumer: @consumer.as_json,
42
+ interactions: @interactions.collect(&:as_json),
43
+ metadata: {
44
+ pactSpecificationVersion: "1.0.0"
45
+ }
46
+ }
47
+ end
48
+
49
+ def as_json(options = {})
50
+ fix_all_the_things to_hash
51
+ end
52
+
53
+ def to_json(options = {})
54
+ as_json.to_json(options)
55
+ end
56
+
57
+ def self.from_hash(hash)
58
+ hash = symbolize_keys(hash)
59
+ new({
60
+ :interactions => hash[:interactions].collect { |hash| Interaction.from_hash(hash)},
61
+ :consumer => ServiceConsumer.from_hash(hash[:consumer]),
62
+ :provider => ServiceProvider.from_hash(hash[:provider])
63
+ })
64
+ end
65
+
66
+ def self.from_json string
67
+ deserialised_object = JSON.load(maintain_backwards_compatiblity_with_producer_keys(string))
68
+ from_hash(deserialised_object)
69
+ end
70
+
71
+ def self.from_uri uri, options = {}
72
+ from_json(Pact::PactFile.read(uri, options))
73
+ end
74
+
75
+ def self.maintain_backwards_compatiblity_with_producer_keys string
76
+ string.gsub('"producer":', '"provider":').gsub('"producer_state":', '"provider_state":')
77
+ end
78
+
79
+ def find_interaction criteria
80
+ interactions = find_interactions criteria
81
+ if interactions.size == 0
82
+ raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
83
+ elsif interactions.size > 1
84
+ raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{provider.name}."
85
+ end
86
+ interactions.first
87
+ end
88
+
89
+ def find_interactions criteria
90
+ interactions.select{ | interaction| interaction.matches_criteria?(criteria)}
91
+ end
92
+
93
+ def each
94
+ interactions.each do | interaction |
95
+ yield interaction
96
+ end
97
+ end
98
+
99
+ def pact_file_name
100
+ file_name consumer.name, provider.name
101
+ end
102
+
103
+ def pactfile_path
104
+ raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && provider && provider.name)
105
+ @pactfile_path ||= file_path consumer.name, provider.name
106
+ end
107
+
108
+ def update_pactfile
109
+ logger.debug "Updating pact file for #{provider.name} at #{pactfile_path}"
110
+ File.open(pactfile_path, 'w') do |f|
111
+ f.write fix_json_formatting(JSON.pretty_generate(self))
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,84 @@
1
+ require 'pact/consumer_contract'
2
+ require 'pact/consumer/interactions_filter'
3
+ require 'pact/consumer_contract/file_name'
4
+
5
+ module Pact
6
+
7
+ class ConsumerContractWriter
8
+
9
+ attr_reader :consumer_contract_details, :pactfile_write_mode, :interactions, :logger
10
+
11
+ def initialize consumer_contract_details, logger
12
+ @logger = logger
13
+ @consumer_contract_details = consumer_contract_details
14
+ @pactfile_write_mode = consumer_contract_details.fetch(:pactfile_write_mode, :overwrite).to_sym
15
+ @interactions = consumer_contract_details.fetch(:interactions)
16
+ end
17
+
18
+ def consumer_contract
19
+ @consumer_contract ||= Pact::ConsumerContract.new(
20
+ consumer: ServiceConsumer.new(name: consumer_contract_details[:consumer][:name]),
21
+ provider: ServiceProvider.new(name: consumer_contract_details[:provider][:name]),
22
+ interactions: interactions_for_new_consumer_contract)
23
+ end
24
+
25
+ def write
26
+ consumer_contract.update_pactfile
27
+ consumer_contract.to_json
28
+ end
29
+
30
+ def interactions_for_new_consumer_contract
31
+ if pactfile_write_mode == :update
32
+ merged_interactions = existing_interactions
33
+ filter = Consumer::UpdatableInteractionsFilter.new(merged_interactions)
34
+ interactions.each {|i| filter << i }
35
+ merged_interactions
36
+ else
37
+ interactions
38
+ end
39
+ end
40
+
41
+ def existing_interactions
42
+ interactions = []
43
+ if pactfile_exists?
44
+ begin
45
+ interactions = existing_consumer_contract.interactions
46
+ info_and_puts "*****************************************************************************"
47
+ info_and_puts "Updating existing file .#{pactfile_path.gsub(Dir.pwd, '')} as config.pactfile_write_mode is :update"
48
+ info_and_puts "Only interactions defined in this test run will be updated."
49
+ info_and_puts "As interactions are identified by description and provider state, pleased note that if either of these have changed, the old interactions won't be removed from the pact file until the specs are next run with :pactfile_write_mode => :overwrite."
50
+ info_and_puts "*****************************************************************************"
51
+ rescue StandardError => e
52
+ warn_and_stderr "Could not load existing consumer contract from #{pactfile_path} due to #{e}"
53
+ logger.error e
54
+ logger.error e.backtrace
55
+ warn_and_stderr "Creating a new file."
56
+ end
57
+ end
58
+ interactions
59
+ end
60
+
61
+ def pactfile_exists?
62
+ File.exist?(pactfile_path)
63
+ end
64
+
65
+ def pactfile_path
66
+ Pact::FileName.file_path consumer_contract_details[:consumer][:name], consumer_contract_details[:provider][:name]
67
+ end
68
+
69
+ def existing_consumer_contract
70
+ Pact::ConsumerContract.from_uri(pactfile_path)
71
+ end
72
+
73
+ def warn_and_stderr msg
74
+ Pact.configuration.error_stream.puts msg
75
+ logger.warn msg
76
+ end
77
+
78
+ def info_and_puts msg
79
+ $stdout.puts msg
80
+ logger.info msg
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,19 @@
1
+ module Pact
2
+
3
+ module FileName
4
+
5
+ extend self
6
+
7
+ def file_name consumer_name, provider_name
8
+ "#{filenamify(consumer_name)}-#{filenamify(provider_name)}.json"
9
+ end
10
+
11
+ def file_path consumer_name, provider_name, pact_dir = Pact.configuration.pact_dir
12
+ File.join(pact_dir, file_name(consumer_name, provider_name))
13
+ end
14
+
15
+ def filenamify name
16
+ name.downcase.gsub(/\s/, '_')
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,51 @@
1
+ module Pact
2
+
3
+ class DuplicateHeaderError < StandardError; end
4
+ class InvalidHeaderNameTypeError < StandardError; end
5
+
6
+ class Headers < Hash
7
+
8
+ def initialize hash = {}
9
+ hash.each_pair do | key, value |
10
+ check_for_invalid key
11
+ self[find_matching_key(key)] = value
12
+ end
13
+ self.freeze
14
+ end
15
+
16
+ def [] key
17
+ super(find_matching_key(key))
18
+ end
19
+
20
+ def fetch *args, &block
21
+ args[0] = find_matching_key(args[0]) if args.first
22
+ super(*args, &block)
23
+ end
24
+
25
+ def key? key
26
+ super(find_matching_key(key))
27
+ end
28
+
29
+ alias_method :has_key?, :key?
30
+ alias_method :include?, :key?
31
+
32
+ private
33
+
34
+ def find_matching_key key
35
+ key = key.to_s
36
+ match = keys.find { |k| k.downcase == key.downcase }
37
+ match.nil? ? key : match
38
+ end
39
+
40
+ def check_for_invalid key
41
+ unless (String === key || Symbol === key)
42
+ raise InvalidHeaderNameTypeError.new "Header name (#{key}) must be a String or a Symbol."
43
+ end
44
+ if key? key
45
+ raise DuplicateHeaderError.new "Duplicate header found (#{find_matching_key(key)} and #{key}. Please use a comma separated single value when multiple headers with the same name are required."
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,67 @@
1
+ require 'pact/consumer_contract/request'
2
+ require 'pact/symbolize_keys'
3
+ require 'pact/shared/active_support_support'
4
+
5
+ module Pact
6
+ class Interaction
7
+ include ActiveSupportSupport
8
+ include SymbolizeKeys
9
+
10
+ attr_accessor :description, :request, :response, :provider_state
11
+
12
+ def initialize attributes = {}
13
+ @description = attributes[:description]
14
+ @request = attributes[:request]
15
+ @response = attributes[:response]
16
+ @provider_state = attributes[:provider_state] || attributes[:providerState]
17
+ end
18
+
19
+ def self.from_hash hash
20
+ request = Pact::Request::Expected.from_hash(hash['request'])
21
+ new(symbolize_keys(hash).merge({request: request}))
22
+ end
23
+
24
+ def to_hash
25
+ hash = { :description => @description }
26
+ hash[:provider_state] = @provider_state if @provider_state #Easier to read when provider state at top
27
+ hash.merge(:request => @request.as_json, :response => @response)
28
+ end
29
+
30
+ def as_json options = {}
31
+ fix_all_the_things to_hash
32
+ end
33
+
34
+ def to_json(options = {})
35
+ as_json.to_json(options)
36
+ end
37
+
38
+ def matches_criteria? criteria
39
+ criteria.each do | key, value |
40
+ unless match_criterion self.send(key.to_s), value
41
+ return false
42
+ end
43
+ end
44
+ true
45
+ end
46
+
47
+ def match_criterion target, criterion
48
+ target == criterion || (criterion.is_a?(Regexp) && criterion.match(target))
49
+ end
50
+
51
+ def == other
52
+ other.is_a?(Interaction) && as_json == other.as_json
53
+ end
54
+
55
+ def eq? other
56
+ self == other
57
+ end
58
+
59
+ def description_with_provider_state_quoted
60
+ provider_state ? "\"#{description}\" given \"#{provider_state}\"" : "\"#{description}\""
61
+ end
62
+
63
+ def to_s
64
+ JSON.pretty_generate(self)
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,24 @@
1
+ module Pact
2
+
3
+ module PactFile
4
+
5
+ extend self
6
+
7
+ def read uri, options = {}
8
+ pact = open(uri.to_s) { | file | file.read }
9
+ if options[:save_pactfile_to_tmp]
10
+ save_pactfile_to_tmp pact, ::File.basename(uri.to_s)
11
+ end
12
+ pact
13
+ rescue StandardError => e
14
+ $stderr.puts "Error reading file from #{uri}"
15
+ $stderr.puts "#{e.to_s} #{e.backtrace.join("\n")}"
16
+ raise e
17
+ end
18
+
19
+ def save_pactfile_to_tmp pact, name
20
+ ::FileUtils.mkdir_p Pact.configuration.tmp_dir
21
+ ::File.open(Pact.configuration.tmp_dir + "/#{name}", "w") { |file| file << pact}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,73 @@
1
+ require 'pact/shared/request'
2
+ require 'pact/shared/null_expectation'
3
+
4
+ module Pact
5
+
6
+ module Request
7
+
8
+ class Expected < Pact::Request::Base
9
+
10
+ DEFAULT_OPTIONS = {:allow_unexpected_keys => false}.freeze
11
+ attr_accessor :options #Temporary hack
12
+
13
+ def self.from_hash(hash)
14
+ sym_hash = symbolize_keys hash
15
+ method = sym_hash.fetch(:method)
16
+ path = sym_hash.fetch(:path)
17
+ query = sym_hash.fetch(:query, key_not_found)
18
+ headers = sym_hash.fetch(:headers, key_not_found)
19
+ body = sym_hash.fetch(:body, key_not_found)
20
+ options = sym_hash.fetch(:options, {})
21
+ new(method, path, headers, body, query, options)
22
+ end
23
+
24
+ def initialize(method, path, headers, body, query, options = {})
25
+ super(method, path, headers, body, query)
26
+ @options = options
27
+ end
28
+
29
+ def matches?(actual_request)
30
+ difference(actual_request).empty?
31
+ end
32
+
33
+ def matches_route? actual_request
34
+ diff({:method => method, :path => path}, {:method => actual_request.method, :path => actual_request.path}).empty?
35
+ end
36
+
37
+ def difference(actual_request)
38
+ request_diff = diff(to_hash_without_body, actual_request.to_hash_without_body)
39
+ unless body.is_a? NullExpectation
40
+ request_diff.merge(body_difference(actual_request.body))
41
+ else
42
+ request_diff
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ def self.key_not_found
49
+ Pact::NullExpectation.new
50
+ end
51
+
52
+ private
53
+
54
+ # Options is a dirty hack to allow Condor to send extra keys in the request,
55
+ # as it's too much work to set up an exactly matching expectation.
56
+ # Need to implement a proper matching strategy and remove this.
57
+ # Do not rely on it!
58
+ def runtime_options
59
+ DEFAULT_OPTIONS.merge(symbolize_keys(options))
60
+ end
61
+
62
+ def body_difference(actual_body)
63
+ body_differ.call({:body => body}, {body: actual_body}, allow_unexpected_keys: runtime_options[:allow_unexpected_keys_in_body])
64
+ end
65
+
66
+ def body_differ
67
+ Pact.configuration.body_differ_for_content_type content_type
68
+ end
69
+
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,28 @@
1
+ require 'pact/symbolize_keys'
2
+
3
+ module Pact
4
+ class ServiceConsumer
5
+ include SymbolizeKeys
6
+
7
+ attr_accessor :name
8
+ def initialize options
9
+ @name = options[:name]
10
+ end
11
+
12
+ def to_s
13
+ name
14
+ end
15
+
16
+ def to_hash
17
+ {name: name}
18
+ end
19
+
20
+ def as_json options = {}
21
+ to_hash
22
+ end
23
+
24
+ def self.from_hash hash
25
+ new(symbolize_keys(hash))
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'pact/symbolize_keys'
2
+
3
+ module Pact
4
+ class ServiceProvider
5
+ include SymbolizeKeys
6
+
7
+ attr_accessor :name
8
+ def initialize options
9
+ @name = options[:name] || '[provider name unknown - please update the pact gem in the consumer project to the latest version and regenerate the pacts]'
10
+ end
11
+
12
+ def to_s
13
+ name
14
+ end
15
+
16
+ def to_hash
17
+ {name: name}
18
+ end
19
+
20
+ def as_json options = {}
21
+ to_hash
22
+ end
23
+
24
+ def self.from_hash hash
25
+ new(symbolize_keys(hash))
26
+ end
27
+ end
28
+ end
@@ -1,110 +1 @@
1
- require 'pact/consumer/service_consumer'
2
- require 'pact/consumer/service_producer'
3
- require 'pact/consumer/interaction'
4
- require 'pact/logging'
5
- require 'pact/json_warning'
6
- require 'date'
7
-
8
- module Pact
9
- class ConsumerContract
10
-
11
- include Pact::Logging
12
- include Pact::JsonWarning
13
-
14
- attr_accessor :interactions
15
- attr_accessor :consumer
16
- attr_accessor :producer
17
-
18
- def initialize(attributes = {})
19
- @interactions = attributes[:interactions] || []
20
- @consumer = attributes[:consumer]
21
- @producer = attributes[:producer]
22
- end
23
-
24
- def as_json(options = {})
25
- {
26
- producer: @producer.as_json,
27
- consumer: @consumer.as_json,
28
- interactions: @interactions.collect(&:as_json),
29
- metadata: {
30
- pact_gem: {
31
- version: Pact::VERSION
32
- }
33
- }
34
- }
35
- end
36
-
37
- def to_json(options = {})
38
- as_json(options).to_json(options)
39
- end
40
-
41
- def self.from_hash(obj)
42
- new({
43
- :interactions => obj['interactions'].collect { |hash| Pact::Consumer::Interaction.from_hash(hash)},
44
- :consumer => Pact::Consumer::ServiceConsumer.from_hash(obj['consumer']),
45
- :producer => Pact::Consumer::ServiceProducer.from_hash(obj['producer'] || {})
46
- })
47
- end
48
-
49
- def self.from_json string
50
- deserialised_object = JSON.load(string)
51
- from_hash(deserialised_object)
52
- end
53
-
54
- def find_interaction criteria
55
- interactions = find_interactions criteria
56
- if interactions.size == 0
57
- raise "Could not find interaction matching #{criteria} in pact file between #{consumer.name} and #{producer.name}."
58
- elsif interactions.size > 1
59
- raise "Found more than 1 interaction matching #{criteria} in pact file between #{consumer.name} and #{producer.name}."
60
- end
61
- interactions.first
62
- end
63
-
64
- def find_interactions criteria
65
- interactions.select{ | interaction| match_criteria? interaction, criteria}
66
- end
67
-
68
- def each
69
- interactions.each do | interaction |
70
- yield interaction
71
- end
72
- end
73
-
74
- def match_criteria? interaction, criteria
75
- criteria.each do | key, value |
76
- unless match_criterion interaction[key.to_s], value
77
- return false
78
- end
79
- end
80
- true
81
- end
82
-
83
- def match_criterion target, criterion
84
- target == criterion || (criterion.is_a?(Regexp) && criterion.match(target))
85
- end
86
-
87
- def pact_file_name
88
- "#{filenamify(consumer.name)}-#{filenamify(producer.name)}.json"
89
- end
90
-
91
- def pactfile_path
92
- raise 'You must first specify a consumer and service name' unless (consumer && consumer.name && producer && producer.name)
93
- @pactfile_path ||= File.join(Pact.configuration.pact_dir, pact_file_name)
94
- end
95
-
96
- def update_pactfile
97
- logger.debug "Updating pact file for #{producer.name} at #{pactfile_path}"
98
- check_for_active_support_json
99
- File.open(pactfile_path, 'w') do |f|
100
- f.write JSON.pretty_generate(self)
101
- end
102
- end
103
-
104
- private
105
-
106
- def filenamify name
107
- name.downcase.gsub(/\s/, '_')
108
- end
109
- end
110
- end
1
+ require 'pact/consumer_contract/consumer_contract'
@@ -0,0 +1,40 @@
1
+ module Pact
2
+ module Doc
3
+
4
+ class DocFile
5
+
6
+ def initialize consumer_contract, dir, consumer_contract_renderer, file_extension
7
+ @dir = dir
8
+ @consumer_contract = consumer_contract
9
+ @consumer_contract_renderer = consumer_contract_renderer
10
+ @file_extension = file_extension
11
+ end
12
+
13
+ def write
14
+ File.open(path, "w") { |io| io << doc_file_contents }
15
+ end
16
+
17
+ def title
18
+ consumer_contract.provider.name
19
+ end
20
+
21
+ def name
22
+ "#{consumer_contract.consumer.name} - #{consumer_contract.provider.name}#{file_extension}"
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :dir, :consumer_contract, :consumer_contract_renderer, :file_extension
28
+
29
+
30
+ def path
31
+ File.join(dir, name)
32
+ end
33
+
34
+ def doc_file_contents
35
+ consumer_contract_renderer.call(consumer_contract)
36
+ end
37
+
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ module Pact
2
+ module Doc
3
+ class Generate
4
+
5
+ def self.call pact_dir = Pact.configuration.pact_dir, doc_dir = Pact.configuration.doc_dir, doc_generators = Pact.configuration.doc_generators
6
+ doc_generators.each{| doc_generator| doc_generator.call pact_dir, doc_dir }
7
+ end
8
+
9
+ end
10
+ end
11
+ end