scimitar 1.0.0 → 1.1.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.
@@ -363,78 +363,94 @@ RSpec.describe Scimitar::Resources::Mixin do
363
363
  # =========================================================================
364
364
 
365
365
  context '#from_scim!' do
366
- context 'writes instance attribute values from a SCIM representation' do
367
- it 'ignoring read-only lists' do
368
- hash = {
369
- 'userName' => 'foo',
370
- 'name' => {'givenName'=>'Foo', 'familyName'=>'Bar'},
371
- 'active' => true,
372
- 'emails' => [{'type'=>'work', 'primary'=>true, 'value'=>'foo.bar@test.com'}],
373
- 'phoneNumbers'=> [{'type'=>'work', 'primary'=>false, 'value'=>'+642201234567' }],
374
- 'groups' => [{'type'=>'Group', 'value'=>'1'}, {'type'=>'Group', 'value'=>'2'}],
375
- 'id' => '42', # Note, String
376
- 'externalId' => 'AA02984',
377
- 'meta' => {'location'=>'https://test.com/mock_users/42', 'resourceType'=>'User'},
378
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
379
- }
366
+ shared_examples 'a creator' do | force_upper_case: |
367
+ context 'which writes instance attribute values from a SCIM representation while' do
368
+ it 'ignoring read-only lists' do
369
+ hash = {
370
+ 'userName' => 'foo',
371
+ 'name' => {'givenName' => 'Foo', 'familyName' => 'Bar'},
372
+ 'active' => true,
373
+ 'emails' => [{'type' => 'work', 'primary' => true, 'value' => 'foo.bar@test.com'}],
374
+ 'phoneNumbers' => [{'type' => 'work', 'primary' => false, 'value' => '+642201234567' }],
375
+ 'groups' => [{'type' => 'Group', 'value' => '1'}, {'type' => 'Group', 'value' => '2'}],
376
+ 'id' => '42', # Note, String
377
+ 'externalId' => 'AA02984',
378
+ 'meta' => {'location' => 'https://test.com/mock_users/42', 'resourceType' => 'User'},
379
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
380
+ }
380
381
 
381
- instance = MockUser.new
382
- instance.home_email_address = 'home@test.com' # Should be cleared as no home e-mail specified in SCIM hash above
383
- instance.from_scim!(scim_hash: hash)
384
-
385
- expect(instance.scim_uid ).to eql('AA02984')
386
- expect(instance.username ).to eql('foo')
387
- expect(instance.first_name ).to eql('Foo')
388
- expect(instance.last_name ).to eql('Bar')
389
- expect(instance.work_email_address).to eql('foo.bar@test.com')
390
- expect(instance.home_email_address).to be_nil
391
- expect(instance.work_phone_number ).to eql('+642201234567')
392
- end
382
+ hash = spec_helper_hupcase(hash) if force_upper_case
393
383
 
394
- it 'honouring read-write lists' do
395
- g1 = MockGroup.create!(display_name: 'Nested group')
396
-
397
- u1 = MockUser.create!(username: '1', first_name: 'Member 1')
398
- u2 = MockUser.create!(username: '2', first_name: 'Member 2')
399
- u3 = MockUser.create!(username: '3', first_name: 'Member 3')
400
-
401
- hash = {
402
- 'displayName' => 'Foo Group',
403
- 'members' => [
404
- {'type'=>'Group', 'value'=>g1.id.to_s},
405
- {'type'=>'User', 'value'=>u1.id.to_s},
406
- {'type'=>'User', 'value'=>u3.id.to_s}
407
- ],
408
- 'externalId' => 'GG01536',
409
- 'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
410
- 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
411
- }
384
+ instance = MockUser.new
385
+ instance.home_email_address = 'home@test.com' # Should be cleared as no home e-mail specified in SCIM hash above
386
+ instance.from_scim!(scim_hash: hash)
412
387
 
413
- instance = MockGroup.new
414
- instance.from_scim!(scim_hash: hash)
388
+ expect(instance.scim_uid ).to eql('AA02984')
389
+ expect(instance.username ).to eql('foo')
390
+ expect(instance.first_name ).to eql('Foo')
391
+ expect(instance.last_name ).to eql('Bar')
392
+ expect(instance.work_email_address).to eql('foo.bar@test.com')
393
+ expect(instance.home_email_address).to be_nil
394
+ expect(instance.work_phone_number ).to eql('+642201234567')
395
+ end
415
396
 
416
- expect(instance.scim_uid ).to eql('GG01536')
417
- expect(instance.display_name ).to eql('Foo Group')
418
- expect(instance.mock_users ).to match_array([u1, u3])
419
- expect(instance.child_mock_groups).to match_array([g1])
397
+ it 'honouring read-write lists' do
398
+ g1 = MockGroup.create!(display_name: 'Nested group')
420
399
 
421
- instance.save!
422
- expect(g1.reload.parent_id).to eql(instance.id)
423
- end
400
+ u1 = MockUser.create!(username: '1', first_name: 'Member 1')
401
+ u2 = MockUser.create!(username: '2', first_name: 'Member 2')
402
+ u3 = MockUser.create!(username: '3', first_name: 'Member 3')
424
403
 
425
- it 'handles missing inbound lists' do
426
- hash = {
427
- 'displayName' => 'Foo Group'
428
- }
404
+ hash = {
405
+ 'displayName' => 'Foo Group',
406
+ 'members' => [
407
+ {'type' => 'Group', 'value' => g1.id.to_s},
408
+ {'type' => 'User', 'value' => u1.id.to_s},
409
+ {'type' => 'User', 'value' => u3.id.to_s}
410
+ ],
411
+ 'externalId' => 'GG01536',
412
+ 'meta' => {'location'=>'https://test.com/mock_groups/1', 'resourceType'=>'Group'},
413
+ 'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:Group']
414
+ }
429
415
 
430
- instance = MockGroup.new
431
- instance.from_scim!(scim_hash: hash)
416
+ hash = spec_helper_hupcase(hash) if force_upper_case
432
417
 
433
- expect(instance.display_name ).to eql('Foo Group')
434
- expect(instance.mock_users ).to be_empty
435
- expect(instance.child_mock_groups).to be_empty
436
- end
437
- end # "context 'writes instance attribute values from a SCIM representation' do"
418
+ instance = MockGroup.new
419
+ instance.from_scim!(scim_hash: hash)
420
+
421
+ expect(instance.scim_uid ).to eql('GG01536')
422
+ expect(instance.display_name ).to eql('Foo Group')
423
+ expect(instance.mock_users ).to match_array([u1, u3])
424
+ expect(instance.child_mock_groups).to match_array([g1])
425
+
426
+ instance.save!
427
+ expect(g1.reload.parent_id).to eql(instance.id)
428
+ end
429
+
430
+ it 'handling missing inbound lists' do
431
+ hash = {
432
+ 'displayName' => 'Foo Group'
433
+ }
434
+
435
+ hash = spec_helper_hupcase(hash) if force_upper_case
436
+
437
+ instance = MockGroup.new
438
+ instance.from_scim!(scim_hash: hash)
439
+
440
+ expect(instance.display_name ).to eql('Foo Group')
441
+ expect(instance.mock_users ).to be_empty
442
+ expect(instance.child_mock_groups).to be_empty
443
+ end
444
+ end # "context 'which writes instance attribute values from a SCIM representation while' do"
445
+ end # "shared_examples 'a creator' do | force_upper_case: |"
446
+
447
+ context 'using schema-matched case' do
448
+ it_behaves_like 'a creator', force_upper_case: false
449
+ end # "context 'using schema-matched case' do"
450
+
451
+ context 'using upper case' do
452
+ it_behaves_like 'a creator', force_upper_case: true
453
+ end # "context 'using upper case' do"
438
454
 
439
455
  it 'clears things not present in input' do
440
456
  instance = MockUser.new
@@ -522,6 +538,22 @@ RSpec.describe Scimitar::Resources::Mixin do
522
538
  end.to raise_error(RuntimeError)
523
539
  end
524
540
 
541
+ it 'complaints about unsupported multiple operators, handling value spaces' do
542
+ expect do
543
+ @instance.send(:all_matching_filter, filter: 'type eq "work with spaces" and primary pr', within_array: []) do
544
+ fail # Block should never be called!
545
+ end
546
+ end.to raise_error(RuntimeError)
547
+ end
548
+
549
+ it 'complaints about unquoted values with spaces' do
550
+ expect do
551
+ @instance.send(:all_matching_filter, filter: 'type eq work with spaces', within_array: []) do
552
+ fail # Block should never be called!
553
+ end
554
+ end.to raise_error(RuntimeError)
555
+ end
556
+
525
557
  it 'calls block with matches' do
526
558
  array = [
527
559
  {
@@ -565,6 +597,10 @@ RSpec.describe Scimitar::Resources::Mixin do
565
597
  {
566
598
  'type' => 'work"',
567
599
  'value' => 'work_trailing_dquote@test.com'
600
+ },
601
+ {
602
+ 'type' => 'spaced',
603
+ 'value' => 'value with spaces'
568
604
  }
569
605
  ]
570
606
 
@@ -585,7 +621,12 @@ RSpec.describe Scimitar::Resources::Mixin do
585
621
  expect(matched_hash['value']).to eql('boolean@test.com')
586
622
  end
587
623
 
588
- expect(call_count).to eql(3)
624
+ @instance.send(:all_matching_filter, filter: 'value eq "value with spaces"', within_array: array) do |matched_hash, index|
625
+ call_count += 1
626
+ expect(matched_hash['type']).to eql('spaced')
627
+ end
628
+
629
+ expect(call_count).to eql(4)
589
630
  end
590
631
  end # "context '#all_matching_filter' do"
591
632
 
@@ -606,7 +647,7 @@ RSpec.describe Scimitar::Resources::Mixin do
606
647
  context 'when prior value already exists' do
607
648
  it 'simple value: overwrites' do
608
649
  path = [ 'userName' ]
609
- scim_hash = { 'userName' => 'bar' }
650
+ scim_hash = { 'userName' => 'bar' }.with_indifferent_case_insensitive_access()
610
651
 
611
652
  @instance.send(
612
653
  :from_patch_backend!,
@@ -621,7 +662,7 @@ RSpec.describe Scimitar::Resources::Mixin do
621
662
 
622
663
  it 'nested simple value: overwrites' do
623
664
  path = [ 'name', 'givenName' ]
624
- scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }
665
+ scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }.with_indifferent_case_insensitive_access()
625
666
 
626
667
  @instance.send(
627
668
  :from_patch_backend!,
@@ -652,18 +693,18 @@ RSpec.describe Scimitar::Resources::Mixin do
652
693
  'value' => 'work@test.com'
653
694
  }
654
695
  ]
655
- }
696
+ }.with_indifferent_case_insensitive_access()
656
697
 
657
698
  @instance.send(
658
699
  :from_patch_backend!,
659
700
  nature: 'add',
660
701
  path: path,
661
- value: 'added_over_origina@test.com',
702
+ value: 'added_over_original@test.com',
662
703
  altering_hash: scim_hash
663
704
  )
664
705
 
665
706
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
666
- expect(scim_hash['emails'][1]['value']).to eql('added_over_origina@test.com')
707
+ expect(scim_hash['emails'][1]['value']).to eql('added_over_original@test.com')
667
708
  end
668
709
 
669
710
  it 'by boolean match: overwrites' do
@@ -678,18 +719,18 @@ RSpec.describe Scimitar::Resources::Mixin do
678
719
  'primary' => true
679
720
  }
680
721
  ]
681
- }
722
+ }.with_indifferent_case_insensitive_access()
682
723
 
683
724
  @instance.send(
684
725
  :from_patch_backend!,
685
726
  nature: 'add',
686
727
  path: path,
687
- value: 'added_over_origina@test.com',
728
+ value: 'added_over_original@test.com',
688
729
  altering_hash: scim_hash
689
730
  )
690
731
 
691
732
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
692
- expect(scim_hash['emails'][1]['value']).to eql('added_over_origina@test.com')
733
+ expect(scim_hash['emails'][1]['value']).to eql('added_over_original@test.com')
693
734
  end
694
735
 
695
736
  it 'multiple matches: overwrites all' do
@@ -705,18 +746,18 @@ RSpec.describe Scimitar::Resources::Mixin do
705
746
  'value' => 'work_2@test.com'
706
747
  }
707
748
  ]
708
- }
749
+ }.with_indifferent_case_insensitive_access()
709
750
 
710
751
  @instance.send(
711
752
  :from_patch_backend!,
712
753
  nature: 'add',
713
754
  path: path,
714
- value: 'added_over_origina@test.com',
755
+ value: 'added_over_original@test.com',
715
756
  altering_hash: scim_hash
716
757
  )
717
758
 
718
- expect(scim_hash['emails'][0]['value']).to eql('added_over_origina@test.com')
719
- expect(scim_hash['emails'][1]['value']).to eql('added_over_origina@test.com')
759
+ expect(scim_hash['emails'][0]['value']).to eql('added_over_original@test.com')
760
+ expect(scim_hash['emails'][1]['value']).to eql('added_over_original@test.com')
720
761
  end
721
762
  end # "context 'with filter mid-path' do"
722
763
 
@@ -729,7 +770,7 @@ RSpec.describe Scimitar::Resources::Mixin do
729
770
  'value' => 'home@test.com'
730
771
  }
731
772
  ]
732
- }
773
+ }.with_indifferent_case_insensitive_access()
733
774
 
734
775
  @instance.send(
735
776
  :from_patch_backend!,
@@ -753,7 +794,7 @@ RSpec.describe Scimitar::Resources::Mixin do
753
794
  {'value' => '2'}
754
795
  ]
755
796
  }
756
- }
797
+ }.with_indifferent_case_insensitive_access()
757
798
 
758
799
  # Example seen at:
759
800
  #
@@ -795,7 +836,7 @@ RSpec.describe Scimitar::Resources::Mixin do
795
836
  context 'when value is not present' do
796
837
  it 'simple value: adds' do
797
838
  path = [ 'userName' ]
798
- scim_hash = {}
839
+ scim_hash = {}.with_indifferent_case_insensitive_access()
799
840
 
800
841
  @instance.send(
801
842
  :from_patch_backend!,
@@ -810,7 +851,7 @@ RSpec.describe Scimitar::Resources::Mixin do
810
851
 
811
852
  it 'nested simple value: adds' do
812
853
  path = [ 'name', 'givenName' ]
813
- scim_hash = {}
854
+ scim_hash = {}.with_indifferent_case_insensitive_access()
814
855
 
815
856
  @instance.send(
816
857
  :from_patch_backend!,
@@ -836,7 +877,7 @@ RSpec.describe Scimitar::Resources::Mixin do
836
877
  'type' => 'work'
837
878
  }
838
879
  ]
839
- }
880
+ }.with_indifferent_case_insensitive_access()
840
881
 
841
882
  @instance.send(
842
883
  :from_patch_backend!,
@@ -861,7 +902,7 @@ RSpec.describe Scimitar::Resources::Mixin do
861
902
  'primary' => true
862
903
  }
863
904
  ]
864
- }
905
+ }.with_indifferent_case_insensitive_access()
865
906
 
866
907
  @instance.send(
867
908
  :from_patch_backend!,
@@ -877,7 +918,7 @@ RSpec.describe Scimitar::Resources::Mixin do
877
918
 
878
919
  it 'with no match: still adds' do
879
920
  path = [ 'emails[type eq "work"]', 'value' ]
880
- scim_hash = {}
921
+ scim_hash = {}.with_indifferent_case_insensitive_access()
881
922
 
882
923
  @instance.send(
883
924
  :from_patch_backend!,
@@ -901,7 +942,7 @@ RSpec.describe Scimitar::Resources::Mixin do
901
942
  'type' => 'work'
902
943
  }
903
944
  ]
904
- }
945
+ }.with_indifferent_case_insensitive_access()
905
946
 
906
947
  @instance.send(
907
948
  :from_patch_backend!,
@@ -918,7 +959,7 @@ RSpec.describe Scimitar::Resources::Mixin do
918
959
 
919
960
  it 'with arrays: appends' do
920
961
  path = [ 'emails' ]
921
- scim_hash = {}
962
+ scim_hash = {}.with_indifferent_case_insensitive_access()
922
963
 
923
964
  @instance.send(
924
965
  :from_patch_backend!,
@@ -943,7 +984,7 @@ RSpec.describe Scimitar::Resources::Mixin do
943
984
  context 'when prior value already exists' do
944
985
  it 'simple value: removes' do
945
986
  path = [ 'userName' ]
946
- scim_hash = { 'userName' => 'bar' }
987
+ scim_hash = { 'userName' => 'bar' }.with_indifferent_case_insensitive_access()
947
988
 
948
989
  @instance.send(
949
990
  :from_patch_backend!,
@@ -958,7 +999,7 @@ RSpec.describe Scimitar::Resources::Mixin do
958
999
 
959
1000
  it 'nested simple value: removes' do
960
1001
  path = [ 'name', 'givenName' ]
961
- scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }
1002
+ scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }.with_indifferent_case_insensitive_access()
962
1003
 
963
1004
  @instance.send(
964
1005
  :from_patch_backend!,
@@ -986,7 +1027,7 @@ RSpec.describe Scimitar::Resources::Mixin do
986
1027
  'value' => 'work@test.com'
987
1028
  }
988
1029
  ]
989
- }
1030
+ }.with_indifferent_case_insensitive_access()
990
1031
 
991
1032
  @instance.send(
992
1033
  :from_patch_backend!,
@@ -1012,7 +1053,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1012
1053
  'primary' => true
1013
1054
  }
1014
1055
  ]
1015
- }
1056
+ }.with_indifferent_case_insensitive_access()
1016
1057
 
1017
1058
  @instance.send(
1018
1059
  :from_patch_backend!,
@@ -1039,7 +1080,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1039
1080
  'value' => 'work_2@test.com'
1040
1081
  }
1041
1082
  ]
1042
- }
1083
+ }.with_indifferent_case_insensitive_access()
1043
1084
 
1044
1085
  @instance.send(
1045
1086
  :from_patch_backend!,
@@ -1068,7 +1109,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1068
1109
  'value' => 'work@test.com'
1069
1110
  }
1070
1111
  ]
1071
- }
1112
+ }.with_indifferent_case_insensitive_access()
1072
1113
 
1073
1114
  @instance.send(
1074
1115
  :from_patch_backend!,
@@ -1094,7 +1135,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1094
1135
  'primary' => true
1095
1136
  }
1096
1137
  ]
1097
- }
1138
+ }.with_indifferent_case_insensitive_access()
1098
1139
 
1099
1140
  @instance.send(
1100
1141
  :from_patch_backend!,
@@ -1125,7 +1166,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1125
1166
  'value' => 'home@test.com'
1126
1167
  },
1127
1168
  ]
1128
- }
1169
+ }.with_indifferent_case_insensitive_access()
1129
1170
 
1130
1171
  @instance.send(
1131
1172
  :from_patch_backend!,
@@ -1149,7 +1190,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1149
1190
  'value' => 'home@test.com'
1150
1191
  }
1151
1192
  ]
1152
- }
1193
+ }.with_indifferent_case_insensitive_access()
1153
1194
 
1154
1195
  @instance.send(
1155
1196
  :from_patch_backend!,
@@ -1166,7 +1207,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1166
1207
  context 'when value is not present' do
1167
1208
  it 'simple value: does nothing' do
1168
1209
  path = [ 'userName' ]
1169
- scim_hash = {}
1210
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1170
1211
 
1171
1212
  @instance.send(
1172
1213
  :from_patch_backend!,
@@ -1181,7 +1222,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1181
1222
 
1182
1223
  it 'nested simple value: does nothing' do
1183
1224
  path = [ 'name', 'givenName' ]
1184
- scim_hash = { 'name' => {'familyName' => 'Bar' } }
1225
+ scim_hash = { 'name' => {'familyName' => 'Bar' } }.with_indifferent_case_insensitive_access()
1185
1226
 
1186
1227
  @instance.send(
1187
1228
  :from_patch_backend!,
@@ -1205,7 +1246,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1205
1246
  'value' => 'home@test.com'
1206
1247
  }
1207
1248
  ]
1208
- }
1249
+ }.with_indifferent_case_insensitive_access()
1209
1250
 
1210
1251
  @instance.send(
1211
1252
  :from_patch_backend!,
@@ -1227,7 +1268,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1227
1268
  'value' => 'home@test.com'
1228
1269
  }
1229
1270
  ]
1230
- }
1271
+ }.with_indifferent_case_insensitive_access()
1231
1272
 
1232
1273
  @instance.send(
1233
1274
  :from_patch_backend!,
@@ -1250,7 +1291,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1250
1291
  'value' => 'home@test.com'
1251
1292
  }
1252
1293
  ]
1253
- }
1294
+ }.with_indifferent_case_insensitive_access()
1254
1295
 
1255
1296
  @instance.send(
1256
1297
  :from_patch_backend!,
@@ -1268,7 +1309,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1268
1309
  context 'with filter at end of path' do
1269
1310
  it 'by string match: does nothing' do
1270
1311
  path = [ 'emails[type eq "work"]' ]
1271
- scim_hash = {}
1312
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1272
1313
 
1273
1314
  @instance.send(
1274
1315
  :from_patch_backend!,
@@ -1290,7 +1331,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1290
1331
  'primary' => false
1291
1332
  }
1292
1333
  ]
1293
- }
1334
+ }.with_indifferent_case_insensitive_access()
1294
1335
 
1295
1336
  @instance.send(
1296
1337
  :from_patch_backend!,
@@ -1307,7 +1348,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1307
1348
 
1308
1349
  it 'remove whole array: does nothing' do
1309
1350
  path = [ 'emails' ]
1310
- scim_hash = {}
1351
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1311
1352
 
1312
1353
  @instance.send(
1313
1354
  :from_patch_backend!,
@@ -1333,7 +1374,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1333
1374
  context 'when prior value already exists' do
1334
1375
  it 'simple value: overwrites' do
1335
1376
  path = [ 'userName' ]
1336
- scim_hash = { 'userName' => 'bar' }
1377
+ scim_hash = { 'userName' => 'bar' }.with_indifferent_case_insensitive_access()
1337
1378
 
1338
1379
  @instance.send(
1339
1380
  :from_patch_backend!,
@@ -1348,7 +1389,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1348
1389
 
1349
1390
  it 'nested simple value: overwrites' do
1350
1391
  path = [ 'name', 'givenName' ]
1351
- scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }
1392
+ scim_hash = { 'name' => { 'givenName' => 'Foo', 'familyName' => 'Bar' } }.with_indifferent_case_insensitive_access()
1352
1393
 
1353
1394
  @instance.send(
1354
1395
  :from_patch_backend!,
@@ -1376,18 +1417,18 @@ RSpec.describe Scimitar::Resources::Mixin do
1376
1417
  'value' => 'work@test.com'
1377
1418
  }
1378
1419
  ]
1379
- }
1420
+ }.with_indifferent_case_insensitive_access()
1380
1421
 
1381
1422
  @instance.send(
1382
1423
  :from_patch_backend!,
1383
1424
  nature: 'replace',
1384
1425
  path: path,
1385
- value: 'added_over_origina@test.com',
1426
+ value: 'added_over_original@test.com',
1386
1427
  altering_hash: scim_hash
1387
1428
  )
1388
1429
 
1389
1430
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1390
- expect(scim_hash['emails'][1]['value']).to eql('added_over_origina@test.com')
1431
+ expect(scim_hash['emails'][1]['value']).to eql('added_over_original@test.com')
1391
1432
  end
1392
1433
 
1393
1434
  it 'by boolean match: overwrites' do
@@ -1402,18 +1443,18 @@ RSpec.describe Scimitar::Resources::Mixin do
1402
1443
  'primary' => true
1403
1444
  }
1404
1445
  ]
1405
- }
1446
+ }.with_indifferent_case_insensitive_access()
1406
1447
 
1407
1448
  @instance.send(
1408
1449
  :from_patch_backend!,
1409
1450
  nature: 'replace',
1410
1451
  path: path,
1411
- value: 'added_over_origina@test.com',
1452
+ value: 'added_over_original@test.com',
1412
1453
  altering_hash: scim_hash
1413
1454
  )
1414
1455
 
1415
1456
  expect(scim_hash['emails'][0]['value']).to eql('home@test.com')
1416
- expect(scim_hash['emails'][1]['value']).to eql('added_over_origina@test.com')
1457
+ expect(scim_hash['emails'][1]['value']).to eql('added_over_original@test.com')
1417
1458
  end
1418
1459
 
1419
1460
  it 'multiple matches: overwrites all' do
@@ -1429,18 +1470,18 @@ RSpec.describe Scimitar::Resources::Mixin do
1429
1470
  'value' => 'work_2@test.com'
1430
1471
  }
1431
1472
  ]
1432
- }
1473
+ }.with_indifferent_case_insensitive_access()
1433
1474
 
1434
1475
  @instance.send(
1435
1476
  :from_patch_backend!,
1436
1477
  nature: 'replace',
1437
1478
  path: path,
1438
- value: 'added_over_origina@test.com',
1479
+ value: 'added_over_original@test.com',
1439
1480
  altering_hash: scim_hash
1440
1481
  )
1441
1482
 
1442
- expect(scim_hash['emails'][0]['value']).to eql('added_over_origina@test.com')
1443
- expect(scim_hash['emails'][1]['value']).to eql('added_over_origina@test.com')
1483
+ expect(scim_hash['emails'][0]['value']).to eql('added_over_original@test.com')
1484
+ expect(scim_hash['emails'][1]['value']).to eql('added_over_original@test.com')
1444
1485
  end
1445
1486
  end # "context 'with filter mid-path' do"
1446
1487
 
@@ -1458,7 +1499,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1458
1499
  'value' => 'work@test.com'
1459
1500
  }
1460
1501
  ]
1461
- }
1502
+ }.with_indifferent_case_insensitive_access()
1462
1503
 
1463
1504
  @instance.send(
1464
1505
  :from_patch_backend!,
@@ -1492,7 +1533,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1492
1533
  'value' => 'home@test.com'
1493
1534
  },
1494
1535
  ]
1495
- }
1536
+ }.with_indifferent_case_insensitive_access()
1496
1537
 
1497
1538
  @instance.send(
1498
1539
  :from_patch_backend!,
@@ -1521,7 +1562,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1521
1562
  'value' => 'home@test.com'
1522
1563
  }
1523
1564
  ]
1524
- }
1565
+ }.with_indifferent_case_insensitive_access()
1525
1566
 
1526
1567
  @instance.send(
1527
1568
  :from_patch_backend!,
@@ -1540,7 +1581,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1540
1581
  context 'when value is not present' do
1541
1582
  it 'simple value: adds' do
1542
1583
  path = [ 'userName' ]
1543
- scim_hash = {}
1584
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1544
1585
 
1545
1586
  @instance.send(
1546
1587
  :from_patch_backend!,
@@ -1555,7 +1596,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1555
1596
 
1556
1597
  it 'nested simple value: adds' do
1557
1598
  path = [ 'name', 'givenName' ]
1558
- scim_hash = {}
1599
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1559
1600
 
1560
1601
  @instance.send(
1561
1602
  :from_patch_backend!,
@@ -1581,7 +1622,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1581
1622
  'type' => 'work'
1582
1623
  }
1583
1624
  ]
1584
- }
1625
+ }.with_indifferent_case_insensitive_access()
1585
1626
 
1586
1627
  @instance.send(
1587
1628
  :from_patch_backend!,
@@ -1606,7 +1647,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1606
1647
  'primary' => true
1607
1648
  }
1608
1649
  ]
1609
- }
1650
+ }.with_indifferent_case_insensitive_access()
1610
1651
 
1611
1652
  @instance.send(
1612
1653
  :from_patch_backend!,
@@ -1631,7 +1672,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1631
1672
  'type' => 'work'
1632
1673
  }
1633
1674
  ]
1634
- }
1675
+ }.with_indifferent_case_insensitive_access()
1635
1676
 
1636
1677
  @instance.send(
1637
1678
  :from_patch_backend!,
@@ -1649,7 +1690,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1649
1690
  context 'with filter at end of path' do
1650
1691
  it 'by string match: adds item' do
1651
1692
  path = [ 'emails[type eq "work"]' ]
1652
- scim_hash = {}
1693
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1653
1694
 
1654
1695
  @instance.send(
1655
1696
  :from_patch_backend!,
@@ -1673,7 +1714,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1673
1714
  'primary' => false
1674
1715
  }
1675
1716
  ]
1676
- }
1717
+ }.with_indifferent_case_insensitive_access()
1677
1718
 
1678
1719
  @instance.send(
1679
1720
  :from_patch_backend!,
@@ -1692,7 +1733,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1692
1733
 
1693
1734
  it 'with arrays: replaces' do
1694
1735
  path = [ 'emails' ]
1695
- scim_hash = {}
1736
+ scim_hash = {}.with_indifferent_case_insensitive_access()
1696
1737
 
1697
1738
  @instance.send(
1698
1739
  :from_patch_backend!,
@@ -1813,7 +1854,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1813
1854
  end
1814
1855
 
1815
1856
  it 'adds across multiple deep matching points' do
1816
- scim_hash = @original_hash.deep_dup()
1857
+ scim_hash = @original_hash.deep_dup().with_indifferent_case_insensitive_access()
1817
1858
  contrived_instance = @contrived_class.new
1818
1859
  contrived_instance.send(
1819
1860
  :from_patch_backend!,
@@ -1836,7 +1877,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1836
1877
  end
1837
1878
 
1838
1879
  it 'replaces across multiple deep matching points' do
1839
- scim_hash = @original_hash.deep_dup()
1880
+ scim_hash = @original_hash.deep_dup().with_indifferent_case_insensitive_access()
1840
1881
  contrived_instance = @contrived_class.new
1841
1882
  contrived_instance.send(
1842
1883
  :from_patch_backend!,
@@ -1861,7 +1902,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1861
1902
  end
1862
1903
 
1863
1904
  it 'removes across multiple deep matching points' do
1864
- scim_hash = @original_hash.deep_dup()
1905
+ scim_hash = @original_hash.deep_dup().with_indifferent_case_insensitive_access()
1865
1906
  contrived_instance = @contrived_class.new
1866
1907
  contrived_instance.send(
1867
1908
  :from_patch_backend!,
@@ -1903,7 +1944,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1903
1944
  'value' => 'work_2@test.com'
1904
1945
  }
1905
1946
  ]
1906
- }
1947
+ }.with_indifferent_case_insensitive_access()
1907
1948
 
1908
1949
  expect do
1909
1950
  @instance.send(
@@ -1920,7 +1961,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1920
1961
  path = [ 'userName[type eq "work"]', 'value' ]
1921
1962
  scim_hash = {
1922
1963
  'userName' => '1234'
1923
- }
1964
+ }.with_indifferent_case_insensitive_access()
1924
1965
 
1925
1966
  expect do
1926
1967
  @instance.send(
@@ -1940,7 +1981,7 @@ RSpec.describe Scimitar::Resources::Mixin do
1940
1981
  'work_1@test.com',
1941
1982
  'work_2@test.com',
1942
1983
  ]
1943
- }
1984
+ }.with_indifferent_case_insensitive_access()
1944
1985
 
1945
1986
  expect do
1946
1987
  @instance.send(
@@ -1961,166 +2002,202 @@ RSpec.describe Scimitar::Resources::Mixin do
1961
2002
  # -------------------------------------------------------------------
1962
2003
  #
1963
2004
  context 'public interface' do
1964
- it 'updates simple values' do
1965
- @instance.update!(username: 'foo')
2005
+ shared_examples 'a patcher' do | force_upper_case: |
2006
+ it 'which updates simple values' do
2007
+ @instance.update!(username: 'foo')
2008
+
2009
+ path = 'userName'
2010
+ path = path.upcase if force_upper_case
2011
+
2012
+ patch = {
2013
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2014
+ 'Operations' => [
2015
+ {
2016
+ 'op' => 'replace',
2017
+ 'path' => path,
2018
+ 'value' => '1234'
2019
+ }
2020
+ ]
2021
+ }
1966
2022
 
1967
- patch = {
1968
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
1969
- 'Operations' => [
1970
- {
1971
- 'op' => 'replace',
1972
- 'path' => 'userName',
1973
- 'value' => '1234'
1974
- }
1975
- ]
1976
- }
2023
+ @instance.from_scim_patch!(patch_hash: patch)
2024
+ expect(@instance.username).to eql('1234')
2025
+ end
1977
2026
 
1978
- @instance.from_scim_patch!(patch_hash: patch)
1979
- expect(@instance.username).to eql('1234')
1980
- end
2027
+ it 'which updates nested values' do
2028
+ @instance.update!(first_name: 'Foo', last_name: 'Bar')
1981
2029
 
1982
- it 'updates nested values' do
1983
- @instance.update!(first_name: 'Foo', last_name: 'Bar')
2030
+ path = 'name.givenName'
2031
+ path = path.upcase if force_upper_case
1984
2032
 
1985
- patch = {
1986
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
1987
- 'Operations' => [
1988
- {
1989
- 'op' => 'replace',
1990
- 'path' => 'name.givenName',
1991
- 'value' => 'Baz'
1992
- }
1993
- ]
1994
- }
2033
+ patch = {
2034
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2035
+ 'Operations' => [
2036
+ {
2037
+ 'op' => 'replace',
2038
+ 'path' => path,
2039
+ 'value' => 'Baz'
2040
+ }
2041
+ ]
2042
+ }
1995
2043
 
1996
- @instance.from_scim_patch!(patch_hash: patch)
1997
- expect(@instance.first_name).to eql('Baz')
1998
- end
2044
+ @instance.from_scim_patch!(patch_hash: patch)
2045
+ expect(@instance.first_name).to eql('Baz')
2046
+ end
1999
2047
 
2000
- it 'updates with filter match' do
2001
- @instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
2048
+ it 'which updates with filter match' do
2049
+ @instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
2002
2050
 
2003
- patch = {
2004
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2005
- 'Operations' => [
2006
- {
2007
- 'op' => 'replace',
2008
- 'path' => 'emails[type eq "work"].value',
2009
- 'value' => 'replaced@test.com'
2010
- }
2011
- ]
2012
- }
2051
+ filter_prefix = 'emails[type'
2052
+ filter_prefix = filter_prefix.upcase if force_upper_case
2013
2053
 
2014
- @instance.from_scim_patch!(patch_hash: patch)
2015
- expect(@instance.work_email_address).to eql('replaced@test.com')
2016
- expect(@instance.home_email_address).to eql('home@test.com')
2017
- end
2054
+ patch = {
2055
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2056
+ 'Operations' => [
2057
+ {
2058
+ 'op' => 'replace',
2059
+ 'path' => filter_prefix + ' eq "work"].value',
2060
+ 'value' => 'replaced@test.com'
2061
+ }
2062
+ ]
2063
+ }
2018
2064
 
2019
- it 'appends e-mails' do
2020
- @instance.update!(work_email_address: 'work@test.com')
2065
+ @instance.from_scim_patch!(patch_hash: patch)
2066
+ expect(@instance.work_email_address).to eql('replaced@test.com')
2067
+ expect(@instance.home_email_address).to eql('home@test.com')
2068
+ end
2021
2069
 
2022
- patch = {
2023
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2024
- 'Operations' => [
2025
- {
2026
- 'op' => 'add',
2027
- 'path' => 'emails[type eq "home"].value',
2028
- 'value' => 'home@test.com'
2029
- }
2030
- ]
2031
- }
2070
+ it 'which appends e-mails' do
2071
+ @instance.update!(work_email_address: 'work@test.com')
2032
2072
 
2033
- @instance.from_scim_patch!(patch_hash: patch)
2034
- expect(@instance.work_email_address).to eql('work@test.com')
2035
- expect(@instance.home_email_address).to eql('home@test.com')
2036
- end
2073
+ filter_prefix = 'emails[type'
2074
+ filter_prefix = filter_prefix.upcase if force_upper_case
2037
2075
 
2038
- it 'removes e-mails' do
2039
- @instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
2076
+ patch = {
2077
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2078
+ 'Operations' => [
2079
+ {
2080
+ 'op' => 'add',
2081
+ 'path' => filter_prefix + ' eq "home"].value',
2082
+ 'value' => 'home@test.com'
2083
+ }
2084
+ ]
2085
+ }
2040
2086
 
2041
- patch = {
2042
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2043
- 'Operations' => [
2044
- {
2045
- 'op' => 'remove',
2046
- 'path' => 'emails[type eq "home"]'
2047
- }
2048
- ]
2049
- }
2087
+ @instance.from_scim_patch!(patch_hash: patch)
2088
+ expect(@instance.work_email_address).to eql('work@test.com')
2089
+ expect(@instance.home_email_address).to eql('home@test.com')
2090
+ end
2050
2091
 
2051
- @instance.from_scim_patch!(patch_hash: patch)
2052
- expect(@instance.work_email_address).to eql('work@test.com')
2053
- expect(@instance.home_email_address).to be_nil
2054
- end
2092
+ it 'which removes e-mails' do
2093
+ @instance.update!(work_email_address: 'work@test.com', home_email_address: 'home@test.com')
2055
2094
 
2056
- it 'can patch the whole object' do
2057
- @instance.update!(username: 'foo')
2095
+ filter_prefix = 'emails[type'
2096
+ filter_prefix = filter_prefix.upcase if force_upper_case
2058
2097
 
2059
- patch = {
2060
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2061
- 'Operations' => [
2062
- {
2063
- 'op' => 'replace',
2064
- 'value' => {'userName' => '1234', 'name' => {'givenName' => 'Bar'}}
2065
- }
2066
- ]
2067
- }
2098
+ patch = {
2099
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2100
+ 'Operations' => [
2101
+ {
2102
+ 'op' => 'remove',
2103
+ 'path' => filter_prefix + ' eq "home"].value',
2104
+ }
2105
+ ]
2106
+ }
2068
2107
 
2069
- @instance.from_scim_patch!(patch_hash: patch)
2070
- expect(@instance.username).to eql('1234')
2071
- expect(@instance.first_name).to eql('Bar')
2072
- end
2108
+ @instance.from_scim_patch!(patch_hash: patch)
2109
+ expect(@instance.work_email_address).to eql('work@test.com')
2110
+ expect(@instance.home_email_address).to be_nil
2111
+ end
2073
2112
 
2074
- it 'treats operation types as case-insensitive' do
2075
- @instance.update!(username: 'foo')
2113
+ it 'which can patch the whole object' do
2114
+ @instance.update!(username: 'foo')
2076
2115
 
2077
- patch = {
2078
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2079
- 'Operations' => [
2080
- {
2081
- 'op' => 'REPLACE', # Note upper case
2082
- 'path' => 'userName',
2083
- 'value' => '1234'
2116
+ hash = {
2117
+ 'userName' => '1234',
2118
+ 'name' => {
2119
+ 'givenName' => 'Bar'
2084
2120
  }
2085
- ]
2086
- }
2121
+ }
2087
2122
 
2088
- @instance.from_scim_patch!(patch_hash: patch)
2089
- expect(@instance.username).to eql('1234')
2090
- end
2123
+ hash = spec_helper_hupcase(hash) if force_upper_case
2091
2124
 
2092
- it 'complains about bad operation types' do
2093
- patch = {
2094
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2095
- 'Operations' => [
2096
- {
2097
- 'op' => 'invalidop',
2098
- 'path' => 'userName',
2099
- 'value' => '1234'
2100
- }
2101
- ]
2102
- }
2125
+ patch = {
2126
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2127
+ 'Operations' => [
2128
+ {
2129
+ 'op' => 'replace',
2130
+ 'value' => hash
2131
+ }
2132
+ ]
2133
+ }
2103
2134
 
2104
- expect { @instance.from_scim_patch!(patch_hash: patch) }.to raise_error(Scimitar::ErrorResponse) do |e|
2105
- expect(e.as_json['scimType']).to eql('invalidSyntax')
2106
- expect(e.as_json[:detail ]).to include('invalidop')
2135
+ @instance.from_scim_patch!(patch_hash: patch)
2136
+ expect(@instance.username).to eql('1234')
2137
+ expect(@instance.first_name).to eql('Bar')
2107
2138
  end
2108
- end
2139
+ end # "shared_examples 'a patcher' do | force_upper_case: |"
2109
2140
 
2110
- it 'complains about a missing target for "remove" operations' do
2111
- patch = {
2112
- 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2113
- 'Operations' => [
2114
- {
2115
- 'op' => 'remove'
2116
- }
2117
- ]
2118
- }
2141
+ context 'using schema-matched case' do
2142
+ it_behaves_like 'a patcher', force_upper_case: false
2143
+ end # "context 'using schema-matched case' do"
2144
+
2145
+ context 'using upper case' do
2146
+ it_behaves_like 'a patcher', force_upper_case: true
2147
+
2148
+ it 'treats operation types as case-insensitive' do
2149
+ @instance.update!(username: 'foo')
2119
2150
 
2120
- expect { @instance.from_scim_patch!(patch_hash: patch) }.to raise_error(Scimitar::ErrorResponse) do |e|
2121
- expect(e.as_json['scimType']).to eql('noTarget')
2151
+ patch = {
2152
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2153
+ 'Operations' => [
2154
+ {
2155
+ 'op' => 'REPLACE', # Note upper case
2156
+ 'path' => 'userName',
2157
+ 'value' => '1234'
2158
+ }
2159
+ ]
2160
+ }
2161
+
2162
+ @instance.from_scim_patch!(patch_hash: patch)
2163
+ expect(@instance.username).to eql('1234')
2122
2164
  end
2123
- end
2165
+ end # "context 'using upper case' do"
2166
+
2167
+ context 'with errors' do
2168
+ it 'complains about bad operation types' do
2169
+ patch = {
2170
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2171
+ 'Operations' => [
2172
+ {
2173
+ 'op' => 'invalidop',
2174
+ 'path' => 'userName',
2175
+ 'value' => '1234'
2176
+ }
2177
+ ]
2178
+ }
2179
+
2180
+ expect { @instance.from_scim_patch!(patch_hash: patch) }.to raise_error(Scimitar::ErrorResponse) do |e|
2181
+ expect(e.as_json['scimType']).to eql('invalidSyntax')
2182
+ expect(e.as_json[:detail ]).to include('invalidop')
2183
+ end
2184
+ end
2185
+
2186
+ it 'complains about a missing target for "remove" operations' do
2187
+ patch = {
2188
+ 'schemas' => ['urn:ietf:params:scim:api:messages:2.0:PatchOp'],
2189
+ 'Operations' => [
2190
+ {
2191
+ 'op' => 'remove'
2192
+ }
2193
+ ]
2194
+ }
2195
+
2196
+ expect { @instance.from_scim_patch!(patch_hash: patch) }.to raise_error(Scimitar::ErrorResponse) do |e|
2197
+ expect(e.as_json['scimType']).to eql('noTarget')
2198
+ end
2199
+ end
2200
+ end # "context 'with errors' do"
2124
2201
  end # "context 'public interface' do"
2125
2202
  end # "context '#from_scim_patch!' do"
2126
2203
  end # "context 'with good class definitons' do"