pact-mock_service 0.2.2 → 0.2.3.pre.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/lib/pact/consumer/app_manager.rb +5 -1
  4. data/lib/pact/consumer/interactions_filter.rb +1 -17
  5. data/lib/pact/consumer/mock_service/actual_interactions.rb +34 -0
  6. data/lib/pact/consumer/mock_service/app.rb +21 -18
  7. data/lib/pact/consumer/mock_service/candidate_interactions.rb +13 -0
  8. data/lib/pact/consumer/mock_service/expected_interactions.rb +17 -0
  9. data/lib/pact/consumer/mock_service/interaction_delete.rb +11 -5
  10. data/lib/pact/consumer/mock_service/interaction_post.rb +69 -6
  11. data/lib/pact/consumer/mock_service/interaction_replay.rb +77 -57
  12. data/lib/pact/consumer/mock_service/log_get.rb +1 -1
  13. data/lib/pact/consumer/mock_service/missing_interactions_get.rb +7 -4
  14. data/lib/pact/consumer/mock_service/pact_post.rb +6 -4
  15. data/lib/pact/consumer/mock_service/rack_request_helper.rb +1 -1
  16. data/lib/pact/consumer/mock_service/verification.rb +46 -0
  17. data/lib/pact/consumer/mock_service/verification_get.rb +17 -12
  18. data/lib/pact/consumer/mock_service/verified_interactions.rb +18 -0
  19. data/lib/pact/consumer/mock_service_client.rb +4 -4
  20. data/lib/pact/consumer/server.rb +2 -0
  21. data/lib/pact/consumer_contract/consumer_contract_writer.rb +2 -0
  22. data/lib/pact/mock_service/cli.rb +21 -14
  23. data/lib/pact/mock_service/version.rb +1 -1
  24. metadata +10 -124
  25. data/.gitignore +0 -30
  26. data/.rspec +0 -3
  27. data/.travis.yml +0 -8
  28. data/Rakefile +0 -6
  29. data/lib/pact/consumer/mock_service/interaction_list.rb +0 -76
  30. data/pact-mock-service.gemspec +0 -39
  31. data/spec/features/mock_multiple_responses_spec.rb +0 -120
  32. data/spec/features/mock_response_spec.rb +0 -71
  33. data/spec/lib/pact/consumer/app_manager_spec.rb +0 -42
  34. data/spec/lib/pact/consumer/mock_service/app_spec.rb +0 -52
  35. data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +0 -78
  36. data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +0 -70
  37. data/spec/lib/pact/consumer/mock_service/interaction_replay_spec.rb +0 -12
  38. data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +0 -88
  39. data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +0 -142
  40. data/spec/lib/pact/consumer/mock_service_client_spec.rb +0 -88
  41. data/spec/lib/pact/consumer/service_consumer_spec.rb +0 -11
  42. data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +0 -128
  43. data/spec/lib/pact/consumer_contract/request_decorator_body_spec.rb +0 -77
  44. data/spec/lib/pact/consumer_contract/request_decorator_headers_spec.rb +0 -69
  45. data/spec/lib/pact/consumer_contract/request_decorator_path_spec.rb +0 -42
  46. data/spec/lib/pact/consumer_contract/request_decorator_query_spec.rb +0 -74
  47. data/spec/lib/pact/consumer_contract/request_decorator_spec.rb +0 -41
  48. data/spec/lib/pact/consumer_contract/response_decorator_spec.rb +0 -10
  49. data/spec/lib/pact/mock_service/interaction_decorator_spec.rb +0 -74
  50. data/spec/lib/pact/mock_service/request_decorator_spec.rb +0 -76
  51. data/spec/lib/pact/mock_service/response_decorator_spec.rb +0 -12
  52. data/spec/spec_helper.rb +0 -16
  53. data/spec/support/a_consumer-a_producer.json +0 -32
  54. data/spec/support/a_consumer-a_provider.json +0 -32
  55. data/spec/support/active_support_if_configured.rb +0 -6
  56. data/spec/support/app_for_config_ru.rb +0 -4
  57. data/spec/support/case-insensitive-response-header-matching.json +0 -21
  58. data/spec/support/case-insensitive-response-header-matching.rb +0 -15
  59. data/spec/support/consumer_contract_template.json +0 -24
  60. data/spec/support/dsl_spec_support.rb +0 -7
  61. data/spec/support/factories.rb +0 -82
  62. data/spec/support/generated_index.md +0 -4
  63. data/spec/support/generated_markdown.md +0 -55
  64. data/spec/support/interaction_view_model.json +0 -63
  65. data/spec/support/interaction_view_model_with_terms.json +0 -50
  66. data/spec/support/markdown_pact.json +0 -48
  67. data/spec/support/missing_provider_states_output.txt +0 -25
  68. data/spec/support/options.json +0 -21
  69. data/spec/support/options_app.rb +0 -15
  70. data/spec/support/pact_helper.rb +0 -57
  71. data/spec/support/shared_examples_for_request.rb +0 -94
  72. data/spec/support/shared_examples_for_response_decorator.rb +0 -25
  73. data/spec/support/spec_support.rb +0 -20
  74. data/spec/support/stubbing.json +0 -22
  75. data/spec/support/stubbing_using_allow.rb +0 -29
  76. data/spec/support/term.json +0 -48
  77. data/spec/support/test_app_fail.json +0 -61
  78. data/spec/support/test_app_pass.json +0 -38
  79. data/spec/support/test_app_with_right_content_type_differ.json +0 -23
  80. data/tasks/spec.rake +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fac0922931cca9e297ccfc60904ef05c85ff1be6
4
- data.tar.gz: a6347beea927a7640ecc44fa9595b8c64f86f860
3
+ metadata.gz: 1e4c79027ecd3c1da41ed2b2305c7d02ab8422bb
4
+ data.tar.gz: b6900749dbe4f721b7c958d2522caa6fdabf0fe4
5
5
  SHA512:
6
- metadata.gz: ff2fc19751548ef67b862bce43420ee777b4176e98bd4dfcf2f45aa99bdabce7b45848d8c4c8c2eec1e60b9b433b3fa8cdb18d101ad1261b5d9fb88ff1bcc64f
7
- data.tar.gz: f3bc038ac5ed668178090ad5e578fb450c940aa7fd097868b89c5a154c6481ce8357028892672e4ae1f810155a1fd8841dbdc0e7ae0392dd4a2580a2caa4801d
6
+ metadata.gz: 8945a97c67fc3f7d6531a963f9325690781e95914f37b41e8ab091fc45bdeaedec1907ec6fc84d42c12fe660f7a343a0cec7396bf519b378cfed4a4abd0b4795
7
+ data.tar.gz: a3c82d60ef644fd1057f2188d6d86571f026d1e59b8fc7333e8a14b91b8b397affb4558bfdc6dfb21546604c45d36197cc22f6eb173f0c509108ab8bf268d57c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@ Do this to generate your change history
2
2
 
3
3
  git log --pretty=format:' * %h - %s (%an, %ad)'
4
4
 
5
+ ### 0.2.3.rc1 (3 Jan 2015)
6
+
7
+ * afd9cf3 - Removed awesome print gem dependency. (Beth, Sat Jan 3 16:49:40 2015 +1100)
8
+ * 5ae2c12 - Added rake task to package pact-mock-service as a standalone executable using Travelling Ruby. (Beth, Sat Jan 3 16:14:20 2015 +1100)
9
+ * b238f2a - Added message to indicate which part of the interactions differ when an interaction with the same description and provider state, but different request/response is added. https://github.com/realestate-com-au/pact/issues/18 (Beth, Sat Jan 3 14:20:36 2015 +1100)
10
+ * cf38365 - Moved check for 'almost duplicate' interaction to when the interaction is set up. If it occurs during replay, the error does not get shown to the user. https://github.com/bethesque/pact-mock_service/issues/1 (Beth, Sat Jan 3 11:10:47 2015 +1100)
11
+ * 1da9a74 - Added --pact-dir to CLI. Make --pact-dir and --log dir if they do not already exist. (Beth, Sat Jan 3 09:07:03 2015 +1100)
12
+ * 4a2a9a2 - Added handler for SIGTERM to shut down mock service. (Beth, Fri Jan 2 12:07:29 2015 +1100)
13
+ * 57c1a14 - Added support to run the mock service on SSL. Important for browser-based consumers. (Tal Rotbart, Wed Dec 31 09:43:52 2014 +1100)
14
+
5
15
  ### 0.2.2 (29 October 2014)
6
16
 
7
17
  * 515ed14 - Added feature tests for mock service to show how it should respond under different circumstances. (Beth, Wed Oct 29 09:21:15 2014 +1100)
@@ -26,7 +26,7 @@ module Pact
26
26
  raise "Currently only http is supported" unless uri.scheme == 'http'
27
27
  raise "Currently only services on localhost are supported" unless uri.host == 'localhost'
28
28
 
29
- register(MockService.new(log_file: create_log_file(name), name: name), uri.port)
29
+ register(MockService.new(log_file: create_log_file(name), name: name, pact_dir: pact_dir), uri.port)
30
30
  end
31
31
 
32
32
  def register(app, port = FindAPort.available_port)
@@ -67,6 +67,10 @@ module Pact
67
67
 
68
68
  private
69
69
 
70
+ def pact_dir
71
+ Pact.configuration.pact_dir
72
+ end
73
+
70
74
  def create_log_file service_name
71
75
  FileUtils::mkdir_p Pact.configuration.log_dir
72
76
  log = File.open(log_file_path(service_name), 'w')
@@ -20,7 +20,6 @@ module Pact
20
20
  end
21
21
 
22
22
  class UpdatableInteractionsFilter < InteractionsFilter
23
-
24
23
  def << interaction
25
24
  if (ndx = index_of(interaction))
26
25
  @interactions[ndx] = interaction
@@ -28,21 +27,6 @@ module Pact
28
27
  @interactions << interaction
29
28
  end
30
29
  end
31
-
32
- end
33
-
34
- class DistinctInteractionsFilter < InteractionsFilter
35
-
36
- def << interaction
37
- if (ndx = index_of(interaction))
38
- if @interactions[ndx] != interaction
39
- raise "Interaction with same description (#{interaction.description}) and provider state (#{interaction.provider_state}) already exists"
40
- end
41
- else
42
- @interactions << interaction
43
- end
44
- end
45
30
  end
46
-
47
31
  end
48
- end
32
+ end
@@ -0,0 +1,34 @@
1
+ require 'pact/consumer/mock_service/candidate_interactions'
2
+
3
+ module Pact
4
+ module Consumer
5
+ class ActualInteractions
6
+
7
+ attr_reader :matched_interactions, :interaction_mismatches, :unexpected_requests
8
+
9
+ def initialize
10
+ clear
11
+ end
12
+
13
+ # For testing, sigh
14
+ def clear
15
+ @matched_interactions = []
16
+ @interaction_mismatches = []
17
+ @unexpected_requests = []
18
+ end
19
+
20
+ def register_matched interaction
21
+ @matched_interactions << interaction
22
+ end
23
+
24
+ def register_unexpected_request request
25
+ @unexpected_requests << request
26
+ end
27
+
28
+ def register_interaction_mismatch interaction_mismatch
29
+ @interaction_mismatches << interaction_mismatch
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -3,9 +3,10 @@ require 'uri'
3
3
  require 'json'
4
4
  require 'logger'
5
5
  require 'awesome_print'
6
- require 'awesome_print/core_ext/logger' #For some reason we get an error indicating that the method 'ap' is private unless we load this specifically
7
6
  require 'pact/consumer/request'
8
- require 'pact/consumer/mock_service/interaction_list'
7
+ require 'pact/consumer/mock_service/expected_interactions'
8
+ require 'pact/consumer/mock_service/actual_interactions'
9
+ require 'pact/consumer/mock_service/verified_interactions'
9
10
  require 'pact/consumer/mock_service/interaction_delete'
10
11
  require 'pact/consumer/mock_service/interaction_post'
11
12
  require 'pact/consumer/mock_service/interaction_replay'
@@ -28,18 +29,21 @@ module Pact
28
29
 
29
30
  def initialize options = {}
30
31
  log_description = configure_logger options
31
- interaction_list = InteractionList.new
32
32
 
33
33
  @name = options.fetch(:name, "MockService")
34
- interactions = []
34
+ pact_dir = options[:pact_dir]
35
+ expected_interactions = ExpectedInteractions.new
36
+ actual_interactions = ActualInteractions.new
37
+ verified_interactions = VerifiedInteractions.new
38
+
35
39
  @handlers = [
36
- MissingInteractionsGet.new(@name, @logger, interaction_list),
37
- VerificationGet.new(@name, @logger, interaction_list, log_description),
38
- InteractionPost.new(@name, @logger, interaction_list),
39
- InteractionDelete.new(@name, @logger, interaction_list),
40
+ MissingInteractionsGet.new(@name, @logger, expected_interactions, actual_interactions),
41
+ VerificationGet.new(@name, @logger, expected_interactions, actual_interactions, log_description),
42
+ InteractionPost.new(@name, @logger, expected_interactions, verified_interactions),
43
+ InteractionDelete.new(@name, @logger, expected_interactions, actual_interactions),
40
44
  LogGet.new(@name, @logger),
41
- PactPost.new(@name, @logger, interactions),
42
- InteractionReplay.new(@name, @logger, interaction_list, interactions)
45
+ PactPost.new(@name, @logger, verified_interactions, pact_dir),
46
+ InteractionReplay.new(@name, @logger, expected_interactions, actual_interactions, verified_interactions)
43
47
  ]
44
48
  end
45
49
 
@@ -47,6 +51,7 @@ module Pact
47
51
  options = {log_file: $stdout}.merge options
48
52
  log_stream = options[:log_file]
49
53
  @logger = Logger.new log_stream
54
+ @logger.formatter = options[:log_formatter] if options[:log_formatter]
50
55
  @logger.level = Pact.configuration.logger.level
51
56
 
52
57
  if log_stream.is_a? File
@@ -64,16 +69,14 @@ module Pact
64
69
  response = []
65
70
  begin
66
71
  relevant_handler = @handlers.detect { |handler| handler.match? env }
67
- response = relevant_handler.respond env
72
+ response = relevant_handler.respond(env)
68
73
  rescue StandardError => e
69
- @logger.error 'Error ocurred in mock service:'
70
- @logger.ap e, :error
71
- @logger.ap e.backtrace
74
+ @logger.error "Error ocurred in mock service: #{e.class} - #{e.message}"
75
+ @logger.error e.backtrace.join("\n")
72
76
  response = [500, {'Content-Type' => 'application/json'}, [{message: e.message, backtrace: e.backtrace}.to_json]]
73
77
  rescue Exception => e
74
- @logger.error 'Exception ocurred in mock service:'
75
- @logger.ap e, :error
76
- @logger.ap e.backtrace
78
+ @logger.error "Exception ocurred in mock service: #{e.class} - #{e.message}"
79
+ @logger.error e.backtrace.join("\n")
77
80
  raise e
78
81
  end
79
82
  response
@@ -81,4 +84,4 @@ module Pact
81
84
 
82
85
  end
83
86
  end
84
- end
87
+ end
@@ -0,0 +1,13 @@
1
+ module Pact
2
+ module Consumer
3
+ class CandidateInteractions < Array
4
+
5
+ def matching_interactions actual_request
6
+ select do | candidate_interaction |
7
+ candidate_interaction.request.matches? actual_request
8
+ end
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ require 'pact/consumer/mock_service/candidate_interactions'
2
+
3
+ module Pact
4
+ module Consumer
5
+ class ExpectedInteractions < Array
6
+
7
+ def find_candidate_interactions actual_request
8
+ CandidateInteractions.new(
9
+ select do | interaction |
10
+ interaction.request.matches_route? actual_request
11
+ end
12
+ )
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -8,11 +8,12 @@ module Pact
8
8
 
9
9
  include RackRequestHelper
10
10
 
11
- attr_accessor :interaction_list
11
+ attr_accessor :expected_interactions, :actual_interactions
12
12
 
13
- def initialize name, logger, interaction_list
13
+ def initialize name, logger, expected_interactions, actual_interactions
14
14
  super name, logger
15
- @interaction_list = interaction_list
15
+ @expected_interactions = expected_interactions
16
+ @actual_interactions = actual_interactions
16
17
  end
17
18
 
18
19
  def request_path
@@ -24,10 +25,15 @@ module Pact
24
25
  end
25
26
 
26
27
  def respond env
27
- interaction_list.clear
28
- logger.info "Cleared interactions before example \"#{params_hash(env)['example_description']}\""
28
+ expected_interactions.clear
29
+ actual_interactions.clear
30
+ logger.info "Cleared interactions before example \"#{example_description(env)}\""
29
31
  [200, {}, ['Deleted interactions']]
30
32
  end
33
+
34
+ def example_description env
35
+ params_hash(env).fetch('example_description', [])[0]
36
+ end
31
37
  end
32
38
  end
33
39
  end
@@ -1,14 +1,15 @@
1
1
  require 'pact/consumer/mock_service/mock_service_administration_endpoint'
2
+ require 'pact/mock_service/interaction_decorator'
3
+ require 'pact/shared/json_differ'
2
4
 
3
5
  module Pact
4
6
  module Consumer
5
7
  class InteractionPost < MockServiceAdministrationEndpoint
6
8
 
7
- attr_accessor :interaction_list
8
-
9
- def initialize name, logger, interaction_list
9
+ def initialize name, logger, expected_interactions, verified_interactions
10
10
  super name, logger
11
- @interaction_list = interaction_list
11
+ @expected_interactions = expected_interactions
12
+ @verified_interactions = verified_interactions
12
13
  end
13
14
 
14
15
  def request_path
@@ -21,12 +22,74 @@ module Pact
21
22
 
22
23
  def respond env
23
24
  request_body = env['rack.input'].string
24
- interaction = Interaction.from_hash(JSON.load(request_body))
25
- interaction_list.add interaction
25
+ interaction = Interaction.from_hash(JSON.load(request_body)) # Load creates the Pact::XXX classes
26
+
27
+ if (previous_interaction = interaction_already_verified_with_same_description_and_provider_state_but_not_equal(interaction))
28
+ handle_almost_duplicate_interaction previous_interaction, interaction
29
+ else
30
+ add_expected_interaction request_body, interaction
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ attr_accessor :expected_interactions, :verified_interactions
37
+
38
+ def add_expected_interaction request_body, interaction
39
+ expected_interactions << interaction
26
40
  logger.info "Registered expected interaction #{interaction.request.method_and_path}"
27
41
  logger.debug JSON.pretty_generate JSON.parse(request_body)
28
42
  [200, {}, ['Added interaction']]
29
43
  end
44
+
45
+ def handle_almost_duplicate_interaction previous_interaction, interaction
46
+ message = InteractionDiffMessage.new(previous_interaction, interaction).to_s
47
+ logger.error message
48
+ [500, {}, [message]]
49
+ end
50
+
51
+ def interaction_already_verified_with_same_description_and_provider_state_but_not_equal interaction
52
+ other = verified_interactions.find_matching_description_and_provider_state interaction
53
+ other && other != interaction ? other : nil
54
+ end
55
+
56
+ class InteractionDiffMessage
57
+
58
+ def initialize previous_interaction, new_interaction
59
+ @previous_interaction = previous_interaction
60
+ @new_interaction = new_interaction
61
+ end
62
+
63
+ def to_s
64
+ "An interaction with same description (#{new_interaction.description.inspect}) and provider state (#{new_interaction.provider_state.inspect}) but a different #{differences} has already been used. Please use a different description or provider state."
65
+ end
66
+
67
+ private
68
+
69
+ attr_reader :previous_interaction, :new_interaction
70
+
71
+ def differences
72
+ diff = Pact::JsonDiffer.call(previous_interaction_hash, new_interaction_hash, allow_unexpected_keys: false)
73
+ diff.keys.collect do | parent_key |
74
+ diff[parent_key].keys.collect do | child_key |
75
+ "#{parent_key} #{child_key}"
76
+ end
77
+ end.flatten.join(", ").reverse.sub(",", "dna ").reverse
78
+ end
79
+
80
+ def previous_interaction_hash
81
+ raw_hash previous_interaction
82
+ end
83
+
84
+ def new_interaction_hash
85
+ raw_hash new_interaction
86
+ end
87
+
88
+ def raw_hash interaction
89
+ JSON.parse(Pact::MockService::InteractionDecorator.new(interaction).to_json)
90
+ end
91
+
92
+ end
30
93
  end
31
94
  end
32
95
  end
@@ -1,25 +1,34 @@
1
1
  require 'pact/matchers'
2
+ require 'pact/consumer/request'
2
3
  require 'pact/consumer/mock_service/rack_request_helper'
3
4
  require 'pact/consumer/mock_service/interaction_mismatch'
4
5
  require 'pact/consumer_contract'
5
- require 'pact/consumer/interactions_filter'
6
6
  require 'pact/mock_service/response_decorator'
7
7
  require 'pact/mock_service/interaction_decorator'
8
8
 
9
9
  module Pact
10
10
  module Consumer
11
11
 
12
+ module PrettyGenerate
13
+ #Doesn't seem to reliably pretty generate unless we go to JSON and back again :(
14
+ def pretty_generate object
15
+ JSON.pretty_generate(JSON.parse(object.to_json))
16
+ end
17
+ end
18
+
12
19
  class InteractionReplay
13
20
  include Pact::Matchers
14
21
  include RackRequestHelper
22
+ include PrettyGenerate
15
23
 
16
- attr_accessor :name, :logger, :interaction_list, :interactions
24
+ attr_accessor :name, :logger, :expected_interactions, :actual_interactions, :verified_interactions
17
25
 
18
- def initialize name, logger, interaction_list, interactions
26
+ def initialize name, logger, expected_interactions, actual_interactions, verified_interactions
19
27
  @name = name
20
28
  @logger = logger
21
- @interaction_list = interaction_list
22
- @interactions = DistinctInteractionsFilter.new(interactions)
29
+ @expected_interactions = expected_interactions
30
+ @actual_interactions = actual_interactions
31
+ @verified_interactions = verified_interactions
23
32
  end
24
33
 
25
34
  def match? env
@@ -32,16 +41,12 @@ module Pact
32
41
 
33
42
  private
34
43
 
35
- def add_verified_interaction interaction
36
- interactions << interaction
37
- end
38
-
39
44
  def find_response request_hash
40
45
  actual_request = Request::Actual.from_hash(request_hash)
41
46
  logger.info "Received request #{actual_request.method_and_path}"
42
47
  logger.debug pretty_generate request_hash
43
- candidate_interactions = interaction_list.find_candidate_interactions actual_request
44
- matching_interactions = find_matching_interactions actual_request, from: candidate_interactions
48
+ candidate_interactions = expected_interactions.find_candidate_interactions actual_request
49
+ matching_interactions = candidate_interactions.matching_interactions actual_request
45
50
 
46
51
  case matching_interactions.size
47
52
  when 0 then handle_unrecognised_request actual_request, candidate_interactions
@@ -51,50 +56,66 @@ module Pact
51
56
  end
52
57
  end
53
58
 
54
- def find_matching_interactions actual_request, opts
55
- candidate_interactions = opts.fetch(:from)
56
- candidate_interactions.select do | candidate_interaction |
57
- candidate_interaction.request.matches? actual_request
58
- end
59
+ def handle_matched_interaction interaction
60
+ HandleMatchedInteraction.call(interaction, verified_interactions, actual_interactions, logger)
59
61
  end
60
62
 
61
- def handle_matched_interaction interaction
62
- interaction_list.register_matched interaction
63
- add_verified_interaction interaction
64
- response = response_from(interaction.response)
65
- logger.info "Found matching response for #{interaction.request.method_and_path}"
66
- logger.debug pretty_generate(Pact::MockService::ResponseDecorator.new(interaction.response))
67
- response
63
+ def handle_more_than_one_matching_interaction actual_request, matching_interactions
64
+ HandleMultipleInteractionsFound.call(actual_request, matching_interactions, logger)
68
65
  end
69
66
 
70
- def multiple_interactions_found_response actual_request, matching_interactions
71
- response = {
72
- message: "Multiple interaction found for #{actual_request.method_and_path}",
73
- matching_interactions: matching_interactions.collect{ | interaction | request_summary_for(interaction) }
74
- }
75
- [500, {'Content-Type' => 'application/json'}, [response.to_json]]
67
+ def handle_unrecognised_request actual_request, candidate_interactions
68
+ HandleUnrecognisedInteraction.call(actual_request, candidate_interactions, actual_interactions, logger)
76
69
  end
77
70
 
78
- def handle_more_than_one_matching_interaction actual_request, matching_interactions
71
+ def logger_info_ap msg
72
+ logger.info msg
73
+ end
74
+
75
+ end
76
+
77
+ class HandleMultipleInteractionsFound
78
+
79
+ extend PrettyGenerate
80
+
81
+ def self.call actual_request, matching_interactions, logger
79
82
  logger.error "Multiple interactions found for #{actual_request.method_and_path}:"
80
83
  matching_interactions.each do | interaction |
81
84
  logger.debug pretty_generate(Pact::MockService::InteractionDecorator.new(interaction))
82
85
  end
83
- multiple_interactions_found_response actual_request, matching_interactions
86
+ response actual_request, matching_interactions
84
87
  end
85
88
 
86
- def interaction_mismatch actual_request, candidate_interactions
87
- InteractionMismatch.new(candidate_interactions, actual_request)
89
+ def self.response actual_request, matching_interactions
90
+ response = {
91
+ message: "Multiple interaction found for #{actual_request.method_and_path}",
92
+ matching_interactions: matching_interactions.collect{ | interaction | request_summary_for(interaction) }
93
+ }
94
+ [500, {'Content-Type' => 'application/json'}, [response.to_json]]
88
95
  end
89
96
 
90
- def request_summary_for interaction
97
+ def self.request_summary_for interaction
91
98
  summary = {:description => interaction.description}
92
99
  summary[:provider_state] if interaction.provider_state
93
100
  summary[:request] = Pact::MockService::RequestDecorator.new(interaction.request)
94
101
  summary
95
102
  end
103
+ end
104
+
105
+ class HandleUnrecognisedInteraction
106
+
107
+ def self.call actual_request, candidate_interactions, actual_interactions, logger
108
+ interaction_mismatch = interaction_mismatch(actual_request, candidate_interactions)
109
+ if candidate_interactions.any?
110
+ actual_interactions.register_interaction_mismatch interaction_mismatch
111
+ else
112
+ actual_interactions.register_unexpected_request actual_request
113
+ end
114
+ log interaction_mismatch, logger
115
+ response interaction_mismatch
116
+ end
96
117
 
97
- def unrecognised_request_response interaction_mismatch
118
+ def self.response interaction_mismatch
98
119
  response = {
99
120
  message: "No interaction found for #{interaction_mismatch.actual_request.method_and_path}",
100
121
  interaction_diffs: interaction_mismatch.to_hash
@@ -102,40 +123,39 @@ module Pact
102
123
  [500, {'Content-Type' => 'application/json'}, [response.to_json]]
103
124
  end
104
125
 
105
- def log_unrecognised_request_and_interaction_diff interaction_mismatch
106
- logger.error "No matching interaction found on #{name} for #{interaction_mismatch.actual_request.method_and_path}"
126
+ def self.interaction_mismatch actual_request, candidate_interactions
127
+ InteractionMismatch.new(candidate_interactions, actual_request)
128
+ end
129
+
130
+ def self.log interaction_mismatch, logger
131
+ logger.error "No matching interaction found for #{interaction_mismatch.actual_request.method_and_path}"
107
132
  logger.error 'Interaction diffs for that route:'
108
133
  logger.error(interaction_mismatch.to_s)
109
134
  end
110
135
 
111
- def handle_unrecognised_request actual_request, candidate_interactions
112
- interaction_mismatch = interaction_mismatch(actual_request, candidate_interactions)
113
- if candidate_interactions.any?
114
- interaction_list.register_interaction_mismatch interaction_mismatch
115
- else
116
- interaction_list.register_unexpected_request actual_request
117
- end
118
- log_unrecognised_request_and_interaction_diff interaction_mismatch
119
- unrecognised_request_response interaction_mismatch
136
+ end
137
+
138
+ class HandleMatchedInteraction
139
+
140
+ extend PrettyGenerate
141
+
142
+ def self.call interaction, verified_interactions, actual_interactions, logger
143
+ actual_interactions.register_matched interaction
144
+ verified_interactions << interaction
145
+ response = response_from(interaction.response)
146
+ logger.info "Found matching response for #{interaction.request.method_and_path}"
147
+ logger.debug pretty_generate(Pact::MockService::ResponseDecorator.new(interaction.response))
148
+ response
120
149
  end
121
150
 
122
- def response_from response
151
+ def self.response_from response
123
152
  [response.status, (Pact::Reification.from_term(response.headers) || {}).to_hash, [render_body(Pact::Reification.from_term(response.body))]]
124
153
  end
125
154
 
126
- def render_body body
155
+ def self.render_body body
127
156
  return '' unless body
128
157
  body.kind_of?(String) ? body.force_encoding('utf-8') : body.to_json
129
158
  end
130
-
131
- def logger_info_ap msg
132
- logger.info msg
133
- end
134
-
135
- #Doesn't seem to reliably pretty generate unless we go to JSON and back again :(
136
- def pretty_generate object
137
- JSON.pretty_generate(JSON.parse(object.to_json))
138
- end
139
159
  end
140
160
  end
141
161
  end
@@ -21,7 +21,7 @@ module Pact
21
21
  end
22
22
 
23
23
  def message env
24
- params_hash(env)['msg']
24
+ params_hash(env).fetch('msg', [])[0]
25
25
  end
26
26
  end
27
27
  end
@@ -1,4 +1,5 @@
1
1
  require 'pact/consumer/mock_service/mock_service_administration_endpoint'
2
+ require 'pact/consumer/mock_service/verification'
2
3
 
3
4
  module Pact
4
5
  module Consumer
@@ -6,9 +7,10 @@ module Pact
6
7
  class MissingInteractionsGet < MockServiceAdministrationEndpoint
7
8
  include RackRequestHelper
8
9
 
9
- def initialize name, logger, interaction_list
10
+ def initialize name, logger, expected_interactions, actual_interactions
10
11
  super name, logger
11
- @interaction_list = interaction_list
12
+ @expected_interactions = expected_interactions
13
+ @actual_interactions = actual_interactions
12
14
  end
13
15
 
14
16
  def request_path
@@ -20,11 +22,12 @@ module Pact
20
22
  end
21
23
 
22
24
  def respond env
23
- number_of_missing_interactions = @interaction_list.missing_interactions.size
25
+ verification = Verification.new(@expected_interactions, @actual_interactions)
26
+ number_of_missing_interactions = verification.missing_interactions.size
24
27
  logger.info "Number of missing interactions for mock \"#{name}\" = #{number_of_missing_interactions}"
25
28
  [200, {}, [{size: number_of_missing_interactions}.to_json]]
26
29
  end
27
30
 
28
31
  end
29
32
  end
30
- end
33
+ end
@@ -5,11 +5,12 @@ module Pact
5
5
  module Consumer
6
6
  class PactPost < MockServiceAdministrationEndpoint
7
7
 
8
- attr_accessor :consumer_contract, :interactions
8
+ attr_accessor :consumer_contract, :verified_interactions, :default_options
9
9
 
10
- def initialize name, logger, interactions
10
+ def initialize name, logger, verified_interactions, pact_dir
11
11
  super name, logger
12
- @interactions = interactions
12
+ @verified_interactions = verified_interactions
13
+ @default_options = {pact_dir: pact_dir}
13
14
  end
14
15
 
15
16
  def request_path
@@ -23,7 +24,8 @@ module Pact
23
24
  def respond env
24
25
  consumer_contract_details = JSON.parse(env['rack.input'].string, symbolize_names: true)
25
26
  logger.info "Writing pact with details #{consumer_contract_details}"
26
- consumer_contract_writer = ConsumerContractWriter.new(consumer_contract_details.merge(interactions: interactions), logger)
27
+ consumer_contract_params = default_options.merge(consumer_contract_details.merge(interactions: verified_interactions))
28
+ consumer_contract_writer = ConsumerContractWriter.new(consumer_contract_params, logger)
27
29
  json = consumer_contract_writer.write
28
30
 
29
31
  [200, {'Content-Type' =>'application/json'}, [json]]
@@ -10,7 +10,7 @@ module Pact
10
10
  }
11
11
 
12
12
  def params_hash env
13
- env["QUERY_STRING"].split("&").collect{| param| param.split("=")}.inject({}){|params, param| params[param.first] = URI.decode(param.last); params }
13
+ CGI::parse env["QUERY_STRING"]
14
14
  end
15
15
 
16
16
  def request_as_hash_from env