scimitar 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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"