hoodoo 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ ########################################################################
2
+ # File:: newrelic_rpm.rb
3
+ # (C):: Loyalty New Zealand 2016
4
+ #
5
+ # Purpose:: Override NewRelic 'require "newrelic_rpm"'. The test suite
6
+ # cannot allow "real" NewRelic to be loaded as this would hook
7
+ # into all kinds of things and interfere with test results,
8
+ # especially for tests covering variant behaviour for when
9
+ # NewRelic is present or absent.
10
+ # ----------------------------------------------------------------------
11
+ # 13-Apr-2016 (ADH): Created.
12
+ ########################################################################
13
+
14
+ # Yes, this is empty, just comments. The whole point is to allow code to
15
+ # be able to require what it thinks is NewRelic without a LoadError
16
+ # exception being raised, but not actually define any NewRelic components
17
+ # until the test suite is good and ready to do so (with mock data).
@@ -6,469 +6,18 @@ require 'spec_helper'
6
6
 
7
7
  describe Hoodoo::Services::Middleware do
8
8
 
9
- class RSpecTestServiceExoticStubImplementation < Hoodoo::Services::Implementation
10
- def list( context )
11
- context.response.set_estimated_resources( [], 88 )
12
- context.response.set_resources( [], 99 )
13
- end
14
- end
15
-
16
- class RSpecTestServiceExoticStubInterface < Hoodoo::Services::Interface
17
- interface :Version do
18
- endpoint :version, RSpecTestServiceExoticStubImplementation
19
- version 2
20
- end
21
- end
22
-
23
- class RSpecTestServiceExoticStub < Hoodoo::Services::Service
24
- comprised_of RSpecTestServiceExoticStubInterface
25
- end
26
-
27
9
  context 'on queue' do
28
- before :each do
29
- @old_queue = ENV[ 'AMQ_URI' ]
30
- ENV[ 'AMQ_URI' ] = 'amqp://test:test@127.0.0.1'
31
- @mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
32
-
33
- @cvar = false
34
- if Hoodoo::Services::Middleware.class_variable_defined?( '@@alchemy' )
35
- @cvar = true
36
- @cvar_val = Hoodoo::Services::Middleware.class_variable_get( '@@alchemy' )
37
- end
38
-
39
- # Need to blow away the discoverer's local cache or the simulation will
40
- # never attempt to run on queue.
41
-
42
- discoverer = @mw.instance_variable_get( '@discoverer' )
43
- discoverer.instance_variable_set( '@known_local_resources', {} ) # Hack for test!
44
- end
45
-
46
- after :each do
47
- ENV[ 'AMQ_URI' ] = @old_queue
48
-
49
- if Hoodoo::Services::Middleware.class_variable_defined?( '@@alchemy' )
50
- if @cvar == true
51
- Hoodoo::Services::Middleware.class_variable_set( '@@alchemy', @cvar_val )
52
- else
53
- Hoodoo::Services::Middleware.remove_class_variable( '@@alchemy' )
54
- end
55
- end
56
- end
57
-
58
- it 'knows it is on-queue' do
59
- expect( Hoodoo::Services::Middleware.on_queue? ).to eq( true )
60
- end
61
-
62
- # TODO: Weak test! Assumes static mappings. Will need modification
63
- # for real Consul discoverer when implemented.
64
- #
65
- it 'returns known queue endpoint locations' do
66
- location = @mw.instance_variable_get( '@discoverer' ).discover( :Version, 2 )
67
- expect( location ).to be_a( Hoodoo::Services::Discovery::ForAMQP )
68
- expect( location.routing_path ).to eq( '/2/Version' )
10
+ before :all do
11
+ Hoodoo::Monkey.disable( extension_module: Hoodoo::Monkey::Patch::NewRelicTracedAMQP )
69
12
  end
70
13
 
71
- # TODO: Update for real Consul discoverer when implemented.
72
- #
73
- # it 'returns "nil" for unknown queue endpoint locations' do
74
- # location = @mw.instance_variable_get( '@discoverer' ).discover( :NotAKnownResource, 2 )
75
- # expect( location ).to be_nil
76
- # end
77
-
78
- context 'calling Alchemy' do
79
- before :each do
80
- mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
81
- @interaction = Hoodoo::Services::Middleware::Interaction.new(
82
- {},
83
- mw,
84
- Hoodoo::Services::Middleware.test_session()
85
- )
86
- @interaction.target_interface = OpenStruct.new
87
- @interaction.context.request.locale = 'fr'
88
-
89
- @mock_alchemy = OpenStruct.new
90
- Hoodoo::Services::Middleware.class_variable_set( '@@alchemy', @mock_alchemy )
91
- end
92
-
93
- def run_expectations( action, full_path, mock_method, mock_query, mock_remote, mock_response )
94
- expect_any_instance_of( Hoodoo::Services::Discovery::ByFlux ).to receive(
95
- :discover_remote
96
- ).and_return(
97
- mock_remote
98
- )
99
-
100
- if mock_query
101
- mock_query = Hoodoo::Utilities.stringify( mock_query )
102
- mock_query[ 'search' ] = URI.encode_www_form( mock_query[ 'search' ] ) if ( mock_query[ 'search' ].is_a?( ::Hash ) )
103
- mock_query[ 'filter' ] = URI.encode_www_form( mock_query[ 'filter' ] ) if ( mock_query[ 'filter' ].is_a?( ::Hash ) )
104
- end
105
-
106
- expect( @mock_alchemy ).to receive( :send_request_to_resource ).once do | message |
107
- expect( message ).to eq( {
108
- 'scheme' => 'http',
109
- 'verb' => mock_method,
110
-
111
- 'host' => 'localhost',
112
- 'port' => 80,
113
- 'path' => full_path,
114
- 'query' => mock_query,
115
-
116
- 'session_id' => @interaction.context.session.session_id,
117
- 'body' => action == :create || action == :update ? '{}' : '',
118
- 'headers' => {
119
- 'Content-Type' => 'application/json; charset=utf-8',
120
- 'Content-Language' => 'fr',
121
- 'Accept-Language' => 'fr',
122
- 'X-Interaction-ID' => @interaction.interaction_id,
123
- 'X-Session-ID' => @interaction.context.session.session_id
124
- },
125
- } )
126
- end.and_return( mock_response )
127
- end
128
-
129
- # All the other tests in this section run with @mw having no
130
- # local services "as far as it is concerned" via the before-each
131
- # code above. We want to make sure that service announcement when
132
- # on-queue *does* announce locally (because at one point it did
133
- # not, which was a bug) so this test provides that coverage.
134
- #
135
- it 'runs local discovery unless that is knocked out' do
136
-
137
- # @mw has local discovery knocked out, so build a new
138
- # one that doesn't. This will have the local discovery data
139
- # available still.
140
- #
141
- @mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
142
-
143
- mock_path = '/2/Version/'
144
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
145
- resource: 'Version',
146
- version: 2
147
- )
148
-
149
- # We expect local discovery, so no discover_remote call.
150
-
151
- expect_any_instance_of( Hoodoo::Services::Discovery::ByFlux ).to_not receive( :discover_remote )
152
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
153
-
154
- # The endpoint should've been called locally; the implementation at
155
- # the top of this file sets an empty array with dataset size 99 *and*
156
- # an estimated dataset size of 88.
157
-
158
- mock_result = endpoint.list()
159
- expect( mock_result ).to be_empty
160
- expect( mock_result.dataset_size ).to eq( 99 )
161
- expect( mock_result.estimated_dataset_size ).to eq( 88 )
162
- end
163
-
164
- it 'complains about a missing Alchemy instance' do
165
- mock_path = '/2/Version/'
166
-
167
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
168
- resource: 'Version',
169
- version: 2
170
- )
171
-
172
- expect_any_instance_of( Hoodoo::Services::Discovery::ByFlux ).to receive(
173
- :discover_remote
174
- ).and_return(
175
- mock_remote
176
- )
177
-
178
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
179
-
180
- # Fragile! Hack for test only.
181
- endpoint.instance_variable_get( '@wrapped_endpoint' ).alchemy = nil
182
-
183
- mock_response = {
184
- 'status_code' => 200,
185
- 'body' => '{"_data":[]}'
186
- }
187
-
188
- expect {
189
- mock_result = endpoint.list()
190
- }.to raise_error( RuntimeError, 'Hoodoo::Client::Endpoint::AMQP cannot be used unless an Alchemy instance has been provided' )
191
- end
192
-
193
- it 'calls #list over Alchemy and handles 200' do
194
- mock_path = '/2/Version'
195
- mock_method = 'GET'
196
- mock_query = { :search => { :foo => :bar } }
197
-
198
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
199
- resource: 'Version',
200
- version: 2
201
- )
202
-
203
- mock_response = {
204
- 'status_code' => 200,
205
- 'body' => '{"_data":[]}'
206
- }
207
-
208
- run_expectations( :list, mock_path + '/', mock_method, mock_query, mock_remote, mock_response )
209
-
210
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
211
- mock_result = endpoint.list( mock_query )
212
-
213
- expect( mock_result ).to eq( Hoodoo::Client::AugmentedArray.new )
214
- end
215
-
216
- it 'calls #show over Alchemy and handles 200' do
217
- mock_path = '/2/Version'
218
- mock_method = 'GET'
219
- mock_query = { :search => { :foo => :bar } }
220
-
221
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
222
- resource: 'Version',
223
- version: 2
224
- )
225
-
226
- mock_response = {
227
- 'status_code' => 200,
228
- 'body' => '{}'
229
- }
230
-
231
- run_expectations( :show, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
232
-
233
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
234
- mock_result = endpoint.show( 'ident', mock_query )
235
-
236
- expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
237
- end
238
-
239
- it 'calls #create over Alchemy and handles 200' do
240
- mock_path = '/2/Version'
241
- mock_method = 'POST'
242
- mock_query = { :search => { :foo => :bar } }
243
-
244
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
245
- resource: 'Version',
246
- version: 2
247
- )
248
-
249
- mock_response = {
250
- 'status_code' => 200,
251
- 'body' => '{}'
252
- }
253
-
254
- run_expectations( :create, mock_path + '/', mock_method, mock_query, mock_remote, mock_response )
255
-
256
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
257
- mock_result = endpoint.create( {}, mock_query )
258
-
259
- expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
260
- end
261
-
262
- it 'calls #update over Alchemy and handles 200' do
263
- mock_path = '/2/Version'
264
- mock_method = 'PATCH'
265
- mock_query = { :search => { :foo => :bar } }
266
-
267
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
268
- resource: 'Version',
269
- version: 2
270
- )
271
-
272
- mock_response = {
273
- 'status_code' => 200,
274
- 'body' => '{}'
275
- }
276
-
277
- run_expectations( :update, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
278
-
279
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
280
- mock_result = endpoint.update( 'ident', {}, mock_query )
281
-
282
- expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
283
- end
284
-
285
- it 'calls #delete over Alchemy and handles 200' do
286
- mock_path = '/2/Version'
287
- mock_method = 'DELETE'
288
- mock_query = { :search => { :foo => :bar } }
289
-
290
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
291
- resource: 'Version',
292
- version: 2
293
- )
294
-
295
- mock_response = {
296
- 'status_code' => 200,
297
- 'body' => '{}'
298
- }
299
-
300
- run_expectations( :delete, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
301
-
302
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
303
- mock_result = endpoint.delete( 'ident', mock_query )
304
-
305
- expect( mock_result ).to eq( Hoodoo::Client::AugmentedHash.new )
306
- end
307
-
308
- it 'calls #show over Alchemy and handles 408' do
309
- mock_path = '/2/Version'
310
- mock_method = 'GET'
311
- mock_query = { :search => { :foo => :bar } }
312
-
313
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
314
- resource: 'Version',
315
- version: 2
316
- )
317
-
318
- mock_response = AlchemyFlux::TimeoutError
319
-
320
- run_expectations( :show, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
321
-
322
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
323
- mock_result = endpoint.show( 'ident', mock_query )
324
-
325
- expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
326
- expect( mock_result.platform_errors ).to_not be_nil
327
-
328
- errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
329
-
330
- expect( errors ).to have_key( 'errors' )
331
- expect( errors[ 'errors' ] ).to be_a( Array )
332
- expect( errors[ 'errors' ][ 0 ] ).to have_key( 'code' )
333
- expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.timeout' )
334
- end
335
-
336
- it 'calls #show over Alchemy and handles 404' do
337
- mock_path = '/2/Version'
338
- mock_method = 'GET'
339
- mock_query = { :search => { :foo => :bar } }
340
-
341
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
342
- resource: 'Version',
343
- version: 2
344
- )
345
-
346
- mock_response = AlchemyFlux::MessageNotDeliveredError
347
-
348
- run_expectations( :show, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
349
-
350
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
351
- mock_result = endpoint.show( 'ident', mock_query )
352
-
353
- expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
354
- expect( mock_result.platform_errors ).to_not be_nil
355
-
356
- errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
357
-
358
- expect( errors ).to have_key( 'errors' )
359
- expect( errors[ 'errors' ] ).to be_a( Array )
360
- expect( errors[ 'errors' ][ 0 ] ).to have_key( 'code' )
361
- expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.not_found' )
362
- end
363
-
364
- it 'calls #show over Alchemy and handles unusual HTTP status codes' do
365
- mock_path = '/2/Version'
366
- mock_method = 'GET'
367
- mock_query = { :search => { :foo => :bar } }
368
-
369
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
370
- resource: 'Version',
371
- version: 2
372
- )
373
-
374
- bad_body_data = '499 Unusual Code'
375
- mock_response = {
376
- 'status_code' => 499,
377
- 'body' => bad_body_data
378
- }
379
-
380
- run_expectations( :show, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
381
-
382
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
383
- mock_result = endpoint.show( 'ident', mock_query )
384
-
385
- expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
386
- expect( mock_result.platform_errors ).to_not be_nil
387
-
388
- errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
389
-
390
- expect( errors ).to have_key( 'errors' )
391
- expect( errors[ 'errors' ] ).to be_a( Array )
392
- expect( errors[ 'errors' ][ 0 ] ).to_not be_nil
393
-
394
- expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.fault' )
395
- expect( errors[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Unexpected raw HTTP status code 499 with non-JSON response' )
396
- expect( errors[ 'errors' ][ 0 ][ 'reference' ] ).to eq( "#{ bad_body_data }" )
397
- end
398
-
399
- it 'calls #show over Alchemy and handles unrecognised return types' do
400
- mock_path = '/2/Version'
401
- mock_method = 'GET'
402
- mock_query = { :search => { :foo => :bar } }
403
-
404
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
405
- resource: 'Version',
406
- version: 2
407
- )
408
-
409
- bad_body_data = '500 Internal Server Error'
410
- mock_response = Object.new
411
-
412
- run_expectations( :show, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
413
-
414
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
415
- mock_result = endpoint.show( 'ident', mock_query )
416
-
417
- expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
418
- expect( mock_result.platform_errors ).to_not be_nil
419
-
420
- errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
421
-
422
- expect( errors ).to have_key( 'errors' )
423
- expect( errors[ 'errors' ] ).to be_a( Array )
424
- expect( errors[ 'errors' ][ 0 ] ).to_not be_nil
425
-
426
- expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.fault' )
427
- expect( errors[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Unexpected raw HTTP status code 500 with non-JSON response' )
428
- expect( errors[ 'errors' ][ 0 ][ 'reference' ] ).to eq( "#{ bad_body_data }" )
429
- end
430
-
431
- it 'calls #show over Alchemy and handles 200 status but bad JSON' do
432
- mock_path = '/2/Version'
433
- mock_method = 'GET'
434
- mock_query = { :search => { :foo => :bar } }
435
-
436
- mock_remote = Hoodoo::Services::Discovery::ForAMQP.new(
437
- resource: 'Version',
438
- version: 2
439
- )
440
-
441
- bad_body_data = 'Not JSON'
442
- mock_response = {
443
- 'status_code' => 200,
444
- 'body' => bad_body_data
445
- }
446
-
447
- run_expectations( :show, mock_path + '/ident', mock_method, mock_query, mock_remote, mock_response )
448
-
449
- endpoint = @mw.inter_resource_endpoint_for( 'Version', 2, @interaction )
450
- mock_result = endpoint.show( 'ident', mock_query )
451
-
452
- expect( mock_result ).to be_a( Hoodoo::Client::AugmentedHash )
453
- expect( mock_result.platform_errors ).to_not be_nil
454
-
455
- errors = mock_result.platform_errors.render( Hoodoo::UUID.generate )
456
-
457
- expect( errors ).to have_key( 'errors' )
458
- expect( errors[ 'errors' ] ).to be_a( Array )
459
- expect( errors[ 'errors' ][ 0 ] ).to_not be_nil
460
-
461
- expect( errors[ 'errors' ][ 0 ][ 'code' ] ).to eq( 'platform.fault' )
462
- expect( errors[ 'errors' ][ 0 ][ 'message' ] ).to eq( 'Could not parse retrieved body data despite receiving HTTP status code 200' )
463
- expect( errors[ 'errors' ][ 0 ][ 'reference' ] ).to eq( "#{ bad_body_data }" )
464
- end
465
- end
14
+ it_behaves_like 'an AMQP-based middleware/client endpoint'
466
15
  end
467
16
 
468
17
  context 'over HTTPS' do
469
18
  before :all do
470
19
  @port = spec_helper_start_svc_app_in_thread_for(
471
- RSpecTestServiceExoticStub,
20
+ RSpecTestServiceExoticStub, # See shared_examples/middleware_amqp.rb
472
21
  true # Use SSL
473
22
  )
474
23
  end
@@ -510,7 +59,7 @@ describe Hoodoo::Services::Middleware do
510
59
 
511
60
  # Set up a middleware instance and mock interaction
512
61
 
513
- mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new )
62
+ mw = Hoodoo::Services::Middleware.new( RSpecTestServiceExoticStub.new ) # See shared_examples/middleware_amqp.rb
514
63
  interaction = Hoodoo::Services::Middleware::Interaction.new(
515
64
  {},
516
65
  mw,