-
Notifications
You must be signed in to change notification settings - Fork 70
/
Copy pathinteraction_replay.rb
191 lines (155 loc) · 7.68 KB
/
interaction_replay.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
require 'pact/matchers'
require 'pact/consumer/request'
require 'pact/mock_service/interactions/interaction_mismatch'
require 'pact/consumer_contract'
require 'pact/mock_service/response_decorator'
require 'pact/mock_service/interaction_decorator'
require 'pact/mock_service/request_handlers/base_request_handler'
module Pact
module MockService
module RequestHandlers
module PrettyGenerate
#Doesn't seem to reliably pretty generate unless we go to JSON and back again :(
def pretty_generate object
begin
JSON.pretty_generate(JSON.parse(object.to_json))
rescue
object.to_s
end
end
end
class InteractionReplay < BaseRequestHandler
include Pact::Matchers
include PrettyGenerate
attr_accessor :name, :logger, :expected_interactions, :actual_interactions, :verified_interactions, :multiple_interactions_handler
def initialize name, logger, session, cors_enabled = false, stub = false
@name = name
@logger = logger
@expected_interactions = session.expected_interactions
@actual_interactions = session.actual_interactions
@verified_interactions = session.verified_interactions
@cors_enabled = cors_enabled
@multiple_interactions_handler = stub ? HandleMultipleInteractionsFoundForStub : HandleMultipleInteractionsFound
end
def match? env
true # default handler
end
def respond env
find_response request_as_hash_from(env)
end
private
def find_response request_hash
actual_request = Pact::Consumer::Request::Actual.from_hash(request_hash)
logger.info "Received request #{actual_request.method_and_path}"
logger.debug pretty_generate request_hash
candidate_interactions = expected_interactions.find_candidate_interactions actual_request
matching_interactions = candidate_interactions.matching_interactions actual_request
case matching_interactions.size
when 0 then handle_unrecognised_request actual_request, candidate_interactions
when 1 then handle_matched_interaction matching_interactions.first
else
handle_more_than_one_matching_interaction actual_request, matching_interactions
end
end
def handle_matched_interaction interaction
HandleMatchedInteraction.call(interaction, verified_interactions, actual_interactions, logger)
end
def handle_more_than_one_matching_interaction actual_request, matching_interactions
multiple_interactions_handler.call(actual_request, matching_interactions, verified_interactions, actual_interactions, logger)
end
def handle_unrecognised_request actual_request, candidate_interactions
HandleUnrecognisedInteraction.call(actual_request, candidate_interactions, actual_interactions, logger)
end
def logger_info_ap msg
logger.info msg
end
end
class HandleMultipleInteractionsFound
extend PrettyGenerate
def self.call actual_request, matching_interactions, verified_interactions, actual_interactions, logger
logger.error "Multiple interactions found for #{actual_request.method_and_path}:"
matching_interactions.each do | interaction |
logger.debug pretty_generate(Pact::MockService::InteractionDecorator.new(interaction))
end
response actual_request, matching_interactions
end
def self.response actual_request, matching_interactions
response = {
message: "Multiple interaction found for #{actual_request.method_and_path}",
matching_interactions: matching_interactions.collect{ | interaction | request_summary_for(interaction) }
}
[500, {'Content-Type' => 'application/json'}, [response.to_json + "\n"]]
end
def self.request_summary_for interaction
summary = {:description => interaction.description}
summary[:provider_state] if interaction.provider_state
summary[:request] = Pact::MockService::RequestDecorator.new(interaction.request)
summary
end
end
class HandleMultipleInteractionsFoundForStub
extend PrettyGenerate
def self.call actual_request, matching_interactions, verified_interactions, actual_interactions, logger
logger.warn "Multiple interactions found for #{actual_request.method_and_path}:"
matching_interactions.each do | interaction |
logger.debug pretty_generate(Pact::MockService::InteractionDecorator.new(interaction))
end
response actual_request, matching_interactions, verified_interactions, actual_interactions, logger
end
def self.response actual_request, matching_interactions, verified_interactions, actual_interactions, logger
logger.warn "Sorting responses by response status and returning first."
interaction = first_most_successful_interaction(matching_interactions)
HandleMatchedInteraction.call(interaction, verified_interactions, actual_interactions, logger)
end
def self.first_most_successful_interaction matching_interactions
matching_interactions.sort{ |i1, i2| Pact::Reification.from_term(i1.response.status) <=> Pact::Reification.from_term(i2.response.status) }.first
end
end
class HandleUnrecognisedInteraction
def self.call actual_request, candidate_interactions, actual_interactions, logger
interaction_mismatch = interaction_mismatch(actual_request, candidate_interactions)
if candidate_interactions.any?
actual_interactions.register_interaction_mismatch interaction_mismatch
else
actual_interactions.register_unexpected_request actual_request
end
log interaction_mismatch, logger
response interaction_mismatch
end
def self.response interaction_mismatch
response = {
message: "No interaction found for #{interaction_mismatch.actual_request.method_and_path}",
interaction_diffs: interaction_mismatch.to_hash
}
[500, {'Content-Type' => 'application/json'}, [response.to_json + "\n"]]
end
def self.interaction_mismatch actual_request, candidate_interactions
Pact::MockService::Interactions::InteractionMismatch.new(candidate_interactions, actual_request)
end
def self.log interaction_mismatch, logger
logger.error "No matching interaction found for #{interaction_mismatch.actual_request.method_and_path}"
logger.error 'Interaction diffs for that route:'
logger.error(interaction_mismatch.to_s)
end
end
class HandleMatchedInteraction
extend PrettyGenerate
def self.call interaction, verified_interactions, actual_interactions, logger
actual_interactions.register_matched interaction
verified_interactions << interaction
response = response_from(interaction.response)
logger.info "Found matching response for #{interaction.request.method_and_path}"
logger.debug pretty_generate(Pact::MockService::ResponseDecorator.new(interaction.response))
response
end
def self.response_from response
[response.status, (Pact::Reification.from_term(response.headers) || {}).to_hash, [render_body(Pact::Reification.from_term(response.body))]]
end
def self.render_body body
return '' if body.nil?
body.kind_of?(String) ? body.force_encoding('utf-8') : body.to_json
end
end
end
end
end