pact-mock_service 0.2.4 → 0.3.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/lib/pact/consumer/mock_service/set_location.rb +28 -0
  4. data/lib/pact/consumer_contract/consumer_contract_writer.rb +2 -2
  5. data/lib/pact/mock_service/app.rb +56 -0
  6. data/lib/pact/{consumer → mock_service}/app_manager.rb +4 -4
  7. data/lib/pact/{consumer/mock_service_client.rb → mock_service/client.rb} +2 -2
  8. data/lib/pact/mock_service/control_server/delegator.rb +1 -12
  9. data/lib/pact/mock_service/control_server/mock_service_creator.rb +1 -1
  10. data/lib/pact/mock_service/interactions/actual_interactions.rb +36 -0
  11. data/lib/pact/mock_service/interactions/candidate_interactions.rb +15 -0
  12. data/lib/pact/mock_service/interactions/expected_interactions.rb +18 -0
  13. data/lib/pact/mock_service/interactions/interaction_diff_message.rb +45 -0
  14. data/lib/pact/mock_service/interactions/interaction_mismatch.rb +74 -0
  15. data/lib/pact/mock_service/interactions/interactions_filter.rb +34 -0
  16. data/lib/pact/mock_service/interactions/verification.rb +48 -0
  17. data/lib/pact/mock_service/interactions/verified_interactions.rb +20 -0
  18. data/lib/pact/mock_service/logger.rb +27 -0
  19. data/lib/pact/mock_service/request_handlers/base_administration_request_handler.rb +42 -0
  20. data/lib/pact/mock_service/request_handlers/base_request_handler.rb +22 -0
  21. data/lib/pact/mock_service/request_handlers/index_get.rb +23 -0
  22. data/lib/pact/mock_service/request_handlers/interaction_delete.rb +36 -0
  23. data/lib/pact/mock_service/request_handlers/interaction_post.rb +41 -0
  24. data/lib/pact/mock_service/request_handlers/interaction_replay.rb +163 -0
  25. data/lib/pact/mock_service/request_handlers/interactions_put.rb +42 -0
  26. data/lib/pact/mock_service/request_handlers/log_get.rb +27 -0
  27. data/lib/pact/mock_service/request_handlers/missing_interactions_get.rb +33 -0
  28. data/lib/pact/mock_service/request_handlers/options.rb +42 -0
  29. data/lib/pact/mock_service/request_handlers/pact_post.rb +38 -0
  30. data/lib/pact/mock_service/request_handlers/verification_get.rb +72 -0
  31. data/lib/pact/mock_service/request_handlers.rb +40 -0
  32. data/lib/pact/mock_service/run.rb +14 -4
  33. data/lib/pact/mock_service/session.rb +70 -0
  34. data/lib/pact/mock_service/spawn.rb +26 -12
  35. data/lib/pact/mock_service/version.rb +1 -1
  36. data/lib/pact/mock_service.rb +2 -1
  37. metadata +29 -24
  38. data/lib/pact/consumer/interactions_filter.rb +0 -32
  39. data/lib/pact/consumer/mock_service/actual_interactions.rb +0 -34
  40. data/lib/pact/consumer/mock_service/app.rb +0 -58
  41. data/lib/pact/consumer/mock_service/candidate_interactions.rb +0 -13
  42. data/lib/pact/consumer/mock_service/expected_interactions.rb +0 -17
  43. data/lib/pact/consumer/mock_service/index_get.rb +0 -22
  44. data/lib/pact/consumer/mock_service/interaction_delete.rb +0 -39
  45. data/lib/pact/consumer/mock_service/interaction_mismatch.rb +0 -73
  46. data/lib/pact/consumer/mock_service/interaction_post.rb +0 -95
  47. data/lib/pact/consumer/mock_service/interaction_replay.rb +0 -162
  48. data/lib/pact/consumer/mock_service/log_get.rb +0 -28
  49. data/lib/pact/consumer/mock_service/missing_interactions_get.rb +0 -32
  50. data/lib/pact/consumer/mock_service/mock_service_administration_endpoint.rb +0 -40
  51. data/lib/pact/consumer/mock_service/options.rb +0 -43
  52. data/lib/pact/consumer/mock_service/pact_post.rb +0 -38
  53. data/lib/pact/consumer/mock_service/request_handlers.rb +0 -41
  54. data/lib/pact/consumer/mock_service/verification.rb +0 -46
  55. data/lib/pact/consumer/mock_service/verification_get.rb +0 -73
  56. data/lib/pact/consumer/mock_service/verified_interactions.rb +0 -18
  57. data/lib/pact/consumer/mock_service.rb +0 -43
@@ -0,0 +1,41 @@
1
+ require 'pact/mock_service/request_handlers/base_administration_request_handler'
2
+ require 'pact/mock_service/session'
3
+
4
+ module Pact
5
+ module MockService
6
+ module RequestHandlers
7
+ class InteractionPost < BaseAdministrationRequestHandler
8
+
9
+ def initialize name, logger, session
10
+ super name, logger
11
+ @session = session
12
+ end
13
+
14
+ def request_path
15
+ '/interactions'
16
+ end
17
+
18
+ def request_method
19
+ 'POST'
20
+ end
21
+
22
+ def respond env
23
+ request_body = env['rack.input'].string
24
+ interaction = Interaction.from_hash(JSON.load(request_body)) # Load creates the Pact::XXX classes
25
+
26
+ begin
27
+ session.add_expected_interaction interaction
28
+ [200, {}, ['Set interactions']]
29
+ rescue AlmostDuplicateInteractionError => e
30
+ [500, {}, e.message]
31
+ end
32
+
33
+ end
34
+
35
+ private
36
+
37
+ attr_accessor :session
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,163 @@
1
+ require 'pact/matchers'
2
+ require 'pact/consumer/request'
3
+ require 'pact/mock_service/interactions/interaction_mismatch'
4
+ require 'pact/consumer_contract'
5
+ require 'pact/mock_service/response_decorator'
6
+ require 'pact/mock_service/interaction_decorator'
7
+ require 'pact/mock_service/request_handlers/base_request_handler'
8
+
9
+ module Pact
10
+ module MockService
11
+ module RequestHandlers
12
+
13
+ module PrettyGenerate
14
+ #Doesn't seem to reliably pretty generate unless we go to JSON and back again :(
15
+ def pretty_generate object
16
+ JSON.pretty_generate(JSON.parse(object.to_json))
17
+ end
18
+ end
19
+
20
+ class InteractionReplay < BaseRequestHandler
21
+ include Pact::Matchers
22
+ include PrettyGenerate
23
+
24
+ attr_accessor :name, :logger, :expected_interactions, :actual_interactions, :verified_interactions
25
+
26
+ def initialize name, logger, session, cors_enabled=false
27
+ @name = name
28
+ @logger = logger
29
+ @expected_interactions = session.expected_interactions
30
+ @actual_interactions = session.actual_interactions
31
+ @verified_interactions = session.verified_interactions
32
+ @cors_enabled = cors_enabled
33
+ end
34
+
35
+ def match? env
36
+ true # default handler
37
+ end
38
+
39
+ def respond env
40
+ find_response request_as_hash_from(env)
41
+ end
42
+
43
+ private
44
+
45
+ def find_response request_hash
46
+ actual_request = Pact::Consumer::Request::Actual.from_hash(request_hash)
47
+ logger.info "Received request #{actual_request.method_and_path}"
48
+ logger.debug pretty_generate request_hash
49
+ candidate_interactions = expected_interactions.find_candidate_interactions actual_request
50
+ matching_interactions = candidate_interactions.matching_interactions actual_request
51
+
52
+ case matching_interactions.size
53
+ when 0 then handle_unrecognised_request actual_request, candidate_interactions
54
+ when 1 then handle_matched_interaction matching_interactions.first
55
+ else
56
+ handle_more_than_one_matching_interaction actual_request, matching_interactions
57
+ end
58
+ end
59
+
60
+ def handle_matched_interaction interaction
61
+ HandleMatchedInteraction.call(interaction, verified_interactions, actual_interactions, logger)
62
+ end
63
+
64
+ def handle_more_than_one_matching_interaction actual_request, matching_interactions
65
+ HandleMultipleInteractionsFound.call(actual_request, matching_interactions, logger)
66
+ end
67
+
68
+ def handle_unrecognised_request actual_request, candidate_interactions
69
+ HandleUnrecognisedInteraction.call(actual_request, candidate_interactions, actual_interactions, logger)
70
+ end
71
+
72
+ def logger_info_ap msg
73
+ logger.info msg
74
+ end
75
+
76
+ end
77
+
78
+ class HandleMultipleInteractionsFound
79
+
80
+ extend PrettyGenerate
81
+
82
+ def self.call actual_request, matching_interactions, logger
83
+ logger.error "Multiple interactions found for #{actual_request.method_and_path}:"
84
+ matching_interactions.each do | interaction |
85
+ logger.debug pretty_generate(Pact::MockService::InteractionDecorator.new(interaction))
86
+ end
87
+ response actual_request, matching_interactions
88
+ end
89
+
90
+ def self.response actual_request, matching_interactions
91
+ response = {
92
+ message: "Multiple interaction found for #{actual_request.method_and_path}",
93
+ matching_interactions: matching_interactions.collect{ | interaction | request_summary_for(interaction) }
94
+ }
95
+ [500, {'Content-Type' => 'application/json'}, [response.to_json]]
96
+ end
97
+
98
+ def self.request_summary_for interaction
99
+ summary = {:description => interaction.description}
100
+ summary[:provider_state] if interaction.provider_state
101
+ summary[:request] = Pact::MockService::RequestDecorator.new(interaction.request)
102
+ summary
103
+ end
104
+ end
105
+
106
+ class HandleUnrecognisedInteraction
107
+
108
+ def self.call actual_request, candidate_interactions, actual_interactions, logger
109
+ interaction_mismatch = interaction_mismatch(actual_request, candidate_interactions)
110
+ if candidate_interactions.any?
111
+ actual_interactions.register_interaction_mismatch interaction_mismatch
112
+ else
113
+ actual_interactions.register_unexpected_request actual_request
114
+ end
115
+ log interaction_mismatch, logger
116
+ response interaction_mismatch
117
+ end
118
+
119
+ def self.response interaction_mismatch
120
+ response = {
121
+ message: "No interaction found for #{interaction_mismatch.actual_request.method_and_path}",
122
+ interaction_diffs: interaction_mismatch.to_hash
123
+ }
124
+ [500, {'Content-Type' => 'application/json'}, [response.to_json]]
125
+ end
126
+
127
+ def self.interaction_mismatch actual_request, candidate_interactions
128
+ Pact::MockService::Interactions::InteractionMismatch.new(candidate_interactions, actual_request)
129
+ end
130
+
131
+ def self.log interaction_mismatch, logger
132
+ logger.error "No matching interaction found for #{interaction_mismatch.actual_request.method_and_path}"
133
+ logger.error 'Interaction diffs for that route:'
134
+ logger.error(interaction_mismatch.to_s)
135
+ end
136
+
137
+ end
138
+
139
+ class HandleMatchedInteraction
140
+
141
+ extend PrettyGenerate
142
+
143
+ def self.call interaction, verified_interactions, actual_interactions, logger
144
+ actual_interactions.register_matched interaction
145
+ verified_interactions << interaction
146
+ response = response_from(interaction.response)
147
+ logger.info "Found matching response for #{interaction.request.method_and_path}"
148
+ logger.debug pretty_generate(Pact::MockService::ResponseDecorator.new(interaction.response))
149
+ response
150
+ end
151
+
152
+ def self.response_from response
153
+ [response.status, (Pact::Reification.from_term(response.headers) || {}).to_hash, [render_body(Pact::Reification.from_term(response.body))]]
154
+ end
155
+
156
+ def self.render_body body
157
+ return '' unless body
158
+ body.kind_of?(String) ? body.force_encoding('utf-8') : body.to_json
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,42 @@
1
+ require 'pact/mock_service/request_handlers/base_administration_request_handler'
2
+ require 'pact/mock_service/interaction_decorator'
3
+ require 'pact/shared/json_differ'
4
+ require 'pact/mock_service/request_handlers/interaction_post' #Refactor diff message
5
+
6
+ module Pact
7
+ module MockService
8
+ module RequestHandlers
9
+ class InteractionsPut < BaseAdministrationRequestHandler
10
+
11
+ def initialize name, logger, session
12
+ super name, logger
13
+ @session = session
14
+ end
15
+
16
+ def request_path
17
+ '/interactions'
18
+ end
19
+
20
+ def request_method
21
+ 'PUT'
22
+ end
23
+
24
+ def respond env
25
+ request_body = JSON.load(env['rack.input'].string)
26
+ interactions = request_body['interactions'].collect { | hash | Interaction.from_hash(hash) }
27
+ begin
28
+ session.set_expected_interactions interactions
29
+ [200, {}, ['Set interactions']]
30
+ rescue AlmostDuplicateInteractionError => e
31
+ [500, {}, e.message]
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_accessor :session
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ require 'pact/mock_service/request_handlers/base_administration_request_handler'
2
+
3
+ module Pact
4
+ module MockService
5
+ module RequestHandlers
6
+ class LogGet < BaseAdministrationRequestHandler
7
+
8
+ def request_path
9
+ '/log'
10
+ end
11
+
12
+ def request_method
13
+ 'GET'
14
+ end
15
+
16
+ def respond env
17
+ logger.info "Debug message from client - #{message(env)}"
18
+ [200, {}, []]
19
+ end
20
+
21
+ def message env
22
+ params_hash(env).fetch('msg', [])[0]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ require 'pact/mock_service/request_handlers/base_administration_request_handler'
2
+ require 'pact/mock_service/interactions/verification'
3
+
4
+ module Pact
5
+ module MockService
6
+ module RequestHandlers
7
+
8
+ class MissingInteractionsGet < BaseAdministrationRequestHandler
9
+
10
+ def initialize name, logger, session
11
+ super name, logger
12
+ @expected_interactions = session.expected_interactions
13
+ @actual_interactions = session.actual_interactions
14
+ end
15
+
16
+ def request_path
17
+ '/interactions/missing'
18
+ end
19
+
20
+ def request_method
21
+ 'GET'
22
+ end
23
+
24
+ def respond env
25
+ verification = Pact::MockService::Interactions::Verification.new(@expected_interactions, @actual_interactions)
26
+ number_of_missing_interactions = verification.missing_interactions.size
27
+ logger.info "Number of missing interactions for mock \"#{name}\" = #{number_of_missing_interactions}"
28
+ [200, {}, [{size: number_of_missing_interactions}.to_json]]
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ require 'pact/mock_service/request_handlers/base_request_handler'
2
+
3
+ module Pact
4
+ module MockService
5
+ module RequestHandlers
6
+
7
+ class Options < BaseRequestHandler
8
+
9
+ attr_reader :name, :logger, :cors_enabled
10
+
11
+ def initialize name, logger, cors_enabled
12
+ @name = name
13
+ @logger = logger
14
+ @cors_enabled = cors_enabled
15
+ end
16
+
17
+ def match? env
18
+ is_options_request?(env) && (cors_enabled || is_administration_request?(env))
19
+ end
20
+
21
+ def respond env
22
+ cors_headers = {
23
+ 'Access-Control-Allow-Origin' => env.fetch('HTTP_ORIGIN','*'),
24
+ 'Access-Control-Allow-Headers' => headers_from(env)["Access-Control-Request-Headers"],
25
+ 'Access-Control-Allow-Methods' => 'DELETE, POST, GET, HEAD, PUT, TRACE, CONNECT'
26
+ }
27
+ logger.info "Received OPTIONS request for mock service administration endpoint #{env['HTTP_ACCESS_CONTROL_REQUEST_METHOD']} #{env['PATH_INFO']}. Returning CORS headers: #{cors_headers.to_json}."
28
+ [200, cors_headers, []]
29
+ end
30
+
31
+ def is_options_request? env
32
+ env['REQUEST_METHOD'] == 'OPTIONS'
33
+ end
34
+
35
+ def is_administration_request? env
36
+ env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"].match(/x-pact-mock-service/i)
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ require 'pact/mock_service/request_handlers/base_administration_request_handler'
2
+ require 'pact/consumer_contract/consumer_contract_writer'
3
+
4
+ module Pact
5
+ module MockService
6
+ module RequestHandlers
7
+ class PactPost < BaseAdministrationRequestHandler
8
+
9
+ attr_accessor :consumer_contract, :verified_interactions, :default_options
10
+
11
+ def initialize name, logger, session
12
+ super name, logger
13
+ @verified_interactions = session.verified_interactions
14
+ @default_options = {}
15
+ @default_options.merge!(session.consumer_contract_details)
16
+ end
17
+
18
+ def request_path
19
+ '/pact'
20
+ end
21
+
22
+ def request_method
23
+ 'POST'
24
+ end
25
+
26
+ def respond env
27
+ consumer_contract_details = JSON.parse(env['rack.input'].string, symbolize_names: true)
28
+ logger.info "Writing pact with details #{consumer_contract_details}"
29
+ consumer_contract_params = default_options.merge(consumer_contract_details.merge(interactions: verified_interactions))
30
+ consumer_contract_writer = ConsumerContractWriter.new(consumer_contract_params, logger)
31
+ json = consumer_contract_writer.write
32
+
33
+ [200, {'Content-Type' =>'application/json'}, [json]]
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,72 @@
1
+ require 'pact/mock_service/request_handlers/base_administration_request_handler'
2
+
3
+ module Pact
4
+ module MockService
5
+ module RequestHandlers
6
+ class VerificationGet < BaseAdministrationRequestHandler
7
+
8
+ def initialize name, logger, session
9
+ super name, logger
10
+ @expected_interactions = session.expected_interactions
11
+ @actual_interactions = session.actual_interactions
12
+ end
13
+
14
+ def request_path
15
+ '/interactions/verification'
16
+ end
17
+
18
+ def request_method
19
+ 'GET'
20
+ end
21
+
22
+ def respond env
23
+ verification = Pact::MockService::Interactions::Verification.new(expected_interactions, actual_interactions)
24
+ if verification.all_matched?
25
+ logger.info "Verifying - interactions matched for example \"#{example_description(env)}\""
26
+ [200, {'Content-Type' => 'text/plain'}, ['Interactions matched']]
27
+ else
28
+ error_message = FailureMessage.new(verification).to_s
29
+ logger.warn "Verifying - actual interactions do not match expected interactions for example \"#{example_description(env)}\". \n#{error_message}"
30
+ logger.warn error_message
31
+ [500, {'Content-Type' => 'text/plain'}, ["Actual interactions do not match expected interactions for mock #{name}.\n\n#{error_message}See #{logger.description} for details."]]
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_accessor :expected_interactions, :actual_interactions
38
+
39
+ def example_description env
40
+ params_hash(env).fetch("example_description", [])[0]
41
+ end
42
+
43
+ class FailureMessage
44
+
45
+ def initialize verification
46
+ @verification = verification
47
+ end
48
+
49
+ def to_s
50
+ titles_and_summaries.collect do | title, summaries |
51
+ "#{title}:\n\t#{summaries.join("\n\t")}\n\n" if summaries.any?
52
+ end.compact.join
53
+
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :verification
59
+
60
+ def titles_and_summaries
61
+ {
62
+ "Incorrect requests" => verification.interaction_mismatches_summaries,
63
+ "Missing requests" => verification.missing_interactions_summaries,
64
+ "Unexpected requests" => verification.unexpected_requests_summaries,
65
+ }
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,40 @@
1
+ require 'pact/mock_service/request_handlers/interaction_post'
2
+ require 'pact/mock_service/request_handlers/interactions_put'
3
+ require 'pact/mock_service/request_handlers/index_get'
4
+ require 'pact/mock_service/request_handlers/interaction_delete'
5
+ require 'pact/mock_service/request_handlers/interaction_replay'
6
+ require 'pact/mock_service/request_handlers/log_get'
7
+ require 'pact/mock_service/request_handlers/options'
8
+ require 'pact/mock_service/request_handlers/missing_interactions_get'
9
+ require 'pact/mock_service/request_handlers/pact_post'
10
+ require 'pact/mock_service/request_handlers/verification_get'
11
+ require 'pact/consumer/request'
12
+ require 'pact/support'
13
+
14
+ module Pact
15
+ module MockService
16
+ module RequestHandlers
17
+
18
+ def self.new *args
19
+ App.new(*args)
20
+ end
21
+
22
+ class App < ::Rack::Cascade
23
+ def initialize name, logger, session, options
24
+ super [
25
+ Options.new(name, logger, options[:cors_enabled]),
26
+ MissingInteractionsGet.new(name, logger, session),
27
+ VerificationGet.new(name, logger, session),
28
+ InteractionPost.new(name, logger, session),
29
+ InteractionsPut.new(name, logger, session),
30
+ InteractionDelete.new(name, logger, session),
31
+ LogGet.new(name, logger),
32
+ PactPost.new(name, logger, session),
33
+ IndexGet.new(name, logger),
34
+ InteractionReplay.new(name, logger, session, options[:cors_enabled])
35
+ ]
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,5 +1,6 @@
1
1
  require 'find_a_port'
2
- require 'pact/consumer/mock_service'
2
+ require 'pact/mock_service/app'
3
+ require 'pact/consumer/mock_service/set_location'
3
4
 
4
5
  module Pact
5
6
  module MockService
@@ -14,7 +15,7 @@ module Pact
14
15
  end
15
16
 
16
17
  def call
17
- require 'pact/consumer/mock_service'
18
+ require 'pact/mock_service/app'
18
19
 
19
20
  trap(:INT) { call_shutdown_hooks }
20
21
  trap(:TERM) { call_shutdown_hooks }
@@ -28,7 +29,8 @@ module Pact
28
29
 
29
30
  def mock_service
30
31
  @mock_service ||= begin
31
- Pact::Consumer::MockService.new(service_options)
32
+ mock_service = Pact::MockService.new(service_options)
33
+ Pact::Consumer::SetLocation.new(mock_service, base_url)
32
34
  end
33
35
  end
34
36
 
@@ -63,7 +65,7 @@ module Pact
63
65
 
64
66
  def webbrick_opts
65
67
  opts = {
66
- :Port => options[:port] || FindAPort.available_port,
68
+ :Port => port,
67
69
  :AccessLog => []
68
70
  }
69
71
  opts.merge!(ssl_opts) if options[:ssl]
@@ -76,6 +78,14 @@ module Pact
76
78
  :SSLCertName => [ %w[CN localhost] ]
77
79
  }
78
80
  end
81
+
82
+ def port
83
+ @port ||= options[:port] || FindAPort.available_port
84
+ end
85
+
86
+ def base_url
87
+ options[:ssl] ? "https://localhost:#{port}" : "http://localhost:#{port}"
88
+ end
79
89
  end
80
90
  end
81
91
  end
@@ -0,0 +1,70 @@
1
+ require 'pact/mock_service/interactions/expected_interactions'
2
+ require 'pact/mock_service/interactions/actual_interactions'
3
+ require 'pact/mock_service/interactions/verified_interactions'
4
+ require 'pact/mock_service/interaction_decorator'
5
+ require 'pact/mock_service/interactions/interaction_diff_message'
6
+
7
+ module Pact
8
+ module MockService
9
+
10
+ class AlmostDuplicateInteractionError < StandardError; end
11
+
12
+ class Session
13
+
14
+ attr_reader :expected_interactions, :actual_interactions, :verified_interactions, :consumer_contract_details, :logger
15
+
16
+ def initialize options
17
+ @logger = options[:logger]
18
+ @expected_interactions = Interactions::ExpectedInteractions.new
19
+ @actual_interactions = Interactions::ActualInteractions.new
20
+ @verified_interactions = Interactions::VerifiedInteractions.new
21
+ @consumer_contract_details = {
22
+ pact_dir: options[:pact_dir],
23
+ consumer: {name: options[:consumer]},
24
+ provider: {name: options[:provider]},
25
+ interactions: verified_interactions
26
+ }
27
+ end
28
+
29
+ def set_expected_interactions interactions
30
+ clear_expected_and_actual_interactions
31
+ interactions.each do | interaction |
32
+ add_expected_interaction interaction
33
+ end
34
+ end
35
+
36
+ def clear_expected_and_actual_interactions
37
+ expected_interactions.clear
38
+ actual_interactions.clear
39
+ end
40
+
41
+ def add_expected_interaction interaction
42
+ if (previous_interaction = interaction_already_verified_with_same_description_and_provider_state_but_not_equal(interaction))
43
+ handle_almost_duplicate_interaction previous_interaction, interaction
44
+ else
45
+ really_add_expected_interaction interaction
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def really_add_expected_interaction interaction
52
+ expected_interactions << interaction
53
+ logger.info "Registered expected interaction #{interaction.request.method_and_path}"
54
+ logger.debug JSON.pretty_generate InteractionDecorator.new(interaction)
55
+ end
56
+
57
+ def handle_almost_duplicate_interaction previous_interaction, interaction
58
+ message = Interactions::InteractionDiffMessage.new(previous_interaction, interaction).to_s
59
+ logger.error message
60
+ raise AlmostDuplicateInteractionError, message
61
+ end
62
+
63
+ def interaction_already_verified_with_same_description_and_provider_state_but_not_equal interaction
64
+ other = verified_interactions.find_matching_description_and_provider_state interaction
65
+ other && other != interaction ? other : nil
66
+ end
67
+
68
+ end
69
+ end
70
+ end