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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/lib/pact/consumer/app_manager.rb +5 -1
- data/lib/pact/consumer/interactions_filter.rb +1 -17
- data/lib/pact/consumer/mock_service/actual_interactions.rb +34 -0
- data/lib/pact/consumer/mock_service/app.rb +21 -18
- data/lib/pact/consumer/mock_service/candidate_interactions.rb +13 -0
- data/lib/pact/consumer/mock_service/expected_interactions.rb +17 -0
- data/lib/pact/consumer/mock_service/interaction_delete.rb +11 -5
- data/lib/pact/consumer/mock_service/interaction_post.rb +69 -6
- data/lib/pact/consumer/mock_service/interaction_replay.rb +77 -57
- data/lib/pact/consumer/mock_service/log_get.rb +1 -1
- data/lib/pact/consumer/mock_service/missing_interactions_get.rb +7 -4
- data/lib/pact/consumer/mock_service/pact_post.rb +6 -4
- data/lib/pact/consumer/mock_service/rack_request_helper.rb +1 -1
- data/lib/pact/consumer/mock_service/verification.rb +46 -0
- data/lib/pact/consumer/mock_service/verification_get.rb +17 -12
- data/lib/pact/consumer/mock_service/verified_interactions.rb +18 -0
- data/lib/pact/consumer/mock_service_client.rb +4 -4
- data/lib/pact/consumer/server.rb +2 -0
- data/lib/pact/consumer_contract/consumer_contract_writer.rb +2 -0
- data/lib/pact/mock_service/cli.rb +21 -14
- data/lib/pact/mock_service/version.rb +1 -1
- metadata +10 -124
- data/.gitignore +0 -30
- data/.rspec +0 -3
- data/.travis.yml +0 -8
- data/Rakefile +0 -6
- data/lib/pact/consumer/mock_service/interaction_list.rb +0 -76
- data/pact-mock-service.gemspec +0 -39
- data/spec/features/mock_multiple_responses_spec.rb +0 -120
- data/spec/features/mock_response_spec.rb +0 -71
- data/spec/lib/pact/consumer/app_manager_spec.rb +0 -42
- data/spec/lib/pact/consumer/mock_service/app_spec.rb +0 -52
- data/spec/lib/pact/consumer/mock_service/interaction_list_spec.rb +0 -78
- data/spec/lib/pact/consumer/mock_service/interaction_mismatch_spec.rb +0 -70
- data/spec/lib/pact/consumer/mock_service/interaction_replay_spec.rb +0 -12
- data/spec/lib/pact/consumer/mock_service/rack_request_helper_spec.rb +0 -88
- data/spec/lib/pact/consumer/mock_service/verification_get_spec.rb +0 -142
- data/spec/lib/pact/consumer/mock_service_client_spec.rb +0 -88
- data/spec/lib/pact/consumer/service_consumer_spec.rb +0 -11
- data/spec/lib/pact/consumer_contract/consumer_contract_writer_spec.rb +0 -128
- data/spec/lib/pact/consumer_contract/request_decorator_body_spec.rb +0 -77
- data/spec/lib/pact/consumer_contract/request_decorator_headers_spec.rb +0 -69
- data/spec/lib/pact/consumer_contract/request_decorator_path_spec.rb +0 -42
- data/spec/lib/pact/consumer_contract/request_decorator_query_spec.rb +0 -74
- data/spec/lib/pact/consumer_contract/request_decorator_spec.rb +0 -41
- data/spec/lib/pact/consumer_contract/response_decorator_spec.rb +0 -10
- data/spec/lib/pact/mock_service/interaction_decorator_spec.rb +0 -74
- data/spec/lib/pact/mock_service/request_decorator_spec.rb +0 -76
- data/spec/lib/pact/mock_service/response_decorator_spec.rb +0 -12
- data/spec/spec_helper.rb +0 -16
- data/spec/support/a_consumer-a_producer.json +0 -32
- data/spec/support/a_consumer-a_provider.json +0 -32
- data/spec/support/active_support_if_configured.rb +0 -6
- data/spec/support/app_for_config_ru.rb +0 -4
- data/spec/support/case-insensitive-response-header-matching.json +0 -21
- data/spec/support/case-insensitive-response-header-matching.rb +0 -15
- data/spec/support/consumer_contract_template.json +0 -24
- data/spec/support/dsl_spec_support.rb +0 -7
- data/spec/support/factories.rb +0 -82
- data/spec/support/generated_index.md +0 -4
- data/spec/support/generated_markdown.md +0 -55
- data/spec/support/interaction_view_model.json +0 -63
- data/spec/support/interaction_view_model_with_terms.json +0 -50
- data/spec/support/markdown_pact.json +0 -48
- data/spec/support/missing_provider_states_output.txt +0 -25
- data/spec/support/options.json +0 -21
- data/spec/support/options_app.rb +0 -15
- data/spec/support/pact_helper.rb +0 -57
- data/spec/support/shared_examples_for_request.rb +0 -94
- data/spec/support/shared_examples_for_response_decorator.rb +0 -25
- data/spec/support/spec_support.rb +0 -20
- data/spec/support/stubbing.json +0 -22
- data/spec/support/stubbing_using_allow.rb +0 -29
- data/spec/support/term.json +0 -48
- data/spec/support/test_app_fail.json +0 -61
- data/spec/support/test_app_pass.json +0 -38
- data/spec/support/test_app_with_right_content_type_differ.json +0 -23
- data/tasks/spec.rake +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1e4c79027ecd3c1da41ed2b2305c7d02ab8422bb
|
4
|
+
data.tar.gz: b6900749dbe4f721b7c958d2522caa6fdabf0fe4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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/
|
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
|
-
|
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,
|
37
|
-
VerificationGet.new(@name, @logger,
|
38
|
-
InteractionPost.new(@name, @logger,
|
39
|
-
InteractionDelete.new(@name, @logger,
|
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,
|
42
|
-
InteractionReplay.new(@name, @logger,
|
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
|
72
|
+
response = relevant_handler.respond(env)
|
68
73
|
rescue StandardError => e
|
69
|
-
@logger.error
|
70
|
-
@logger.
|
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
|
75
|
-
@logger.
|
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,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 :
|
11
|
+
attr_accessor :expected_interactions, :actual_interactions
|
12
12
|
|
13
|
-
def initialize name, logger,
|
13
|
+
def initialize name, logger, expected_interactions, actual_interactions
|
14
14
|
super name, logger
|
15
|
-
@
|
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
|
-
|
28
|
-
|
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
|
-
|
8
|
-
|
9
|
-
def initialize name, logger, interaction_list
|
9
|
+
def initialize name, logger, expected_interactions, verified_interactions
|
10
10
|
super name, logger
|
11
|
-
@
|
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
|
-
|
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, :
|
24
|
+
attr_accessor :name, :logger, :expected_interactions, :actual_interactions, :verified_interactions
|
17
25
|
|
18
|
-
def initialize name, logger,
|
26
|
+
def initialize name, logger, expected_interactions, actual_interactions, verified_interactions
|
19
27
|
@name = name
|
20
28
|
@logger = logger
|
21
|
-
@
|
22
|
-
@
|
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 =
|
44
|
-
matching_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
|
55
|
-
|
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
|
62
|
-
|
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
|
71
|
-
|
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
|
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
|
-
|
86
|
+
response actual_request, matching_interactions
|
84
87
|
end
|
85
88
|
|
86
|
-
def
|
87
|
-
|
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
|
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
|
106
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
@@ -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,
|
10
|
+
def initialize name, logger, expected_interactions, actual_interactions
|
10
11
|
super name, logger
|
11
|
-
@
|
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
|
-
|
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, :
|
8
|
+
attr_accessor :consumer_contract, :verified_interactions, :default_options
|
9
9
|
|
10
|
-
def initialize name, logger,
|
10
|
+
def initialize name, logger, verified_interactions, pact_dir
|
11
11
|
super name, logger
|
12
|
-
@
|
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
|
-
|
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"]
|
13
|
+
CGI::parse env["QUERY_STRING"]
|
14
14
|
end
|
15
15
|
|
16
16
|
def request_as_hash_from env
|