pact-v2 2.0.0.pre.preview1

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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1321 -0
  3. data/LICENSE.txt +23 -0
  4. data/bin/pact +4 -0
  5. data/lib/pact/cli/generate_pact_docs.rb +4 -0
  6. data/lib/pact/cli/run_pact_verification.rb +99 -0
  7. data/lib/pact/cli/spec_criteria.rb +26 -0
  8. data/lib/pact/cli.rb +45 -0
  9. data/lib/pact/consumer/configuration/configuration_extensions.rb +90 -0
  10. data/lib/pact/consumer/configuration/dsl.rb +11 -0
  11. data/lib/pact/consumer/configuration/mock_service.rb +112 -0
  12. data/lib/pact/consumer/configuration/service_consumer.rb +51 -0
  13. data/lib/pact/consumer/configuration/service_provider.rb +40 -0
  14. data/lib/pact/consumer/configuration.rb +10 -0
  15. data/lib/pact/consumer/consumer_contract_builder.rb +82 -0
  16. data/lib/pact/consumer/consumer_contract_builders.rb +10 -0
  17. data/lib/pact/consumer/interaction_builder.rb +45 -0
  18. data/lib/pact/consumer/rspec.rb +35 -0
  19. data/lib/pact/consumer/spec_hooks.rb +40 -0
  20. data/lib/pact/consumer/world.rb +37 -0
  21. data/lib/pact/consumer.rb +7 -0
  22. data/lib/pact/doc/README.md +13 -0
  23. data/lib/pact/doc/doc_file.rb +40 -0
  24. data/lib/pact/doc/generate.rb +11 -0
  25. data/lib/pact/doc/generator.rb +82 -0
  26. data/lib/pact/doc/interaction_view_model.rb +124 -0
  27. data/lib/pact/doc/markdown/consumer_contract_renderer.rb +68 -0
  28. data/lib/pact/doc/markdown/generator.rb +26 -0
  29. data/lib/pact/doc/markdown/index_renderer.rb +43 -0
  30. data/lib/pact/doc/markdown/interaction.erb +14 -0
  31. data/lib/pact/doc/markdown/interaction_renderer.rb +43 -0
  32. data/lib/pact/doc/sort_interactions.rb +16 -0
  33. data/lib/pact/hal/authorization_header_redactor.rb +32 -0
  34. data/lib/pact/hal/entity.rb +110 -0
  35. data/lib/pact/hal/http_client.rb +128 -0
  36. data/lib/pact/hal/link.rb +112 -0
  37. data/lib/pact/hal/non_json_entity.rb +28 -0
  38. data/lib/pact/hash_refinements.rb +17 -0
  39. data/lib/pact/pact_broker/fetch_pact_uris_for_verification.rb +112 -0
  40. data/lib/pact/pact_broker/fetch_pacts.rb +103 -0
  41. data/lib/pact/pact_broker/notices.rb +34 -0
  42. data/lib/pact/pact_broker/pact_selection_description.rb +66 -0
  43. data/lib/pact/pact_broker.rb +25 -0
  44. data/lib/pact/project_root.rb +7 -0
  45. data/lib/pact/provider/configuration/configuration_extension.rb +69 -0
  46. data/lib/pact/provider/configuration/dsl.rb +18 -0
  47. data/lib/pact/provider/configuration/message_provider_dsl.rb +63 -0
  48. data/lib/pact/provider/configuration/pact_verification.rb +48 -0
  49. data/lib/pact/provider/configuration/pact_verification_from_broker.rb +126 -0
  50. data/lib/pact/provider/configuration/service_provider_config.rb +32 -0
  51. data/lib/pact/provider/configuration/service_provider_dsl.rb +107 -0
  52. data/lib/pact/provider/configuration.rb +7 -0
  53. data/lib/pact/provider/context.rb +0 -0
  54. data/lib/pact/provider/help/console_text.rb +76 -0
  55. data/lib/pact/provider/help/content.rb +38 -0
  56. data/lib/pact/provider/help/pact_diff.rb +43 -0
  57. data/lib/pact/provider/help/prompt_text.rb +49 -0
  58. data/lib/pact/provider/help/write.rb +56 -0
  59. data/lib/pact/provider/matchers/messages.rb +66 -0
  60. data/lib/pact/provider/pact_helper_locator.rb +24 -0
  61. data/lib/pact/provider/pact_source.rb +40 -0
  62. data/lib/pact/provider/pact_spec_runner.rb +188 -0
  63. data/lib/pact/provider/pact_uri.rb +55 -0
  64. data/lib/pact/provider/pact_verification.rb +17 -0
  65. data/lib/pact/provider/print_missing_provider_states.rb +35 -0
  66. data/lib/pact/provider/request.rb +77 -0
  67. data/lib/pact/provider/rspec/backtrace_formatter.rb +43 -0
  68. data/lib/pact/provider/rspec/calculate_exit_code.rb +18 -0
  69. data/lib/pact/provider/rspec/custom_options_file +0 -0
  70. data/lib/pact/provider/rspec/formatter_rspec_2.rb +76 -0
  71. data/lib/pact/provider/rspec/formatter_rspec_3.rb +195 -0
  72. data/lib/pact/provider/rspec/json_formatter.rb +100 -0
  73. data/lib/pact/provider/rspec/matchers.rb +80 -0
  74. data/lib/pact/provider/rspec/pact_broker_formatter.rb +76 -0
  75. data/lib/pact/provider/rspec.rb +234 -0
  76. data/lib/pact/provider/state/provider_state.rb +180 -0
  77. data/lib/pact/provider/state/provider_state_configured_modules.rb +15 -0
  78. data/lib/pact/provider/state/provider_state_manager.rb +42 -0
  79. data/lib/pact/provider/state/provider_state_proxy.rb +39 -0
  80. data/lib/pact/provider/state/set_up.rb +13 -0
  81. data/lib/pact/provider/state/tear_down.rb +13 -0
  82. data/lib/pact/provider/test_methods.rb +77 -0
  83. data/lib/pact/provider/verification_report.rb +36 -0
  84. data/lib/pact/provider/verification_results/create.rb +88 -0
  85. data/lib/pact/provider/verification_results/publish.rb +143 -0
  86. data/lib/pact/provider/verification_results/publish_all.rb +50 -0
  87. data/lib/pact/provider/verification_results/verification_result.rb +40 -0
  88. data/lib/pact/provider/world.rb +50 -0
  89. data/lib/pact/provider.rb +3 -0
  90. data/lib/pact/retry.rb +37 -0
  91. data/lib/pact/tasks/task_helper.rb +62 -0
  92. data/lib/pact/tasks/verification_task.rb +95 -0
  93. data/lib/pact/tasks.rb +2 -0
  94. data/lib/pact/templates/help.erb +22 -0
  95. data/lib/pact/templates/provider_state.erb +14 -0
  96. data/lib/pact/utils/metrics.rb +100 -0
  97. data/lib/pact/utils/string.rb +35 -0
  98. data/lib/pact/v2/configuration.rb +23 -0
  99. data/lib/pact/v2/consumer/grpc_interaction_builder.rb +187 -0
  100. data/lib/pact/v2/consumer/http_interaction_builder.rb +163 -0
  101. data/lib/pact/v2/consumer/interaction_contents.rb +54 -0
  102. data/lib/pact/v2/consumer/message_interaction_builder.rb +280 -0
  103. data/lib/pact/v2/consumer/mock_server.rb +99 -0
  104. data/lib/pact/v2/consumer/pact_config/base.rb +24 -0
  105. data/lib/pact/v2/consumer/pact_config/grpc.rb +26 -0
  106. data/lib/pact/v2/consumer/pact_config/http.rb +55 -0
  107. data/lib/pact/v2/consumer/pact_config/message.rb +17 -0
  108. data/lib/pact/v2/consumer/pact_config.rb +24 -0
  109. data/lib/pact/v2/consumer.rb +8 -0
  110. data/lib/pact/v2/matchers/base.rb +67 -0
  111. data/lib/pact/v2/matchers/v1/equality.rb +19 -0
  112. data/lib/pact/v2/matchers/v2/regex.rb +19 -0
  113. data/lib/pact/v2/matchers/v2/type.rb +17 -0
  114. data/lib/pact/v2/matchers/v3/boolean.rb +17 -0
  115. data/lib/pact/v2/matchers/v3/date.rb +18 -0
  116. data/lib/pact/v2/matchers/v3/date_time.rb +18 -0
  117. data/lib/pact/v2/matchers/v3/decimal.rb +17 -0
  118. data/lib/pact/v2/matchers/v3/each.rb +42 -0
  119. data/lib/pact/v2/matchers/v3/include.rb +17 -0
  120. data/lib/pact/v2/matchers/v3/integer.rb +17 -0
  121. data/lib/pact/v2/matchers/v3/number.rb +17 -0
  122. data/lib/pact/v2/matchers/v3/time.rb +18 -0
  123. data/lib/pact/v2/matchers/v4/each_key.rb +26 -0
  124. data/lib/pact/v2/matchers/v4/each_key_value.rb +32 -0
  125. data/lib/pact/v2/matchers/v4/each_value.rb +33 -0
  126. data/lib/pact/v2/matchers/v4/not_empty.rb +17 -0
  127. data/lib/pact/v2/matchers.rb +94 -0
  128. data/lib/pact/v2/native/blocking_verifier.rb +17 -0
  129. data/lib/pact/v2/native/logger.rb +25 -0
  130. data/lib/pact/v2/provider/async_message_verifier.rb +28 -0
  131. data/lib/pact/v2/provider/base_verifier.rb +242 -0
  132. data/lib/pact/v2/provider/grpc_verifier.rb +38 -0
  133. data/lib/pact/v2/provider/gruf_server.rb +75 -0
  134. data/lib/pact/v2/provider/http_server.rb +79 -0
  135. data/lib/pact/v2/provider/http_verifier.rb +43 -0
  136. data/lib/pact/v2/provider/message_provider_servlet.rb +79 -0
  137. data/lib/pact/v2/provider/mixed_verifier.rb +22 -0
  138. data/lib/pact/v2/provider/pact_broker_proxy.rb +71 -0
  139. data/lib/pact/v2/provider/pact_broker_proxy_runner.rb +77 -0
  140. data/lib/pact/v2/provider/pact_config/async.rb +29 -0
  141. data/lib/pact/v2/provider/pact_config/base.rb +101 -0
  142. data/lib/pact/v2/provider/pact_config/grpc.rb +26 -0
  143. data/lib/pact/v2/provider/pact_config/http.rb +27 -0
  144. data/lib/pact/v2/provider/pact_config/mixed.rb +39 -0
  145. data/lib/pact/v2/provider/pact_config.rb +26 -0
  146. data/lib/pact/v2/provider/provider_server_runner.rb +89 -0
  147. data/lib/pact/v2/provider/provider_state_configuration.rb +32 -0
  148. data/lib/pact/v2/provider/provider_state_servlet.rb +86 -0
  149. data/lib/pact/v2/provider.rb +8 -0
  150. data/lib/pact/v2/railtie.rb +13 -0
  151. data/lib/pact/v2/rspec/support/pact_consumer_helpers.rb +80 -0
  152. data/lib/pact/v2/rspec/support/pact_message_helpers.rb +42 -0
  153. data/lib/pact/v2/rspec/support/pact_provider_helpers.rb +129 -0
  154. data/lib/pact/v2/rspec/support/waterdrop/pact_waterdrop_client.rb +27 -0
  155. data/lib/pact/v2/rspec/support/webmock/webmock_helpers.rb +30 -0
  156. data/lib/pact/v2/rspec.rb +17 -0
  157. data/lib/pact/v2/tasks/pact.rake +13 -0
  158. data/lib/pact/v2/version.rb +8 -0
  159. data/lib/pact/v2.rb +71 -0
  160. data/lib/pact/version.rb +4 -0
  161. data/lib/pact.rb +13 -0
  162. data/lib/tasks/pact.rake +34 -0
  163. data/pact.gemspec +106 -0
  164. metadata +529 -0
data/LICENSE.txt ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 REA Group Ltd
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
+
data/bin/pact ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pact/cli'
4
+ Pact::CLI.start
@@ -0,0 +1,4 @@
1
+ require 'pact/doc/doc_file'
2
+ require 'pact/doc/generate'
3
+ require 'pact/doc/markdown/generator'
4
+ require 'pact/consumer'
@@ -0,0 +1,99 @@
1
+ require 'pact/cli/spec_criteria'
2
+
3
+ module Pact
4
+ module Cli
5
+ class RunPactVerification
6
+ attr_reader :options
7
+
8
+ def initialize options
9
+ @options = options
10
+ end
11
+
12
+ def self.call options
13
+ new(options).call
14
+ end
15
+
16
+ def call
17
+ configure_output
18
+ initialize_rspec
19
+ setup_load_path
20
+ load_pact_helper
21
+ run_specs
22
+ end
23
+
24
+ private
25
+
26
+ def initialize_rspec
27
+ # With RSpec3, if the pact_helper loads a library that adds its own formatter before we set one,
28
+ # we will get a ProgressFormatter too, and get little dots sprinkled throughout our output.
29
+ # Load a NilFormatter here to prevent that.
30
+ require 'rspec'
31
+ require 'pact/rspec'
32
+ ::RSpec.configuration.add_formatter Pact::RSpec.formatter_class.const_get('NilFormatter')
33
+ end
34
+
35
+ def setup_load_path
36
+ require 'pact/provider/pact_spec_runner'
37
+ lib = File.join(Dir.pwd, "lib") # Assume we are running from within the project root. RSpec is smarter about this.
38
+ spec = File.join(Dir.pwd, "spec")
39
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
40
+ $LOAD_PATH.unshift(spec) if Dir.exist?(spec) && !$LOAD_PATH.include?(spec)
41
+ end
42
+
43
+ def load_pact_helper
44
+ load options[:pact_helper]
45
+ end
46
+
47
+ def run_specs
48
+ exit_code = if options[:pact_uri].is_a?(String)
49
+ run_with_pact_url_string
50
+ elsif options[:pact_uri]
51
+ run_with_pact_uri_object # from pact-provider-verifier
52
+ else
53
+ run_with_configured_pacts_from_pact_helper
54
+ end
55
+ exit exit_code
56
+ end
57
+
58
+ def run_with_pact_url_string
59
+ pact_repository_uri_options = {}
60
+ pact_repository_uri_options[:username] = ENV['PACT_BROKER_USERNAME'] if ENV['PACT_BROKER_USERNAME']
61
+ pact_repository_uri_options[:password] = ENV['PACT_BROKER_PASSWORD'] if ENV['PACT_BROKER_PASSWORD']
62
+ pact_repository_uri_options[:token] = ENV['PACT_BROKER_TOKEN']
63
+ pact_repository_uri_options[:username] = options[:pact_broker_username] if options[:pact_broker_username]
64
+ pact_repository_uri_options[:password] = options[:pact_broker_password] if options[:pact_broker_password]
65
+ pact_uri = ::Pact::Provider::PactURI.new(options[:pact_uri], pact_repository_uri_options)
66
+ Pact::Provider::PactSpecRunner.new([pact_uri], pact_spec_options).run
67
+ end
68
+
69
+ def run_with_pact_uri_object
70
+ Pact::Provider::PactSpecRunner.new([options[:pact_uri]], pact_spec_options).run
71
+ end
72
+
73
+ def run_with_configured_pacts_from_pact_helper
74
+ pact_urls = Pact.provider_world.pact_urls
75
+ Pact::Provider::PactSpecRunner.new(pact_urls, pact_spec_options).run
76
+ end
77
+
78
+ def pact_spec_options
79
+ {
80
+ full_backtrace: options[:backtrace],
81
+ verbose: options[:verbose] || ENV['VERBOSE'] == 'true',
82
+ criteria: SpecCriteria.call(options),
83
+ format: options[:format],
84
+ out: options[:out],
85
+ ignore_failures: options[:ignore_failures],
86
+ request_customizer: options[:request_customizer]
87
+ }
88
+ end
89
+
90
+ def configure_output
91
+ if options[:format] == 'json' && !options[:out]
92
+ # Don't want to mess up the JSON parsing with messages to stdout, so send it to stderr
93
+ require 'pact/configuration'
94
+ Pact.configuration.output_stream = Pact.configuration.error_stream
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,26 @@
1
+ module Pact
2
+ module Cli
3
+ class SpecCriteria
4
+
5
+ def self.call options
6
+ criteria = {}
7
+
8
+ criteria[:description] = Regexp.new(options[:description]) if options[:description]
9
+ criteria[:_id] = options[:pact_broker_interaction_id] if options[:pact_broker_interaction_id]
10
+ criteria[:index] = options[:interaction_index] if options[:interaction_index]
11
+
12
+ provider_state = options[:provider_state]
13
+
14
+ if provider_state
15
+ if provider_state.length == 0
16
+ criteria[:provider_state] = nil #Allow PACT_PROVIDER_STATE="" to mean no provider state
17
+ else
18
+ criteria[:provider_state] = Regexp.new(provider_state)
19
+ end
20
+ end
21
+
22
+ criteria
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/pact/cli.rb ADDED
@@ -0,0 +1,45 @@
1
+ require 'thor'
2
+ require 'pact/consumer/configuration'
3
+ require 'pact/provider/configuration'
4
+
5
+ module Pact
6
+ class CLI < Thor
7
+ def self.exit_on_failure? # Thor 1.0 deprecation guard
8
+ false
9
+ end
10
+
11
+ desc 'verify', "Verify a pact"
12
+ method_option :pact_helper, aliases: "-h", desc: "Pact helper file", :required => true
13
+ method_option :pact_uri, aliases: "-p", desc: "Pact URI"
14
+ method_option :ignore_failures, type: :boolean, default: false, desc: "Process will always exit with exit code 0", hide: true
15
+ method_option :pact_broker_username, aliases: "-u", desc: "Pact broker user name"
16
+ method_option :pact_broker_password, aliases: "-w", desc: "Pact broker password"
17
+ method_option :pact_broker_token, aliases: "-k", desc: "Pact broker token"
18
+ method_option :backtrace, aliases: "-b", desc: "Show full backtrace", :default => false, :type => :boolean
19
+ method_option :verbose, aliases: "-v", desc: "Show verbose HTTP logging", :default => false, :type => :boolean
20
+ method_option :interactions_replay_order, aliases: "-o",
21
+ desc: "Interactions replay order: randomised or recorded (default)",
22
+ default: Pact.configuration.interactions_replay_order
23
+ method_option :description, aliases: "-d", desc: "Interaction description filter"
24
+ method_option :provider_state, aliases: "-s", desc: "Provider state filter"
25
+ method_option :interaction_index, type: :numeric, desc: "Index filter"
26
+ method_option :pact_broker_interaction_id, desc: "Pact Broker interaction ID filter"
27
+ method_option :format, aliases: "-f", banner: "FORMATTER", desc: "RSpec formatter. Defaults to custom Pact formatter. [j]son may also be used."
28
+ method_option :out, aliases: "-o", banner: "FILE", desc: "Write output to a file instead of $stdout."
29
+
30
+ def verify
31
+ require 'pact/cli/run_pact_verification'
32
+ Cli::RunPactVerification.call(options)
33
+ end
34
+
35
+ desc 'docs', "Generate Pact documentation in markdown"
36
+ method_option :pact_dir, desc: "Directory containing the pacts", default: Pact.configuration.pact_dir
37
+ method_option :doc_dir, desc: "Documentation directory", default: Pact.configuration.doc_dir
38
+
39
+ def docs
40
+ require 'pact/cli/generate_pact_docs'
41
+ require 'pact/doc/generator'
42
+ Pact::Doc::Generate.call(options[:pact_dir], options[:doc_dir], [Pact::Doc::Markdown::Generator])
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,90 @@
1
+ require 'pact/configuration'
2
+ require 'pact/doc/markdown/generator'
3
+
4
+ module Pact
5
+ module Consumer
6
+ module Configuration
7
+
8
+ module ConfigurationExtensions
9
+
10
+ DOC_GENERATORS = { markdown: Pact::Doc::Markdown::Generator }
11
+
12
+ def doc_dir
13
+ @doc_dir ||= File.expand_path("./doc/pacts")
14
+ end
15
+
16
+ def doc_dir= doc_dir
17
+ @doc_dir = doc_dir
18
+ end
19
+
20
+ def reports_dir
21
+ @reports_dir ||= default_reports_dir
22
+ end
23
+
24
+ def default_reports_dir
25
+ File.expand_path("./reports/pacts")
26
+ end
27
+
28
+ def reports_dir= reports_dir
29
+ @reports_dir = reports_dir
30
+ end
31
+
32
+ def add_provider_verification &block
33
+ provider_verifications << block
34
+ end
35
+
36
+ def provider_verifications
37
+ @provider_verifications ||= []
38
+ end
39
+
40
+ def doc_generator= doc_generator
41
+ add_doc_generator doc_generator
42
+ end
43
+
44
+ def add_doc_generator doc_generator
45
+ doc_generators << begin
46
+ if DOC_GENERATORS[doc_generator]
47
+ DOC_GENERATORS[doc_generator]
48
+ elsif doc_generator.respond_to?(:call)
49
+ doc_generator
50
+ else
51
+ raise "doc_generator needs to respond to call, or be in the preconfigured list: #{DOC_GENERATORS.keys}"
52
+ end
53
+ end
54
+ end
55
+
56
+ def doc_generators
57
+ @doc_generators ||= []
58
+ end
59
+
60
+ def pactfile_write_mode
61
+ @pactfile_write_mode ||= :overwrite
62
+ if @pactfile_write_mode == :smart
63
+ is_rake_running? ? :overwrite : :update
64
+ else
65
+ @pactfile_write_mode
66
+ end
67
+ end
68
+
69
+ def pactfile_write_mode= pactfile_write_mode
70
+ @pactfile_write_mode = pactfile_write_mode
71
+ end
72
+
73
+ def pactfile_write_order
74
+ @pactfile_write_order ||= :chronological #or :alphabetical
75
+ end
76
+
77
+ def pactfile_write_order= pactfile_write_order
78
+ @pactfile_write_order = pactfile_write_order.to_sym
79
+ end
80
+
81
+ private
82
+
83
+ #Would love a better way of determining this! It sure won't work on windows.
84
+ def is_rake_running?
85
+ `ps -ef | grep rake | grep #{Process.ppid} | grep -v 'grep'`.size > 0
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,11 @@
1
+ require 'pact/consumer/configuration/service_consumer'
2
+
3
+ module Pact
4
+ module Consumer
5
+ module DSL
6
+ def service_consumer name, &block
7
+ Configuration::ServiceConsumer.build(name, &block)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,112 @@
1
+ require 'pact/mock_service/version'
2
+ require 'pact/mock_service/app_manager'
3
+ require 'pact/consumer/consumer_contract_builder'
4
+ require 'pact/consumer/consumer_contract_builders'
5
+ require 'pact/consumer/world'
6
+
7
+ module Pact
8
+ module Consumer
9
+ module Configuration
10
+ class MockService
11
+
12
+ extend Pact::DSL
13
+
14
+ attr_accessor :port, :host, :standalone, :verify, :provider_name, :consumer_name, :pact_specification_version
15
+
16
+ def initialize name, consumer_name, provider_name
17
+ @name = name
18
+ @consumer_name = consumer_name
19
+ @provider_name = provider_name
20
+ @port = nil
21
+ @host = "localhost"
22
+ @standalone = false
23
+ @verify = true
24
+ @pact_specification_version = '2'
25
+ @finalized = false
26
+ end
27
+
28
+ dsl do
29
+ def port port
30
+ self.port = port
31
+ end
32
+
33
+ def host host
34
+ self.host = host
35
+ end
36
+
37
+ def standalone standalone
38
+ self.standalone = standalone
39
+ end
40
+
41
+ def verify verify
42
+ self.verify = verify
43
+ end
44
+
45
+ def pact_specification_version pact_specification_version
46
+ self.pact_specification_version = pact_specification_version
47
+ end
48
+ end
49
+
50
+ def finalize
51
+ raise 'Already finalized!' if @finalized
52
+ register_mock_service
53
+ configure_consumer_contract_builder
54
+ @finalized = true
55
+ end
56
+
57
+ private
58
+
59
+ def register_mock_service
60
+ unless standalone
61
+ url = "http://#{host}#{port.nil? ? '' : ":#{port}"}"
62
+ ret = Pact::MockService::AppManager.instance.register_mock_service_for(provider_name, url, mock_service_options)
63
+ raise "pact-mock_service(v#{Pact::MockService::VERSION}) does not support 'find available port' feature" unless ret
64
+ @port = ret
65
+ end
66
+ end
67
+
68
+ def configure_consumer_contract_builder
69
+ consumer_contract_builder = create_consumer_contract_builder
70
+ create_consumer_contract_builders_method consumer_contract_builder
71
+ setup_verification(consumer_contract_builder) if verify
72
+ consumer_contract_builder
73
+ end
74
+
75
+ def create_consumer_contract_builder
76
+ consumer_contract_builder_fields = {
77
+ :consumer_name => consumer_name,
78
+ :provider_name => provider_name,
79
+ :pactfile_write_mode => Pact.configuration.pactfile_write_mode,
80
+ :port => port,
81
+ :host => host,
82
+ :pact_dir => Pact.configuration.pact_dir
83
+ }
84
+ Pact::Consumer::ConsumerContractBuilder.new consumer_contract_builder_fields
85
+ end
86
+
87
+ def setup_verification consumer_contract_builder
88
+ Pact.configuration.add_provider_verification do | example_description |
89
+ consumer_contract_builder.verify example_description
90
+ end
91
+ end
92
+
93
+ # This makes the consumer_contract_builder available via a module method with the given identifier
94
+ # as the method name.
95
+ # I feel this should be defined somewhere else, but I'm not sure where
96
+ def create_consumer_contract_builders_method consumer_contract_builder
97
+ Pact::Consumer::ConsumerContractBuilders.send(:define_method, @name.to_sym) do
98
+ consumer_contract_builder
99
+ end
100
+ Pact.consumer_world.add_consumer_contract_builder consumer_contract_builder
101
+ end
102
+
103
+ def mock_service_options
104
+ {
105
+ pact_specification_version: pact_specification_version,
106
+ find_available_port: port.nil?,
107
+ }
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,51 @@
1
+ require 'pact/shared/dsl'
2
+
3
+ module Pact
4
+ module Consumer
5
+ module Configuration
6
+ class ServiceConsumer
7
+
8
+ extend Pact::DSL
9
+
10
+ attr_accessor :app, :port, :name
11
+
12
+ def initialize name
13
+ @name = name
14
+ @app = nil
15
+ @port = nil
16
+ end
17
+
18
+ dsl do
19
+ def app app
20
+ self.app = app
21
+ end
22
+
23
+ def port port
24
+ self.port = port
25
+ end
26
+
27
+ def has_pact_with service_provider_name, &block
28
+ ServiceProvider.build(service_provider_name, name, &block)
29
+ end
30
+ end
31
+
32
+ def finalize
33
+ validate
34
+ register_consumer_app if @app
35
+ end
36
+
37
+ private
38
+
39
+ def validate
40
+ raise "Please provide a consumer name" unless (name && !name.empty?)
41
+ raise "Please provide a port for the consumer app" if app && !port
42
+ end
43
+
44
+
45
+ def register_consumer_app
46
+ Pact::MockService::AppManager.instance.register app, port
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,40 @@
1
+ require 'pact/shared/dsl'
2
+ require 'pact/consumer/configuration/mock_service'
3
+
4
+ module Pact
5
+ module Consumer
6
+ module Configuration
7
+ class ServiceProvider
8
+
9
+ extend Pact::DSL
10
+
11
+ attr_accessor :service, :consumer_name, :name
12
+
13
+ def initialize name, consumer_name
14
+ @name = name
15
+ @service = nil
16
+ @consumer_name = consumer_name
17
+ end
18
+
19
+ dsl do
20
+ def mock_service name, &block
21
+ self.service = MockService.build(name, consumer_name, self.name, &block)
22
+ end
23
+ end
24
+
25
+ def finalize
26
+ validate
27
+ end
28
+
29
+ private
30
+
31
+ def validate
32
+ raise "Please configure a service for #{name}" unless service
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,10 @@
1
+ require 'pact/configuration'
2
+ require 'pact/consumer/consumer_contract_builders'
3
+ require 'pact/consumer/consumer_contract_builder'
4
+ require 'pact/consumer/configuration/service_consumer'
5
+ require 'pact/consumer/configuration/service_provider'
6
+ require 'pact/consumer/configuration/dsl'
7
+ require 'pact/consumer/configuration/configuration_extensions'
8
+
9
+ Pact.send(:extend, Pact::Consumer::DSL)
10
+ Pact::Configuration.send(:include, Pact::Consumer::Configuration::ConfigurationExtensions)
@@ -0,0 +1,82 @@
1
+ require 'uri'
2
+ require 'json/add/regexp'
3
+ require 'pact/logging'
4
+ require 'pact/mock_service/client'
5
+ require 'pact/consumer/interaction_builder'
6
+
7
+ module Pact
8
+ module Consumer
9
+
10
+ class ConsumerContractBuilder
11
+
12
+ include Pact::Logging
13
+
14
+ attr_reader :mock_service_base_url
15
+
16
+ def initialize(attributes)
17
+ @interaction_builder = nil
18
+ @consumer_contract_details = {
19
+ consumer: {name: attributes[:consumer_name]},
20
+ provider: {name: attributes[:provider_name]},
21
+ pactfile_write_mode: attributes[:pactfile_write_mode].to_s,
22
+ pact_dir: attributes.fetch(:pact_dir)
23
+ }
24
+ @mock_service_client = Pact::MockService::Client.new(attributes[:port], attributes[:host])
25
+ @mock_service_base_url = "http://#{attributes[:host]}:#{attributes[:port]}"
26
+ end
27
+
28
+ def without_writing_to_pact
29
+ interaction_builder.without_writing_to_pact
30
+ end
31
+
32
+ def given(provider_state)
33
+ interaction_builder.given(provider_state)
34
+ end
35
+
36
+ def upon_receiving(description)
37
+ interaction_builder.upon_receiving(description)
38
+ end
39
+
40
+ def verify example_description
41
+ mock_service_client.verify example_description
42
+ end
43
+
44
+ def log msg
45
+ mock_service_client.log msg
46
+ end
47
+
48
+ def write_pact
49
+ mock_service_client.write_pact @consumer_contract_details
50
+ end
51
+
52
+ def wait_for_interactions options = {}
53
+ wait_max_seconds = options.fetch(:wait_max_seconds, 3)
54
+ poll_interval = options.fetch(:poll_interval, 0.1)
55
+ mock_service_client.wait_for_interactions wait_max_seconds, poll_interval
56
+ end
57
+
58
+ # @raise Pact::InvalidInteractionError
59
+ def handle_interaction_fully_defined interaction
60
+ interaction.validate!
61
+ mock_service_client.add_expected_interaction interaction #TODO: What will happen if duplicate added?
62
+ self.interaction_builder = nil
63
+ end
64
+
65
+ private
66
+
67
+ attr_reader :mock_service_client
68
+ attr_writer :interaction_builder
69
+
70
+ def interaction_builder
71
+ @interaction_builder ||=
72
+ begin
73
+ interaction_builder = InteractionBuilder.new do | interaction |
74
+ handle_interaction_fully_defined(interaction)
75
+ end
76
+ interaction_builder
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,10 @@
1
+ module Pact
2
+ module Consumer
3
+ module ConsumerContractBuilders
4
+ # Placeholder for mock service methods which will be dynamically created
5
+ # from the Pact configuration.
6
+
7
+ # To be included in RSpec
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,45 @@
1
+ require 'net/http'
2
+ require 'pact/reification'
3
+ require 'pact/consumer_contract/interaction'
4
+
5
+ module Pact
6
+ module Consumer
7
+ class InteractionBuilder
8
+
9
+ attr_reader :interaction
10
+
11
+ def initialize &block
12
+ @interaction = Interaction.new
13
+ @callback = block
14
+ end
15
+
16
+ def without_writing_to_pact
17
+ interaction.metadata ||= {}
18
+ interaction.metadata[:write_to_pact] = false
19
+ self
20
+ end
21
+
22
+ def upon_receiving description
23
+ @interaction.description = description
24
+ self
25
+ end
26
+
27
+ def given provider_state
28
+ @interaction.provider_state = provider_state.nil? ? nil : provider_state.to_s
29
+ self
30
+ end
31
+
32
+ def with(request_details)
33
+ interaction.request = Pact::Request::Expected.from_hash(request_details)
34
+ self
35
+ end
36
+
37
+ def will_respond_with(response)
38
+ interaction.response = Pact::Response.new(response)
39
+ @callback.call interaction
40
+ self
41
+ end
42
+
43
+ end
44
+ end
45
+ end