forked from aws/aws-sdk-ruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathclient_stubs.rb
239 lines (215 loc) · 6.87 KB
/
client_stubs.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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
require 'thread'
module Aws
# This module provides the ability to specify the data and/or errors to
# return when a client is using stubbed responses. Pass
# `:stub_responses => true` to a client constructor to enable this
# behavior.
module ClientStubs
# @api private
def self.included(subclass)
subclass.add_plugin('Aws::Plugins::StubResponses')
end
def initialize(*args)
@stubs = {}
@stub_mutex = Mutex.new
super
end
# Configures what data / errors should be returned from the named operation
# when response stubbing is enabled.
#
# ## Basic usage
#
# By default, fake responses are generated. You can override the default
# fake data with specific response data by passing a hash.
#
# # enable response stubbing in the client constructor
# client = Aws::S3::Client.new(stub_responses: true)
#
# # specify the response data for #list_buckets
# client.stub_responses(:list_buckets, buckets:[{name:'aws-sdk'}])
#
# # no api calls made, stub returned
# client.list_buckets.map(&:name)
# #=> ['aws-sdk']
#
# ## Stubbing Errors
#
# When stubbing is enabled, the SDK will default to generate
# fake responses with placeholder values. You can override the data
# returned. You can also specify errors it should raise.
#
# client.stub_responses(:get_object, 'NotFound')
# client.get_object(bucket:'aws-sdk', key:'foo')
# #=> raises Aws::S3::Errors::NotFound
#
# client.stub_responses(:get_object, Timeout::Error)
# client.get_object(bucket:'aws-sdk', key:'foo')
# #=> raises new Timeout::Error
#
# client.stub_responses(:get_object, RuntimeError.new('custom message'))
# client.get_object(bucket:'aws-sdk', key:'foo')
# #=> raises the given runtime error object
#
# ## Stubbing Multiple Responses
#
# Calling an operation multiple times will return similar responses.
# You can configure multiple stubs and they will be returned in sequence.
#
#
# client.stub_responses(:head_object, [
# 'NotFound',
# { content_length: 150 },
# ])
#
# client.head_object(bucket:'aws-sdk', key:'foo')
# #=> raises Aws::S3::Errors::NotFound
#
# resp = client.head_object(bucket:'aws-sdk', key:'foo')
# resp.content_length #=> 150
#
# @param [Symbol] operation_name
# @param [Mixed] stubs One or more responses to return from the named
# operation.
# @return [void]
# @raise [RuntimeError] Raises a runtime error when called
# on a client that has not enabled response stubbing via
# `:stub_responses => true`.
def stub_responses(operation_name, *stubs)
if config.stub_responses
apply_stubs(operation_name, stubs.flatten)
else
msg = 'stubbing is not enabled; enable stubbing in the constructor '
msg << 'with `:stub_responses => true`'
raise msg
end
end
# @api private
def next_stub(operation_name)
@stub_mutex.synchronize do
stubs = @stubs[operation_name.to_sym] || []
case stubs.length
when 0 then new_stub(operation_name)
when 1 then stubs.first
else stubs.shift
end
end
end
private
# @param [Symbol] operation_name
# @param [Hash, nil] data
# @return [Structure]
def new_stub(operation_name, data = nil)
Stub.new(operation(operation_name).output).format(data || {})
end
def apply_stubs(operation_name, stubs)
@stub_mutex.synchronize do
@stubs[operation_name.to_sym] = stubs.map do |stub|
case stub
when Exception then stub
when String then service_error_class(stub)
when Hash then new_stub(operation_name, stub)
else stub
end
end
end
end
def service_error_class(name)
svc_module = Aws.const_get(self.class.name.split('::')[1])
svc_module.const_get(:Errors).const_get(name)
end
class Stub
# @param [Seahorse::Models::Shapes::Structure] output_shape This should
# be the output shape for an operation.
def initialize(output_shape)
@shape = output_shape
end
# @param [Hash] data An optional hash of data to format into the stubbed
# object.
def format(data = {})
if @shape.nil?
empty_stub(data)
else
validate_data(data)
stub(@shape, data)
end
end
private
def stub(shape, value)
case shape
when Seahorse::Model::Shapes::Structure then stub_structure(shape, value)
when Seahorse::Model::Shapes::List then stub_list(shape, value || [])
when Seahorse::Model::Shapes::Map then stub_map(shape, value || {})
else stub_scalar(shape, value)
end
end
def stub_structure(shape, hash)
if hash
structure_obj(shape, hash)
else
nil
end
end
def structure_obj(shape, hash)
stubs = Structure.new(shape.member_names)
shape.members.each do |member_name, member_shape|
value = structure_value(shape, member_name, member_shape, hash)
stubs[member_name] = stub(member_shape, value)
end
stubs
end
def structure_value(shape, member_name, member_shape, hash)
if hash.key?(member_name)
hash[member_name]
elsif
Seahorse::Model::Shapes::Structure === member_shape &&
shape.required.include?(member_name)
then
{}
else
nil
end
end
def stub_list(shape, array)
stubs = []
array.each do |value|
stubs << stub(shape.member, value)
end
stubs
end
def stub_map(shape, value)
stubs = {}
value.each do |key, value|
stubs[key] = stub(shape.value, value)
end
stubs
end
def stub_scalar(shape, value)
if value.nil?
case shape
when Seahorse::Model::Shapes::String then shape.name
when Seahorse::Model::Shapes::Integer then 0
when Seahorse::Model::Shapes::Float then 0.0
when Seahorse::Model::Shapes::Boolean then false
when Seahorse::Model::Shapes::Timestamp then Time.now
else nil
end
else
value
end
end
def empty_stub(data)
if data.empty?
Structure.new(data)
else
msg = 'unable to generate a stubbed response from the given data; '
msg << 'this operation does not return data'
raise ArgumentError, msg
end
end
def validate_data(data)
args = [@shape, { validate_required:false }]
Seahorse::Client::ParamValidator.new(*args).validate!(data)
end
end
end
end