scimitar 1.5.2 → 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.
- checksums.yaml +4 -4
 - data/app/controllers/scimitar/active_record_backed_resources_controller.rb +6 -27
 - data/app/controllers/scimitar/application_controller.rb +9 -29
 - data/app/models/scimitar/engine_configuration.rb +3 -7
 - data/app/models/scimitar/error_response.rb +0 -12
 - data/app/models/scimitar/errors.rb +1 -1
 - data/app/models/scimitar/lists/query_parser.rb +4 -14
 - data/app/models/scimitar/resources/base.rb +1 -1
 - data/app/models/scimitar/resources/mixin.rb +4 -113
 - data/app/models/scimitar/schema/address.rb +0 -1
 - data/app/models/scimitar/schema/attribute.rb +1 -1
 - data/app/models/scimitar/schema/base.rb +3 -1
 - data/app/models/scimitar/schema/vdtp.rb +1 -1
 - data/config/initializers/scimitar.rb +70 -86
 - data/lib/scimitar/version.rb +2 -2
 - data/spec/apps/dummy/app/controllers/mock_groups_controller.rb +1 -1
 - data/spec/apps/dummy/app/models/mock_group.rb +1 -1
 - data/spec/apps/dummy/app/models/mock_user.rb +8 -19
 - data/spec/apps/dummy/config/application.rb +1 -0
 - data/spec/apps/dummy/config/environments/test.rb +28 -5
 - data/spec/apps/dummy/config/initializers/scimitar.rb +9 -44
 - data/spec/apps/dummy/config/routes.rb +0 -4
 - data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -9
 - data/spec/apps/dummy/db/migrate/20210308044214_create_join_table_mock_groups_mock_users.rb +3 -8
 - data/spec/apps/dummy/db/schema.rb +4 -10
 - data/spec/controllers/scimitar/application_controller_spec.rb +1 -70
 - data/spec/controllers/scimitar/schemas_controller_spec.rb +2 -2
 - data/spec/models/scimitar/complex_types/email_spec.rb +2 -0
 - data/spec/models/scimitar/lists/query_parser_spec.rb +9 -9
 - data/spec/models/scimitar/resources/base_spec.rb +66 -161
 - data/spec/models/scimitar/resources/base_validation_spec.rb +2 -27
 - data/spec/models/scimitar/resources/mixin_spec.rb +43 -757
 - data/spec/models/scimitar/resources/user_spec.rb +4 -4
 - data/spec/models/scimitar/schema/attribute_spec.rb +3 -0
 - data/spec/models/scimitar/schema/base_spec.rb +1 -1
 - data/spec/models/scimitar/schema/user_spec.rb +0 -10
 - data/spec/requests/active_record_backed_resources_controller_spec.rb +40 -309
 - data/spec/requests/application_controller_spec.rb +3 -17
 - metadata +7 -7
 
| 
         @@ -42,25 +42,25 @@ RSpec.describe Scimitar::Resources::User do 
     | 
|
| 
       42 
42 
     | 
    
         
             
                let(:user) { described_class.new }
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
44 
     | 
    
         
             
                it 'adds the error when the value is a string' do
         
     | 
| 
       45 
     | 
    
         
            -
                  user.add_errors_from_hash({key: 'some error'})
         
     | 
| 
      
 45 
     | 
    
         
            +
                  user.add_errors_from_hash(errors_hash: {key: 'some error'})
         
     | 
| 
       46 
46 
     | 
    
         
             
                  expect(user.errors.messages.to_h).to eql({key: ['some error']})
         
     | 
| 
       47 
47 
     | 
    
         
             
                  expect(user.errors.full_messages).to eql(['Key some error'])
         
     | 
| 
       48 
48 
     | 
    
         
             
                end
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
                it 'adds the error when the value is an array' do
         
     | 
| 
       51 
     | 
    
         
            -
                  user.add_errors_from_hash({key: ['error1', 'error2']})
         
     | 
| 
      
 51 
     | 
    
         
            +
                  user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']})
         
     | 
| 
       52 
52 
     | 
    
         
             
                  expect(user.errors.messages.to_h).to eql({key: ['error1', 'error2']})
         
     | 
| 
       53 
53 
     | 
    
         
             
                  expect(user.errors.full_messages).to eql(['Key error1', 'Key error2'])
         
     | 
| 
       54 
54 
     | 
    
         
             
                end
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
       56 
56 
     | 
    
         
             
                it 'adds the error with prefix when the value is a string' do
         
     | 
| 
       57 
     | 
    
         
            -
                  user.add_errors_from_hash({key: 'some error'}, prefix: :pre)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  user.add_errors_from_hash(errors_hash: {key: 'some error'}, prefix: :pre)
         
     | 
| 
       58 
58 
     | 
    
         
             
                  expect(user.errors.messages.to_h).to eql({:'pre.key' => ['some error']})
         
     | 
| 
       59 
59 
     | 
    
         
             
                  expect(user.errors.full_messages).to eql(['Pre key some error'])
         
     | 
| 
       60 
60 
     | 
    
         
             
                end
         
     | 
| 
       61 
61 
     | 
    
         | 
| 
       62 
62 
     | 
    
         
             
                it 'adds the error wity prefix when the value is an array' do
         
     | 
| 
       63 
     | 
    
         
            -
                  user.add_errors_from_hash({key: ['error1', 'error2']}, prefix: :pre)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  user.add_errors_from_hash(errors_hash: {key: ['error1', 'error2']}, prefix: :pre)
         
     | 
| 
       64 
64 
     | 
    
         
             
                  expect(user.errors.messages.to_h).to eql({:'pre.key' => ['error1', 'error2']})
         
     | 
| 
       65 
65 
     | 
    
         
             
                  expect(user.errors.full_messages).to eql(['Pre key error1', 'Pre key error2'])
         
     | 
| 
       66 
66 
     | 
    
         
             
                end
         
     | 
| 
         @@ -21,8 +21,10 @@ RSpec.describe Scimitar::Schema::Attribute do 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  expect(name.type).to eql('complex')
         
     | 
| 
       22 
22 
     | 
    
         
             
                  expect(name.subAttributes).to eql(Scimitar::Schema::Name.scim_attributes)
         
     | 
| 
       23 
23 
     | 
    
         
             
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
       24 
25 
     | 
    
         
             
              end
         
     | 
| 
       25 
26 
     | 
    
         | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       26 
28 
     | 
    
         
             
              context '#valid?' do
         
     | 
| 
       27 
29 
     | 
    
         
             
                it 'is invalid if attribute is required but value is blank' do
         
     | 
| 
       28 
30 
     | 
    
         
             
                  attribute = described_class.new(name: 'userName', type: 'string', required: true)
         
     | 
| 
         @@ -74,4 +76,5 @@ RSpec.describe Scimitar::Schema::Attribute do 
     | 
|
| 
       74 
76 
     | 
    
         
             
                  expect(described_class.new(name: 'startDate', type: 'dateTime').valid?('gaga')).to be(false)
         
     | 
| 
       75 
77 
     | 
    
         
             
                end
         
     | 
| 
       76 
78 
     | 
    
         
             
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
       77 
80 
     | 
    
         
             
            end
         
     | 
| 
         @@ -419,16 +419,6 @@ RSpec.describe Scimitar::Schema::User do 
     | 
|
| 
       419 
419 
     | 
    
         
             
                        "name": "type",
         
     | 
| 
       420 
420 
     | 
    
         
             
                        "type": "string"
         
     | 
| 
       421 
421 
     | 
    
         
             
                      },
         
     | 
| 
       422 
     | 
    
         
            -
                      {
         
     | 
| 
       423 
     | 
    
         
            -
                        "multiValued": false,
         
     | 
| 
       424 
     | 
    
         
            -
                        "required": false,
         
     | 
| 
       425 
     | 
    
         
            -
                        "caseExact": false,
         
     | 
| 
       426 
     | 
    
         
            -
                        "mutability": "readWrite",
         
     | 
| 
       427 
     | 
    
         
            -
                        "uniqueness": "none",
         
     | 
| 
       428 
     | 
    
         
            -
                        "returned": "default",
         
     | 
| 
       429 
     | 
    
         
            -
                        "name": "primary",
         
     | 
| 
       430 
     | 
    
         
            -
                        "type": "boolean"
         
     | 
| 
       431 
     | 
    
         
            -
                      },
         
     | 
| 
       432 
422 
     | 
    
         
             
                      {
         
     | 
| 
       433 
423 
     | 
    
         
             
                        "multiValued": false,
         
     | 
| 
       434 
424 
     | 
    
         
             
                        "required": false,
         
     | 
| 
         @@ -1,27 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'spec_helper'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'time'
         
     | 
| 
       3 
2 
     | 
    
         | 
| 
       4 
3 
     | 
    
         
             
            RSpec.describe Scimitar::ActiveRecordBackedResourcesController do
         
     | 
| 
       5 
4 
     | 
    
         
             
              before :each do
         
     | 
| 
       6 
5 
     | 
    
         
             
                allow_any_instance_of(Scimitar::ApplicationController).to receive(:authenticated?).and_return(true)
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                 
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
       11 
     | 
    
         
            -
                # With UUID based IDs, testing life is made easier by ensuring that the
         
     | 
| 
       12 
     | 
    
         
            -
                # creation order matches an ascending UUID sort order (which is what would
         
     | 
| 
       13 
     | 
    
         
            -
                # happen if we were using integer primary keys).
         
     | 
| 
       14 
     | 
    
         
            -
                #
         
     | 
| 
       15 
     | 
    
         
            -
                lmt = Time.parse("2023-01-09 14:25:00 +1300")
         
     | 
| 
       16 
     | 
    
         
            -
                ids = 3.times.map { SecureRandom.uuid }.sort()
         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                @u1 = MockUser.create(primary_key: ids.shift(), username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com', scim_uid: '001', created_at: lmt, updated_at: lmt + 1)
         
     | 
| 
       19 
     | 
    
         
            -
                @u2 = MockUser.create(primary_key: ids.shift(), username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com', scim_uid: '002', created_at: lmt, updated_at: lmt + 2)
         
     | 
| 
       20 
     | 
    
         
            -
                @u3 = MockUser.create(primary_key: ids.shift(), username: '3', first_name: 'Foo',                   home_email_address: 'home_3@test.com', scim_uid: '003', created_at: lmt, updated_at: lmt + 3)
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                @g1 = MockGroup.create!(display_name: 'Group 1')
         
     | 
| 
       23 
     | 
    
         
            -
                @g2 = MockGroup.create!(display_name: 'Group 2')
         
     | 
| 
       24 
     | 
    
         
            -
                @g3 = MockGroup.create!(display_name: 'Group 3')
         
     | 
| 
      
 7 
     | 
    
         
            +
                @u1 = MockUser.create(username: '1', first_name: 'Foo', last_name: 'Ark', home_email_address: 'home_1@test.com')
         
     | 
| 
      
 8 
     | 
    
         
            +
                @u2 = MockUser.create(username: '2', first_name: 'Foo', last_name: 'Bar', home_email_address: 'home_2@test.com')
         
     | 
| 
      
 9 
     | 
    
         
            +
                @u3 = MockUser.create(username: '3', first_name: 'Foo',                   home_email_address: 'home_3@test.com')
         
     | 
| 
       25 
10 
     | 
    
         
             
              end
         
     | 
| 
       26 
11 
     | 
    
         | 
| 
       27 
12 
     | 
    
         
             
              # ===========================================================================
         
     | 
| 
         @@ -44,65 +29,26 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       44 
29 
     | 
    
         
             
                end # "context 'with no items' do"
         
     | 
| 
       45 
30 
     | 
    
         | 
| 
       46 
31 
     | 
    
         
             
                context 'with items' do
         
     | 
| 
       47 
     | 
    
         
            -
                   
     | 
| 
       48 
     | 
    
         
            -
                     
     | 
| 
       49 
     | 
    
         
            -
                      get '/Users', params: { format: :scim }
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                      expect(response.status).to eql(200)
         
     | 
| 
       52 
     | 
    
         
            -
                      result = JSON.parse(response.body)
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                      expect(result['totalResults']).to eql(3)
         
     | 
| 
       55 
     | 
    
         
            -
                      expect(result['Resources'].size).to eql(3)
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
                      ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       58 
     | 
    
         
            -
                      expect(ids).to match_array([@u1.primary_key.to_s, @u2.primary_key.to_s, @u3.primary_key.to_s])
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                      usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       61 
     | 
    
         
            -
                      expect(usernames).to match_array(['1', '2', '3'])
         
     | 
| 
       62 
     | 
    
         
            -
                    end
         
     | 
| 
       63 
     | 
    
         
            -
                  end # "context 'with a UUID, renamed primary key column' do"
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                  context 'with an integer, conventionally named primary key column' do
         
     | 
| 
       66 
     | 
    
         
            -
                    it 'returns all items' do
         
     | 
| 
       67 
     | 
    
         
            -
                      get '/Groups', params: { format: :scim }
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                      expect(response.status).to eql(200)
         
     | 
| 
       70 
     | 
    
         
            -
                      result = JSON.parse(response.body)
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                      expect(result['totalResults']).to eql(3)
         
     | 
| 
       73 
     | 
    
         
            -
                      expect(result['Resources'].size).to eql(3)
         
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
       75 
     | 
    
         
            -
                      ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       76 
     | 
    
         
            -
                      expect(ids).to match_array([@g1.id.to_s, @g2.id.to_s, @g3.id.to_s])
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
       78 
     | 
    
         
            -
                      usernames = result['Resources'].map { |resource| resource['displayName'] }
         
     | 
| 
       79 
     | 
    
         
            -
                      expect(usernames).to match_array(['Group 1', 'Group 2', 'Group 3'])
         
     | 
| 
       80 
     | 
    
         
            -
                    end
         
     | 
| 
       81 
     | 
    
         
            -
                  end # "context 'with an integer, conventionally named primary key column' do"
         
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
                  it 'applies a filter, with case-insensitive value comparison' do
         
     | 
| 
       84 
     | 
    
         
            -
                    get '/Users', params: {
         
     | 
| 
       85 
     | 
    
         
            -
                      format: :scim,
         
     | 
| 
       86 
     | 
    
         
            -
                      filter: 'name.givenName eq "FOO" and name.familyName pr and emails ne "home_1@test.com"'
         
     | 
| 
       87 
     | 
    
         
            -
                    }
         
     | 
| 
      
 32 
     | 
    
         
            +
                  it 'returns all items' do
         
     | 
| 
      
 33 
     | 
    
         
            +
                    get '/Users', params: { format: :scim }
         
     | 
| 
       88 
34 
     | 
    
         | 
| 
       89 
35 
     | 
    
         
             
                    expect(response.status).to eql(200)
         
     | 
| 
       90 
36 
     | 
    
         
             
                    result = JSON.parse(response.body)
         
     | 
| 
       91 
37 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
                    expect(result['totalResults']).to eql( 
     | 
| 
       93 
     | 
    
         
            -
                    expect(result['Resources'].size).to eql( 
     | 
| 
      
 38 
     | 
    
         
            +
                    expect(result['totalResults']).to eql(3)
         
     | 
| 
      
 39 
     | 
    
         
            +
                    expect(result['Resources'].size).to eql(3)
         
     | 
| 
       94 
40 
     | 
    
         | 
| 
       95 
41 
     | 
    
         
             
                    ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       96 
     | 
    
         
            -
                    expect(ids).to match_array([@u2. 
     | 
| 
      
 42 
     | 
    
         
            +
                    expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s, @u3.id.to_s])
         
     | 
| 
       97 
43 
     | 
    
         | 
| 
       98 
44 
     | 
    
         
             
                    usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       99 
     | 
    
         
            -
                    expect(usernames).to match_array(['2'])
         
     | 
| 
      
 45 
     | 
    
         
            +
                    expect(usernames).to match_array(['1', '2', '3'])
         
     | 
| 
       100 
46 
     | 
    
         
             
                  end
         
     | 
| 
       101 
47 
     | 
    
         | 
| 
       102 
     | 
    
         
            -
                  it 'applies a filter, with case-insensitive  
     | 
| 
      
 48 
     | 
    
         
            +
                  it 'applies a filter, with case-insensitive value comparison' do
         
     | 
| 
       103 
49 
     | 
    
         
             
                    get '/Users', params: {
         
     | 
| 
       104 
50 
     | 
    
         
             
                      format: :scim,
         
     | 
| 
       105 
     | 
    
         
            -
                      filter: 'name. 
     | 
| 
      
 51 
     | 
    
         
            +
                      filter: 'name.givenName eq "Foo" and name.familyName pr and emails ne "home_1@TEST.COM"'
         
     | 
| 
       106 
52 
     | 
    
         
             
                    }
         
     | 
| 
       107 
53 
     | 
    
         | 
| 
       108 
54 
     | 
    
         
             
                    expect(response.status).to eql(200)
         
     | 
| 
         @@ -112,74 +58,12 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       112 
58 
     | 
    
         
             
                    expect(result['Resources'].size).to eql(1)
         
     | 
| 
       113 
59 
     | 
    
         | 
| 
       114 
60 
     | 
    
         
             
                    ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       115 
     | 
    
         
            -
                    expect(ids).to match_array([@u2. 
     | 
| 
      
 61 
     | 
    
         
            +
                    expect(ids).to match_array([@u2.id.to_s])
         
     | 
| 
       116 
62 
     | 
    
         | 
| 
       117 
63 
     | 
    
         
             
                    usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       118 
64 
     | 
    
         
             
                    expect(usernames).to match_array(['2'])
         
     | 
| 
       119 
65 
     | 
    
         
             
                  end
         
     | 
| 
       120 
66 
     | 
    
         | 
| 
       121 
     | 
    
         
            -
                  # Strange attribute capitalisation in tests here builds on test coverage
         
     | 
| 
       122 
     | 
    
         
            -
                  # for now-fixed GitHub issue #37.
         
     | 
| 
       123 
     | 
    
         
            -
                  #
         
     | 
| 
       124 
     | 
    
         
            -
                  context '"meta" / IDs (GitHub issue #36)' do
         
     | 
| 
       125 
     | 
    
         
            -
                    it 'applies a filter on primary keys, using direct comparison (rather than e.g. case-insensitive operators)' do
         
     | 
| 
       126 
     | 
    
         
            -
                      get '/Users', params: {
         
     | 
| 
       127 
     | 
    
         
            -
                        format: :scim,
         
     | 
| 
       128 
     | 
    
         
            -
                        filter: "id eq \"#{@u3.primary_key}\""
         
     | 
| 
       129 
     | 
    
         
            -
                      }
         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
                      expect(response.status).to eql(200)
         
     | 
| 
       132 
     | 
    
         
            -
                      result = JSON.parse(response.body)
         
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
                      expect(result['totalResults']).to eql(1)
         
     | 
| 
       135 
     | 
    
         
            -
                      expect(result['Resources'].size).to eql(1)
         
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
                      ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       138 
     | 
    
         
            -
                      expect(ids).to match_array([@u3.primary_key.to_s])
         
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
                      usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       141 
     | 
    
         
            -
                      expect(usernames).to match_array(['3'])
         
     | 
| 
       142 
     | 
    
         
            -
                    end
         
     | 
| 
       143 
     | 
    
         
            -
             
     | 
| 
       144 
     | 
    
         
            -
                    it 'applies a filter on external IDs, using direct comparison' do
         
     | 
| 
       145 
     | 
    
         
            -
                      get '/Users', params: {
         
     | 
| 
       146 
     | 
    
         
            -
                        format: :scim,
         
     | 
| 
       147 
     | 
    
         
            -
                        filter: "externalID eq \"#{@u2.scim_uid}\""
         
     | 
| 
       148 
     | 
    
         
            -
                      }
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
                      expect(response.status).to eql(200)
         
     | 
| 
       151 
     | 
    
         
            -
                      result = JSON.parse(response.body)
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                      expect(result['totalResults']).to eql(1)
         
     | 
| 
       154 
     | 
    
         
            -
                      expect(result['Resources'].size).to eql(1)
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                      ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       157 
     | 
    
         
            -
                      expect(ids).to match_array([@u2.primary_key.to_s])
         
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
                      usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       160 
     | 
    
         
            -
                      expect(usernames).to match_array(['2'])
         
     | 
| 
       161 
     | 
    
         
            -
                    end
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                    it 'applies a filter on "meta" entries, using direct comparison' do
         
     | 
| 
       164 
     | 
    
         
            -
                      get '/Users', params: {
         
     | 
| 
       165 
     | 
    
         
            -
                        format: :scim,
         
     | 
| 
       166 
     | 
    
         
            -
                        filter: "Meta.LastModified eq \"#{@u3.updated_at}\""
         
     | 
| 
       167 
     | 
    
         
            -
                      }
         
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
                      expect(response.status).to eql(200)
         
     | 
| 
       170 
     | 
    
         
            -
                      result = JSON.parse(response.body)
         
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
                      expect(result['totalResults']).to eql(1)
         
     | 
| 
       173 
     | 
    
         
            -
                      expect(result['Resources'].size).to eql(1)
         
     | 
| 
       174 
     | 
    
         
            -
             
     | 
| 
       175 
     | 
    
         
            -
                      ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       176 
     | 
    
         
            -
                      expect(ids).to match_array([@u3.primary_key.to_s])
         
     | 
| 
       177 
     | 
    
         
            -
             
     | 
| 
       178 
     | 
    
         
            -
                      usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       179 
     | 
    
         
            -
                      expect(usernames).to match_array(['3'])
         
     | 
| 
       180 
     | 
    
         
            -
                    end
         
     | 
| 
       181 
     | 
    
         
            -
                  end # "context '"meta" / IDs (GitHub issue #36)' do"
         
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
       183 
67 
     | 
    
         
             
                  it 'obeys a page size' do
         
     | 
| 
       184 
68 
     | 
    
         
             
                    get '/Users', params: {
         
     | 
| 
       185 
69 
     | 
    
         
             
                      format: :scim,
         
     | 
| 
         @@ -193,7 +77,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       193 
77 
     | 
    
         
             
                    expect(result['Resources'].size).to eql(2)
         
     | 
| 
       194 
78 
     | 
    
         | 
| 
       195 
79 
     | 
    
         
             
                    ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       196 
     | 
    
         
            -
                    expect(ids).to match_array([@u1. 
     | 
| 
      
 80 
     | 
    
         
            +
                    expect(ids).to match_array([@u1.id.to_s, @u2.id.to_s])
         
     | 
| 
       197 
81 
     | 
    
         | 
| 
       198 
82 
     | 
    
         
             
                    usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       199 
83 
     | 
    
         
             
                    expect(usernames).to match_array(['1', '2'])
         
     | 
| 
         @@ -212,7 +96,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       212 
96 
     | 
    
         
             
                    expect(result['Resources'].size).to eql(2)
         
     | 
| 
       213 
97 
     | 
    
         | 
| 
       214 
98 
     | 
    
         
             
                    ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
       215 
     | 
    
         
            -
                    expect(ids).to match_array([@u2. 
     | 
| 
      
 99 
     | 
    
         
            +
                    expect(ids).to match_array([@u2.id.to_s, @u3.id.to_s])
         
     | 
| 
       216 
100 
     | 
    
         | 
| 
       217 
101 
     | 
    
         
             
                    usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
       218 
102 
     | 
    
         
             
                    expect(usernames).to match_array(['2', '3'])
         
     | 
| 
         @@ -236,34 +120,18 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       236 
120 
     | 
    
         
             
              # ===========================================================================
         
     | 
| 
       237 
121 
     | 
    
         | 
| 
       238 
122 
     | 
    
         
             
              context '#show' do
         
     | 
| 
       239 
     | 
    
         
            -
                 
     | 
| 
       240 
     | 
    
         
            -
                   
     | 
| 
       241 
     | 
    
         
            -
             
     | 
| 
       242 
     | 
    
         
            -
                    get "/Users/#{@u2.primary_key}", params: { format: :scim }
         
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
                    expect(response.status).to eql(200)
         
     | 
| 
       245 
     | 
    
         
            -
                    result = JSON.parse(response.body)
         
     | 
| 
       246 
     | 
    
         
            -
             
     | 
| 
       247 
     | 
    
         
            -
                    expect(result['id']).to eql(@u2.primary_key.to_s)
         
     | 
| 
       248 
     | 
    
         
            -
                    expect(result['userName']).to eql('2')
         
     | 
| 
       249 
     | 
    
         
            -
                    expect(result['name']['familyName']).to eql('Bar')
         
     | 
| 
       250 
     | 
    
         
            -
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       251 
     | 
    
         
            -
                  end
         
     | 
| 
       252 
     | 
    
         
            -
                end # "context 'with a UUID, renamed primary key column' do"
         
     | 
| 
      
 123 
     | 
    
         
            +
                it 'shows an item' do
         
     | 
| 
      
 124 
     | 
    
         
            +
                  expect_any_instance_of(MockUsersController).to receive(:show).once.and_call_original
         
     | 
| 
      
 125 
     | 
    
         
            +
                  get "/Users/#{@u2.id}", params: { format: :scim }
         
     | 
| 
       253 
126 
     | 
    
         | 
| 
       254 
     | 
    
         
            -
             
     | 
| 
       255 
     | 
    
         
            -
                   
     | 
| 
       256 
     | 
    
         
            -
                    expect_any_instance_of(MockGroupsController).to receive(:show).once.and_call_original
         
     | 
| 
       257 
     | 
    
         
            -
                    get "/Groups/#{@g2.id}", params: { format: :scim }
         
     | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
       259 
     | 
    
         
            -
                    expect(response.status).to eql(200)
         
     | 
| 
       260 
     | 
    
         
            -
                    result = JSON.parse(response.body)
         
     | 
| 
      
 127 
     | 
    
         
            +
                  expect(response.status).to eql(200)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  result = JSON.parse(response.body)
         
     | 
| 
       261 
129 
     | 
    
         | 
| 
       262 
     | 
    
         
            -
             
     | 
| 
       263 
     | 
    
         
            -
             
     | 
| 
       264 
     | 
    
         
            -
             
     | 
| 
       265 
     | 
    
         
            -
                   
     | 
| 
       266 
     | 
    
         
            -
                end 
     | 
| 
      
 130 
     | 
    
         
            +
                  expect(result['id']).to eql(@u2.id.to_s) # Note - ID was converted String; not Integer
         
     | 
| 
      
 131 
     | 
    
         
            +
                  expect(result['userName']).to eql('2')
         
     | 
| 
      
 132 
     | 
    
         
            +
                  expect(result['name']['familyName']).to eql('Bar')
         
     | 
| 
      
 133 
     | 
    
         
            +
                  expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
       267 
135 
     | 
    
         | 
| 
       268 
136 
     | 
    
         
             
                it 'renders 404' do
         
     | 
| 
       269 
137 
     | 
    
         
             
                  get '/Users/xyz', params: { format: :scim }
         
     | 
| 
         @@ -296,7 +164,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       296 
164 
     | 
    
         
             
                      expect(response.status).to eql(201)
         
     | 
| 
       297 
165 
     | 
    
         
             
                      result = JSON.parse(response.body)
         
     | 
| 
       298 
166 
     | 
    
         | 
| 
       299 
     | 
    
         
            -
                      expect(result['id']).to eql(new_mock. 
     | 
| 
      
 167 
     | 
    
         
            +
                      expect(result['id']).to eql(new_mock.id.to_s)
         
     | 
| 
       300 
168 
     | 
    
         
             
                      expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       301 
169 
     | 
    
         
             
                      expect(new_mock.username).to eql('4')
         
     | 
| 
       302 
170 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -312,7 +180,6 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       312 
180 
     | 
    
         
             
                          givenName: 'Given',
         
     | 
| 
       313 
181 
     | 
    
         
             
                          familyName: 'Family'
         
     | 
| 
       314 
182 
     | 
    
         
             
                        },
         
     | 
| 
       315 
     | 
    
         
            -
                        meta: { resourceType: 'User' },
         
     | 
| 
       316 
183 
     | 
    
         
             
                        emails: [
         
     | 
| 
       317 
184 
     | 
    
         
             
                          {
         
     | 
| 
       318 
185 
     | 
    
         
             
                            type: 'work',
         
     | 
| 
         @@ -337,7 +204,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       337 
204 
     | 
    
         
             
                      expect(response.status).to eql(201)
         
     | 
| 
       338 
205 
     | 
    
         
             
                      result = JSON.parse(response.body)
         
     | 
| 
       339 
206 
     | 
    
         | 
| 
       340 
     | 
    
         
            -
                      expect(result['id']).to eql(new_mock. 
     | 
| 
      
 207 
     | 
    
         
            +
                      expect(result['id']).to eql(new_mock.id.to_s)
         
     | 
| 
       341 
208 
     | 
    
         
             
                      expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       342 
209 
     | 
    
         
             
                      expect(new_mock.username).to eql('4')
         
     | 
| 
       343 
210 
     | 
    
         
             
                      expect(new_mock.first_name).to eql('Given')
         
     | 
| 
         @@ -411,13 +278,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       411 
278 
     | 
    
         | 
| 
       412 
279 
     | 
    
         
             
                    expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
         
     | 
| 
       413 
280 
     | 
    
         
             
                    expect {
         
     | 
| 
       414 
     | 
    
         
            -
                      put "/Users/#{@u2. 
     | 
| 
      
 281 
     | 
    
         
            +
                      put "/Users/#{@u2.id}", params: attributes.merge(format: :scim)
         
     | 
| 
       415 
282 
     | 
    
         
             
                    }.to_not change { MockUser.count }
         
     | 
| 
       416 
283 
     | 
    
         | 
| 
       417 
284 
     | 
    
         
             
                    expect(response.status).to eql(200)
         
     | 
| 
       418 
285 
     | 
    
         
             
                    result = JSON.parse(response.body)
         
     | 
| 
       419 
286 
     | 
    
         | 
| 
       420 
     | 
    
         
            -
                    expect(result['id']).to eql(@u2. 
     | 
| 
      
 287 
     | 
    
         
            +
                    expect(result['id']).to eql(@u2.id.to_s)
         
     | 
| 
       421 
288 
     | 
    
         
             
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       422 
289 
     | 
    
         | 
| 
       423 
290 
     | 
    
         
             
                    @u2.reload
         
     | 
| 
         @@ -439,7 +306,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       439 
306 
     | 
    
         | 
| 
       440 
307 
     | 
    
         
             
                it 'notes schema validation failures' do
         
     | 
| 
       441 
308 
     | 
    
         
             
                  expect {
         
     | 
| 
       442 
     | 
    
         
            -
                    put "/Users/#{@u2. 
     | 
| 
      
 309 
     | 
    
         
            +
                    put "/Users/#{@u2.id}", params: {
         
     | 
| 
       443 
310 
     | 
    
         
             
                      format: :scim
         
     | 
| 
       444 
311 
     | 
    
         
             
                      # userName parameter is required by schema, but missing
         
     | 
| 
       445 
312 
     | 
    
         
             
                    }
         
     | 
| 
         @@ -518,13 +385,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       518 
385 
     | 
    
         | 
| 
       519 
386 
     | 
    
         
             
                    expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
         
     | 
| 
       520 
387 
     | 
    
         
             
                    expect {
         
     | 
| 
       521 
     | 
    
         
            -
                      patch "/Users/#{@u2. 
     | 
| 
      
 388 
     | 
    
         
            +
                      patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
         
     | 
| 
       522 
389 
     | 
    
         
             
                    }.to_not change { MockUser.count }
         
     | 
| 
       523 
390 
     | 
    
         | 
| 
       524 
391 
     | 
    
         
             
                    expect(response.status).to eql(200)
         
     | 
| 
       525 
392 
     | 
    
         
             
                    result = JSON.parse(response.body)
         
     | 
| 
       526 
393 
     | 
    
         | 
| 
       527 
     | 
    
         
            -
                    expect(result['id']).to eql(@u2. 
     | 
| 
      
 394 
     | 
    
         
            +
                    expect(result['id']).to eql(@u2.id.to_s)
         
     | 
| 
       528 
395 
     | 
    
         
             
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       529 
396 
     | 
    
         | 
| 
       530 
397 
     | 
    
         
             
                    @u2.reload
         
     | 
| 
         @@ -555,13 +422,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       555 
422 
     | 
    
         | 
| 
       556 
423 
     | 
    
         
             
                      expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
         
     | 
| 
       557 
424 
     | 
    
         
             
                      expect {
         
     | 
| 
       558 
     | 
    
         
            -
                        patch "/Users/#{@u2. 
     | 
| 
      
 425 
     | 
    
         
            +
                        patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
         
     | 
| 
       559 
426 
     | 
    
         
             
                      }.to_not change { MockUser.count }
         
     | 
| 
       560 
427 
     | 
    
         | 
| 
       561 
428 
     | 
    
         
             
                      expect(response.status).to eql(200)
         
     | 
| 
       562 
429 
     | 
    
         
             
                      result = JSON.parse(response.body)
         
     | 
| 
       563 
430 
     | 
    
         | 
| 
       564 
     | 
    
         
            -
                      expect(result['id']).to eql(@u2. 
     | 
| 
      
 431 
     | 
    
         
            +
                      expect(result['id']).to eql(@u2.id.to_s)
         
     | 
| 
       565 
432 
     | 
    
         
             
                      expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       566 
433 
     | 
    
         | 
| 
       567 
434 
     | 
    
         
             
                      @u2.reload
         
     | 
| 
         @@ -587,13 +454,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       587 
454 
     | 
    
         | 
| 
       588 
455 
     | 
    
         
             
                      expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
         
     | 
| 
       589 
456 
     | 
    
         
             
                      expect {
         
     | 
| 
       590 
     | 
    
         
            -
                        patch "/Users/#{@u2. 
     | 
| 
      
 457 
     | 
    
         
            +
                        patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
         
     | 
| 
       591 
458 
     | 
    
         
             
                      }.to_not change { MockUser.count }
         
     | 
| 
       592 
459 
     | 
    
         | 
| 
       593 
460 
     | 
    
         
             
                      expect(response.status).to eql(200)
         
     | 
| 
       594 
461 
     | 
    
         
             
                      result = JSON.parse(response.body)
         
     | 
| 
       595 
462 
     | 
    
         | 
| 
       596 
     | 
    
         
            -
                      expect(result['id']).to eql(@u2. 
     | 
| 
      
 463 
     | 
    
         
            +
                      expect(result['id']).to eql(@u2.id.to_s)
         
     | 
| 
       597 
464 
     | 
    
         
             
                      expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       598 
465 
     | 
    
         | 
| 
       599 
466 
     | 
    
         
             
                      @u2.reload
         
     | 
| 
         @@ -619,13 +486,13 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       619 
486 
     | 
    
         | 
| 
       620 
487 
     | 
    
         
             
                      expect_any_instance_of(MockUsersController).to receive(:update).once.and_call_original
         
     | 
| 
       621 
488 
     | 
    
         
             
                      expect {
         
     | 
| 
       622 
     | 
    
         
            -
                        patch "/Users/#{@u2. 
     | 
| 
      
 489 
     | 
    
         
            +
                        patch "/Users/#{@u2.id}", params: payload.merge(format: :scim)
         
     | 
| 
       623 
490 
     | 
    
         
             
                      }.to_not change { MockUser.count }
         
     | 
| 
       624 
491 
     | 
    
         | 
| 
       625 
492 
     | 
    
         
             
                      expect(response.status).to eql(200)
         
     | 
| 
       626 
493 
     | 
    
         
             
                      result = JSON.parse(response.body)
         
     | 
| 
       627 
494 
     | 
    
         | 
| 
       628 
     | 
    
         
            -
                      expect(result['id']).to eql(@u2. 
     | 
| 
      
 495 
     | 
    
         
            +
                      expect(result['id']).to eql(@u2.id.to_s)
         
     | 
| 
       629 
496 
     | 
    
         
             
                      expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       630 
497 
     | 
    
         | 
| 
       631 
498 
     | 
    
         
             
                      @u2.reload
         
     | 
| 
         @@ -649,7 +516,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       649 
516 
     | 
    
         | 
| 
       650 
517 
     | 
    
         
             
                it 'notes Rails validation failures' do
         
     | 
| 
       651 
518 
     | 
    
         
             
                  expect {
         
     | 
| 
       652 
     | 
    
         
            -
                    patch "/Users/#{@u2. 
     | 
| 
      
 519 
     | 
    
         
            +
                    patch "/Users/#{@u2.id}", params: {
         
     | 
| 
       653 
520 
     | 
    
         
             
                      format: :scim,
         
     | 
| 
       654 
521 
     | 
    
         
             
                      Operations: [
         
     | 
| 
       655 
522 
     | 
    
         
             
                        {
         
     | 
| 
         @@ -693,142 +560,6 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       693 
560 
     | 
    
         
             
                  result = JSON.parse(response.body)
         
     | 
| 
       694 
561 
     | 
    
         
             
                  expect(result['status']).to eql('404')
         
     | 
| 
       695 
562 
     | 
    
         
             
                end
         
     | 
| 
       696 
     | 
    
         
            -
             
     | 
| 
       697 
     | 
    
         
            -
                context 'when removing users from groups' do
         
     | 
| 
       698 
     | 
    
         
            -
                  before :each do
         
     | 
| 
       699 
     | 
    
         
            -
                    @g1.mock_users << @u1
         
     | 
| 
       700 
     | 
    
         
            -
                    @g1.mock_users << @u2
         
     | 
| 
       701 
     | 
    
         
            -
                    @g1.mock_users << @u3
         
     | 
| 
       702 
     | 
    
         
            -
             
     | 
| 
       703 
     | 
    
         
            -
                    # (Self-check) Verify group representation
         
     | 
| 
       704 
     | 
    
         
            -
                    #
         
     | 
| 
       705 
     | 
    
         
            -
                    get "/Groups/#{@g1.id}", params: { format: :scim }
         
     | 
| 
       706 
     | 
    
         
            -
             
     | 
| 
       707 
     | 
    
         
            -
                    expect(response.status).to eql(200)
         
     | 
| 
       708 
     | 
    
         
            -
                    result = JSON.parse(response.body)
         
     | 
| 
       709 
     | 
    
         
            -
             
     | 
| 
       710 
     | 
    
         
            -
                    expect(result['members'].map { |m| m['value'] }.sort()).to eql(MockUser.pluck(:primary_key).sort())
         
     | 
| 
       711 
     | 
    
         
            -
                  end
         
     | 
| 
       712 
     | 
    
         
            -
             
     | 
| 
       713 
     | 
    
         
            -
                  it 'can remove all users' do
         
     | 
| 
       714 
     | 
    
         
            -
                    expect {
         
     | 
| 
       715 
     | 
    
         
            -
                      expect {
         
     | 
| 
       716 
     | 
    
         
            -
                        patch "/Groups/#{@g1.id}", params: {
         
     | 
| 
       717 
     | 
    
         
            -
                          format: :scim,
         
     | 
| 
       718 
     | 
    
         
            -
                          Operations: [
         
     | 
| 
       719 
     | 
    
         
            -
                            {
         
     | 
| 
       720 
     | 
    
         
            -
                              op: 'remove',
         
     | 
| 
       721 
     | 
    
         
            -
                              path: 'members'
         
     | 
| 
       722 
     | 
    
         
            -
                            }
         
     | 
| 
       723 
     | 
    
         
            -
                          ]
         
     | 
| 
       724 
     | 
    
         
            -
                        }
         
     | 
| 
       725 
     | 
    
         
            -
                      }.to_not change { MockUser.count }
         
     | 
| 
       726 
     | 
    
         
            -
                    }.to_not change { MockGroup.count }
         
     | 
| 
       727 
     | 
    
         
            -
             
     | 
| 
       728 
     | 
    
         
            -
                    get "/Groups/#{@g1.id}", params: { format: :scim }
         
     | 
| 
       729 
     | 
    
         
            -
             
     | 
| 
       730 
     | 
    
         
            -
                    expect(response.status).to eql(200)
         
     | 
| 
       731 
     | 
    
         
            -
                    result = JSON.parse(response.body)
         
     | 
| 
       732 
     | 
    
         
            -
             
     | 
| 
       733 
     | 
    
         
            -
                    expect(result['members']).to be_empty
         
     | 
| 
       734 
     | 
    
         
            -
                    expect(@g1.reload().mock_users).to be_empty
         
     | 
| 
       735 
     | 
    
         
            -
                  end
         
     | 
| 
       736 
     | 
    
         
            -
             
     | 
| 
       737 
     | 
    
         
            -
                  # Define via 'let':
         
     | 
| 
       738 
     | 
    
         
            -
                  #
         
     | 
| 
       739 
     | 
    
         
            -
                  # * Hash 'payload', to send via 'patch'
         
     | 
| 
       740 
     | 
    
         
            -
                  # * MockUser 'removed_user', which is the user that should be removed
         
     | 
| 
       741 
     | 
    
         
            -
                  #
         
     | 
| 
       742 
     | 
    
         
            -
                  shared_examples 'a user remover' do
         
     | 
| 
       743 
     | 
    
         
            -
                    it 'which removes the identified user' do
         
     | 
| 
       744 
     | 
    
         
            -
                      expect {
         
     | 
| 
       745 
     | 
    
         
            -
                        expect {
         
     | 
| 
       746 
     | 
    
         
            -
                          patch "/Groups/#{@g1.id}", params: payload()
         
     | 
| 
       747 
     | 
    
         
            -
                        }.to_not change { MockUser.count }
         
     | 
| 
       748 
     | 
    
         
            -
                      }.to_not change { MockGroup.count }
         
     | 
| 
       749 
     | 
    
         
            -
             
     | 
| 
       750 
     | 
    
         
            -
                      expected_remaining_user_ids = MockUser
         
     | 
| 
       751 
     | 
    
         
            -
                        .where.not(primary_key: removed_user().id)
         
     | 
| 
       752 
     | 
    
         
            -
                        .pluck(:primary_key)
         
     | 
| 
       753 
     | 
    
         
            -
                        .sort()
         
     | 
| 
       754 
     | 
    
         
            -
             
     | 
| 
       755 
     | 
    
         
            -
                      get "/Groups/#{@g1.id}", params: { format: :scim }
         
     | 
| 
       756 
     | 
    
         
            -
             
     | 
| 
       757 
     | 
    
         
            -
                      expect(response.status).to eql(200)
         
     | 
| 
       758 
     | 
    
         
            -
                      result = JSON.parse(response.body)
         
     | 
| 
       759 
     | 
    
         
            -
             
     | 
| 
       760 
     | 
    
         
            -
                      expect(result['members'].map { |m| m['value'] }.sort()).to eql(expected_remaining_user_ids)
         
     | 
| 
       761 
     | 
    
         
            -
                      expect(@g1.reload().mock_users.map(&:primary_key).sort()).to eql(expected_remaining_user_ids)
         
     | 
| 
       762 
     | 
    
         
            -
                    end
         
     | 
| 
       763 
     | 
    
         
            -
                  end
         
     | 
| 
       764 
     | 
    
         
            -
             
     | 
| 
       765 
     | 
    
         
            -
                  # https://tools.ietf.org/html/rfc7644#section-3.5.2.2
         
     | 
| 
       766 
     | 
    
         
            -
                  #
         
     | 
| 
       767 
     | 
    
         
            -
                  context 'and using an RFC-compliant payload' do
         
     | 
| 
       768 
     | 
    
         
            -
                    let(:removed_user) { @u2 }
         
     | 
| 
       769 
     | 
    
         
            -
                    let(:payload) do
         
     | 
| 
       770 
     | 
    
         
            -
                      {
         
     | 
| 
       771 
     | 
    
         
            -
                        format: :scim,
         
     | 
| 
       772 
     | 
    
         
            -
                        Operations: [
         
     | 
| 
       773 
     | 
    
         
            -
                          {
         
     | 
| 
       774 
     | 
    
         
            -
                            op: 'remove',
         
     | 
| 
       775 
     | 
    
         
            -
                            path: "members[value eq \"#{removed_user().primary_key}\"]",
         
     | 
| 
       776 
     | 
    
         
            -
                          }
         
     | 
| 
       777 
     | 
    
         
            -
                        ]
         
     | 
| 
       778 
     | 
    
         
            -
                      }
         
     | 
| 
       779 
     | 
    
         
            -
                    end
         
     | 
| 
       780 
     | 
    
         
            -
             
     | 
| 
       781 
     | 
    
         
            -
                    it_behaves_like 'a user remover'
         
     | 
| 
       782 
     | 
    
         
            -
                  end # context 'and using an RFC-compliant payload' do
         
     | 
| 
       783 
     | 
    
         
            -
             
     | 
| 
       784 
     | 
    
         
            -
                  # https://learn.microsoft.com/en-us/azure/active-directory/app-provisioning/use-scim-to-provision-users-and-groups#update-group-remove-members
         
     | 
| 
       785 
     | 
    
         
            -
                  #
         
     | 
| 
       786 
     | 
    
         
            -
                  context 'and using a Microsoft variant payload' do
         
     | 
| 
       787 
     | 
    
         
            -
                    let(:removed_user) { @u2 }
         
     | 
| 
       788 
     | 
    
         
            -
                    let(:payload) do
         
     | 
| 
       789 
     | 
    
         
            -
                      {
         
     | 
| 
       790 
     | 
    
         
            -
                        format: :scim,
         
     | 
| 
       791 
     | 
    
         
            -
                        Operations: [
         
     | 
| 
       792 
     | 
    
         
            -
                          {
         
     | 
| 
       793 
     | 
    
         
            -
                            op: 'remove',
         
     | 
| 
       794 
     | 
    
         
            -
                            path: 'members',
         
     | 
| 
       795 
     | 
    
         
            -
                            value: [{
         
     | 
| 
       796 
     | 
    
         
            -
                              '$ref' => nil,
         
     | 
| 
       797 
     | 
    
         
            -
                              'value' => removed_user().primary_key
         
     | 
| 
       798 
     | 
    
         
            -
                            }]
         
     | 
| 
       799 
     | 
    
         
            -
                          }
         
     | 
| 
       800 
     | 
    
         
            -
                        ]
         
     | 
| 
       801 
     | 
    
         
            -
                      }
         
     | 
| 
       802 
     | 
    
         
            -
                    end
         
     | 
| 
       803 
     | 
    
         
            -
             
     | 
| 
       804 
     | 
    
         
            -
                    it_behaves_like 'a user remover'
         
     | 
| 
       805 
     | 
    
         
            -
                  end # context 'and using a Microsoft variant payload' do
         
     | 
| 
       806 
     | 
    
         
            -
             
     | 
| 
       807 
     | 
    
         
            -
                  # https://help.salesforce.com/s/articleView?id=sf.identity_scim_manage_groups.htm&type=5
         
     | 
| 
       808 
     | 
    
         
            -
                  #
         
     | 
| 
       809 
     | 
    
         
            -
                  context 'and using a Salesforce variant payload' do
         
     | 
| 
       810 
     | 
    
         
            -
                    let(:removed_user) { @u2 }
         
     | 
| 
       811 
     | 
    
         
            -
                    let(:payload) do
         
     | 
| 
       812 
     | 
    
         
            -
                      {
         
     | 
| 
       813 
     | 
    
         
            -
                        format: :scim,
         
     | 
| 
       814 
     | 
    
         
            -
                        Operations: [
         
     | 
| 
       815 
     | 
    
         
            -
                          {
         
     | 
| 
       816 
     | 
    
         
            -
                            op: 'remove',
         
     | 
| 
       817 
     | 
    
         
            -
                            path: 'members',
         
     | 
| 
       818 
     | 
    
         
            -
                            value: {
         
     | 
| 
       819 
     | 
    
         
            -
                              'members' => [{
         
     | 
| 
       820 
     | 
    
         
            -
                                '$ref' => nil,
         
     | 
| 
       821 
     | 
    
         
            -
                                'value' => removed_user().primary_key
         
     | 
| 
       822 
     | 
    
         
            -
                              }]
         
     | 
| 
       823 
     | 
    
         
            -
                            }
         
     | 
| 
       824 
     | 
    
         
            -
                          }
         
     | 
| 
       825 
     | 
    
         
            -
                        ]
         
     | 
| 
       826 
     | 
    
         
            -
                      }
         
     | 
| 
       827 
     | 
    
         
            -
                    end
         
     | 
| 
       828 
     | 
    
         
            -
             
     | 
| 
       829 
     | 
    
         
            -
                    it_behaves_like 'a user remover'
         
     | 
| 
       830 
     | 
    
         
            -
                  end # context 'and using a Salesforce variant payload' do
         
     | 
| 
       831 
     | 
    
         
            -
                end # "context 'when removing users from groups' do"
         
     | 
| 
       832 
563 
     | 
    
         
             
              end # "context '#update' do"
         
     | 
| 
       833 
564 
     | 
    
         | 
| 
       834 
565 
     | 
    
         
             
              # ===========================================================================
         
     | 
| 
         @@ -838,7 +569,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       838 
569 
     | 
    
         
             
                  expect_any_instance_of(MockUsersController).to receive(:destroy).once.and_call_original
         
     | 
| 
       839 
570 
     | 
    
         
             
                  expect_any_instance_of(MockUser).to receive(:destroy!).once.and_call_original
         
     | 
| 
       840 
571 
     | 
    
         
             
                  expect {
         
     | 
| 
       841 
     | 
    
         
            -
                    delete "/Users/#{@u2. 
     | 
| 
      
 572 
     | 
    
         
            +
                    delete "/Users/#{@u2.id}", params: { format: :scim }
         
     | 
| 
       842 
573 
     | 
    
         
             
                  }.to change { MockUser.count }.by(-1)
         
     | 
| 
       843 
574 
     | 
    
         | 
| 
       844 
575 
     | 
    
         
             
                  expect(response.status).to eql(204)
         
     | 
| 
         @@ -850,7 +581,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       850 
581 
     | 
    
         
             
                  expect_any_instance_of(MockUser).to_not receive(:destroy!)
         
     | 
| 
       851 
582 
     | 
    
         | 
| 
       852 
583 
     | 
    
         
             
                  expect {
         
     | 
| 
       853 
     | 
    
         
            -
                    delete "/CustomDestroyUsers/#{@u2. 
     | 
| 
      
 584 
     | 
    
         
            +
                    delete "/CustomDestroyUsers/#{@u2.id}", params: { format: :scim }
         
     | 
| 
       854 
585 
     | 
    
         
             
                  }.to_not change { MockUser.count }
         
     | 
| 
       855 
586 
     | 
    
         | 
| 
       856 
587 
     | 
    
         
             
                  expect(response.status).to eql(204)
         
     | 
| 
         @@ -11,14 +11,11 @@ RSpec.describe Scimitar::ApplicationController do 
     | 
|
| 
       11 
11 
     | 
    
         
             
              end
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
              context 'format handling' do
         
     | 
| 
       14 
     | 
    
         
            -
                it 'renders " 
     | 
| 
      
 14 
     | 
    
         
            +
                it 'renders "not acceptable" if the request does not use SCIM type' do
         
     | 
| 
       15 
15 
     | 
    
         
             
                  get '/CustomRequestVerifiers', params: { format: :html }
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
                  expect(response).to have_http_status(: 
     | 
| 
       18 
     | 
    
         
            -
                   
     | 
| 
       19 
     | 
    
         
            -
                  expect(parsed_body['request']['is_scim'     ]).to eql(true)
         
     | 
| 
       20 
     | 
    
         
            -
                  expect(parsed_body['request']['format'      ]).to eql('application/scim+json')
         
     | 
| 
       21 
     | 
    
         
            -
                  expect(parsed_body['request']['content_type']).to eql('application/scim+json')
         
     | 
| 
      
 17 
     | 
    
         
            +
                  expect(response).to have_http_status(:not_acceptable)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  expect(JSON.parse(response.body)['detail']).to eql('Only application/scim+json type is accepted.')
         
     | 
| 
       22 
19 
     | 
    
         
             
                end
         
     | 
| 
       23 
20 
     | 
    
         | 
| 
       24 
21 
     | 
    
         
             
                it 'renders 400 if given bad JSON' do
         
     | 
| 
         @@ -26,7 +23,6 @@ RSpec.describe Scimitar::ApplicationController do 
     | 
|
| 
       26 
23 
     | 
    
         | 
| 
       27 
24 
     | 
    
         
             
                  expect(response).to have_http_status(:bad_request)
         
     | 
| 
       28 
25 
     | 
    
         
             
                  expect(JSON.parse(response.body)['detail']).to start_with('Invalid JSON - ')
         
     | 
| 
       29 
     | 
    
         
            -
                  expect(JSON.parse(response.body)['detail']).to include("'not-json-12345'")
         
     | 
| 
       30 
26 
     | 
    
         
             
                end
         
     | 
| 
       31 
27 
     | 
    
         | 
| 
       32 
28 
     | 
    
         
             
                it 'translates Content-Type to Rails request format' do
         
     | 
| 
         @@ -39,16 +35,6 @@ RSpec.describe Scimitar::ApplicationController do 
     | 
|
| 
       39 
35 
     | 
    
         
             
                  expect(parsed_body['request']['content_type']).to eql('application/scim+json')
         
     | 
| 
       40 
36 
     | 
    
         
             
                end
         
     | 
| 
       41 
37 
     | 
    
         | 
| 
       42 
     | 
    
         
            -
                it 'translates Content-Type with charset to Rails request format' do
         
     | 
| 
       43 
     | 
    
         
            -
                  get '/CustomRequestVerifiers', headers: { 'CONTENT_TYPE' => 'application/scim+json; charset=utf-8' }
         
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
       45 
     | 
    
         
            -
                  expect(response).to have_http_status(:ok)
         
     | 
| 
       46 
     | 
    
         
            -
                  parsed_body = JSON.parse(response.body)
         
     | 
| 
       47 
     | 
    
         
            -
                  expect(parsed_body['request']['is_scim'     ]).to eql(true)
         
     | 
| 
       48 
     | 
    
         
            -
                  expect(parsed_body['request']['format'      ]).to eql('application/scim+json')
         
     | 
| 
       49 
     | 
    
         
            -
                  expect(parsed_body['request']['content_type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
       50 
     | 
    
         
            -
                end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
38 
     | 
    
         
             
                it 'translates Rails request format to header' do
         
     | 
| 
       53 
39 
     | 
    
         
             
                  get '/CustomRequestVerifiers', params: { format: :scim }
         
     | 
| 
       54 
40 
     | 
    
         |