scimitar 1.0.3 → 2.0.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.
@@ -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',
@@ -29,25 +42,25 @@ RSpec.describe Scimitar::Resources::User do
29
42
  let(:user) { described_class.new }
30
43
 
31
44
  it 'adds the error when the value is a string' do
32
- user.add_errors_from_hash(key: 'some error')
45
+ user.add_errors_from_hash(errors_hash: {key: 'some error'})
33
46
  expect(user.errors.messages.to_h).to eql({key: ['some error']})
34
47
  expect(user.errors.full_messages).to eql(['Key some error'])
35
48
  end
36
49
 
37
50
  it 'adds the error when the value is an array' do
38
- user.add_errors_from_hash(key: ['error1', 'error2'])
51
+ user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']})
39
52
  expect(user.errors.messages.to_h).to eql({key: ['error1', 'error2']})
40
53
  expect(user.errors.full_messages).to eql(['Key error1', 'Key error2'])
41
54
  end
42
55
 
43
56
  it 'adds the error with prefix when the value is a string' do
44
- user.add_errors_from_hash({key: 'some error'}, prefix: :pre)
57
+ user.add_errors_from_hash(errors_hash: {key: 'some error'}, prefix: :pre)
45
58
  expect(user.errors.messages.to_h).to eql({:'pre.key' => ['some error']})
46
59
  expect(user.errors.full_messages).to eql(['Pre key some error'])
47
60
  end
48
61
 
49
62
  it 'adds the error wity prefix when the value is an array' do
50
- user.add_errors_from_hash({key: ['error1', 'error2']}, prefix: :pre)
63
+ user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']}, prefix: :pre)
51
64
  expect(user.errors.messages.to_h).to eql({:'pre.key' => ['error1', 'error2']})
52
65
  expect(user.errors.full_messages).to eql(['Pre key error1', 'Pre key error2'])
53
66
  end
@@ -146,36 +146,35 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
146
146
 
147
147
  context '#create' do
148
148
  context 'creates an item' do
149
- it 'with minimal parameters' do
150
- mock_before = MockUser.all.to_a
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
- expect_any_instance_of(MockUsersController).to receive(:create).once.and_call_original
153
- expect {
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
- mock_after = MockUser.all.to_a
161
- new_mock = (mock_after - mock_before).first
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
- expect(response.status).to eql(201)
164
- result = JSON.parse(response.body)
161
+ mock_after = MockUser.all.to_a
162
+ new_mock = (mock_after - mock_before).first
165
163
 
166
- expect(result['id']).to eql(new_mock.id.to_s)
167
- expect(result['meta']['resourceType']).to eql('User')
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
- # A bit of extra coverage just for general confidence.
172
- #
173
- it 'with more comprehensive parameters' do
174
- mock_before = MockUser.all.to_a
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
- expect {
177
- post "/Users", params: {
178
- format: :scim,
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',
@@ -192,22 +191,36 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
192
191
  }
193
192
  ]
194
193
  }
195
- }.to change { MockUser.count }.by(1)
196
194
 
197
- mock_after = MockUser.all.to_a
198
- new_mock = (mock_after - mock_before).first
195
+ attributes = spec_helper_hupcase(attributes) if force_upper_case
199
196
 
200
- expect(response.status).to eql(201)
201
- result = JSON.parse(response.body)
197
+ expect {
198
+ post "/Users", params: attributes.merge(format: :scim)
199
+ }.to change { MockUser.count }.by(1)
202
200
 
203
- expect(result['id']).to eql(new_mock.id.to_s)
204
- expect(result['meta']['resourceType']).to eql('User')
205
- expect(new_mock.username).to eql('4')
206
- expect(new_mock.first_name).to eql('Given')
207
- expect(new_mock.last_name).to eql('Family')
208
- expect(new_mock.home_email_address).to eql('home_4@test.com')
209
- expect(new_mock.work_email_address).to eql('work_4@test.com')
210
- end
201
+ mock_after = MockUser.all.to_a
202
+ new_mock = (mock_after - mock_before).first
203
+
204
+ expect(response.status).to eql(201)
205
+ result = JSON.parse(response.body)
206
+
207
+ expect(result['id']).to eql(new_mock.id.to_s)
208
+ expect(result['meta']['resourceType']).to eql('User')
209
+ expect(new_mock.username).to eql('4')
210
+ expect(new_mock.first_name).to eql('Given')
211
+ expect(new_mock.last_name).to eql('Family')
212
+ expect(new_mock.home_email_address).to eql('home_4@test.com')
213
+ expect(new_mock.work_email_address).to eql('work_4@test.com')
214
+ end
215
+ end # "shared_examples 'a creator' do | force_upper_case: |"
216
+
217
+ context 'using schema-matched case' do
218
+ it_behaves_like 'a creator', force_upper_case: false
219
+ end # "context 'using schema-matched case' do"
220
+
221
+ context 'using upper case' do
222
+ it_behaves_like 'a creator', force_upper_case: true
223
+ end # "context 'using upper case' do"
211
224
  end
212
225
 
213
226
  it 'returns 409 for duplicates (by Rails validation)' do
@@ -258,28 +271,38 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
258
271
  # ===========================================================================
259
272
 
260
273
  context '#replace' do
261
- it 'replaces all attributes in an instance' do
262
- expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
263
- expect {
264
- put "/Users/#{@u2.id}", params: {
265
- format: :scim,
266
- userName: '4' # Minimum required by schema
267
- }
268
- }.to_not change { MockUser.count }
274
+ shared_examples 'a replacer' do | force_upper_case: |
275
+ it 'which replaces all attributes in an instance' do
276
+ attributes = { userName: '4' } # Minimum required by schema
277
+ attributes = spec_helper_hupcase(attributes) if force_upper_case
269
278
 
270
- expect(response.status).to eql(200)
271
- result = JSON.parse(response.body)
279
+ expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
280
+ expect {
281
+ put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
282
+ }.to_not change { MockUser.count }
272
283
 
273
- expect(result['id']).to eql(@u2.id.to_s)
274
- expect(result['meta']['resourceType']).to eql('User')
284
+ expect(response.status).to eql(200)
285
+ result = JSON.parse(response.body)
275
286
 
276
- @u2.reload
287
+ expect(result['id']).to eql(@u2.id.to_s)
288
+ expect(result['meta']['resourceType']).to eql('User')
277
289
 
278
- expect(@u2.username).to eql('4')
279
- expect(@u2.first_name).to be_nil
280
- expect(@u2.last_name).to be_nil
281
- expect(@u2.home_email_address).to be_nil
282
- end
290
+ @u2.reload
291
+
292
+ expect(@u2.username).to eql('4')
293
+ expect(@u2.first_name).to be_nil
294
+ expect(@u2.last_name).to be_nil
295
+ expect(@u2.home_email_address).to be_nil
296
+ end
297
+ end # "shared_examples 'a replacer' do | force_upper_case: |"
298
+
299
+ context 'using schema-matched case' do
300
+ it_behaves_like 'a replacer', force_upper_case: false
301
+ end # "context 'using schema-matched case' do"
302
+
303
+ context 'using upper case' do
304
+ it_behaves_like 'a replacer', force_upper_case: true
305
+ end # "context 'using upper case' do"
283
306
 
284
307
  it 'notes schema validation failures' do
285
308
  expect {
@@ -341,11 +364,9 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
341
364
  # ===========================================================================
342
365
 
343
366
  context '#update' do
344
- it 'patches specific attributes' do
345
- expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
346
- expect {
347
- patch "/Users/#{@u2.id}", params: {
348
- format: :scim,
367
+ shared_examples 'an updater' do | force_upper_case: |
368
+ it 'which patches specific attributes' do
369
+ payload = {
349
370
  Operations: [
350
371
  {
351
372
  op: 'add',
@@ -359,33 +380,36 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
359
380
  }
360
381
  ]
361
382
  }
362
- }.to_not change { MockUser.count }
363
383
 
364
- expect(response.status).to eql(200)
365
- result = JSON.parse(response.body)
384
+ payload = spec_helper_hupcase(payload) if force_upper_case
366
385
 
367
- expect(result['id']).to eql(@u2.id.to_s)
368
- expect(result['meta']['resourceType']).to eql('User')
386
+ expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
387
+ expect {
388
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
389
+ }.to_not change { MockUser.count }
369
390
 
370
- @u2.reload
391
+ expect(response.status).to eql(200)
392
+ result = JSON.parse(response.body)
371
393
 
372
- expect(@u2.username).to eql('4')
373
- expect(@u2.first_name).to eql('Foo')
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
394
+ expect(result['id']).to eql(@u2.id.to_s)
395
+ expect(result['meta']['resourceType']).to eql('User')
378
396
 
379
- context 'clears attributes' do
380
- before :each do
381
- @u2.update!(work_email_address: 'work_2@test.com')
397
+ @u2.reload
398
+
399
+ expect(@u2.username).to eql('4')
400
+ expect(@u2.first_name).to eql('Foo')
401
+ expect(@u2.last_name).to eql('Bar')
402
+ expect(@u2.home_email_address).to eql('home_2@test.com')
403
+ expect(@u2.work_email_address).to eql('work_4@test.com')
382
404
  end
383
405
 
384
- it 'with simple paths' do
385
- expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
386
- expect {
387
- patch "/Users/#{@u2.id}", params: {
388
- format: :scim,
406
+ context 'which clears attributes' do
407
+ before :each do
408
+ @u2.update!(work_email_address: 'work_2@test.com')
409
+ end
410
+
411
+ it 'with simple paths' do
412
+ payload = {
389
413
  Operations: [
390
414
  {
391
415
  op: 'remove',
@@ -393,28 +417,31 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
393
417
  }
394
418
  ]
395
419
  }
396
- }.to_not change { MockUser.count }
397
420
 
398
- expect(response.status).to eql(200)
399
- result = JSON.parse(response.body)
421
+ payload = spec_helper_hupcase(payload) if force_upper_case
400
422
 
401
- expect(result['id']).to eql(@u2.id.to_s)
402
- expect(result['meta']['resourceType']).to eql('User')
423
+ expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
424
+ expect {
425
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
426
+ }.to_not change { MockUser.count }
403
427
 
404
- @u2.reload
428
+ expect(response.status).to eql(200)
429
+ result = JSON.parse(response.body)
405
430
 
406
- expect(@u2.username).to eql('2')
407
- expect(@u2.first_name).to be_nil
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
431
+ expect(result['id']).to eql(@u2.id.to_s)
432
+ expect(result['meta']['resourceType']).to eql('User')
412
433
 
413
- it 'by array entry filter match' do
414
- expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
415
- expect {
416
- patch "/Users/#{@u2.id}", params: {
417
- format: :scim,
434
+ @u2.reload
435
+
436
+ expect(@u2.username).to eql('2')
437
+ expect(@u2.first_name).to be_nil
438
+ expect(@u2.last_name).to eql('Bar')
439
+ expect(@u2.home_email_address).to eql('home_2@test.com')
440
+ expect(@u2.work_email_address).to eql('work_2@test.com')
441
+ end
442
+
443
+ it 'by array entry filter match' do
444
+ payload = {
418
445
  Operations: [
419
446
  {
420
447
  op: 'remove',
@@ -422,28 +449,31 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
422
449
  }
423
450
  ]
424
451
  }
425
- }.to_not change { MockUser.count }
426
452
 
427
- expect(response.status).to eql(200)
428
- result = JSON.parse(response.body)
453
+ payload = spec_helper_hupcase(payload) if force_upper_case
429
454
 
430
- expect(result['id']).to eql(@u2.id.to_s)
431
- expect(result['meta']['resourceType']).to eql('User')
455
+ expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
456
+ expect {
457
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
458
+ }.to_not change { MockUser.count }
432
459
 
433
- @u2.reload
460
+ expect(response.status).to eql(200)
461
+ result = JSON.parse(response.body)
434
462
 
435
- expect(@u2.username).to eql('2')
436
- expect(@u2.first_name).to eql('Foo')
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
463
+ expect(result['id']).to eql(@u2.id.to_s)
464
+ expect(result['meta']['resourceType']).to eql('User')
441
465
 
442
- it 'by whole collection' do
443
- expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
444
- expect {
445
- patch "/Users/#{@u2.id}", params: {
446
- format: :scim,
466
+ @u2.reload
467
+
468
+ expect(@u2.username).to eql('2')
469
+ expect(@u2.first_name).to eql('Foo')
470
+ expect(@u2.last_name).to eql('Bar')
471
+ expect(@u2.home_email_address).to eql('home_2@test.com')
472
+ expect(@u2.work_email_address).to be_nil
473
+ end
474
+
475
+ it 'by whole collection' do
476
+ payload = {
447
477
  Operations: [
448
478
  {
449
479
  op: 'remove',
@@ -451,23 +481,38 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
451
481
  }
452
482
  ]
453
483
  }
454
- }.to_not change { MockUser.count }
455
484
 
456
- expect(response.status).to eql(200)
457
- result = JSON.parse(response.body)
485
+ payload = spec_helper_hupcase(payload) if force_upper_case
458
486
 
459
- expect(result['id']).to eql(@u2.id.to_s)
460
- expect(result['meta']['resourceType']).to eql('User')
487
+ expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
488
+ expect {
489
+ patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
490
+ }.to_not change { MockUser.count }
461
491
 
462
- @u2.reload
492
+ expect(response.status).to eql(200)
493
+ result = JSON.parse(response.body)
463
494
 
464
- expect(@u2.username).to eql('2')
465
- expect(@u2.first_name).to eql('Foo')
466
- expect(@u2.last_name).to eql('Bar')
467
- expect(@u2.home_email_address).to be_nil
468
- expect(@u2.work_email_address).to be_nil
469
- end
470
- end # "context 'clears attributes' do"
495
+ expect(result['id']).to eql(@u2.id.to_s)
496
+ expect(result['meta']['resourceType']).to eql('User')
497
+
498
+ @u2.reload
499
+
500
+ expect(@u2.username).to eql('2')
501
+ expect(@u2.first_name).to eql('Foo')
502
+ expect(@u2.last_name).to eql('Bar')
503
+ expect(@u2.home_email_address).to be_nil
504
+ expect(@u2.work_email_address).to be_nil
505
+ end
506
+ end # "context 'which clears attributes' do"
507
+ end # "shared_examples 'an updater' do | force_upper_case: |"
508
+
509
+ context 'using schema-matched case' do
510
+ it_behaves_like 'an updater', force_upper_case: false
511
+ end # "context 'using schema-matched case' do"
512
+
513
+ context 'using upper case' do
514
+ it_behaves_like 'an updater', force_upper_case: true
515
+ end # "context 'using upper case' do"
471
516
 
472
517
  it 'notes Rails validation failures' do
473
518
  expect {
@@ -23,7 +23,6 @@ RSpec.describe Scimitar::ApplicationController do
23
23
 
24
24
  expect(response).to have_http_status(:bad_request)
25
25
  expect(JSON.parse(response.body)['detail']).to start_with('Invalid JSON - ')
26
- expect(JSON.parse(response.body)['detail']).to include("'not-json-12345'")
27
26
  end
28
27
 
29
28
  it 'translates Content-Type to Rails request format' 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