pact 1.67.5 → 2.0.1

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 (240) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/lib/pact/configuration.rb +21 -0
  4. data/lib/pact/consumer/grpc_interaction_builder.rb +192 -0
  5. data/lib/pact/consumer/http_interaction_builder.rb +160 -0
  6. data/lib/pact/consumer/interaction_contents.rb +62 -0
  7. data/lib/pact/consumer/message_interaction_builder.rb +286 -0
  8. data/lib/pact/consumer/mock_server.rb +106 -0
  9. data/lib/pact/consumer/pact_config/base.rb +22 -0
  10. data/lib/pact/consumer/pact_config/grpc.rb +24 -0
  11. data/lib/pact/consumer/pact_config/http.rb +53 -0
  12. data/lib/pact/consumer/pact_config/message.rb +15 -0
  13. data/lib/pact/consumer/pact_config/plugin_async_message.rb +24 -0
  14. data/lib/pact/consumer/pact_config/plugin_http.rb +24 -0
  15. data/lib/pact/consumer/pact_config/plugin_sync_message.rb +24 -0
  16. data/lib/pact/consumer/pact_config.rb +28 -0
  17. data/lib/pact/consumer/plugin_async_message_interaction_builder.rb +169 -0
  18. data/lib/pact/consumer/plugin_http_interaction_builder.rb +199 -0
  19. data/lib/pact/consumer/plugin_sync_message_interaction_builder.rb +178 -0
  20. data/lib/pact/consumer.rb +6 -7
  21. data/lib/pact/generators/base.rb +285 -0
  22. data/lib/pact/generators.rb +47 -0
  23. data/lib/pact/matchers/base.rb +72 -0
  24. data/lib/pact/matchers/v1/equality.rb +17 -0
  25. data/lib/pact/matchers/v2/regex.rb +26 -0
  26. data/lib/pact/matchers/v2/type.rb +18 -0
  27. data/lib/pact/matchers/v3/boolean.rb +18 -0
  28. data/lib/pact/matchers/v3/content_type.rb +30 -0
  29. data/lib/pact/matchers/v3/date.rb +22 -0
  30. data/lib/pact/matchers/v3/date_time.rb +22 -0
  31. data/lib/pact/matchers/v3/decimal.rb +18 -0
  32. data/lib/pact/matchers/v3/each.rb +46 -0
  33. data/lib/pact/matchers/v3/include.rb +18 -0
  34. data/lib/pact/matchers/v3/integer.rb +18 -0
  35. data/lib/pact/matchers/v3/null.rb +13 -0
  36. data/lib/pact/matchers/v3/number.rb +18 -0
  37. data/lib/pact/matchers/v3/semver.rb +22 -0
  38. data/lib/pact/matchers/v3/time.rb +22 -0
  39. data/lib/pact/matchers/v3/values.rb +13 -0
  40. data/lib/pact/matchers/v4/each_key.rb +34 -0
  41. data/lib/pact/matchers/v4/each_key_value.rb +38 -0
  42. data/lib/pact/matchers/v4/each_value.rb +40 -0
  43. data/lib/pact/matchers/v4/not_empty.rb +22 -0
  44. data/lib/pact/matchers/v4/status_code.rb +15 -0
  45. data/lib/pact/matchers.rb +108 -0
  46. data/lib/pact/native/blocking_verifier.rb +15 -0
  47. data/lib/pact/native/logger.rb +24 -0
  48. data/lib/pact/provider/async_message_verifier.rb +29 -0
  49. data/lib/pact/provider/base_verifier.rb +259 -0
  50. data/lib/pact/provider/grpc_verifier.rb +40 -0
  51. data/lib/pact/provider/gruf_server.rb +73 -0
  52. data/lib/pact/provider/http_server.rb +77 -0
  53. data/lib/pact/provider/http_verifier.rb +45 -0
  54. data/lib/pact/provider/message_provider_servlet.rb +77 -0
  55. data/lib/pact/provider/mixed_verifier.rb +21 -0
  56. data/lib/pact/provider/pact_broker_proxy.rb +62 -0
  57. data/lib/pact/provider/pact_broker_proxy_runner.rb +78 -0
  58. data/lib/pact/provider/pact_config/async.rb +28 -0
  59. data/lib/pact/provider/pact_config/base.rb +101 -0
  60. data/lib/pact/provider/pact_config/grpc.rb +24 -0
  61. data/lib/pact/provider/pact_config/http.rb +25 -0
  62. data/lib/pact/provider/pact_config/mixed.rb +38 -0
  63. data/lib/pact/provider/pact_config.rb +24 -0
  64. data/lib/pact/provider/provider_server_runner.rb +90 -0
  65. data/lib/pact/provider/provider_state_configuration.rb +32 -0
  66. data/lib/pact/provider/provider_state_servlet.rb +84 -0
  67. data/lib/pact/provider.rb +6 -3
  68. data/lib/pact/railtie.rb +11 -0
  69. data/lib/pact/{v2/rspec → rspec}/support/pact_consumer_helpers.rb +8 -8
  70. data/lib/pact/{v2/rspec → rspec}/support/pact_provider_helpers.rb +25 -24
  71. data/lib/pact/{v2/rspec → rspec}/support/webmock/webmock_helpers.rb +1 -1
  72. data/lib/pact/{v2/rspec.rb → rspec.rb} +6 -5
  73. data/lib/pact/support/core_ext.rb +93 -0
  74. data/lib/pact/tasks/pact.rake +13 -0
  75. data/lib/pact/version.rb +1 -1
  76. data/lib/pact.rb +52 -11
  77. data/pact.gemspec +47 -64
  78. metadata +146 -404
  79. data/lib/pact/cli/generate_pact_docs.rb +0 -4
  80. data/lib/pact/cli/run_pact_verification.rb +0 -99
  81. data/lib/pact/cli/spec_criteria.rb +0 -26
  82. data/lib/pact/cli.rb +0 -45
  83. data/lib/pact/consumer/configuration/configuration_extensions.rb +0 -90
  84. data/lib/pact/consumer/configuration/dsl.rb +0 -11
  85. data/lib/pact/consumer/configuration/mock_service.rb +0 -112
  86. data/lib/pact/consumer/configuration/service_consumer.rb +0 -51
  87. data/lib/pact/consumer/configuration/service_provider.rb +0 -40
  88. data/lib/pact/consumer/configuration.rb +0 -10
  89. data/lib/pact/consumer/consumer_contract_builder.rb +0 -82
  90. data/lib/pact/consumer/consumer_contract_builders.rb +0 -10
  91. data/lib/pact/consumer/interaction_builder.rb +0 -45
  92. data/lib/pact/consumer/rspec.rb +0 -35
  93. data/lib/pact/consumer/spec_hooks.rb +0 -40
  94. data/lib/pact/consumer/world.rb +0 -37
  95. data/lib/pact/doc/README.md +0 -13
  96. data/lib/pact/doc/doc_file.rb +0 -40
  97. data/lib/pact/doc/generate.rb +0 -11
  98. data/lib/pact/doc/generator.rb +0 -82
  99. data/lib/pact/doc/interaction_view_model.rb +0 -124
  100. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +0 -68
  101. data/lib/pact/doc/markdown/generator.rb +0 -26
  102. data/lib/pact/doc/markdown/index_renderer.rb +0 -43
  103. data/lib/pact/doc/markdown/interaction.erb +0 -14
  104. data/lib/pact/doc/markdown/interaction_renderer.rb +0 -43
  105. data/lib/pact/doc/sort_interactions.rb +0 -16
  106. data/lib/pact/hal/authorization_header_redactor.rb +0 -32
  107. data/lib/pact/hal/entity.rb +0 -110
  108. data/lib/pact/hal/http_client.rb +0 -146
  109. data/lib/pact/hal/link.rb +0 -112
  110. data/lib/pact/hal/non_json_entity.rb +0 -28
  111. data/lib/pact/hash_refinements.rb +0 -17
  112. data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +0 -112
  113. data/lib/pact/pact_broker/fetch_pacts.rb +0 -103
  114. data/lib/pact/pact_broker/notices.rb +0 -34
  115. data/lib/pact/pact_broker/pact_selection_description.rb +0 -66
  116. data/lib/pact/pact_broker.rb +0 -25
  117. data/lib/pact/project_root.rb +0 -7
  118. data/lib/pact/provider/configuration/configuration_extension.rb +0 -69
  119. data/lib/pact/provider/configuration/dsl.rb +0 -18
  120. data/lib/pact/provider/configuration/message_provider_dsl.rb +0 -63
  121. data/lib/pact/provider/configuration/pact_verification.rb +0 -48
  122. data/lib/pact/provider/configuration/pact_verification_from_broker.rb +0 -126
  123. data/lib/pact/provider/configuration/service_provider_config.rb +0 -32
  124. data/lib/pact/provider/configuration/service_provider_dsl.rb +0 -107
  125. data/lib/pact/provider/configuration.rb +0 -7
  126. data/lib/pact/provider/context.rb +0 -0
  127. data/lib/pact/provider/help/console_text.rb +0 -76
  128. data/lib/pact/provider/help/content.rb +0 -38
  129. data/lib/pact/provider/help/pact_diff.rb +0 -43
  130. data/lib/pact/provider/help/prompt_text.rb +0 -49
  131. data/lib/pact/provider/help/write.rb +0 -56
  132. data/lib/pact/provider/matchers/messages.rb +0 -66
  133. data/lib/pact/provider/pact_helper_locator.rb +0 -24
  134. data/lib/pact/provider/pact_source.rb +0 -40
  135. data/lib/pact/provider/pact_spec_runner.rb +0 -188
  136. data/lib/pact/provider/pact_uri.rb +0 -55
  137. data/lib/pact/provider/pact_verification.rb +0 -17
  138. data/lib/pact/provider/print_missing_provider_states.rb +0 -35
  139. data/lib/pact/provider/request.rb +0 -90
  140. data/lib/pact/provider/rspec/backtrace_formatter.rb +0 -43
  141. data/lib/pact/provider/rspec/calculate_exit_code.rb +0 -18
  142. data/lib/pact/provider/rspec/custom_options_file +0 -0
  143. data/lib/pact/provider/rspec/formatter_rspec_2.rb +0 -76
  144. data/lib/pact/provider/rspec/formatter_rspec_3.rb +0 -195
  145. data/lib/pact/provider/rspec/json_formatter.rb +0 -100
  146. data/lib/pact/provider/rspec/matchers.rb +0 -80
  147. data/lib/pact/provider/rspec/pact_broker_formatter.rb +0 -76
  148. data/lib/pact/provider/rspec.rb +0 -234
  149. data/lib/pact/provider/state/provider_state.rb +0 -180
  150. data/lib/pact/provider/state/provider_state_configured_modules.rb +0 -15
  151. data/lib/pact/provider/state/provider_state_manager.rb +0 -42
  152. data/lib/pact/provider/state/provider_state_proxy.rb +0 -39
  153. data/lib/pact/provider/state/set_up.rb +0 -13
  154. data/lib/pact/provider/state/tear_down.rb +0 -13
  155. data/lib/pact/provider/test_methods.rb +0 -77
  156. data/lib/pact/provider/verification_report.rb +0 -36
  157. data/lib/pact/provider/verification_results/create.rb +0 -88
  158. data/lib/pact/provider/verification_results/publish.rb +0 -143
  159. data/lib/pact/provider/verification_results/publish_all.rb +0 -50
  160. data/lib/pact/provider/verification_results/verification_result.rb +0 -40
  161. data/lib/pact/provider/world.rb +0 -50
  162. data/lib/pact/retry.rb +0 -37
  163. data/lib/pact/tasks/task_helper.rb +0 -62
  164. data/lib/pact/tasks/verification_task.rb +0 -95
  165. data/lib/pact/tasks.rb +0 -2
  166. data/lib/pact/templates/help.erb +0 -22
  167. data/lib/pact/templates/provider_state.erb +0 -14
  168. data/lib/pact/utils/metrics.rb +0 -100
  169. data/lib/pact/utils/string.rb +0 -35
  170. data/lib/pact/v2/configuration.rb +0 -23
  171. data/lib/pact/v2/consumer/grpc_interaction_builder.rb +0 -194
  172. data/lib/pact/v2/consumer/http_interaction_builder.rb +0 -162
  173. data/lib/pact/v2/consumer/interaction_contents.rb +0 -62
  174. data/lib/pact/v2/consumer/message_interaction_builder.rb +0 -288
  175. data/lib/pact/v2/consumer/mock_server.rb +0 -108
  176. data/lib/pact/v2/consumer/pact_config/base.rb +0 -24
  177. data/lib/pact/v2/consumer/pact_config/grpc.rb +0 -26
  178. data/lib/pact/v2/consumer/pact_config/http.rb +0 -55
  179. data/lib/pact/v2/consumer/pact_config/message.rb +0 -17
  180. data/lib/pact/v2/consumer/pact_config/plugin_async_message.rb +0 -26
  181. data/lib/pact/v2/consumer/pact_config/plugin_http.rb +0 -26
  182. data/lib/pact/v2/consumer/pact_config/plugin_sync_message.rb +0 -26
  183. data/lib/pact/v2/consumer/pact_config.rb +0 -30
  184. data/lib/pact/v2/consumer/plugin_async_message_interaction_builder.rb +0 -171
  185. data/lib/pact/v2/consumer/plugin_http_interaction_builder.rb +0 -201
  186. data/lib/pact/v2/consumer/plugin_sync_message_interaction_builder.rb +0 -180
  187. data/lib/pact/v2/consumer.rb +0 -8
  188. data/lib/pact/v2/generators/base.rb +0 -287
  189. data/lib/pact/v2/generators.rb +0 -49
  190. data/lib/pact/v2/matchers/base.rb +0 -74
  191. data/lib/pact/v2/matchers/v1/equality.rb +0 -19
  192. data/lib/pact/v2/matchers/v2/regex.rb +0 -19
  193. data/lib/pact/v2/matchers/v2/type.rb +0 -17
  194. data/lib/pact/v2/matchers/v3/boolean.rb +0 -17
  195. data/lib/pact/v2/matchers/v3/content_type.rb +0 -32
  196. data/lib/pact/v2/matchers/v3/date.rb +0 -18
  197. data/lib/pact/v2/matchers/v3/date_time.rb +0 -18
  198. data/lib/pact/v2/matchers/v3/decimal.rb +0 -17
  199. data/lib/pact/v2/matchers/v3/each.rb +0 -42
  200. data/lib/pact/v2/matchers/v3/include.rb +0 -17
  201. data/lib/pact/v2/matchers/v3/integer.rb +0 -17
  202. data/lib/pact/v2/matchers/v3/null.rb +0 -16
  203. data/lib/pact/v2/matchers/v3/number.rb +0 -17
  204. data/lib/pact/v2/matchers/v3/semver.rb +0 -23
  205. data/lib/pact/v2/matchers/v3/time.rb +0 -18
  206. data/lib/pact/v2/matchers/v3/values.rb +0 -16
  207. data/lib/pact/v2/matchers/v4/each_key.rb +0 -26
  208. data/lib/pact/v2/matchers/v4/each_key_value.rb +0 -32
  209. data/lib/pact/v2/matchers/v4/each_value.rb +0 -33
  210. data/lib/pact/v2/matchers/v4/not_empty.rb +0 -24
  211. data/lib/pact/v2/matchers/v4/status_code.rb +0 -17
  212. data/lib/pact/v2/matchers.rb +0 -110
  213. data/lib/pact/v2/native/blocking_verifier.rb +0 -17
  214. data/lib/pact/v2/native/logger.rb +0 -25
  215. data/lib/pact/v2/provider/async_message_verifier.rb +0 -28
  216. data/lib/pact/v2/provider/base_verifier.rb +0 -242
  217. data/lib/pact/v2/provider/grpc_verifier.rb +0 -38
  218. data/lib/pact/v2/provider/gruf_server.rb +0 -75
  219. data/lib/pact/v2/provider/http_server.rb +0 -79
  220. data/lib/pact/v2/provider/http_verifier.rb +0 -43
  221. data/lib/pact/v2/provider/message_provider_servlet.rb +0 -79
  222. data/lib/pact/v2/provider/mixed_verifier.rb +0 -22
  223. data/lib/pact/v2/provider/pact_broker_proxy.rb +0 -66
  224. data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +0 -80
  225. data/lib/pact/v2/provider/pact_config/async.rb +0 -29
  226. data/lib/pact/v2/provider/pact_config/base.rb +0 -103
  227. data/lib/pact/v2/provider/pact_config/grpc.rb +0 -26
  228. data/lib/pact/v2/provider/pact_config/http.rb +0 -27
  229. data/lib/pact/v2/provider/pact_config/mixed.rb +0 -40
  230. data/lib/pact/v2/provider/pact_config.rb +0 -26
  231. data/lib/pact/v2/provider/provider_server_runner.rb +0 -92
  232. data/lib/pact/v2/provider/provider_state_configuration.rb +0 -32
  233. data/lib/pact/v2/provider/provider_state_servlet.rb +0 -86
  234. data/lib/pact/v2/provider.rb +0 -8
  235. data/lib/pact/v2/railtie.rb +0 -13
  236. data/lib/pact/v2/tasks/pact.rake +0 -13
  237. data/lib/pact/v2.rb +0 -71
  238. data/lib/tasks/pact.rake +0 -34
  239. /data/lib/pact/{v2/rspec → rspec}/support/pact_message_helpers.rb +0 -0
  240. /data/lib/pact/{v2/rspec → rspec}/support/waterdrop/pact_waterdrop_client.rb +0 -0
@@ -1,288 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pact/ffi/message_consumer"
4
- require "pact/ffi/plugin_consumer"
5
- require "pact/ffi/logger"
6
-
7
- module Pact
8
- module V2
9
- module Consumer
10
- class MessageInteractionBuilder
11
- META_CONTENT_TYPE_HEADER = "contentType"
12
-
13
- JSON_CONTENT_TYPE = "application/json"
14
- PROTO_CONTENT_TYPE = "application/protobuf"
15
-
16
- PROTOBUF_PLUGIN_NAME = "protobuf"
17
- PROTOBUF_PLUGIN_VERSION = "0.6.5"
18
-
19
- # https://docs.rs/pact_ffi/latest/pact_ffi/mock_server/handles/fn.pactffi_write_message_pact_file.html
20
- WRITE_PACT_FILE_ERRORS = {
21
- 1 => {reason: :file_not_accessible, status: 1, description: "The pact file was not able to be written"},
22
- 2 => {reason: :internal_error, status: 2, description: "The message pact for the given handle was not found"}
23
- }.freeze
24
-
25
- class PluginInitError < Pact::V2::FfiError; end
26
-
27
- # https://docs.rs/pact_ffi/0.4.17/pact_ffi/plugins/fn.pactffi_using_plugin.html
28
- INIT_PLUGIN_ERRORS = {
29
- 1 => {reason: :internal_error, status: 1, description: "A general panic was caught"},
30
- 2 => {reason: :plugin_load_failed, status: 2, description: "Failed to load the plugin"},
31
- 3 => {reason: :invalid_handle, status: 3, description: "Pact Handle is not valid"}
32
- }.freeze
33
-
34
- # https://docs.rs/pact_ffi/0.4.17/pact_ffi/plugins/fn.pactffi_interaction_contents.html
35
- CREATE_INTERACTION_ERRORS = {
36
- 1 => {reason: :internal_error, status: 1, description: "A general panic was caught"},
37
- 2 => {reason: :mock_server_already_running, status: 2, description: "The mock server has already been started"},
38
- 3 => {reason: :invalid_handle, status: 3, description: "The interaction handle is invalid"},
39
- 4 => {reason: :invalid_content_type, status: 4, description: "The content type is not valid"},
40
- 5 => {reason: :invalid_contents, status: 5, description: "The contents JSON is not valid JSON"},
41
- 6 => {reason: :plugin_error, status: 6, description: "The plugin returned an error"}
42
- }.freeze
43
-
44
- class CreateInteractionError < Pact::V2::FfiError; end
45
-
46
- class InteractionMismatchesError < Pact::V2::Error; end
47
-
48
- class InteractionBuilderError < Pact::V2::Error; end
49
-
50
- def initialize(pact_config, description: nil)
51
- @pact_config = pact_config
52
- @description = description
53
-
54
- @json_contents = nil
55
- @proto_contents = nil
56
- @proto_path = nil
57
- @proto_message_class = nil
58
- @proto_include_dirs = []
59
- @meta = {}
60
- @headers = {}
61
- @provider_state_meta = nil
62
- end
63
-
64
- def given(provider_state, metadata = {})
65
- @provider_state_meta = {provider_state => metadata}
66
- self
67
- end
68
-
69
- def upon_receiving(description)
70
- @description = description
71
- self
72
- end
73
-
74
- def with_json_contents(contents_hash)
75
- @json_contents = InteractionContents.basic(contents_hash)
76
- self
77
- end
78
-
79
- def with_proto_class(proto_path, message_class_name, include_dirs = [])
80
- absolute_path = File.expand_path(proto_path)
81
- raise InteractionBuilderError.new("proto file #{proto_path} does not exist") unless File.exist?(absolute_path)
82
-
83
- @proto_path = absolute_path
84
- @proto_message_class = message_class_name
85
- @proto_include_dirs = include_dirs.map { |dir| File.expand_path(dir) }
86
- self
87
- end
88
-
89
- def with_pact_protobuf_plugin_version(version)
90
- raise InteractionBuilderError.new("version is required") if version.blank?
91
-
92
- @proto_plugin_version = version
93
- self
94
- end
95
-
96
- def with_proto_contents(contents_hash)
97
- @proto_contents = InteractionContents.plugin(contents_hash)
98
- self
99
- end
100
-
101
- def with_metadata(meta_hash)
102
- @meta = InteractionContents.basic(meta_hash)
103
- self
104
- end
105
-
106
- def with_headers(headers_hash)
107
- @headers = InteractionContents.basic(headers_hash)
108
- self
109
- end
110
-
111
- def with_header(key, value)
112
- @headers[key] = value
113
- self
114
- end
115
-
116
- def validate!
117
- if proto_interaction?
118
- raise InteractionBuilderError.new("proto_path / proto_message are not defined, please set ones with #with_proto_message") if @proto_contents.blank? || @proto_message_class.blank?
119
- raise InteractionBuilderError.new("invalid request format, should be a hash") unless @proto_contents.is_a?(Hash)
120
- else
121
- raise InteractionBuilderError.new("invalid request format, should be a hash") unless @json_contents.is_a?(Hash)
122
- end
123
- raise InteractionBuilderError.new("description is required for message interactions, please set one with #upon_receiving") if @description.blank?
124
- end
125
-
126
- def execute(&block)
127
- raise InteractionBuilderError.new("interaction is designed to be used one-time only") if defined?(@used)
128
-
129
- validate!
130
- pact_handle = init_pact
131
- init_plugin!(pact_handle) if proto_interaction?
132
-
133
- message_pact = PactFfi::MessageConsumer.new_message_interaction(pact_handle, @description)
134
-
135
- configure_interaction!(message_pact)
136
-
137
- # strip out matchers and get raw payload/metadata
138
- payload, metadata = fetch_reified_message(pact_handle)
139
- configure_provider_state(message_pact, metadata)
140
-
141
- yield(payload, metadata)
142
-
143
- write_pacts!(pact_handle, @pact_config.pact_dir)
144
- ensure
145
- @used = true
146
- PactFfi::MessageConsumer.free_handle(message_pact)
147
- PactFfi::PluginConsumer.cleanup_plugins(pact_handle)
148
- PactFfi.free_pact_handle(pact_handle)
149
- end
150
-
151
- def build_interaction_json
152
- return JSON.dump(@json_contents) unless proto_interaction?
153
-
154
- contents = {
155
- "pact:proto": @proto_path,
156
- "pact:message-type": @proto_message_class,
157
- "pact:content-type": PROTO_CONTENT_TYPE
158
- }.merge(@proto_contents)
159
-
160
- contents["pact:protobuf-config"] = {additionalIncludes: @proto_include_dirs} if @proto_include_dirs.present?
161
-
162
- JSON.dump(contents)
163
- end
164
-
165
- private
166
-
167
- def write_pacts!(handle, dir)
168
- result = PactFfi.write_message_pact_file(handle, @pact_config.pact_dir, false)
169
- return result if WRITE_PACT_FILE_ERRORS[result].blank?
170
-
171
- error = WRITE_PACT_FILE_ERRORS[result]
172
- raise WritePactsError.new("There was an error while trying to write pact file to #{dir}", error[:reason], error[:status])
173
- end
174
-
175
- def init_pact
176
- handle = PactFfi::MessageConsumer.new_message_pact(@pact_config.consumer_name, @pact_config.provider_name)
177
- PactFfi.with_specification(handle, PactFfi::FfiSpecificationVersion["SPECIFICATION_VERSION_V4"])
178
- PactFfi.with_pact_metadata(handle, "pact-ruby-v2", "pact-ffi", PactFfi.version)
179
-
180
- Pact::V2::Native::Logger.log_to_stdout(@pact_config.log_level)
181
-
182
- handle
183
- end
184
-
185
- def fetch_reified_message(pact_handle)
186
- iterator = PactFfi::MessageConsumer.pact_handle_get_message_iter(pact_handle)
187
- raise InteractionBuilderError.new("cannot get message iterator: internal error") if iterator.blank?
188
-
189
- message_handle = PactFfi.pact_message_iter_next(iterator)
190
- raise InteractionBuilderError.new("cannot get message from iterator: no messages") if message_handle.blank?
191
-
192
- contents = fetch_reified_message_body(message_handle)
193
- meta = fetch_reified_message_headers(message_handle)
194
-
195
- [contents, meta.compact]
196
- ensure
197
- PactFfi.pact_message_iter_delete(iterator) if iterator.present?
198
- end
199
-
200
- def fetch_reified_message_headers(message_handle)
201
- meta = {"headers" => {}}
202
-
203
- meta[META_CONTENT_TYPE_HEADER] = PactFfi.message_find_metadata(message_handle, META_CONTENT_TYPE_HEADER)
204
-
205
- @meta.each_key do |key|
206
- meta[key.to_s] = PactFfi.message_find_metadata(message_handle, key.to_s)
207
- end
208
-
209
- @headers.each_key do |key|
210
- meta["headers"][key.to_s] = PactFfi.message_find_metadata(message_handle, key.to_s)
211
- end
212
-
213
- meta
214
- end
215
-
216
- def configure_provider_state(message_pact, reified_metadata)
217
- content_type = reified_metadata[META_CONTENT_TYPE_HEADER]
218
- @provider_state_meta&.each_pair do |provider_state, meta|
219
- if meta.present?
220
- meta.each_pair { |k, v| PactFfi.given_with_param(message_pact, provider_state, k.to_s, v.to_s) }
221
- PactFfi.given_with_param(message_pact, provider_state, META_CONTENT_TYPE_HEADER, content_type.to_s) if content_type
222
- elsif content_type.present?
223
- PactFfi.given_with_param(message_pact, provider_state, META_CONTENT_TYPE_HEADER, content_type.to_s)
224
- else
225
- PactFfi.given(message_pact, provider_state)
226
- end
227
- end
228
- end
229
-
230
- def fetch_reified_message_body(message_handle)
231
- if proto_interaction?
232
- len = PactFfi::MessageConsumer.get_contents_length(message_handle)
233
- ptr = PactFfi::MessageConsumer.get_contents_bin(message_handle)
234
- return nil if ptr.blank? || len == 0
235
-
236
- return String.new(ptr.read_string_length(len))
237
- end
238
-
239
- contents = PactFfi::MessageConsumer.get_contents(message_handle)
240
- return nil if contents.blank?
241
-
242
- JSON.parse(contents)
243
- end
244
-
245
- def configure_interaction!(message_pact)
246
- interaction_json = build_interaction_json
247
-
248
- if proto_interaction?
249
- result = PactFfi::PluginConsumer.interaction_contents(message_pact, 0, PROTO_CONTENT_TYPE, interaction_json)
250
- if CREATE_INTERACTION_ERRORS[result].present?
251
- error = CREATE_INTERACTION_ERRORS[result]
252
- raise CreateInteractionError.new("There was an error while trying to add interaction \"#{@description}\"", error[:reason], error[:status])
253
- end
254
- else
255
- result = PactFfi.with_body(message_pact, 0, JSON_CONTENT_TYPE, interaction_json)
256
- unless result
257
- raise InteractionMismatchesError.new("There was an error while trying to add message interaction contents \"#{@description}\"")
258
- end
259
- end
260
-
261
- # meta should be configured last to avoid resetting after body is set
262
- InteractionContents.basic(@meta.merge(@headers)).each_pair do |key, value|
263
- PactFfi::MessageConsumer.with_metadata_v2(message_pact, key.to_s, JSON.dump(value))
264
- end
265
- end
266
-
267
- def init_plugin!(pact_handle)
268
- result = PactFfi::PluginConsumer.using_plugin(pact_handle, PROTOBUF_PLUGIN_NAME, @proto_plugin_version || PROTOBUF_PLUGIN_VERSION)
269
- return result if INIT_PLUGIN_ERRORS[result].blank?
270
-
271
- error = INIT_PLUGIN_ERRORS[result]
272
- raise PluginInitError.new("There was an error while trying to initialize plugin #{PROTOBUF_PLUGIN_NAME}/#{@proto_plugin_version || PROTOBUF_PLUGIN_VERSION}", error[:reason], error[:status])
273
- end
274
-
275
- def serialize_metadata(metadata_hash)
276
- metadata = metadata_hash.deep_dup
277
- serialize_as!(metadata, :basic)
278
-
279
- metadata
280
- end
281
-
282
- def proto_interaction?
283
- @proto_contents.present?
284
- end
285
- end
286
- end
287
- end
288
- end
@@ -1,108 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "pact/ffi/mock_server"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- class MockServer
9
- attr_reader :host, :port, :transport, :handle, :url
10
-
11
- TRANSPORT_HTTP = "http"
12
- TRANSPORT_GRPC = "grpc"
13
-
14
- class MockServerCreateError < Pact::V2::FfiError; end
15
-
16
- class WritePactsError < Pact::V2::FfiError; end
17
-
18
- # https://docs.rs/pact_ffi/0.4.17/pact_ffi/mock_server/fn.pactffi_create_mock_server_for_transport.html
19
- CREATE_TRANSPORT_ERRORS = {
20
- -1 => {reason: :invalid_handle, status: -1, description: "An invalid handle was received. Handles should be created with pactffi_new_pact"},
21
- -2 => {reason: :invalid_transport_json, status: -2, description: "Transport_config is not valid JSON"},
22
- -3 => {reason: :mock_server_not_started, status: -3, description: "The mock server could not be started"},
23
- -4 => {reason: :internal_error, status: -4, description: "The method panicked"},
24
- -5 => {reason: :invalid_host, status: -5, description: "The address is not valid"}
25
- }.freeze
26
-
27
- # https://docs.rs/pact_ffi/0.4.17/pact_ffi/mock_server/fn.pactffi_write_pact_file.html
28
- WRITE_PACT_FILE_ERRORS = {
29
- 1 => {reason: :internal_error, status: 1, description: "A general panic was caught"},
30
- 2 => {reason: :file_not_accessible, status: 2, description: "The pact file was not able to be written"},
31
- 3 => {reason: :mock_server_not_found, status: 3, description: "A mock server with the provided port was not found"}
32
- }.freeze
33
-
34
- def self.create_for_grpc!(pact:, host: "127.0.0.1", port: 0)
35
- new(pact: pact, transport: TRANSPORT_GRPC, host: host, port: port)
36
- end
37
-
38
- def self.create_for_http!(pact:, host: "127.0.0.1", port: 0)
39
- new(pact: pact, transport: TRANSPORT_HTTP, host: host, port: port)
40
- end
41
-
42
- def self.create_for_transport!(pact:, transport:, host: "127.0.0.1", port: 0)
43
- new(pact: pact, transport: transport, host: host, port: port)
44
- end
45
-
46
- def initialize(pact:, transport:, host:, port:)
47
-
48
- @pact = pact
49
- @transport = transport
50
- @host = host
51
- @port = port
52
-
53
- @handle = init_transport!
54
- # the returned handle is the port number
55
- # we set it here, so we can consume a port number of 0
56
- # and allow pact to assign a random available port
57
- @port = @handle
58
- # construct the url for the mock server
59
- # as a convenience for the user
60
- @url = "#{transport}://#{host}:#{@handle}"
61
- # TODO: handle auto-GC of native memory
62
- # ObjectSpace.define_finalizer(self, proc do
63
- # cleanup
64
- # end)
65
- end
66
-
67
- def write_pacts!(dir)
68
- result = PactFfi::MockServer.write_pact_file(@handle, dir, false)
69
- return result if WRITE_PACT_FILE_ERRORS[result].blank?
70
-
71
- error = WRITE_PACT_FILE_ERRORS[result]
72
- raise WritePactsError.new("There was an error while trying to write pact file to #{dir}", error[:reason], error[:status])
73
- end
74
-
75
- def matched?
76
- PactFfi::MockServer.matched(@handle)
77
- end
78
-
79
- def mismatches
80
- PactFfi::MockServer.mismatches(@handle)
81
- end
82
-
83
- def cleanup
84
- PactFfi::MockServer.cleanup(@handle)
85
- end
86
-
87
- def cleanup_plugins
88
- PactFfi::PluginConsumer.cleanup_plugins(@handle)
89
- end
90
-
91
- def free_pact_handle
92
- PactFfi.free_pact_handle(@handle)
93
- end
94
-
95
- private
96
-
97
- def init_transport!
98
- handle = PactFfi::MockServer.create_for_transport(@pact, @host, @port, @transport, nil)
99
- # the returned handle is the port number
100
- return handle if CREATE_TRANSPORT_ERRORS[handle].blank?
101
-
102
- error = CREATE_TRANSPORT_ERRORS[handle]
103
- raise MockServerCreateError.new("There was an error while trying to create mock server for transport:#{@transport}", error[:reason], error[:status])
104
- end
105
- end
106
- end
107
- end
108
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Pact
4
- module V2
5
- module Consumer
6
- module PactConfig
7
- class Base
8
- attr_reader :consumer_name, :provider_name, :pact_dir, :log_level
9
-
10
- def initialize(consumer_name:, provider_name:, opts: {})
11
- @consumer_name = consumer_name
12
- @provider_name = provider_name
13
- @pact_dir = opts[:pact_dir] || (defined?(Rails) ? Rails.root.join("../pacts").to_s : "pacts")
14
- @log_level = opts[:log_level] || :info
15
- end
16
-
17
- def new_interaction(description = nil)
18
- raise Pact::V2::ImplementationRequired, "#new_interaction should be implemented"
19
- end
20
- end
21
- end
22
- end
23
- end
24
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- class Grpc < Base
10
- attr_reader :mock_host, :mock_port
11
-
12
- def initialize(consumer_name:, provider_name:, opts: {})
13
- super
14
-
15
- @mock_host = opts[:mock_host] || "127.0.0.1"
16
- @mock_port = opts[:mock_port] || 3009
17
- end
18
-
19
- def new_interaction(description = nil)
20
- GrpcInteractionBuilder.new(self, description: description)
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- class Http < Base
10
- attr_reader :mock_host, :mock_port, :pact_handle
11
-
12
- def initialize(consumer_name:, provider_name:, opts: {})
13
- super
14
-
15
- @mock_host = opts[:mock_host] || "127.0.0.1"
16
- @mock_port = opts[:mock_port] || 0
17
- @log_level = opts[:log_level] || :info
18
- @pact_specification = get_pact_specification(opts)
19
- @pact_handle = init_pact
20
- end
21
-
22
- def new_interaction(description = nil)
23
- HttpInteractionBuilder.new(self, description: description)
24
- end
25
-
26
- def reset_pact
27
- @pact_handle = init_pact
28
- end
29
-
30
- def get_pact_specification(opts)
31
- pact_spec_version = opts[:pact_specification] || "V4"
32
- unless pact_spec_version.match?(/^v?[1-4](\.\d+){0,2}$/i)
33
- raise ArgumentError, "Invalid pact specification version format \n Valid versions are 1, 1.1, 2, 3, 4. Default is V4 \n V prefix is optional, and case insensitive"
34
- end
35
- pact_spec_version = pact_spec_version.upcase
36
- pact_spec_version = "V#{pact_spec_version}" unless pact_spec_version.start_with?("V")
37
- pact_spec_version = pact_spec_version.sub(/(\.0+)+$/, "")
38
- pact_spec_version = pact_spec_version.tr(".", "_")
39
- PactFfi::FfiSpecificationVersion["SPECIFICATION_VERSION_#{pact_spec_version.upcase}"]
40
- end
41
-
42
- def init_pact
43
- handle = PactFfi.new_pact(consumer_name, provider_name)
44
- PactFfi.with_specification(handle, @pact_specification)
45
- PactFfi.with_pact_metadata(handle, "pact-ruby-v2", "pact-ffi", PactFfi.version)
46
-
47
- Pact::V2::Native::Logger.log_to_stdout(@log_level)
48
-
49
- handle
50
- end
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- class Message < Base
10
- def new_interaction(description = nil)
11
- MessageInteractionBuilder.new(self, description: description)
12
- end
13
- end
14
- end
15
- end
16
- end
17
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- class PluginAsyncMessage < Base
10
- attr_reader :mock_host, :mock_port
11
-
12
- def initialize(consumer_name:, provider_name:, opts: {})
13
- super
14
-
15
- @mock_host = opts[:mock_host] || "127.0.0.1"
16
- @mock_port = opts[:mock_port] || 0
17
- end
18
-
19
- def new_interaction(description = nil)
20
- PluginAsyncMessageInteractionBuilder.new(self, description: description)
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- class PluginHttp < Base
10
- attr_reader :mock_host, :mock_port
11
-
12
- def initialize(consumer_name:, provider_name:, opts: {})
13
- super
14
-
15
- @mock_host = opts[:mock_host] || "127.0.0.1"
16
- @mock_port = opts[:mock_port] || 0
17
- end
18
-
19
- def new_interaction(description = nil)
20
- PluginHttpInteractionBuilder.new(self, description: description)
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- class PluginSyncMessage < Base
10
- attr_reader :mock_host, :mock_port
11
-
12
- def initialize(consumer_name:, provider_name:, opts: {})
13
- super
14
-
15
- @mock_host = opts[:mock_host] || "127.0.0.1"
16
- @mock_port = opts[:mock_port] || 0
17
- end
18
-
19
- def new_interaction(description = nil)
20
- PluginSyncMessageInteractionBuilder.new(self, description: description)
21
- end
22
- end
23
- end
24
- end
25
- end
26
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "pact_config/grpc"
4
-
5
- module Pact
6
- module V2
7
- module Consumer
8
- module PactConfig
9
- def self.new(transport_type, consumer_name:, provider_name:, opts: {})
10
- case transport_type
11
- when :http
12
- Http.new(consumer_name: consumer_name, provider_name: provider_name, opts: opts)
13
- when :grpc
14
- Grpc.new(consumer_name: consumer_name, provider_name: provider_name, opts: opts)
15
- when :message
16
- Message.new(consumer_name: consumer_name, provider_name: provider_name, opts: opts)
17
- when :plugin_sync_message
18
- PluginSyncMessage.new(consumer_name: consumer_name, provider_name: provider_name, opts: opts)
19
- when :plugin_async_message
20
- PluginAsyncMessage.new(consumer_name: consumer_name, provider_name: provider_name, opts: opts)
21
- when :plugin_http
22
- PluginHttp.new(consumer_name: consumer_name, provider_name: provider_name, opts: opts)
23
- else
24
- raise ArgumentError, "unknown transport_type: #{transport_type}"
25
- end
26
- end
27
- end
28
- end
29
- end
30
- end