pact-mock_service 0.2.4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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