pact-mock_service 0.2.2 → 0.2.3.pre.rc1

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 (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