scimitar 1.0.3 → 1.2.1
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
- data/app/controllers/scimitar/active_record_backed_resources_controller.rb +2 -2
- data/app/controllers/scimitar/application_controller.rb +8 -3
- data/app/models/scimitar/complex_types/base.rb +43 -1
- data/app/models/scimitar/resources/base.rb +23 -5
- data/app/models/scimitar/resources/mixin.rb +34 -26
- data/app/models/scimitar/schema/base.rb +3 -1
- data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +86 -0
- data/lib/scimitar/version.rb +2 -2
- data/lib/scimitar.rb +1 -0
- data/spec/models/scimitar/resources/base_spec.rb +108 -58
- data/spec/models/scimitar/resources/mixin_spec.rb +316 -264
- data/spec/models/scimitar/resources/user_spec.rb +13 -0
- data/spec/requests/active_record_backed_resources_controller_spec.rb +173 -127
- data/spec/requests/application_controller_spec.rb +6 -3
- data/spec/requests/engine_spec.rb +26 -1
- data/spec/spec_helper.rb +27 -0
- data/spec/spec_helper_spec.rb +30 -0
- data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +61 -0
- metadata +19 -11
@@ -15,6 +15,19 @@ RSpec.describe Scimitar::Resources::User do
|
|
15
15
|
expect(user.as_json['name']['errors']).to be_nil
|
16
16
|
end
|
17
17
|
|
18
|
+
it 'treats attributes as case-insensitive' do
|
19
|
+
user = described_class.new(name: Scimitar::ComplexTypes::Name.new(
|
20
|
+
FAMILYNAME: 'Smith',
|
21
|
+
GIVENNAME: 'John',
|
22
|
+
FORMATTED: 'John Smith'
|
23
|
+
))
|
24
|
+
|
25
|
+
expect(user.name.familyName).to eql('Smith')
|
26
|
+
expect(user.name.givenName).to eql('John')
|
27
|
+
expect(user.name.formatted).to eql('John Smith')
|
28
|
+
expect(user.as_json['name']['errors']).to be_nil
|
29
|
+
end
|
30
|
+
|
18
31
|
it 'validates that the provided name matches the name schema' do
|
19
32
|
user = described_class.new(name: Scimitar::ComplexTypes::Email.new(
|
20
33
|
value: 'john@smoth.com',
|
@@ -146,41 +146,41 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
146
146
|
|
147
147
|
context '#create' do
|
148
148
|
context 'creates an item' do
|
149
|
-
|
150
|
-
|
149
|
+
shared_examples 'a creator' do | force_upper_case: |
|
150
|
+
it 'with minimal parameters' do
|
151
|
+
mock_before = MockUser.all.to_a
|
151
152
|
|
152
|
-
|
153
|
-
|
154
|
-
post "/Users", params: {
|
155
|
-
format: :scim,
|
156
|
-
userName: '4' # Minimum required by schema
|
157
|
-
}
|
158
|
-
}.to change { MockUser.count }.by(1)
|
153
|
+
attributes = { userName: '4' } # Minimum required by schema
|
154
|
+
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
159
155
|
|
160
|
-
|
161
|
-
|
156
|
+
expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
|
157
|
+
expect {
|
158
|
+
post "/Users", params: attributes.merge(format: :scim)
|
159
|
+
}.to change { MockUser.count }.by(1)
|
162
160
|
|
163
|
-
|
164
|
-
|
161
|
+
mock_after = MockUser.all.to_a
|
162
|
+
new_mock = (mock_after - mock_before).first
|
165
163
|
|
166
|
-
|
167
|
-
|
168
|
-
expect(new_mock.username).to eql('4')
|
169
|
-
end
|
164
|
+
expect(response.status).to eql(201)
|
165
|
+
result = JSON.parse(response.body)
|
170
166
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
167
|
+
expect(result['id']).to eql(new_mock.id.to_s)
|
168
|
+
expect(result['meta']['resourceType']).to eql('User')
|
169
|
+
expect(new_mock.username).to eql('4')
|
170
|
+
end
|
175
171
|
|
176
|
-
|
177
|
-
|
178
|
-
|
172
|
+
# A bit of extra coverage just for general confidence.
|
173
|
+
#
|
174
|
+
it 'with more comprehensive parameters' do
|
175
|
+
mock_before = MockUser.all.to_a
|
176
|
+
|
177
|
+
attributes = {
|
179
178
|
userName: '4',
|
180
179
|
name: {
|
181
180
|
givenName: 'Given',
|
182
181
|
familyName: 'Family'
|
183
182
|
},
|
183
|
+
meta: { resourceType: 'User' },
|
184
184
|
emails: [
|
185
185
|
{
|
186
186
|
type: 'work',
|
@@ -192,22 +192,36 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
192
192
|
}
|
193
193
|
]
|
194
194
|
}
|
195
|
-
}.to change { MockUser.count }.by(1)
|
196
195
|
|
197
|
-
|
198
|
-
new_mock = (mock_after - mock_before).first
|
196
|
+
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
199
197
|
|
200
|
-
|
201
|
-
|
198
|
+
expect {
|
199
|
+
post "/Users", params: attributes.merge(format: :scim)
|
200
|
+
}.to change { MockUser.count }.by(1)
|
202
201
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
202
|
+
mock_after = MockUser.all.to_a
|
203
|
+
new_mock = (mock_after - mock_before).first
|
204
|
+
|
205
|
+
expect(response.status).to eql(201)
|
206
|
+
result = JSON.parse(response.body)
|
207
|
+
|
208
|
+
expect(result['id']).to eql(new_mock.id.to_s)
|
209
|
+
expect(result['meta']['resourceType']).to eql('User')
|
210
|
+
expect(new_mock.username).to eql('4')
|
211
|
+
expect(new_mock.first_name).to eql('Given')
|
212
|
+
expect(new_mock.last_name).to eql('Family')
|
213
|
+
expect(new_mock.home_email_address).to eql('home_4@test.com')
|
214
|
+
expect(new_mock.work_email_address).to eql('work_4@test.com')
|
215
|
+
end
|
216
|
+
end # "shared_examples 'a creator' do | force_upper_case: |"
|
217
|
+
|
218
|
+
context 'using schema-matched case' do
|
219
|
+
it_behaves_like 'a creator', force_upper_case: false
|
220
|
+
end # "context 'using schema-matched case' do"
|
221
|
+
|
222
|
+
context 'using upper case' do
|
223
|
+
it_behaves_like 'a creator', force_upper_case: true
|
224
|
+
end # "context 'using upper case' do"
|
211
225
|
end
|
212
226
|
|
213
227
|
it 'returns 409 for duplicates (by Rails validation)' do
|
@@ -258,28 +272,38 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
258
272
|
# ===========================================================================
|
259
273
|
|
260
274
|
context '#replace' do
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
format: :scim,
|
266
|
-
userName: '4' # Minimum required by schema
|
267
|
-
}
|
268
|
-
}.to_not change { MockUser.count }
|
275
|
+
shared_examples 'a replacer' do | force_upper_case: |
|
276
|
+
it 'which replaces all attributes in an instance' do
|
277
|
+
attributes = { userName: '4' } # Minimum required by schema
|
278
|
+
attributes = spec_helper_hupcase(attributes) if force_upper_case
|
269
279
|
|
270
|
-
|
271
|
-
|
280
|
+
expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
|
281
|
+
expect {
|
282
|
+
put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
|
283
|
+
}.to_not change { MockUser.count }
|
272
284
|
|
273
|
-
|
274
|
-
|
285
|
+
expect(response.status).to eql(200)
|
286
|
+
result = JSON.parse(response.body)
|
275
287
|
|
276
|
-
|
288
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
289
|
+
expect(result['meta']['resourceType']).to eql('User')
|
277
290
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
291
|
+
@u2.reload
|
292
|
+
|
293
|
+
expect(@u2.username).to eql('4')
|
294
|
+
expect(@u2.first_name).to be_nil
|
295
|
+
expect(@u2.last_name).to be_nil
|
296
|
+
expect(@u2.home_email_address).to be_nil
|
297
|
+
end
|
298
|
+
end # "shared_examples 'a replacer' do | force_upper_case: |"
|
299
|
+
|
300
|
+
context 'using schema-matched case' do
|
301
|
+
it_behaves_like 'a replacer', force_upper_case: false
|
302
|
+
end # "context 'using schema-matched case' do"
|
303
|
+
|
304
|
+
context 'using upper case' do
|
305
|
+
it_behaves_like 'a replacer', force_upper_case: true
|
306
|
+
end # "context 'using upper case' do"
|
283
307
|
|
284
308
|
it 'notes schema validation failures' do
|
285
309
|
expect {
|
@@ -341,11 +365,9 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
341
365
|
# ===========================================================================
|
342
366
|
|
343
367
|
context '#update' do
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
patch "/Users/#{@u2.id}", params: {
|
348
|
-
format: :scim,
|
368
|
+
shared_examples 'an updater' do | force_upper_case: |
|
369
|
+
it 'which patches specific attributes' do
|
370
|
+
payload = {
|
349
371
|
Operations: [
|
350
372
|
{
|
351
373
|
op: 'add',
|
@@ -359,33 +381,36 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
359
381
|
}
|
360
382
|
]
|
361
383
|
}
|
362
|
-
}.to_not change { MockUser.count }
|
363
384
|
|
364
|
-
|
365
|
-
result = JSON.parse(response.body)
|
385
|
+
payload = spec_helper_hupcase(payload) if force_upper_case
|
366
386
|
|
367
|
-
|
368
|
-
|
387
|
+
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
388
|
+
expect {
|
389
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
390
|
+
}.to_not change { MockUser.count }
|
369
391
|
|
370
|
-
|
392
|
+
expect(response.status).to eql(200)
|
393
|
+
result = JSON.parse(response.body)
|
371
394
|
|
372
|
-
|
373
|
-
|
374
|
-
expect(@u2.last_name).to eql('Bar')
|
375
|
-
expect(@u2.home_email_address).to eql('home_2@test.com')
|
376
|
-
expect(@u2.work_email_address).to eql('work_4@test.com')
|
377
|
-
end
|
395
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
396
|
+
expect(result['meta']['resourceType']).to eql('User')
|
378
397
|
|
379
|
-
|
380
|
-
|
381
|
-
@u2.
|
398
|
+
@u2.reload
|
399
|
+
|
400
|
+
expect(@u2.username).to eql('4')
|
401
|
+
expect(@u2.first_name).to eql('Foo')
|
402
|
+
expect(@u2.last_name).to eql('Bar')
|
403
|
+
expect(@u2.home_email_address).to eql('home_2@test.com')
|
404
|
+
expect(@u2.work_email_address).to eql('work_4@test.com')
|
382
405
|
end
|
383
406
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
407
|
+
context 'which clears attributes' do
|
408
|
+
before :each do
|
409
|
+
@u2.update!(work_email_address: 'work_2@test.com')
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'with simple paths' do
|
413
|
+
payload = {
|
389
414
|
Operations: [
|
390
415
|
{
|
391
416
|
op: 'remove',
|
@@ -393,28 +418,31 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
393
418
|
}
|
394
419
|
]
|
395
420
|
}
|
396
|
-
}.to_not change { MockUser.count }
|
397
421
|
|
398
|
-
|
399
|
-
result = JSON.parse(response.body)
|
422
|
+
payload = spec_helper_hupcase(payload) if force_upper_case
|
400
423
|
|
401
|
-
|
402
|
-
|
424
|
+
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
425
|
+
expect {
|
426
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
427
|
+
}.to_not change { MockUser.count }
|
403
428
|
|
404
|
-
|
429
|
+
expect(response.status).to eql(200)
|
430
|
+
result = JSON.parse(response.body)
|
405
431
|
|
406
|
-
|
407
|
-
|
408
|
-
expect(@u2.last_name).to eql('Bar')
|
409
|
-
expect(@u2.home_email_address).to eql('home_2@test.com')
|
410
|
-
expect(@u2.work_email_address).to eql('work_2@test.com')
|
411
|
-
end
|
432
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
433
|
+
expect(result['meta']['resourceType']).to eql('User')
|
412
434
|
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
435
|
+
@u2.reload
|
436
|
+
|
437
|
+
expect(@u2.username).to eql('2')
|
438
|
+
expect(@u2.first_name).to be_nil
|
439
|
+
expect(@u2.last_name).to eql('Bar')
|
440
|
+
expect(@u2.home_email_address).to eql('home_2@test.com')
|
441
|
+
expect(@u2.work_email_address).to eql('work_2@test.com')
|
442
|
+
end
|
443
|
+
|
444
|
+
it 'by array entry filter match' do
|
445
|
+
payload = {
|
418
446
|
Operations: [
|
419
447
|
{
|
420
448
|
op: 'remove',
|
@@ -422,28 +450,31 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
422
450
|
}
|
423
451
|
]
|
424
452
|
}
|
425
|
-
}.to_not change { MockUser.count }
|
426
453
|
|
427
|
-
|
428
|
-
result = JSON.parse(response.body)
|
454
|
+
payload = spec_helper_hupcase(payload) if force_upper_case
|
429
455
|
|
430
|
-
|
431
|
-
|
456
|
+
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
457
|
+
expect {
|
458
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
459
|
+
}.to_not change { MockUser.count }
|
432
460
|
|
433
|
-
|
461
|
+
expect(response.status).to eql(200)
|
462
|
+
result = JSON.parse(response.body)
|
434
463
|
|
435
|
-
|
436
|
-
|
437
|
-
expect(@u2.last_name).to eql('Bar')
|
438
|
-
expect(@u2.home_email_address).to eql('home_2@test.com')
|
439
|
-
expect(@u2.work_email_address).to be_nil
|
440
|
-
end
|
464
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
465
|
+
expect(result['meta']['resourceType']).to eql('User')
|
441
466
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
467
|
+
@u2.reload
|
468
|
+
|
469
|
+
expect(@u2.username).to eql('2')
|
470
|
+
expect(@u2.first_name).to eql('Foo')
|
471
|
+
expect(@u2.last_name).to eql('Bar')
|
472
|
+
expect(@u2.home_email_address).to eql('home_2@test.com')
|
473
|
+
expect(@u2.work_email_address).to be_nil
|
474
|
+
end
|
475
|
+
|
476
|
+
it 'by whole collection' do
|
477
|
+
payload = {
|
447
478
|
Operations: [
|
448
479
|
{
|
449
480
|
op: 'remove',
|
@@ -451,23 +482,38 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
|
|
451
482
|
}
|
452
483
|
]
|
453
484
|
}
|
454
|
-
}.to_not change { MockUser.count }
|
455
485
|
|
456
|
-
|
457
|
-
result = JSON.parse(response.body)
|
486
|
+
payload = spec_helper_hupcase(payload) if force_upper_case
|
458
487
|
|
459
|
-
|
460
|
-
|
488
|
+
expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
|
489
|
+
expect {
|
490
|
+
patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
|
491
|
+
}.to_not change { MockUser.count }
|
461
492
|
|
462
|
-
|
493
|
+
expect(response.status).to eql(200)
|
494
|
+
result = JSON.parse(response.body)
|
463
495
|
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
496
|
+
expect(result['id']).to eql(@u2.id.to_s)
|
497
|
+
expect(result['meta']['resourceType']).to eql('User')
|
498
|
+
|
499
|
+
@u2.reload
|
500
|
+
|
501
|
+
expect(@u2.username).to eql('2')
|
502
|
+
expect(@u2.first_name).to eql('Foo')
|
503
|
+
expect(@u2.last_name).to eql('Bar')
|
504
|
+
expect(@u2.home_email_address).to be_nil
|
505
|
+
expect(@u2.work_email_address).to be_nil
|
506
|
+
end
|
507
|
+
end # "context 'which clears attributes' do"
|
508
|
+
end # "shared_examples 'an updater' do | force_upper_case: |"
|
509
|
+
|
510
|
+
context 'using schema-matched case' do
|
511
|
+
it_behaves_like 'an updater', force_upper_case: false
|
512
|
+
end # "context 'using schema-matched case' do"
|
513
|
+
|
514
|
+
context 'using upper case' do
|
515
|
+
it_behaves_like 'an updater', force_upper_case: true
|
516
|
+
end # "context 'using upper case' do"
|
471
517
|
|
472
518
|
it 'notes Rails validation failures' do
|
473
519
|
expect {
|
@@ -11,11 +11,14 @@ RSpec.describe Scimitar::ApplicationController do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
context 'format handling' do
|
14
|
-
it 'renders "
|
14
|
+
it 'renders "OK" if the request does not provide any Content-Type value' do
|
15
15
|
get '/CustomRequestVerifiers', params: { format: :html }
|
16
16
|
|
17
|
-
expect(response).to have_http_status(:
|
18
|
-
|
17
|
+
expect(response).to have_http_status(:ok)
|
18
|
+
parsed_body = JSON.parse(response.body)
|
19
|
+
expect(parsed_body['request']['is_scim' ]).to eql(true)
|
20
|
+
expect(parsed_body['request']['format' ]).to eql('application/scim+json')
|
21
|
+
expect(parsed_body['request']['content_type']).to eql('application/scim+json')
|
19
22
|
end
|
20
23
|
|
21
24
|
it 'renders 400 if given bad JSON' do
|
@@ -10,11 +10,36 @@ RSpec.describe Scimitar::Engine do
|
|
10
10
|
# "params" given here as a String, expecting the engine's custom parser to
|
11
11
|
# decode it for us.
|
12
12
|
#
|
13
|
-
it 'decodes JSON', type: :model do
|
13
|
+
it 'decodes simple JSON', type: :model do
|
14
14
|
post '/Users.scim', params: '{"userName": "foo"}', headers: { 'CONTENT_TYPE' => 'application/scim+json' }
|
15
15
|
|
16
16
|
expect(response.status).to eql(201)
|
17
17
|
expect(JSON.parse(response.body)['userName']).to eql('foo')
|
18
18
|
end
|
19
|
+
|
20
|
+
it 'decodes nested JSON', type: :model do
|
21
|
+
post '/Users.scim', params: '{"userName": "foo", "name": {"givenName": "bar", "familyName": "baz"}}', headers: { 'CONTENT_TYPE' => 'application/scim+json' }
|
22
|
+
|
23
|
+
expect(response.status).to eql(201)
|
24
|
+
expect(JSON.parse(response.body)['userName']).to eql('foo')
|
25
|
+
expect(JSON.parse(response.body)['name']['givenName']).to eql('bar')
|
26
|
+
expect(JSON.parse(response.body)['name']['familyName']).to eql('baz')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'is case insensitive at the top level', type: :model do
|
30
|
+
post '/Users.scim', params: '{"USERNAME": "foo"}', headers: { 'CONTENT_TYPE' => 'application/scim+json' }
|
31
|
+
|
32
|
+
expect(response.status).to eql(201)
|
33
|
+
expect(JSON.parse(response.body)['userName']).to eql('foo')
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is case insensitive in nested levels', type: :model do
|
37
|
+
post '/Users.scim', params: '{"USERNAME": "foo", "NAME": {"GIVENNAME": "bar", "FAMILYNAME": "baz"}}', headers: { 'CONTENT_TYPE' => 'application/scim+json' }
|
38
|
+
|
39
|
+
expect(response.status).to eql(201)
|
40
|
+
expect(JSON.parse(response.body)['userName']).to eql('foo')
|
41
|
+
expect(JSON.parse(response.body)['name']['givenName']).to eql('bar')
|
42
|
+
expect(JSON.parse(response.body)['name']['familyName']).to eql('baz')
|
43
|
+
end
|
19
44
|
end # "context 'parameter parser' do"
|
20
45
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -64,3 +64,30 @@ def spec_helper_capture_stdout( &block )
|
|
64
64
|
|
65
65
|
return result
|
66
66
|
end
|
67
|
+
|
68
|
+
# Recursively transform the keys of any given Hash or any Hashes in a given
|
69
|
+
# Array into uppercase form, retaining Symbol or String keys. Returns the
|
70
|
+
# transformed duplicate structure.
|
71
|
+
#
|
72
|
+
# Only Hashes or Hash entries within an Array are converted. Other data is left
|
73
|
+
# alone. The original input item is not modified.
|
74
|
+
#
|
75
|
+
# IMPORTANT: HashWithIndifferentAccess or similar subclasses are not supported.
|
76
|
+
#
|
77
|
+
# +item+:: Hash or Array that might contain some Hashes.
|
78
|
+
#
|
79
|
+
def spec_helper_hupcase(item)
|
80
|
+
if item.is_a?(Hash)
|
81
|
+
rehash = item.transform_keys(&:upcase)
|
82
|
+
rehash.each do | key, value |
|
83
|
+
rehash[key] = spec_helper_hupcase(value)
|
84
|
+
end
|
85
|
+
rehash
|
86
|
+
elsif item.is_a?(Array)
|
87
|
+
item.map do | array_entry |
|
88
|
+
spec_helper_hupcase(array_entry)
|
89
|
+
end
|
90
|
+
else
|
91
|
+
item
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Self-test. Sometimes tests call into utility methods in spec_helper.rb;
|
2
|
+
# if those aren't doing what they should, then the tests might give false
|
3
|
+
# positive passes.
|
4
|
+
#
|
5
|
+
require 'spec_helper'
|
6
|
+
|
7
|
+
RSpec.describe 'spec_helper.rb self-test' do
|
8
|
+
context '#spec_helper_hupcase' do
|
9
|
+
it 'converts a flat Hash, preserving data type of keys' do
|
10
|
+
input = {:one => 1, 'two' => 2}
|
11
|
+
output = spec_helper_hupcase(input)
|
12
|
+
|
13
|
+
expect(output).to eql({:ONE => 1, 'TWO' => 2})
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'converts a nested Hash' do
|
17
|
+
input = {:one => 1, 'two' => {:tHrEe => {'fOuR' => 4}}}
|
18
|
+
output = spec_helper_hupcase(input)
|
19
|
+
|
20
|
+
expect(output).to eql({:ONE => 1, 'TWO' => {:THREE => {'FOUR' => 4}}})
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'converts an Array with Hashes' do
|
24
|
+
input = {:one => 1, 'two' => [true, 42, {:tHrEe => {'fOuR' => 4}}]}
|
25
|
+
output = spec_helper_hupcase(input)
|
26
|
+
|
27
|
+
expect(output).to eql({:ONE => 1, 'TWO' => [true, 42, {:THREE => {'FOUR' => 4}}]})
|
28
|
+
end
|
29
|
+
end # "context '#spec_helper_hupcase' do"
|
30
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Scimitar::Support::HashWithIndifferentCaseInsensitiveAccess do
|
4
|
+
shared_examples 'an indifferent access, case insensitive Hash' do
|
5
|
+
context 'where keys set as strings' do
|
6
|
+
it 'can be retrieved via any case' do
|
7
|
+
subject()['foo'] = 1
|
8
|
+
|
9
|
+
expect(subject()['FOO']).to eql(1)
|
10
|
+
expect(subject()[:FoO ]).to eql(1)
|
11
|
+
expect(subject()['bar']).to be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'can be checked for via any case' do
|
15
|
+
subject()['foo'] = 1
|
16
|
+
|
17
|
+
expect(subject()).to have_key('FOO')
|
18
|
+
expect(subject()).to have_key(:FoO )
|
19
|
+
expect(subject()).to_not have_key('bar')
|
20
|
+
end
|
21
|
+
end # "context 'where keys set as strings' do"
|
22
|
+
|
23
|
+
context 'where keys set as symbols' do
|
24
|
+
it 'retrieves via any case' do
|
25
|
+
subject()[:foo] = 1
|
26
|
+
|
27
|
+
expect(subject()['FOO']).to eql(1)
|
28
|
+
expect(subject()[:FoO ]).to eql(1)
|
29
|
+
expect(subject()['bar']).to be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'enquires via any case' do
|
33
|
+
subject()[:foo] = 1
|
34
|
+
|
35
|
+
expect(subject()).to have_key('FOO')
|
36
|
+
expect(subject()).to have_key(:FoO )
|
37
|
+
expect(subject()).to_not have_key('bar')
|
38
|
+
end
|
39
|
+
end # "context 'where keys set as symbols' do"
|
40
|
+
end # "shared_examples 'an indifferent access, case insensitive Hash' do"
|
41
|
+
|
42
|
+
context 'when created directly' do
|
43
|
+
subject do
|
44
|
+
described_class.new()
|
45
|
+
end
|
46
|
+
|
47
|
+
it_behaves_like 'an indifferent access, case insensitive Hash'
|
48
|
+
end # "context 'when created directly' do"
|
49
|
+
|
50
|
+
context 'when created through conversion' do
|
51
|
+
subject do
|
52
|
+
{ 'test' => 2 }.with_indifferent_access.with_indifferent_case_insensitive_access()
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'includes the original data' do
|
56
|
+
expect(subject()[:TEST]).to eql(2)
|
57
|
+
end
|
58
|
+
|
59
|
+
it_behaves_like 'an indifferent access, case insensitive Hash'
|
60
|
+
end # "context 'converting hashes' do"
|
61
|
+
end
|