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
@@ -1,162 +0,0 @@
1
- require 'pact/matchers'
2
- require 'pact/consumer/request'
3
- require 'pact/consumer/mock_service/rack_request_helper'
4
- require 'pact/consumer/mock_service/interaction_mismatch'
5
- require 'pact/consumer_contract'
6
- require 'pact/mock_service/response_decorator'
7
- require 'pact/mock_service/interaction_decorator'
8
-
9
- module Pact
10
- module Consumer
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
-
19
- class InteractionReplay
20
- include Pact::Matchers
21
- include RackRequestHelper
22
- include PrettyGenerate
23
-
24
- attr_accessor :name, :logger, :expected_interactions, :actual_interactions, :verified_interactions
25
-
26
- def initialize name, logger, expected_interactions, actual_interactions, verified_interactions, cors_enabled=false
27
- @name = name
28
- @logger = logger
29
- @expected_interactions = expected_interactions
30
- @actual_interactions = actual_interactions
31
- @verified_interactions = 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 = 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
- 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
@@ -1,28 +0,0 @@
1
- require 'pact/consumer/mock_service/mock_service_administration_endpoint'
2
-
3
- module Pact
4
- module Consumer
5
- class LogGet < MockServiceAdministrationEndpoint
6
-
7
- include RackRequestHelper
8
-
9
- def request_path
10
- '/log'
11
- end
12
-
13
- def request_method
14
- 'GET'
15
- end
16
-
17
-
18
- def respond env
19
- logger.info "Debug message from client - #{message(env)}"
20
- [200, {}, []]
21
- end
22
-
23
- def message env
24
- params_hash(env).fetch('msg', [])[0]
25
- end
26
- end
27
- end
28
- end
@@ -1,32 +0,0 @@
1
- require 'pact/consumer/mock_service/mock_service_administration_endpoint'
2
- require 'pact/consumer/mock_service/verification'
3
-
4
- module Pact
5
- module Consumer
6
-
7
- class MissingInteractionsGet < MockServiceAdministrationEndpoint
8
- include RackRequestHelper
9
-
10
- def initialize name, logger, expected_interactions, actual_interactions
11
- super name, logger
12
- @expected_interactions = expected_interactions
13
- @actual_interactions = 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 = 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
@@ -1,40 +0,0 @@
1
- require 'pact/consumer/mock_service/rack_request_helper'
2
- module Pact
3
- module Consumer
4
- class MockServiceAdministrationEndpoint
5
-
6
- include RackRequestHelper
7
-
8
- attr_accessor :logger, :name
9
-
10
- def initialize name, logger
11
- @name = name
12
- @logger = logger
13
- end
14
-
15
- def match? env
16
- has_mock_service_header?(env) && path_matches?(env) && method_matches?(env)
17
- end
18
-
19
- def has_mock_service_header? env
20
- env['HTTP_X_PACT_MOCK_SERVICE']
21
- end
22
-
23
- def path_matches? env
24
- env['PATH_INFO'].chomp("/") == request_path
25
- end
26
-
27
- def method_matches? env
28
- env['REQUEST_METHOD'] == request_method
29
- end
30
-
31
- def request_path
32
- raise NotImplementedError
33
- end
34
-
35
- def request_method
36
- raise NotImplementedError
37
- end
38
- end
39
- end
40
- end
@@ -1,43 +0,0 @@
1
- require 'pact/consumer/mock_service/rack_request_helper'
2
- require 'pact/consumer/mock_service/mock_service_administration_endpoint'
3
-
4
- module Pact
5
- module Consumer
6
-
7
- class Options
8
-
9
- include RackRequestHelper
10
-
11
- attr_reader :name, :logger, :cors_enabled
12
-
13
- def initialize name, logger, cors_enabled
14
- @name = name
15
- @logger = logger
16
- @cors_enabled = cors_enabled
17
- end
18
-
19
- def match? env
20
- is_options_request?(env) && (cors_enabled || is_administration_request?(env))
21
- end
22
-
23
- def respond env
24
- cors_headers = {
25
- 'Access-Control-Allow-Origin' => env.fetch('HTTP_ORIGIN','*'),
26
- 'Access-Control-Allow-Headers' => headers_from(env)["Access-Control-Request-Headers"],
27
- 'Access-Control-Allow-Methods' => 'DELETE, POST, GET, HEAD, PUT, TRACE, CONNECT'
28
- }
29
- 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}."
30
- [200, cors_headers, []]
31
- end
32
-
33
- def is_options_request? env
34
- env['REQUEST_METHOD'] == 'OPTIONS'
35
- end
36
-
37
- def is_administration_request? env
38
- env["HTTP_ACCESS_CONTROL_REQUEST_HEADERS"].match(/x-pact-mock-service/i)
39
- end
40
-
41
- end
42
- end
43
- end
@@ -1,38 +0,0 @@
1
- require 'pact/consumer/mock_service/mock_service_administration_endpoint'
2
- require 'pact/consumer_contract/consumer_contract_writer'
3
-
4
- module Pact
5
- module Consumer
6
- class PactPost < MockServiceAdministrationEndpoint
7
-
8
- attr_accessor :consumer_contract, :verified_interactions, :default_options
9
-
10
- def initialize name, logger, verified_interactions, pact_dir, consumer_contract_details
11
- super name, logger
12
- @verified_interactions = verified_interactions
13
- @default_options = {pact_dir: pact_dir}
14
- if consumer_contract_details
15
- @default_options.merge!(consumer_contract_details)
16
- end
17
- end
18
-
19
- def request_path
20
- '/pact'
21
- end
22
-
23
- def request_method
24
- 'POST'
25
- end
26
-
27
- def respond env
28
- consumer_contract_details = JSON.parse(env['rack.input'].string, symbolize_names: true)
29
- logger.info "Writing pact with details #{consumer_contract_details}"
30
- consumer_contract_params = default_options.merge(consumer_contract_details.merge(interactions: verified_interactions))
31
- consumer_contract_writer = ConsumerContractWriter.new(consumer_contract_params, logger)
32
- json = consumer_contract_writer.write
33
-
34
- [200, {'Content-Type' =>'application/json'}, [json]]
35
- end
36
- end
37
- end
38
- end
@@ -1,41 +0,0 @@
1
- require 'pact/consumer/mock_service/expected_interactions'
2
- require 'pact/consumer/mock_service/actual_interactions'
3
- require 'pact/consumer/mock_service/verified_interactions'
4
- require 'pact/consumer/mock_service/interaction_delete'
5
- require 'pact/consumer/mock_service/interaction_post'
6
- require 'pact/consumer/mock_service/interaction_replay'
7
- require 'pact/consumer/mock_service/missing_interactions_get'
8
- require 'pact/consumer/mock_service/verification_get'
9
- require 'pact/consumer/mock_service/log_get'
10
- require 'pact/consumer/mock_service/pact_post'
11
- require 'pact/consumer/mock_service/index_get'
12
- require 'pact/consumer/mock_service/options'
13
- require 'pact/consumer/request'
14
- require 'pact/support'
15
-
16
- module Pact
17
- module Consumer
18
- class MockService
19
- class RequestHandlers
20
- def initialize name, logger, expected_interactions, actual_interactions, verified_interactions, options
21
- @handlers = [
22
- Options.new(name, logger, options[:cors_enabled]),
23
- MissingInteractionsGet.new(name, logger, expected_interactions, actual_interactions),
24
- VerificationGet.new(name, logger, expected_interactions, actual_interactions, options[:log_description]),
25
- InteractionPost.new(name, logger, expected_interactions, verified_interactions),
26
- InteractionDelete.new(name, logger, expected_interactions, actual_interactions),
27
- LogGet.new(name, logger),
28
- PactPost.new(name, logger, verified_interactions, options[:pact_dir], options[:consumer_contract_details]),
29
- IndexGet.new(name, logger),
30
- InteractionReplay.new(name, logger, expected_interactions, actual_interactions, verified_interactions, options[:cors_enabled])
31
- ]
32
- end
33
-
34
- def call env
35
- relevant_handler = @handlers.detect { |handler| handler.match? env }
36
- response = relevant_handler.respond(env)
37
- end
38
- end
39
- end
40
- end
41
- end
@@ -1,46 +0,0 @@
1
- module Pact
2
- module Consumer
3
- class Verification
4
-
5
- def initialize expected_interactions, actual_interactions
6
- @expected_interactions = expected_interactions
7
- @actual_interactions = actual_interactions
8
- end
9
-
10
- def all_matched?
11
- interaction_diffs.empty?
12
- end
13
-
14
- def interaction_diffs
15
- {
16
- :missing_interactions => missing_interactions_summaries,
17
- :interaction_mismatches => interaction_mismatches_summaries,
18
- :unexpected_requests => unexpected_requests_summaries
19
- }.each_with_object({}) do | (key, value), hash |
20
- hash[key] = value if value.any?
21
- end
22
- end
23
-
24
- def missing_interactions_summaries
25
- missing_interactions.collect(&:request).collect(&:method_and_path)
26
- end
27
-
28
- def interaction_mismatches_summaries
29
- actual_interactions.interaction_mismatches.collect(&:short_summary)
30
- end
31
-
32
- def unexpected_requests_summaries
33
- actual_interactions.unexpected_requests.collect(&:method_and_path)
34
- end
35
-
36
- def missing_interactions
37
- expected_interactions - actual_interactions.matched_interactions - @actual_interactions.interaction_mismatches.collect(&:candidate_interactions).flatten
38
- end
39
-
40
- private
41
-
42
- attr_reader :expected_interactions, :actual_interactions
43
-
44
- end
45
- end
46
- end
@@ -1,73 +0,0 @@
1
- require 'pact/consumer/mock_service/mock_service_administration_endpoint'
2
-
3
- module Pact
4
- module Consumer
5
- class VerificationGet < MockServiceAdministrationEndpoint
6
-
7
- include RackRequestHelper
8
-
9
- def initialize name, logger, expected_interactions, actual_interactions, log_description
10
- super name, logger
11
- @expected_interactions = expected_interactions
12
- @actual_interactions = actual_interactions
13
- @log_description = log_description
14
- end
15
-
16
- def request_path
17
- '/interactions/verification'
18
- end
19
-
20
- def request_method
21
- 'GET'
22
- end
23
-
24
- def respond env
25
- verification = Verification.new(expected_interactions, actual_interactions)
26
- if verification.all_matched?
27
- logger.info "Verifying - interactions matched for example \"#{example_description(env)}\""
28
- [200, {'Content-Type' => 'text/plain'}, ['Interactions matched']]
29
- else
30
- error_message = FailureMessage.new(verification).to_s
31
- logger.warn "Verifying - actual interactions do not match expected interactions for example \"#{example_description(env)}\". \n#{error_message}"
32
- logger.warn error_message
33
- [500, {'Content-Type' => 'text/plain'}, ["Actual interactions do not match expected interactions for mock #{name}.\n\n#{error_message}See #{log_description} for details."]]
34
- end
35
- end
36
-
37
- private
38
-
39
- attr_accessor :expected_interactions, :actual_interactions, :log_description
40
-
41
- def example_description env
42
- params_hash(env).fetch("example_description", [])[0]
43
- end
44
-
45
- class FailureMessage
46
-
47
- def initialize verification
48
- @verification = verification
49
- end
50
-
51
- def to_s
52
- titles_and_summaries.collect do | title, summaries |
53
- "#{title}:\n\t#{summaries.join("\n\t")}\n\n" if summaries.any?
54
- end.compact.join
55
-
56
- end
57
-
58
- private
59
-
60
- attr_reader :verification
61
-
62
- def titles_and_summaries
63
- {
64
- "Incorrect requests" => verification.interaction_mismatches_summaries,
65
- "Missing requests" => verification.missing_interactions_summaries,
66
- "Unexpected requests" => verification.unexpected_requests_summaries,
67
- }
68
- end
69
-
70
- end
71
- end
72
- end
73
- end
@@ -1,18 +0,0 @@
1
- module Pact
2
- module Consumer
3
- class VerifiedInteractions < Array
4
-
5
- def << interaction
6
- unless find_matching_description_and_provider_state interaction
7
- super
8
- end
9
- end
10
-
11
- def find_matching_description_and_provider_state interaction
12
- find do |candidate_interaction|
13
- candidate_interaction.matches_criteria?(description: interaction.description, provider_state: interaction.provider_state)
14
- end
15
- end
16
- end
17
- end
18
- end
@@ -1,43 +0,0 @@
1
- require 'pact/consumer/mock_service/app'
2
- require 'pact/consumer/mock_service/error_handler'
3
-
4
- module Pact
5
- module Consumer
6
-
7
- class MockService
8
-
9
- def initialize options = {}
10
- logger, log_description = configure_logger(options)
11
- app_options = options.merge(logger: logger, log_description: log_description)
12
- @app = Rack::Builder.app do
13
- use ErrorHandler, logger
14
- use CorsOriginHeaderMiddleware, options[:cors_enabled]
15
- run App.new(app_options)
16
- end
17
- end
18
-
19
- def call env
20
- @app.call env
21
- end
22
-
23
- def shutdown
24
- @app.shutdown
25
- end
26
-
27
- def configure_logger options
28
- options = {log_file: $stdout}.merge options
29
- log_stream = options[:log_file]
30
- logger = Logger.new log_stream
31
- logger.formatter = options[:log_formatter] if options[:log_formatter]
32
- logger.level = Pact.configuration.logger.level
33
-
34
- log_description = if log_stream.is_a? File
35
- File.absolute_path(log_stream).gsub(Dir.pwd + "/", '')
36
- else
37
- "standard out/err"
38
- end
39
- return logger, log_description
40
- end
41
- end
42
- end
43
- end