strelka 0.15.0 → 0.16.0
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
- 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
|
|