strelka 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/ChangeLog +3293 -3058
- data/History.rdoc +17 -0
- data/Manifest.txt +3 -0
- data/Rakefile +2 -2
- data/contrib/hoetemplate/lib/file_name.rb.erb +3 -2
- data/contrib/hoetemplate/spec/file_name_spec.rb.erb +1 -1
- data/examples/apps/auth-demo +1 -2
- data/examples/apps/auth-demo2 +1 -2
- data/examples/apps/sessions-demo +1 -2
- data/examples/gen-config.rb +1 -2
- data/lib/strelka.rb +92 -17
- data/lib/strelka/app.rb +7 -6
- data/lib/strelka/app/auth.rb +5 -5
- data/lib/strelka/app/errors.rb +1 -1
- data/lib/strelka/app/filters.rb +1 -1
- data/lib/strelka/app/negotiation.rb +1 -1
- data/lib/strelka/app/parameters.rb +1 -1
- data/lib/strelka/app/restresources.rb +14 -21
- data/lib/strelka/app/routing.rb +5 -6
- data/lib/strelka/app/sessions.rb +3 -1
- data/lib/strelka/app/templating.rb +1 -1
- data/lib/strelka/authprovider.rb +1 -1
- data/lib/strelka/authprovider/basic.rb +1 -0
- data/lib/strelka/authprovider/hostaccess.rb +1 -0
- data/lib/strelka/behavior/plugin.rb +2 -2
- data/lib/strelka/cli.rb +2 -1
- data/lib/strelka/command/config.rb +2 -1
- data/lib/strelka/command/discover.rb +2 -1
- data/lib/strelka/command/start.rb +2 -1
- data/lib/strelka/constants.rb +1 -1
- data/lib/strelka/cookie.rb +1 -1
- data/lib/strelka/cookieset.rb +1 -1
- data/lib/strelka/discovery.rb +1 -1
- data/lib/strelka/httprequest.rb +4 -4
- data/lib/strelka/httprequest/acceptparams.rb +1 -1
- data/lib/strelka/httprequest/auth.rb +3 -1
- data/lib/strelka/httprequest/negotiation.rb +1 -1
- data/lib/strelka/httprequest/session.rb +3 -1
- data/lib/strelka/httpresponse.rb +2 -3
- data/lib/strelka/httpresponse/negotiation.rb +1 -1
- data/lib/strelka/httpresponse/session.rb +1 -1
- data/lib/strelka/mixins.rb +26 -5
- data/lib/strelka/multipartparser.rb +3 -3
- data/lib/strelka/paramvalidator.rb +4 -4
- data/lib/strelka/plugins.rb +14 -5
- data/lib/strelka/router.rb +1 -1
- data/lib/strelka/router/default.rb +1 -1
- data/lib/strelka/router/exclusive.rb +1 -1
- data/lib/strelka/session.rb +1 -0
- data/lib/strelka/session/db.rb +1 -0
- data/lib/strelka/session/default.rb +1 -0
- data/lib/strelka/testing.rb +454 -14
- data/lib/strelka/websocketserver.rb +150 -36
- data/lib/strelka/websocketserver/heartbeat.rb +163 -0
- data/lib/strelka/websocketserver/routing.rb +46 -19
- data/spec/constants.rb +1 -1
- data/spec/helpers.rb +15 -6
- data/spec/strelka/app/auth_spec.rb +5 -3
- data/spec/strelka/app/errors_spec.rb +2 -2
- data/spec/strelka/app/filters_spec.rb +2 -2
- data/spec/strelka/app/negotiation_spec.rb +2 -2
- data/spec/strelka/app/parameters_spec.rb +5 -5
- data/spec/strelka/app/restresources_spec.rb +8 -6
- data/spec/strelka/app/routing_spec.rb +3 -3
- data/spec/strelka/app/sessions_spec.rb +4 -2
- data/spec/strelka/app/templating_spec.rb +2 -2
- data/spec/strelka/app_spec.rb +5 -24
- data/spec/strelka/authprovider/basic_spec.rb +3 -2
- data/spec/strelka/authprovider/hostaccess_spec.rb +3 -2
- data/spec/strelka/authprovider_spec.rb +3 -2
- data/spec/strelka/cli_spec.rb +7 -4
- data/spec/strelka/cookie_spec.rb +2 -2
- data/spec/strelka/cookieset_spec.rb +2 -2
- data/spec/strelka/discovery_spec.rb +2 -2
- data/spec/strelka/exceptions_spec.rb +2 -2
- data/spec/strelka/httprequest/acceptparams_spec.rb +2 -2
- data/spec/strelka/httprequest/auth_spec.rb +3 -2
- data/spec/strelka/httprequest/negotiation_spec.rb +2 -2
- data/spec/strelka/httprequest/session_spec.rb +3 -2
- data/spec/strelka/httprequest_spec.rb +7 -2
- data/spec/strelka/httpresponse/negotiation_spec.rb +6 -5
- data/spec/strelka/httpresponse/session_spec.rb +3 -2
- data/spec/strelka/httpresponse_spec.rb +4 -3
- data/spec/strelka/mixins_spec.rb +85 -2
- data/spec/strelka/multipartparser_spec.rb +5 -4
- data/spec/strelka/paramvalidator_spec.rb +15 -10
- data/spec/strelka/plugins_spec.rb +24 -2
- data/spec/strelka/router/default_spec.rb +2 -2
- data/spec/strelka/router/exclusive_spec.rb +2 -2
- data/spec/strelka/router_spec.rb +2 -2
- data/spec/strelka/session/db_spec.rb +3 -2
- data/spec/strelka/session/default_spec.rb +3 -2
- data/spec/strelka/session_spec.rb +3 -2
- data/spec/strelka/testing_spec.rb +772 -0
- data/spec/strelka/websocketserver/heartbeat_spec.rb +19 -0
- data/spec/strelka/websocketserver/routing_spec.rb +31 -29
- data/spec/strelka/websocketserver_spec.rb +210 -75
- data/spec/strelka_spec.rb +172 -2
- metadata +43 -36
- metadata.gz.sig +0 -0
data/lib/strelka/session.rb
CHANGED
data/lib/strelka/session/db.rb
CHANGED
data/lib/strelka/testing.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# -*- ruby -*-
|
2
2
|
# vim: set nosta noet ts=4 sw=4:
|
3
|
-
#
|
3
|
+
# frozen-string-literal: true
|
4
|
+
|
5
|
+
require 'loggability'
|
4
6
|
|
5
7
|
require 'rspec'
|
6
8
|
require 'rspec/matchers'
|
@@ -9,10 +11,6 @@ require 'rspec/matchers'
|
|
9
11
|
# and libraries.
|
10
12
|
module Strelka::Testing
|
11
13
|
|
12
|
-
###############
|
13
|
-
module_function
|
14
|
-
###############
|
15
|
-
|
16
14
|
#
|
17
15
|
# Matchers
|
18
16
|
#
|
@@ -24,15 +22,15 @@ module Strelka::Testing
|
|
24
22
|
end
|
25
23
|
end
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
match do |collection|
|
30
|
-
collection.all? {|obj| obj.is_a?(expected) }
|
31
|
-
end
|
32
|
-
end
|
25
|
+
RSpec::Matchers.define_negated_matcher( :exclude, :include )
|
26
|
+
|
33
27
|
|
34
28
|
# finish_with matcher
|
35
29
|
class FinishWithMatcher
|
30
|
+
extend Loggability
|
31
|
+
|
32
|
+
log_to :strelka
|
33
|
+
|
36
34
|
|
37
35
|
### Create a new matcher for the specified +status+, +expected_message+, and
|
38
36
|
### +expected_headers+.
|
@@ -87,7 +85,7 @@ module Strelka::Testing
|
|
87
85
|
nil
|
88
86
|
end
|
89
87
|
|
90
|
-
|
88
|
+
self.log.debug "Test proc called; status info is: %p" % [ status_info ]
|
91
89
|
|
92
90
|
return self.check_finish( status_info ) &&
|
93
91
|
self.check_status_code( status_info ) &&
|
@@ -141,13 +139,20 @@ module Strelka::Testing
|
|
141
139
|
headers = self.expected_headers or return true
|
142
140
|
return true if headers.empty?
|
143
141
|
|
144
|
-
status_headers = status_info[:headers]
|
142
|
+
status_headers = Mongrel2::Table.new( status_info[:headers] )
|
145
143
|
headers.each do |name, value|
|
144
|
+
self.log.debug "Testing for %p header: %p" % [ name, value ]
|
146
145
|
unless status_value = status_headers[ name ]
|
147
146
|
@failure = "a %s header" % [ name ]
|
148
147
|
return false
|
149
148
|
end
|
150
149
|
|
150
|
+
if status_value.empty?
|
151
|
+
@failure = "a %s header matching %p, but it was blank" % [ name, value ]
|
152
|
+
return false
|
153
|
+
end
|
154
|
+
|
155
|
+
self.log.debug " got value: %p" % [ status_value ]
|
151
156
|
if value.respond_to?( :match )
|
152
157
|
unless value.match( status_value )
|
153
158
|
@failure = "a %s header matching %p, but got %p" %
|
@@ -182,12 +187,447 @@ module Strelka::Testing
|
|
182
187
|
end # class FinishWithMatcher
|
183
188
|
|
184
189
|
|
190
|
+
# RSpec matcher for matching Strelka::HTTPResponse body
|
191
|
+
#
|
192
|
+
# Expect that the response consists of JSON of some sort:
|
193
|
+
#
|
194
|
+
# expect( last_response ).to have_json_body
|
195
|
+
#
|
196
|
+
# Expect that it's a JSON body that deserializes as an Object:
|
197
|
+
#
|
198
|
+
# expect( last_response ).to have_json_body( Object )
|
199
|
+
# # -or-
|
200
|
+
# expect( last_response ).to have_json_body( Hash )
|
201
|
+
#
|
202
|
+
# Expect that it's a JSON body that deserializes as an Array:
|
203
|
+
#
|
204
|
+
# expect( last_response ).to have_json_body( Array )
|
205
|
+
#
|
206
|
+
# Expect that it's a JSON body that deserializes as an Object that has
|
207
|
+
# expected keys:
|
208
|
+
#
|
209
|
+
# expect( last_response ).to have_json_body( Object ).
|
210
|
+
# that_includes( :id, :first_name, :last_name )
|
211
|
+
#
|
212
|
+
# Expect that it's a JSON body that deserializes as an Object that has
|
213
|
+
# expected keys and values:
|
214
|
+
#
|
215
|
+
# expect( last_response ).to have_json_body( Object ).
|
216
|
+
# that_includes(
|
217
|
+
# id: 118,
|
218
|
+
# first_name: 'Princess',
|
219
|
+
# last_name: 'Buttercup'
|
220
|
+
# )
|
221
|
+
#
|
222
|
+
# Expect that it's a JSON body that has other expected stuff:
|
223
|
+
#
|
224
|
+
# expect( last_response ).to have_json_body( Object ).
|
225
|
+
# that_includes(
|
226
|
+
# last_name: a_string_matching(/humperdink/i),
|
227
|
+
# profile: a_hash_including(:age, :eyecolor, :tracking_ability)
|
228
|
+
# )
|
229
|
+
#
|
230
|
+
# Expect a JSON Array with objects that all match the criteria:
|
231
|
+
#
|
232
|
+
# expect( last_response ).to have_json_body( Array ).
|
233
|
+
# of_lenth( 20 ).
|
234
|
+
# and( all( be_an(Integer) ) )
|
235
|
+
#
|
236
|
+
class HaveJSONBodyMatcher
|
237
|
+
extend Loggability
|
238
|
+
include RSpec::Matchers
|
239
|
+
|
240
|
+
|
241
|
+
log_to :strelka
|
242
|
+
|
243
|
+
|
244
|
+
### Create a new matcher that expects a response with a JSON body. If +expected_type+
|
245
|
+
### is not specified, any JSON body will be sufficient for a match.
|
246
|
+
def initialize( expected_type=nil )
|
247
|
+
@expected_type = expected_type
|
248
|
+
@additional_expectations = []
|
249
|
+
@response = nil
|
250
|
+
@failure_description = nil
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
attr_reader :expected_type,
|
255
|
+
:additional_expectations,
|
256
|
+
:response,
|
257
|
+
:failure_description
|
258
|
+
|
259
|
+
|
260
|
+
### RSpec matcher API -- returns +true+ if all expectations of the specified
|
261
|
+
### +response+ are met.
|
262
|
+
def matches?( response )
|
263
|
+
@response = response
|
264
|
+
|
265
|
+
return self.correct_content_type? &&
|
266
|
+
self.correct_json_type? &&
|
267
|
+
self.matches_additional_expectations?
|
268
|
+
rescue Yajl::ParseError => err
|
269
|
+
return self.fail_with "Response has invalid JSON body: %s" % [ err.message ]
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
### RSpec matcher API -- return a message describing an expectation failure.
|
274
|
+
def failure_message
|
275
|
+
return "\n---\n%s\n---\n\nReason: %s\n" % [
|
276
|
+
self.pretty_print_response,
|
277
|
+
self.failure_description
|
278
|
+
]
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
### RSpec matcher API -- return a message describing an expectation being met
|
283
|
+
### when the matcher was used in a negated context.
|
284
|
+
def failure_message_when_negated
|
285
|
+
msg = "expected response not to have a %s" % [ self.describe_type_expectation ]
|
286
|
+
msg << " and " << self.describe_additional_expectations.join( ', ' ) unless
|
287
|
+
self.additional_expectations.emtpy?
|
288
|
+
msg << ", but it did."
|
289
|
+
|
290
|
+
return "\n---\n%s\n---\n\nReason: %s\n" % [
|
291
|
+
self.pretty_print_response,
|
292
|
+
msg
|
293
|
+
]
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
### Return the response's body parsed as JSON.
|
298
|
+
def parsed_response_body
|
299
|
+
return @parsed_response_body ||=
|
300
|
+
Yajl::Parser.parse( self.response.body, check_utf8: true, symbolize_keys: true )
|
301
|
+
end
|
302
|
+
|
303
|
+
|
304
|
+
#
|
305
|
+
# Mutators
|
306
|
+
#
|
307
|
+
|
308
|
+
### Add an additional expectation that the JSON body contains the specified +members+.
|
309
|
+
def that_includes( *memberset )
|
310
|
+
@additional_expectations << include( *memberset )
|
311
|
+
return self
|
312
|
+
end
|
313
|
+
alias_method :which_includes, :that_includes
|
314
|
+
|
315
|
+
|
316
|
+
### Add an additional expectation that the JSON body does not contain the
|
317
|
+
### specified +members+.
|
318
|
+
def that_excludes( *memberset )
|
319
|
+
@additional_expectations << exclude( *memberset )
|
320
|
+
return self
|
321
|
+
end
|
322
|
+
|
323
|
+
|
324
|
+
### Add an additional expectation that the JSON body contain the specified
|
325
|
+
### +number+ of members.
|
326
|
+
def of_length( number )
|
327
|
+
@additional_expectations << have_attributes( length: number )
|
328
|
+
return self
|
329
|
+
end
|
330
|
+
alias_method :of_size, :of_length
|
331
|
+
|
332
|
+
|
333
|
+
### Add the specified +matchers+ as expectations of the Hash or Array that's
|
334
|
+
### parsed from the JSON body.
|
335
|
+
def and( *matchers )
|
336
|
+
@additional_expectations.concat( matchers )
|
337
|
+
return self
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
#########
|
342
|
+
protected
|
343
|
+
#########
|
344
|
+
|
345
|
+
### Return a String that contains a pretty-printed version of the response object.
|
346
|
+
def pretty_print_response
|
347
|
+
return self.response.to_s
|
348
|
+
end
|
349
|
+
|
350
|
+
|
351
|
+
### Return +false+ after setting the failure message to +message+.
|
352
|
+
def fail_with( message )
|
353
|
+
@failure_description = message
|
354
|
+
self.log.error "Failing with: %s" % [ message ]
|
355
|
+
return false
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
### Returns +true+ if the response has a JSON content-type header.
|
360
|
+
def correct_content_type?
|
361
|
+
content_type = self.response.headers[:content_type] or
|
362
|
+
return self.fail_with "response doesn't have a Content-type header"
|
363
|
+
|
364
|
+
return fail_with "response's Content-type is %p" % [ content_type ] unless
|
365
|
+
content_type.start_with?( 'application/json' ) ||
|
366
|
+
content_type.match?( %r|\Aapplication/(vnd\.)?\w+\+json\b| )
|
367
|
+
|
368
|
+
return true
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
### Return an Array of text describing the expectation that the body be an
|
373
|
+
### Object or an Array, if a type was expected. If no type was expected, returns
|
374
|
+
### an empty Array.
|
375
|
+
def describe_type_expectation
|
376
|
+
return case self.expected_type
|
377
|
+
when Object, Hash
|
378
|
+
"a JSON Object/Hash body"
|
379
|
+
when Array
|
380
|
+
"a JSON Array body"
|
381
|
+
else
|
382
|
+
"a JSON body"
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
|
387
|
+
### Check that the JSON body of the response has the correct type, if a type
|
388
|
+
### was specified.
|
389
|
+
def correct_json_type?
|
390
|
+
return self.parsed_response_body unless self.expected_type
|
391
|
+
|
392
|
+
if self.expected_type == Array
|
393
|
+
return self.fail_with( "response body isn't a JSON Array" ) unless
|
394
|
+
self.parsed_response_body.is_a?( Array )
|
395
|
+
elsif self.expected_type == Object || self.expected_type == Hash
|
396
|
+
return self.fail_with( "response body isn't a JSON Object" ) unless
|
397
|
+
self.parsed_response_body.is_a?( Hash )
|
398
|
+
else
|
399
|
+
warn "A valid JSON response can't be a %p!" % [ self.expected_type ]
|
400
|
+
end
|
401
|
+
|
402
|
+
return true
|
403
|
+
end
|
404
|
+
|
405
|
+
|
406
|
+
### Return an Array of descriptions of the members that were expected to be included in the
|
407
|
+
### response body, if any were specified. If none were specified, returns an empty
|
408
|
+
### Array.
|
409
|
+
def describe_additional_expectations
|
410
|
+
return self.additional_expectations.map( &:description )
|
411
|
+
end
|
412
|
+
|
413
|
+
|
414
|
+
### Check that any additional matchers registered via the `.and` mutator also
|
415
|
+
### match the parsed response body.
|
416
|
+
def matches_additional_expectations?
|
417
|
+
return self.additional_expectations.all? do |matcher|
|
418
|
+
matcher.matches?( self.parsed_response_body ) or
|
419
|
+
fail_with( matcher.failure_message )
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
end # class HaveJSONBodyMatcher
|
424
|
+
|
425
|
+
|
426
|
+
# RSpec matcher for matching Strelka::HTTPResponse body from a collection endpoint
|
427
|
+
#
|
428
|
+
# Expect that the response is a JSON Array of Objects:
|
429
|
+
#
|
430
|
+
# expect( last_response ).to have_json_collection
|
431
|
+
#
|
432
|
+
# Expect that there be 4 Objects in the collection:
|
433
|
+
#
|
434
|
+
# expect( last_response ).to have_json_collection.of_length( 4 )
|
435
|
+
#
|
436
|
+
# Expect that the collection's objects each have an `id` field with the specified
|
437
|
+
# IDs:
|
438
|
+
#
|
439
|
+
# expect( last_response ).to have_json_collection.with_ids( 3, 6, 11, 14 )
|
440
|
+
# # -or- with an Array of IDs (no need to splat them)
|
441
|
+
# ids = [3, 6, 11, 14]
|
442
|
+
# expect( last_response ).to have_json_collection.with_ids( ids )
|
443
|
+
#
|
444
|
+
# Expect that the collection's objects have the same IDs as an Array of model
|
445
|
+
# objects (or other objects that respond to #pk):
|
446
|
+
#
|
447
|
+
# payments = payment_fixture_factory.take( 4 )
|
448
|
+
# expect( last_response ).to have_json_collection.
|
449
|
+
# with_same_ids_as( payments )
|
450
|
+
#
|
451
|
+
# Expect that the collection's objects have the same IDs as an Array of Hashes with
|
452
|
+
# `:id` fields:
|
453
|
+
#
|
454
|
+
# payment_rows = payments_table.where( sender_id: 71524 ).all
|
455
|
+
# expect( last_response ).to have_json_collection.
|
456
|
+
# with_same_ids_as( payment_rows )
|
457
|
+
#
|
458
|
+
# Expect that the collection's objects appear in the same order as the source Array:
|
459
|
+
#
|
460
|
+
# payments = payment_fixture_factory.take( 4 )
|
461
|
+
# expect( last_response ).to have_json_collection.
|
462
|
+
# with_same_ids_as( payments ).in_same_order
|
463
|
+
#
|
464
|
+
# Add aggregate matchers for each object in the collection:
|
465
|
+
#
|
466
|
+
# expect( last_response ).to have_json_collection.
|
467
|
+
# with_same_ids_as( payments ).
|
468
|
+
# and_all( include(amount_cents: a_value > 0) )
|
469
|
+
#
|
470
|
+
class HaveJSONCollectionMatcher < HaveJSONBodyMatcher
|
471
|
+
include RSpec::Matchers
|
472
|
+
|
473
|
+
|
474
|
+
### Overridden to set the expected type to Array.
|
475
|
+
def initialize # :notnew:
|
476
|
+
super( Array )
|
477
|
+
|
478
|
+
@additional_expectations << all( be_a Hash )
|
479
|
+
|
480
|
+
@expected_ids = nil
|
481
|
+
@collection_ids = nil
|
482
|
+
@extra_ids = nil
|
483
|
+
@missing_ids = nil
|
484
|
+
@order_enforced = false
|
485
|
+
end
|
486
|
+
|
487
|
+
|
488
|
+
######
|
489
|
+
public
|
490
|
+
######
|
491
|
+
|
492
|
+
# Sets of IDs, actual vs. expected
|
493
|
+
attr_reader :expected_ids,
|
494
|
+
:collection_ids,
|
495
|
+
:extra_ids,
|
496
|
+
:missing_ids,
|
497
|
+
:order_enforced
|
498
|
+
|
499
|
+
|
500
|
+
### Overridden to include matching against collection IDs.
|
501
|
+
def matches?( response )
|
502
|
+
return false unless super( response )
|
503
|
+
|
504
|
+
if @expected_ids
|
505
|
+
@collection_ids = self.parsed_response_body.collect {|obj| obj[:id] }
|
506
|
+
@extra_ids = @collection_ids - @expected_ids
|
507
|
+
@missing_ids = @expected_ids - @collection_ids
|
508
|
+
end
|
509
|
+
|
510
|
+
return self.has_required_ids?
|
511
|
+
end
|
512
|
+
|
513
|
+
|
514
|
+
### Return an Array of text describing the expectation that the body be an
|
515
|
+
### Object or an Array, if a type was expected. If no type was expected, returns
|
516
|
+
### an empty Array.
|
517
|
+
def describe_type_expectation
|
518
|
+
return "a JSON collection (Array of Objects)"
|
519
|
+
end
|
520
|
+
|
521
|
+
|
522
|
+
### Add the specified +matchers+ as expectations of each member of the collection.
|
523
|
+
def and_all( *matchers )
|
524
|
+
matchers = matchers.map {|m| all( m ) }
|
525
|
+
@additional_expectations.concat( matchers )
|
526
|
+
return self
|
527
|
+
end
|
528
|
+
|
529
|
+
|
530
|
+
### Set the expectation that the given +expected_ids+ will be present as the
|
531
|
+
### values of the `:id` field of the collection.
|
532
|
+
def with_ids( *expected_ids )
|
533
|
+
self.and_all( include :id )
|
534
|
+
@expected_ids = expected_ids.flatten( 1 )
|
535
|
+
return self
|
536
|
+
end
|
537
|
+
|
538
|
+
|
539
|
+
### Add an expectation that the collection's objects all have an ':id' field,
|
540
|
+
### and that the corresponding values be the same as the primary key values of
|
541
|
+
### the given +objects+ (fetched via their #pk methods).
|
542
|
+
def with_same_ids_as( *objects )
|
543
|
+
objects.flatten!( 1 )
|
544
|
+
|
545
|
+
ids = if objects.first.respond_to?( :pk )
|
546
|
+
objects.flatten.map( &:pk )
|
547
|
+
else
|
548
|
+
objects.map {|obj| obj[:id] }
|
549
|
+
end
|
550
|
+
|
551
|
+
return self.with_ids( *ids )
|
552
|
+
end
|
553
|
+
|
554
|
+
|
555
|
+
### Enforce ordering when matching IDs.
|
556
|
+
def in_same_order
|
557
|
+
@order_enforced = true
|
558
|
+
return self
|
559
|
+
end
|
560
|
+
|
561
|
+
|
562
|
+
### Adds an expectation that all members of the resulting collection have each
|
563
|
+
### of the keys in the specified +fieldset+.
|
564
|
+
def with_fields( *fieldset )
|
565
|
+
return self.and_all( include *fieldset )
|
566
|
+
end
|
567
|
+
alias_method :and_fields, :with_fields
|
568
|
+
|
569
|
+
|
570
|
+
#########
|
571
|
+
protected
|
572
|
+
#########
|
573
|
+
|
574
|
+
### Returns +true+ if the collection contains exactly the IDs specified by
|
575
|
+
### #with_same_ids_as, or if no IDs were specified.
|
576
|
+
def has_required_ids?
|
577
|
+
return true unless @expected_ids
|
578
|
+
|
579
|
+
if @order_enforced && @expected_ids != @collection_ids
|
580
|
+
return self.fail_with "expected collection IDs to be %p, but they were: %p" %
|
581
|
+
[ @expected_ids, @collection_ids ]
|
582
|
+
elsif @missing_ids && !@missing_ids.empty?
|
583
|
+
return self.fail_with( "collection is missing expected IDs: %p" % [@missing_ids] )
|
584
|
+
elsif @extra_ids && !@extra_ids.empty?
|
585
|
+
return self.fail_with( "collection has extra IDs: %p" % [@extra_ids] )
|
586
|
+
end
|
587
|
+
|
588
|
+
return true
|
589
|
+
end
|
590
|
+
|
591
|
+
end # class HaveJSONCollectionMatcher
|
592
|
+
|
593
|
+
|
594
|
+
|
595
|
+
###############
|
596
|
+
module_function
|
597
|
+
###############
|
598
|
+
|
185
599
|
### Match a response thrown via the +finish_with+ function.
|
186
600
|
def finish_with( status, message=nil, headers={} )
|
187
|
-
FinishWithMatcher.new( status, message, headers )
|
601
|
+
return FinishWithMatcher.new( status, message, headers )
|
602
|
+
end
|
603
|
+
|
604
|
+
|
605
|
+
### Create a new matcher that will expect the response to have a JSON body of
|
606
|
+
### the +expected_type+. If +expected_type+ is omitted, any JSON body will be sufficient
|
607
|
+
### for a match.
|
608
|
+
def have_json_body( expected_type=nil )
|
609
|
+
return HaveJSONBodyMatcher.new( expected_type )
|
188
610
|
end
|
189
611
|
|
190
612
|
|
613
|
+
### Create a new matcher that will expect the response to have a JSON body which is
|
614
|
+
### an Array of Objects (Hashes).
|
615
|
+
def have_json_collection
|
616
|
+
return HaveJSONCollectionMatcher.new
|
617
|
+
end
|
618
|
+
|
619
|
+
|
620
|
+
### Parse the body of the last response and return it as a Ruby object.
|
621
|
+
def last_response_json_body( expected_type=nil )
|
622
|
+
@have_json_body_matcher ||= begin
|
623
|
+
matcher = have_json_body( expected_type )
|
624
|
+
expect( last_response ).to( matcher )
|
625
|
+
matcher
|
626
|
+
end
|
627
|
+
|
628
|
+
return @have_json_body_matcher.parsed_response_body
|
629
|
+
end
|
630
|
+
|
191
631
|
end # module Strelka::Testing
|
192
632
|
|
193
633
|
|