hs-pact-mock_service 3.9.2
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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +494 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +137 -0
- data/bin/pact-mock-service +3 -0
- data/bin/pact-stub-service +3 -0
- data/lib/pact/consumer/mock_service/cors_origin_header_middleware.rb +31 -0
- data/lib/pact/consumer/mock_service/error_handler.rb +31 -0
- data/lib/pact/consumer/mock_service/rack_request_helper.rb +65 -0
- data/lib/pact/consumer/mock_service/set_location.rb +28 -0
- data/lib/pact/consumer/server.rb +111 -0
- data/lib/pact/consumer_contract/consumer_contract_decorator.rb +53 -0
- data/lib/pact/consumer_contract/consumer_contract_writer.rb +181 -0
- data/lib/pact/consumer_contract/interaction_decorator.rb +40 -0
- data/lib/pact/consumer_contract/request_decorator.rb +88 -0
- data/lib/pact/consumer_contract/response_decorator.rb +47 -0
- data/lib/pact/mock_service/app.rb +85 -0
- data/lib/pact/mock_service/app_manager.rb +156 -0
- data/lib/pact/mock_service/cli/custom_thor.rb +74 -0
- data/lib/pact/mock_service/cli/pidfile.rb +99 -0
- data/lib/pact/mock_service/cli.rb +213 -0
- data/lib/pact/mock_service/client.rb +79 -0
- data/lib/pact/mock_service/control_server/app.rb +42 -0
- data/lib/pact/mock_service/control_server/delegator.rb +44 -0
- data/lib/pact/mock_service/control_server/index.rb +25 -0
- data/lib/pact/mock_service/control_server/mock_service_creator.rb +32 -0
- data/lib/pact/mock_service/control_server/mock_services.rb +26 -0
- data/lib/pact/mock_service/control_server/require_pacticipant_headers.rb +20 -0
- data/lib/pact/mock_service/control_server/run.rb +73 -0
- data/lib/pact/mock_service/errors.rb +9 -0
- data/lib/pact/mock_service/interaction_decorator.rb +49 -0
- data/lib/pact/mock_service/interactions/actual_interactions.rb +36 -0
- data/lib/pact/mock_service/interactions/candidate_interactions.rb +15 -0
- data/lib/pact/mock_service/interactions/expected_interactions.rb +18 -0
- data/lib/pact/mock_service/interactions/interaction_diff_message.rb +45 -0
- data/lib/pact/mock_service/interactions/interaction_mismatch.rb +74 -0
- data/lib/pact/mock_service/interactions/interactions_filter.rb +66 -0
- data/lib/pact/mock_service/interactions/verification.rb +52 -0
- data/lib/pact/mock_service/interactions/verified_interactions.rb +20 -0
- data/lib/pact/mock_service/logger.rb +40 -0
- data/lib/pact/mock_service/request_decorator.rb +36 -0
- data/lib/pact/mock_service/request_handlers/base_administration_request_handler.rb +42 -0
- data/lib/pact/mock_service/request_handlers/base_request_handler.rb +30 -0
- data/lib/pact/mock_service/request_handlers/index_get.rb +23 -0
- data/lib/pact/mock_service/request_handlers/interaction_delete.rb +38 -0
- data/lib/pact/mock_service/request_handlers/interaction_post.rb +43 -0
- data/lib/pact/mock_service/request_handlers/interaction_replay.rb +191 -0
- data/lib/pact/mock_service/request_handlers/interactions_put.rb +44 -0
- data/lib/pact/mock_service/request_handlers/log_get.rb +27 -0
- data/lib/pact/mock_service/request_handlers/missing_interactions_get.rb +33 -0
- data/lib/pact/mock_service/request_handlers/options.rb +64 -0
- data/lib/pact/mock_service/request_handlers/pact_post.rb +38 -0
- data/lib/pact/mock_service/request_handlers/session_delete.rb +32 -0
- data/lib/pact/mock_service/request_handlers/verification_get.rb +74 -0
- data/lib/pact/mock_service/request_handlers.rb +42 -0
- data/lib/pact/mock_service/response_decorator.rb +31 -0
- data/lib/pact/mock_service/run.rb +125 -0
- data/lib/pact/mock_service/server/respawn.rb +20 -0
- data/lib/pact/mock_service/server/spawn.rb +37 -0
- data/lib/pact/mock_service/server/wait_for_server_up.rb +44 -0
- data/lib/pact/mock_service/server/webrick_request_monkeypatch.rb +15 -0
- data/lib/pact/mock_service/session.rb +96 -0
- data/lib/pact/mock_service/spawn.rb +86 -0
- data/lib/pact/mock_service/version.rb +5 -0
- data/lib/pact/mock_service.rb +2 -0
- data/lib/pact/stub_service/cli.rb +71 -0
- data/lib/pact/support/expand_file_list.rb +26 -0
- metadata +399 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module MockService
|
|
3
|
+
module Interactions
|
|
4
|
+
class Verification
|
|
5
|
+
|
|
6
|
+
def initialize expected_interactions, actual_interactions
|
|
7
|
+
@expected_interactions = expected_interactions
|
|
8
|
+
@actual_interactions = actual_interactions
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def all_matched?
|
|
12
|
+
interaction_diffs.empty?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def interaction_diffs
|
|
16
|
+
{
|
|
17
|
+
:missing_interactions => missing_interactions_summaries,
|
|
18
|
+
:interaction_mismatches => interaction_mismatches_summaries,
|
|
19
|
+
:unexpected_requests => unexpected_requests_summaries
|
|
20
|
+
}.each_with_object({}) do | (key, value), hash |
|
|
21
|
+
hash[key] = value if value.any?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def missing_interactions_summaries
|
|
26
|
+
missing_interactions.collect(&:request).collect(&:method_and_path)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def interaction_mismatches_summaries
|
|
30
|
+
actual_interactions.interaction_mismatches.collect(&:short_summary)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def unexpected_requests_summaries
|
|
34
|
+
actual_interactions.unexpected_requests.collect(&:method_and_path)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def missing_interactions
|
|
38
|
+
expected_interactions - actual_interactions.matched_interactions - @actual_interactions.interaction_mismatches.collect(&:candidate_interactions).flatten
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def interaction_mismatches
|
|
42
|
+
actual_interactions.interaction_mismatches
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
attr_reader :expected_interactions, :actual_interactions
|
|
48
|
+
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module MockService
|
|
3
|
+
module Interactions
|
|
4
|
+
class VerifiedInteractions < Array
|
|
5
|
+
|
|
6
|
+
def << interaction
|
|
7
|
+
unless find_matching_description_and_provider_state interaction
|
|
8
|
+
super
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def find_matching_description_and_provider_state interaction
|
|
13
|
+
find do |candidate_interaction|
|
|
14
|
+
candidate_interaction.matches_criteria?(description: interaction.description, provider_state: interaction.provider_state)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'logger'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module MockService
|
|
5
|
+
class Logger < ::Logger
|
|
6
|
+
|
|
7
|
+
attr_reader :description
|
|
8
|
+
|
|
9
|
+
def initialize stream
|
|
10
|
+
super stream
|
|
11
|
+
@description = if stream.is_a? File
|
|
12
|
+
File.absolute_path(stream).gsub(Dir.pwd + "/", '')
|
|
13
|
+
else
|
|
14
|
+
"standard out/err"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.from_options options
|
|
19
|
+
log_stream = options[:log_file] || $stdout
|
|
20
|
+
logger = new log_stream
|
|
21
|
+
logger.formatter = options[:log_formatter] if options[:log_formatter]
|
|
22
|
+
logger.level = logger_level(options[:log_level])
|
|
23
|
+
logger
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.logger_level log_level_string
|
|
27
|
+
if log_level_string
|
|
28
|
+
begin
|
|
29
|
+
Kernel.const_get('Logger').const_get(log_level_string.upcase)
|
|
30
|
+
rescue NameError
|
|
31
|
+
$stderr.puts "WARN: Ignoring log level '#{log_level_string}' as it is not a valid value. Valid values are: DEBUG INFO WARN ERROR FATAL. Using DEBUG."
|
|
32
|
+
Logger::DEBUG
|
|
33
|
+
end
|
|
34
|
+
else
|
|
35
|
+
Logger::DEBUG
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Pact
|
|
2
|
+
module MockService
|
|
3
|
+
class RequestDecorator
|
|
4
|
+
|
|
5
|
+
def initialize request
|
|
6
|
+
@request = request
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def to_json(options = {})
|
|
10
|
+
as_json.to_json(options)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def as_json options = {}
|
|
14
|
+
to_hash
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_hash
|
|
18
|
+
hash = {
|
|
19
|
+
method: request.method,
|
|
20
|
+
path: request.path,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
hash[:query] = request.query if request.specified?(:query)
|
|
24
|
+
hash[:headers] = request.headers if request.specified?(:headers)
|
|
25
|
+
hash[:body] = request.body if request.specified?(:body)
|
|
26
|
+
hash[:options] = request.options if request.options.any?
|
|
27
|
+
hash
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
attr_reader :request
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'pact/consumer/mock_service/rack_request_helper'
|
|
2
|
+
require 'pact/mock_service/request_handlers/base_request_handler'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module MockService
|
|
6
|
+
module RequestHandlers
|
|
7
|
+
class BaseAdministrationRequestHandler < BaseRequestHandler
|
|
8
|
+
|
|
9
|
+
attr_accessor :logger, :name
|
|
10
|
+
|
|
11
|
+
def initialize name, logger
|
|
12
|
+
@name = name
|
|
13
|
+
@logger = logger
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def match? env
|
|
17
|
+
has_mock_service_header?(env) && path_matches?(env) && method_matches?(env)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def has_mock_service_header? env
|
|
21
|
+
env['HTTP_X_PACT_MOCK_SERVICE']
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def path_matches? env
|
|
25
|
+
env['PATH_INFO'].chomp("/") == request_path
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def method_matches? env
|
|
29
|
+
env['REQUEST_METHOD'] == request_method
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def request_path
|
|
33
|
+
raise NotImplementedError
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def request_method
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'pact/consumer/mock_service/rack_request_helper'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module MockService
|
|
5
|
+
module RequestHandlers
|
|
6
|
+
class BaseRequestHandler
|
|
7
|
+
|
|
8
|
+
NOT_FOUND_RESPONSE = [404, {}, []].freeze
|
|
9
|
+
|
|
10
|
+
include Pact::Consumer::RackRequestHelper
|
|
11
|
+
|
|
12
|
+
def match? env
|
|
13
|
+
raise NotImplementedError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call env
|
|
17
|
+
match?(env) ? respond(env) : NOT_FOUND_RESPONSE
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def json_response json = nil, status = 200
|
|
21
|
+
[status, {'Content-Type' => 'application/json'}, json ? [json + "\n"]: []]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def text_response text = nil, status = 200
|
|
25
|
+
[status, {'Content-Type' => 'text/plain'}, text ? [text + "\n"]: []]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require 'pact/mock_service/request_handlers/base_administration_request_handler'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module MockService
|
|
5
|
+
module RequestHandlers
|
|
6
|
+
|
|
7
|
+
class IndexGet < BaseAdministrationRequestHandler
|
|
8
|
+
|
|
9
|
+
def request_path
|
|
10
|
+
''
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def request_method
|
|
14
|
+
'GET'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def respond env
|
|
18
|
+
text_response('Mock service running')
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'pact/mock_service/request_handlers/base_administration_request_handler'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module MockService
|
|
5
|
+
module RequestHandlers
|
|
6
|
+
|
|
7
|
+
class InteractionDelete < BaseAdministrationRequestHandler
|
|
8
|
+
|
|
9
|
+
attr_accessor :session
|
|
10
|
+
|
|
11
|
+
def initialize name, logger, session
|
|
12
|
+
super name, logger
|
|
13
|
+
@session = session
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def request_path
|
|
17
|
+
'/interactions'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def request_method
|
|
21
|
+
'DELETE'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def respond env
|
|
25
|
+
session.clear_expected_and_actual_interactions
|
|
26
|
+
example_desc = example_description(env)
|
|
27
|
+
example_desc = example_desc ? " for example #{example_desc.inspect}" : ''
|
|
28
|
+
logger.info "Cleared interactions#{example_desc}"
|
|
29
|
+
text_response('Cleared interactions')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def example_description env
|
|
33
|
+
params_hash(env).fetch('example_description', [])[0]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'pact/mock_service/request_handlers/base_administration_request_handler'
|
|
2
|
+
require 'pact/mock_service/session'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module MockService
|
|
6
|
+
module RequestHandlers
|
|
7
|
+
class InteractionPost < BaseAdministrationRequestHandler
|
|
8
|
+
|
|
9
|
+
def initialize name, logger, session, pact_specification_version
|
|
10
|
+
super name, logger
|
|
11
|
+
@session = session
|
|
12
|
+
@pact_specification_version = pact_specification_version
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def request_path
|
|
16
|
+
'/interactions'
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def request_method
|
|
20
|
+
'POST'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def respond env
|
|
24
|
+
request_body = env['rack.input'].string
|
|
25
|
+
parsing_options = { pact_specification_version: pact_specification_version }
|
|
26
|
+
interaction = Interaction.from_hash(JSON.load(request_body), parsing_options) # Load creates the Pact::XXX classes
|
|
27
|
+
|
|
28
|
+
begin
|
|
29
|
+
session.add_expected_interaction interaction
|
|
30
|
+
text_response 'Registered interactions'
|
|
31
|
+
rescue ::Pact::Error => e
|
|
32
|
+
text_response e.message, 500
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_accessor :session, :pact_specification_version
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
require 'pact/matchers'
|
|
2
|
+
require 'pact/consumer/request'
|
|
3
|
+
require 'pact/mock_service/interactions/interaction_mismatch'
|
|
4
|
+
require 'pact/consumer_contract'
|
|
5
|
+
require 'pact/mock_service/response_decorator'
|
|
6
|
+
require 'pact/mock_service/interaction_decorator'
|
|
7
|
+
require 'pact/mock_service/request_handlers/base_request_handler'
|
|
8
|
+
|
|
9
|
+
module Pact
|
|
10
|
+
module MockService
|
|
11
|
+
module RequestHandlers
|
|
12
|
+
|
|
13
|
+
module PrettyGenerate
|
|
14
|
+
#Doesn't seem to reliably pretty generate unless we go to JSON and back again :(
|
|
15
|
+
def pretty_generate object
|
|
16
|
+
begin
|
|
17
|
+
JSON.pretty_generate(JSON.parse(object.to_json))
|
|
18
|
+
rescue
|
|
19
|
+
object.to_s
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class InteractionReplay < BaseRequestHandler
|
|
25
|
+
include Pact::Matchers
|
|
26
|
+
include PrettyGenerate
|
|
27
|
+
|
|
28
|
+
attr_accessor :name, :logger, :expected_interactions, :actual_interactions, :verified_interactions, :multiple_interactions_handler
|
|
29
|
+
|
|
30
|
+
def initialize name, logger, session, cors_enabled = false, stub = false
|
|
31
|
+
@name = name
|
|
32
|
+
@logger = logger
|
|
33
|
+
@expected_interactions = session.expected_interactions
|
|
34
|
+
@actual_interactions = session.actual_interactions
|
|
35
|
+
@verified_interactions = session.verified_interactions
|
|
36
|
+
@cors_enabled = cors_enabled
|
|
37
|
+
@multiple_interactions_handler = stub ? HandleMultipleInteractionsFoundForStub : HandleMultipleInteractionsFound
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def match? env
|
|
41
|
+
true # default handler
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def respond env
|
|
45
|
+
find_response request_as_hash_from(env)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def find_response request_hash
|
|
51
|
+
actual_request = Pact::Consumer::Request::Actual.from_hash(request_hash)
|
|
52
|
+
logger.info "Received request #{actual_request.method_and_path}"
|
|
53
|
+
logger.debug pretty_generate request_hash
|
|
54
|
+
candidate_interactions = expected_interactions.find_candidate_interactions actual_request
|
|
55
|
+
matching_interactions = candidate_interactions.matching_interactions actual_request
|
|
56
|
+
|
|
57
|
+
case matching_interactions.size
|
|
58
|
+
when 0 then handle_unrecognised_request actual_request, candidate_interactions
|
|
59
|
+
when 1 then handle_matched_interaction matching_interactions.first
|
|
60
|
+
else
|
|
61
|
+
handle_more_than_one_matching_interaction actual_request, matching_interactions
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def handle_matched_interaction interaction
|
|
66
|
+
HandleMatchedInteraction.call(interaction, verified_interactions, actual_interactions, logger)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def handle_more_than_one_matching_interaction actual_request, matching_interactions
|
|
70
|
+
multiple_interactions_handler.call(actual_request, matching_interactions, verified_interactions, actual_interactions, logger)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def handle_unrecognised_request actual_request, candidate_interactions
|
|
74
|
+
HandleUnrecognisedInteraction.call(actual_request, candidate_interactions, actual_interactions, logger)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def logger_info_ap msg
|
|
78
|
+
logger.info msg
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
class HandleMultipleInteractionsFound
|
|
84
|
+
|
|
85
|
+
extend PrettyGenerate
|
|
86
|
+
|
|
87
|
+
def self.call actual_request, matching_interactions, verified_interactions, actual_interactions, logger
|
|
88
|
+
logger.error "Multiple interactions found for #{actual_request.method_and_path}:"
|
|
89
|
+
matching_interactions.each do | interaction |
|
|
90
|
+
logger.debug pretty_generate(Pact::MockService::InteractionDecorator.new(interaction))
|
|
91
|
+
end
|
|
92
|
+
response actual_request, matching_interactions
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def self.response actual_request, matching_interactions
|
|
96
|
+
response = {
|
|
97
|
+
message: "Multiple interaction found for #{actual_request.method_and_path}",
|
|
98
|
+
matching_interactions: matching_interactions.collect{ | interaction | request_summary_for(interaction) }
|
|
99
|
+
}
|
|
100
|
+
[500, {'Content-Type' => 'application/json'}, [response.to_json + "\n"]]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.request_summary_for interaction
|
|
104
|
+
summary = {:description => interaction.description}
|
|
105
|
+
summary[:provider_state] if interaction.provider_state
|
|
106
|
+
summary[:request] = Pact::MockService::RequestDecorator.new(interaction.request)
|
|
107
|
+
summary
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class HandleMultipleInteractionsFoundForStub
|
|
112
|
+
|
|
113
|
+
extend PrettyGenerate
|
|
114
|
+
|
|
115
|
+
def self.call actual_request, matching_interactions, verified_interactions, actual_interactions, logger
|
|
116
|
+
logger.warn "Multiple interactions found for #{actual_request.method_and_path}:"
|
|
117
|
+
matching_interactions.each do | interaction |
|
|
118
|
+
logger.debug pretty_generate(Pact::MockService::InteractionDecorator.new(interaction))
|
|
119
|
+
end
|
|
120
|
+
response actual_request, matching_interactions, verified_interactions, actual_interactions, logger
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def self.response actual_request, matching_interactions, verified_interactions, actual_interactions, logger
|
|
124
|
+
logger.warn "Sorting responses by response status and returning first."
|
|
125
|
+
interaction = first_most_successful_interaction(matching_interactions)
|
|
126
|
+
HandleMatchedInteraction.call(interaction, verified_interactions, actual_interactions, logger)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def self.first_most_successful_interaction matching_interactions
|
|
130
|
+
matching_interactions.sort{ |i1, i2| Pact::Reification.from_term(i1.response.status) <=> Pact::Reification.from_term(i2.response.status) }.first
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
class HandleUnrecognisedInteraction
|
|
135
|
+
|
|
136
|
+
def self.call actual_request, candidate_interactions, actual_interactions, logger
|
|
137
|
+
interaction_mismatch = interaction_mismatch(actual_request, candidate_interactions)
|
|
138
|
+
if candidate_interactions.any?
|
|
139
|
+
actual_interactions.register_interaction_mismatch interaction_mismatch
|
|
140
|
+
else
|
|
141
|
+
actual_interactions.register_unexpected_request actual_request
|
|
142
|
+
end
|
|
143
|
+
log interaction_mismatch, logger
|
|
144
|
+
response interaction_mismatch
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def self.response interaction_mismatch
|
|
148
|
+
response = {
|
|
149
|
+
message: "No interaction found for #{interaction_mismatch.actual_request.method_and_path}",
|
|
150
|
+
interaction_diffs: interaction_mismatch.to_hash
|
|
151
|
+
}
|
|
152
|
+
[500, {'Content-Type' => 'application/json'}, [response.to_json + "\n"]]
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def self.interaction_mismatch actual_request, candidate_interactions
|
|
156
|
+
Pact::MockService::Interactions::InteractionMismatch.new(candidate_interactions, actual_request)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def self.log interaction_mismatch, logger
|
|
160
|
+
logger.error "No matching interaction found for #{interaction_mismatch.actual_request.method_and_path}"
|
|
161
|
+
logger.error 'Interaction diffs for that route:'
|
|
162
|
+
logger.error(interaction_mismatch.to_s)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
class HandleMatchedInteraction
|
|
168
|
+
|
|
169
|
+
extend PrettyGenerate
|
|
170
|
+
|
|
171
|
+
def self.call interaction, verified_interactions, actual_interactions, logger
|
|
172
|
+
actual_interactions.register_matched interaction
|
|
173
|
+
verified_interactions << interaction
|
|
174
|
+
response = response_from(interaction.response)
|
|
175
|
+
logger.info "Found matching response for #{interaction.request.method_and_path}"
|
|
176
|
+
logger.debug pretty_generate(Pact::MockService::ResponseDecorator.new(interaction.response))
|
|
177
|
+
response
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def self.response_from response
|
|
181
|
+
[response.status, (Pact::Reification.from_term(response.headers) || {}).to_hash, [render_body(Pact::Reification.from_term(response.body))]]
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def self.render_body body
|
|
185
|
+
return '' if body.nil?
|
|
186
|
+
body.kind_of?(String) ? body.force_encoding('utf-8') : body.to_json
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require 'pact/mock_service/request_handlers/base_administration_request_handler'
|
|
2
|
+
require 'pact/mock_service/interaction_decorator'
|
|
3
|
+
require 'pact/shared/json_differ'
|
|
4
|
+
require 'pact/mock_service/request_handlers/interaction_post' #Refactor diff message
|
|
5
|
+
|
|
6
|
+
module Pact
|
|
7
|
+
module MockService
|
|
8
|
+
module RequestHandlers
|
|
9
|
+
class InteractionsPut < BaseAdministrationRequestHandler
|
|
10
|
+
|
|
11
|
+
def initialize name, logger, session, pact_specification_version
|
|
12
|
+
super name, logger
|
|
13
|
+
@session = session
|
|
14
|
+
@pact_specification_version = pact_specification_version
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def request_path
|
|
18
|
+
'/interactions'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def request_method
|
|
22
|
+
'PUT'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def respond env
|
|
26
|
+
request_body = JSON.load(env['rack.input'].string)
|
|
27
|
+
parsing_options = { pact_specification_version: pact_specification_version }
|
|
28
|
+
interactions = request_body['interactions'].collect { | hash | Interaction.from_hash(hash, parsing_options) }
|
|
29
|
+
begin
|
|
30
|
+
session.set_expected_interactions interactions
|
|
31
|
+
text_response('Registered interactions')
|
|
32
|
+
rescue Pact::Error => e
|
|
33
|
+
text_response(e.message, 500)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
attr_accessor :session, :pact_specification_version
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'pact/mock_service/request_handlers/base_administration_request_handler'
|
|
2
|
+
|
|
3
|
+
module Pact
|
|
4
|
+
module MockService
|
|
5
|
+
module RequestHandlers
|
|
6
|
+
class LogGet < BaseAdministrationRequestHandler
|
|
7
|
+
|
|
8
|
+
def request_path
|
|
9
|
+
'/log'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def request_method
|
|
13
|
+
'GET'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def respond env
|
|
17
|
+
logger.info "Debug message from client - #{message(env)}"
|
|
18
|
+
text_response
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def message env
|
|
22
|
+
params_hash(env).fetch('msg', [])[0]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require 'pact/mock_service/request_handlers/base_administration_request_handler'
|
|
2
|
+
require 'pact/mock_service/interactions/verification'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module MockService
|
|
6
|
+
module RequestHandlers
|
|
7
|
+
|
|
8
|
+
class MissingInteractionsGet < BaseAdministrationRequestHandler
|
|
9
|
+
|
|
10
|
+
def initialize name, logger, session
|
|
11
|
+
super name, logger
|
|
12
|
+
@expected_interactions = session.expected_interactions
|
|
13
|
+
@actual_interactions = session.actual_interactions
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def request_path
|
|
17
|
+
'/interactions/missing'
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def request_method
|
|
21
|
+
'GET'
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def respond env
|
|
25
|
+
verification = Pact::MockService::Interactions::Verification.new(@expected_interactions, @actual_interactions)
|
|
26
|
+
number_of_missing_interactions = verification.missing_interactions.size
|
|
27
|
+
logger.info "Number of missing interactions for mock \"#{name}\" = #{number_of_missing_interactions}"
|
|
28
|
+
json_response({size: number_of_missing_interactions}.to_json)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|