scimitar 1.8.1 → 1.10.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/README.md +27 -20
 - data/app/controllers/scimitar/active_record_backed_resources_controller.rb +5 -4
 - data/app/controllers/scimitar/resource_types_controller.rb +0 -2
 - data/app/controllers/scimitar/resources_controller.rb +0 -2
 - data/app/controllers/scimitar/schemas_controller.rb +361 -3
 - data/app/controllers/scimitar/service_provider_configurations_controller.rb +0 -1
 - data/app/models/scimitar/engine_configuration.rb +3 -1
 - data/app/models/scimitar/lists/query_parser.rb +88 -3
 - data/app/models/scimitar/resources/base.rb +48 -14
 - data/app/models/scimitar/resources/mixin.rb +531 -71
 - data/app/models/scimitar/schema/name.rb +2 -2
 - data/app/models/scimitar/schema/user.rb +10 -10
 - data/config/initializers/scimitar.rb +41 -0
 - data/lib/scimitar/engine.rb +57 -12
 - data/lib/scimitar/support/hash_with_indifferent_case_insensitive_access.rb +140 -10
 - data/lib/scimitar/support/utilities.rb +60 -0
 - data/lib/scimitar/version.rb +2 -2
 - data/spec/apps/dummy/app/models/mock_user.rb +18 -3
 - data/spec/apps/dummy/config/initializers/scimitar.rb +31 -2
 - data/spec/apps/dummy/db/migrate/20210304014602_create_mock_users.rb +1 -0
 - data/spec/apps/dummy/db/schema.rb +1 -0
 - data/spec/controllers/scimitar/schemas_controller_spec.rb +342 -54
 - data/spec/models/scimitar/lists/query_parser_spec.rb +70 -0
 - data/spec/models/scimitar/resources/base_spec.rb +20 -12
 - data/spec/models/scimitar/resources/base_validation_spec.rb +16 -3
 - data/spec/models/scimitar/resources/mixin_spec.rb +754 -122
 - data/spec/models/scimitar/schema/user_spec.rb +2 -2
 - data/spec/requests/active_record_backed_resources_controller_spec.rb +312 -5
 - data/spec/requests/engine_spec.rb +75 -0
 - data/spec/spec_helper.rb +1 -1
 - data/spec/support/hash_with_indifferent_case_insensitive_access_spec.rb +108 -0
 - metadata +22 -22
 
| 
         @@ -27,7 +27,7 @@ RSpec.describe Scimitar::Schema::User do 
     | 
|
| 
       27 
27 
     | 
    
         
             
                    "subAttributes": [
         
     | 
| 
       28 
28 
     | 
    
         
             
                      {
         
     | 
| 
       29 
29 
     | 
    
         
             
                        "multiValued": false,
         
     | 
| 
       30 
     | 
    
         
            -
                        "required":  
     | 
| 
      
 30 
     | 
    
         
            +
                        "required": false,
         
     | 
| 
       31 
31 
     | 
    
         
             
                        "caseExact": false,
         
     | 
| 
       32 
32 
     | 
    
         
             
                        "mutability": "readWrite",
         
     | 
| 
       33 
33 
     | 
    
         
             
                        "uniqueness": "none",
         
     | 
| 
         @@ -37,7 +37,7 @@ RSpec.describe Scimitar::Schema::User do 
     | 
|
| 
       37 
37 
     | 
    
         
             
                      },
         
     | 
| 
       38 
38 
     | 
    
         
             
                      {
         
     | 
| 
       39 
39 
     | 
    
         
             
                        "multiValued": false,
         
     | 
| 
       40 
     | 
    
         
            -
                        "required":  
     | 
| 
      
 40 
     | 
    
         
            +
                        "required": false,
         
     | 
| 
       41 
41 
     | 
    
         
             
                        "caseExact": false,
         
     | 
| 
       42 
42 
     | 
    
         
             
                        "mutability": "readWrite",
         
     | 
| 
       43 
43 
     | 
    
         
             
                        "uniqueness": "none",
         
     | 
| 
         @@ -14,7 +14,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       14 
14 
     | 
    
         
             
                ids = 3.times.map { SecureRandom.uuid }.sort()
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                @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)
         
     | 
| 
       17 
     | 
    
         
            -
                @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)
         
     | 
| 
      
 17 
     | 
    
         
            +
                @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, password: 'oldpassword')
         
     | 
| 
       18 
18 
     | 
    
         
             
                @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)
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
                @g1 = MockGroup.create!(display_name: 'Group 1')
         
     | 
| 
         @@ -107,6 +107,37 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       107 
107 
     | 
    
         
             
                    expect(usernames).to match_array(['2'])
         
     | 
| 
       108 
108 
     | 
    
         
             
                  end
         
     | 
| 
       109 
109 
     | 
    
         | 
| 
      
 110 
     | 
    
         
            +
                  it 'returns only the requested attributes' do
         
     | 
| 
      
 111 
     | 
    
         
            +
                    get '/Users', params: {
         
     | 
| 
      
 112 
     | 
    
         
            +
                      format: :scim,
         
     | 
| 
      
 113 
     | 
    
         
            +
                      attributes: "id,name"
         
     | 
| 
      
 114 
     | 
    
         
            +
                    }
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                    expect(response.status                 ).to eql(200)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                    result = JSON.parse(response.body)
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    expect(result['totalResults']).to eql(3)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    expect(result['Resources'].size).to eql(3)
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    keys = result['Resources'].map { |resource| resource.keys }.flatten.uniq
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    expect(keys).to match_array(%w[
         
     | 
| 
      
 127 
     | 
    
         
            +
                      id
         
     | 
| 
      
 128 
     | 
    
         
            +
                      meta
         
     | 
| 
      
 129 
     | 
    
         
            +
                      name
         
     | 
| 
      
 130 
     | 
    
         
            +
                      schemas
         
     | 
| 
      
 131 
     | 
    
         
            +
                      urn:ietf:params:scim:schemas:extension:enterprise:2.0:User
         
     | 
| 
      
 132 
     | 
    
         
            +
                      urn:ietf:params:scim:schemas:extension:manager:1.0:User
         
     | 
| 
      
 133 
     | 
    
         
            +
                    ])
         
     | 
| 
      
 134 
     | 
    
         
            +
                    expect(result.dig('Resources', 0, 'id')).to eql @u1.primary_key.to_s
         
     | 
| 
      
 135 
     | 
    
         
            +
                    expect(result.dig('Resources', 0, 'name', 'givenName')).to eql 'Foo'
         
     | 
| 
      
 136 
     | 
    
         
            +
                    expect(result.dig('Resources', 0, 'name', 'familyName')).to eql 'Ark'
         
     | 
| 
      
 137 
     | 
    
         
            +
                  end
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                  # https://github.com/RIPAGlobal/scimitar/issues/37
         
     | 
| 
      
 140 
     | 
    
         
            +
                  #
         
     | 
| 
       110 
141 
     | 
    
         
             
                  it 'applies a filter, with case-insensitive attribute matching (GitHub issue #37)' do
         
     | 
| 
       111 
142 
     | 
    
         
             
                    get '/Users', params: {
         
     | 
| 
       112 
143 
     | 
    
         
             
                      format: :scim,
         
     | 
| 
         @@ -128,6 +159,30 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       128 
159 
     | 
    
         
             
                    expect(usernames).to match_array(['2'])
         
     | 
| 
       129 
160 
     | 
    
         
             
                  end
         
     | 
| 
       130 
161 
     | 
    
         | 
| 
      
 162 
     | 
    
         
            +
                  # https://github.com/RIPAGlobal/scimitar/issues/115
         
     | 
| 
      
 163 
     | 
    
         
            +
                  #
         
     | 
| 
      
 164 
     | 
    
         
            +
                  it 'handles broken Microsoft filters (GitHub issue #115)' do
         
     | 
| 
      
 165 
     | 
    
         
            +
                    get '/Users', params: {
         
     | 
| 
      
 166 
     | 
    
         
            +
                      format: :scim,
         
     | 
| 
      
 167 
     | 
    
         
            +
                      filter: 'name[givenName eq "FOO"].familyName pr and emails ne "home_1@test.com"'
         
     | 
| 
      
 168 
     | 
    
         
            +
                    }
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                    expect(response.status                 ).to eql(200)
         
     | 
| 
      
 171 
     | 
    
         
            +
                    expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                    result = JSON.parse(response.body)
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                    expect(result['totalResults']).to eql(1)
         
     | 
| 
      
 176 
     | 
    
         
            +
                    expect(result['Resources'].size).to eql(1)
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                    ids = result['Resources'].map { |resource| resource['id'] }
         
     | 
| 
      
 179 
     | 
    
         
            +
                    expect(ids).to match_array([@u2.primary_key.to_s])
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                    usernames = result['Resources'].map { |resource| resource['userName'] }
         
     | 
| 
      
 182 
     | 
    
         
            +
                    expect(usernames).to match_array(['2'])
         
     | 
| 
      
 183 
     | 
    
         
            +
                  end
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
             
     | 
| 
       131 
186 
     | 
    
         
             
                  # Strange attribute capitalisation in tests here builds on test coverage
         
     | 
| 
       132 
187 
     | 
    
         
             
                  # for now-fixed GitHub issue #37.
         
     | 
| 
       133 
188 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -345,6 +400,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       345 
400 
     | 
    
         | 
| 
       346 
401 
     | 
    
         
             
                      attributes = {
         
     | 
| 
       347 
402 
     | 
    
         
             
                        userName: '4',
         
     | 
| 
      
 403 
     | 
    
         
            +
                        password: 'correcthorsebatterystaple',
         
     | 
| 
       348 
404 
     | 
    
         
             
                        name: {
         
     | 
| 
       349 
405 
     | 
    
         
             
                          givenName: 'Given',
         
     | 
| 
       350 
406 
     | 
    
         
             
                          familyName: 'Family'
         
     | 
| 
         @@ -379,11 +435,82 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       379 
435 
     | 
    
         
             
                      expect(result['id']).to eql(new_mock.id.to_s)
         
     | 
| 
       380 
436 
     | 
    
         
             
                      expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       381 
437 
     | 
    
         
             
                      expect(new_mock.username).to eql('4')
         
     | 
| 
      
 438 
     | 
    
         
            +
                      expect(new_mock.password).to eql('correcthorsebatterystaple')
         
     | 
| 
       382 
439 
     | 
    
         
             
                      expect(new_mock.first_name).to eql('Given')
         
     | 
| 
       383 
440 
     | 
    
         
             
                      expect(new_mock.last_name).to eql('Family')
         
     | 
| 
       384 
441 
     | 
    
         
             
                      expect(new_mock.home_email_address).to eql('home_4@test.com')
         
     | 
| 
       385 
442 
     | 
    
         
             
                      expect(new_mock.work_email_address).to eql('work_4@test.com')
         
     | 
| 
       386 
443 
     | 
    
         
             
                    end
         
     | 
| 
      
 444 
     | 
    
         
            +
             
     | 
| 
      
 445 
     | 
    
         
            +
                    it 'with schema ID value keys without inline attributes' do
         
     | 
| 
      
 446 
     | 
    
         
            +
                      mock_before = MockUser.all.to_a
         
     | 
| 
      
 447 
     | 
    
         
            +
             
     | 
| 
      
 448 
     | 
    
         
            +
                      attributes = {
         
     | 
| 
      
 449 
     | 
    
         
            +
                        userName: '4',
         
     | 
| 
      
 450 
     | 
    
         
            +
                        'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User': {
         
     | 
| 
      
 451 
     | 
    
         
            +
                          organization: 'Foo Bar!',
         
     | 
| 
      
 452 
     | 
    
         
            +
                          department:   'Bar Foo!'
         
     | 
| 
      
 453 
     | 
    
         
            +
                        },
         
     | 
| 
      
 454 
     | 
    
         
            +
                        'urn:ietf:params:scim:schemas:extension:manager:1.0:User': {
         
     | 
| 
      
 455 
     | 
    
         
            +
                          manager: 'Foo Baz!'
         
     | 
| 
      
 456 
     | 
    
         
            +
                        }
         
     | 
| 
      
 457 
     | 
    
         
            +
                      }
         
     | 
| 
      
 458 
     | 
    
         
            +
             
     | 
| 
      
 459 
     | 
    
         
            +
                      attributes = spec_helper_hupcase(attributes) if force_upper_case
         
     | 
| 
      
 460 
     | 
    
         
            +
             
     | 
| 
      
 461 
     | 
    
         
            +
                      expect {
         
     | 
| 
      
 462 
     | 
    
         
            +
                        post "/Users", params: attributes.merge(format: :scim)
         
     | 
| 
      
 463 
     | 
    
         
            +
                      }.to change { MockUser.count }.by(1)
         
     | 
| 
      
 464 
     | 
    
         
            +
             
     | 
| 
      
 465 
     | 
    
         
            +
                      mock_after = MockUser.all.to_a
         
     | 
| 
      
 466 
     | 
    
         
            +
                      new_mock = (mock_after - mock_before).first
         
     | 
| 
      
 467 
     | 
    
         
            +
             
     | 
| 
      
 468 
     | 
    
         
            +
                      expect(response.status                 ).to eql(201)
         
     | 
| 
      
 469 
     | 
    
         
            +
                      expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 470 
     | 
    
         
            +
             
     | 
| 
      
 471 
     | 
    
         
            +
                      result = JSON.parse(response.body)
         
     | 
| 
      
 472 
     | 
    
         
            +
             
     | 
| 
      
 473 
     | 
    
         
            +
                      expect(new_mock.organization).to eql('Foo Bar!')
         
     | 
| 
      
 474 
     | 
    
         
            +
                      expect(new_mock.department  ).to eql('Bar Foo!')
         
     | 
| 
      
 475 
     | 
    
         
            +
                      expect(new_mock.manager     ).to eql('Foo Baz!')
         
     | 
| 
      
 476 
     | 
    
         
            +
             
     | 
| 
      
 477 
     | 
    
         
            +
                      expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization']).to eql(new_mock.organization)
         
     | 
| 
      
 478 
     | 
    
         
            +
                      expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['department'  ]).to eql(new_mock.department  )
         
     | 
| 
      
 479 
     | 
    
         
            +
                      expect(result['urn:ietf:params:scim:schemas:extension:manager:1.0:User'   ]['manager'     ]).to eql(new_mock.manager     )
         
     | 
| 
      
 480 
     | 
    
         
            +
                    end
         
     | 
| 
      
 481 
     | 
    
         
            +
             
     | 
| 
      
 482 
     | 
    
         
            +
                    it 'with schema ID value keys that have inline attributes' do
         
     | 
| 
      
 483 
     | 
    
         
            +
                      mock_before = MockUser.all.to_a
         
     | 
| 
      
 484 
     | 
    
         
            +
             
     | 
| 
      
 485 
     | 
    
         
            +
                      attributes = {
         
     | 
| 
      
 486 
     | 
    
         
            +
                        userName: '4',
         
     | 
| 
      
 487 
     | 
    
         
            +
                        'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization': 'Foo Bar!',
         
     | 
| 
      
 488 
     | 
    
         
            +
                        'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department':   'Bar Foo!',
         
     | 
| 
      
 489 
     | 
    
         
            +
                        'urn:ietf:params:scim:schemas:extension:manager:1.0:User:manager':         'Foo Baz!'
         
     | 
| 
      
 490 
     | 
    
         
            +
                      }
         
     | 
| 
      
 491 
     | 
    
         
            +
             
     | 
| 
      
 492 
     | 
    
         
            +
                      attributes = spec_helper_hupcase(attributes) if force_upper_case
         
     | 
| 
      
 493 
     | 
    
         
            +
             
     | 
| 
      
 494 
     | 
    
         
            +
                      expect {
         
     | 
| 
      
 495 
     | 
    
         
            +
                        post "/Users", params: attributes.merge(format: :scim)
         
     | 
| 
      
 496 
     | 
    
         
            +
                      }.to change { MockUser.count }.by(1)
         
     | 
| 
      
 497 
     | 
    
         
            +
             
     | 
| 
      
 498 
     | 
    
         
            +
                      mock_after = MockUser.all.to_a
         
     | 
| 
      
 499 
     | 
    
         
            +
                      new_mock = (mock_after - mock_before).first
         
     | 
| 
      
 500 
     | 
    
         
            +
             
     | 
| 
      
 501 
     | 
    
         
            +
                      expect(response.status                 ).to eql(201)
         
     | 
| 
      
 502 
     | 
    
         
            +
                      expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 503 
     | 
    
         
            +
             
     | 
| 
      
 504 
     | 
    
         
            +
                      result = JSON.parse(response.body)
         
     | 
| 
      
 505 
     | 
    
         
            +
             
     | 
| 
      
 506 
     | 
    
         
            +
                      expect(new_mock.organization).to eql('Foo Bar!')
         
     | 
| 
      
 507 
     | 
    
         
            +
                      expect(new_mock.department  ).to eql('Bar Foo!')
         
     | 
| 
      
 508 
     | 
    
         
            +
                      expect(new_mock.manager     ).to eql('Foo Baz!')
         
     | 
| 
      
 509 
     | 
    
         
            +
             
     | 
| 
      
 510 
     | 
    
         
            +
                      expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['organization']).to eql(new_mock.organization)
         
     | 
| 
      
 511 
     | 
    
         
            +
                      expect(result['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User']['department'  ]).to eql(new_mock.department  )
         
     | 
| 
      
 512 
     | 
    
         
            +
                      expect(result['urn:ietf:params:scim:schemas:extension:manager:1.0:User'   ]['manager'     ]).to eql(new_mock.manager     )
         
     | 
| 
      
 513 
     | 
    
         
            +
                    end
         
     | 
| 
       387 
514 
     | 
    
         
             
                  end # "shared_examples 'a creator' do | force_upper_case: |"
         
     | 
| 
       388 
515 
     | 
    
         | 
| 
       389 
516 
     | 
    
         
             
                  context 'using schema-matched case' do
         
     | 
| 
         @@ -511,6 +638,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       511 
638 
     | 
    
         
             
                    expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
       512 
639 
     | 
    
         | 
| 
       513 
640 
     | 
    
         
             
                    result = JSON.parse(response.body)
         
     | 
| 
      
 641 
     | 
    
         
            +
             
     | 
| 
       514 
642 
     | 
    
         
             
                    expect(result['scimType']).to eql('invalidValue')
         
     | 
| 
       515 
643 
     | 
    
         
             
                    expect(result['detail']).to include('is reserved')
         
     | 
| 
       516 
644 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -522,14 +650,41 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       522 
650 
     | 
    
         
             
              context '#replace' do
         
     | 
| 
       523 
651 
     | 
    
         
             
                shared_examples 'a replacer' do | force_upper_case: |
         
     | 
| 
       524 
652 
     | 
    
         
             
                  it 'which replaces all attributes in an instance' do
         
     | 
| 
       525 
     | 
    
         
            -
                    attributes = { userName: '4' } 
     | 
| 
      
 653 
     | 
    
         
            +
                    attributes = { userName: '4' } # Minimum required by schema
         
     | 
| 
       526 
654 
     | 
    
         
             
                    attributes = spec_helper_hupcase(attributes) if force_upper_case
         
     | 
| 
       527 
655 
     | 
    
         | 
| 
       528 
656 
     | 
    
         
             
                    # Prove that certain known pathways are called; can then unit test
         
     | 
| 
       529 
657 
     | 
    
         
             
                    # those if need be and be sure that this covers #replace actions.
         
     | 
| 
       530 
658 
     | 
    
         
             
                    #
         
     | 
| 
       531 
659 
     | 
    
         
             
                    expect_any_instance_of(MockUsersController).to receive(:replace).once.and_call_original
         
     | 
| 
       532 
     | 
    
         
            -
                    expect_any_instance_of(MockUsersController).to receive(:save! 
     | 
| 
      
 660 
     | 
    
         
            +
                    expect_any_instance_of(MockUsersController).to receive(:save!  ).once.and_call_original
         
     | 
| 
      
 661 
     | 
    
         
            +
                    expect {
         
     | 
| 
      
 662 
     | 
    
         
            +
                      put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
         
     | 
| 
      
 663 
     | 
    
         
            +
                    }.to_not change { MockUser.count }
         
     | 
| 
      
 664 
     | 
    
         
            +
             
     | 
| 
      
 665 
     | 
    
         
            +
                    expect(response.status                 ).to eql(200)
         
     | 
| 
      
 666 
     | 
    
         
            +
                    expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 667 
     | 
    
         
            +
             
     | 
| 
      
 668 
     | 
    
         
            +
                    result = JSON.parse(response.body)
         
     | 
| 
      
 669 
     | 
    
         
            +
             
     | 
| 
      
 670 
     | 
    
         
            +
                    expect(result['id']).to eql(@u2.primary_key.to_s)
         
     | 
| 
      
 671 
     | 
    
         
            +
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
      
 672 
     | 
    
         
            +
             
     | 
| 
      
 673 
     | 
    
         
            +
                    expect(result).to     have_key('name')
         
     | 
| 
      
 674 
     | 
    
         
            +
                    expect(result).to_not have_key('password')
         
     | 
| 
      
 675 
     | 
    
         
            +
             
     | 
| 
      
 676 
     | 
    
         
            +
                    @u2.reload
         
     | 
| 
      
 677 
     | 
    
         
            +
             
     | 
| 
      
 678 
     | 
    
         
            +
                    expect(@u2.username).to eql('4')
         
     | 
| 
      
 679 
     | 
    
         
            +
                    expect(@u2.first_name).to be_nil
         
     | 
| 
      
 680 
     | 
    
         
            +
                    expect(@u2.last_name).to be_nil
         
     | 
| 
      
 681 
     | 
    
         
            +
                    expect(@u2.home_email_address).to be_nil
         
     | 
| 
      
 682 
     | 
    
         
            +
                    expect(@u2.password).to be_nil
         
     | 
| 
      
 683 
     | 
    
         
            +
                  end
         
     | 
| 
      
 684 
     | 
    
         
            +
             
     | 
| 
      
 685 
     | 
    
         
            +
                  it 'can replace passwords' do
         
     | 
| 
      
 686 
     | 
    
         
            +
                    attributes = { userName: '4', password: 'correcthorsebatterystaple' }
         
     | 
| 
      
 687 
     | 
    
         
            +
                    attributes = spec_helper_hupcase(attributes) if force_upper_case
         
     | 
| 
       533 
688 
     | 
    
         | 
| 
       534 
689 
     | 
    
         
             
                    expect {
         
     | 
| 
       535 
690 
     | 
    
         
             
                      put "/Users/#{@u2.primary_key}", params: attributes.merge(format: :scim)
         
     | 
| 
         @@ -543,12 +698,16 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       543 
698 
     | 
    
         
             
                    expect(result['id']).to eql(@u2.primary_key.to_s)
         
     | 
| 
       544 
699 
     | 
    
         
             
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       545 
700 
     | 
    
         | 
| 
      
 701 
     | 
    
         
            +
                    expect(result).to     have_key('name')
         
     | 
| 
      
 702 
     | 
    
         
            +
                    expect(result).to_not have_key('password')
         
     | 
| 
      
 703 
     | 
    
         
            +
             
     | 
| 
       546 
704 
     | 
    
         
             
                    @u2.reload
         
     | 
| 
       547 
705 
     | 
    
         | 
| 
       548 
706 
     | 
    
         
             
                    expect(@u2.username).to eql('4')
         
     | 
| 
       549 
707 
     | 
    
         
             
                    expect(@u2.first_name).to be_nil
         
     | 
| 
       550 
708 
     | 
    
         
             
                    expect(@u2.last_name).to be_nil
         
     | 
| 
       551 
709 
     | 
    
         
             
                    expect(@u2.home_email_address).to be_nil
         
     | 
| 
      
 710 
     | 
    
         
            +
                    expect(@u2.password).to eql('correcthorsebatterystaple')
         
     | 
| 
       552 
711 
     | 
    
         
             
                  end
         
     | 
| 
       553 
712 
     | 
    
         
             
                end # "shared_examples 'a replacer' do | force_upper_case: |"
         
     | 
| 
       554 
713 
     | 
    
         | 
| 
         @@ -626,7 +785,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       626 
785 
     | 
    
         | 
| 
       627 
786 
     | 
    
         
             
                context 'with a block' do
         
     | 
| 
       628 
787 
     | 
    
         
             
                  it 'invokes the block' do
         
     | 
| 
       629 
     | 
    
         
            -
                    attributes = { userName: '4' } 
     | 
| 
      
 788 
     | 
    
         
            +
                    attributes = { userName: '4' } # Minimum required by schema
         
     | 
| 
       630 
789 
     | 
    
         | 
| 
       631 
790 
     | 
    
         
             
                    expect_any_instance_of(CustomReplaceMockUsersController).to receive(:replace).once.and_call_original
         
     | 
| 
       632 
791 
     | 
    
         
             
                    expect {
         
     | 
| 
         @@ -681,7 +840,7 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       681 
840 
     | 
    
         | 
| 
       682 
841 
     | 
    
         
             
              context '#update' do
         
     | 
| 
       683 
842 
     | 
    
         
             
                shared_examples 'an updater' do | force_upper_case: |
         
     | 
| 
       684 
     | 
    
         
            -
                  it 'which patches  
     | 
| 
      
 843 
     | 
    
         
            +
                  it 'which patches regular attributes' do
         
     | 
| 
       685 
844 
     | 
    
         
             
                    payload = {
         
     | 
| 
       686 
845 
     | 
    
         
             
                      Operations: [
         
     | 
| 
       687 
846 
     | 
    
         
             
                        {
         
     | 
| 
         @@ -717,6 +876,9 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       717 
876 
     | 
    
         
             
                    expect(result['id']).to eql(@u2.primary_key.to_s)
         
     | 
| 
       718 
877 
     | 
    
         
             
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
       719 
878 
     | 
    
         | 
| 
      
 879 
     | 
    
         
            +
                    expect(result).to     have_key('name')
         
     | 
| 
      
 880 
     | 
    
         
            +
                    expect(result).to_not have_key('password')
         
     | 
| 
      
 881 
     | 
    
         
            +
             
     | 
| 
       720 
882 
     | 
    
         
             
                    @u2.reload
         
     | 
| 
       721 
883 
     | 
    
         | 
| 
       722 
884 
     | 
    
         
             
                    expect(@u2.username).to eql('4')
         
     | 
| 
         @@ -724,6 +886,151 @@ RSpec.describe Scimitar::ActiveRecordBackedResourcesController do 
     | 
|
| 
       724 
886 
     | 
    
         
             
                    expect(@u2.last_name).to eql('Bar')
         
     | 
| 
       725 
887 
     | 
    
         
             
                    expect(@u2.home_email_address).to eql('home_2@test.com')
         
     | 
| 
       726 
888 
     | 
    
         
             
                    expect(@u2.work_email_address).to eql('work_4@test.com')
         
     | 
| 
      
 889 
     | 
    
         
            +
                    expect(@u2.password).to eql('oldpassword')
         
     | 
| 
      
 890 
     | 
    
         
            +
                  end
         
     | 
| 
      
 891 
     | 
    
         
            +
             
     | 
| 
      
 892 
     | 
    
         
            +
                  context 'which' do
         
     | 
| 
      
 893 
     | 
    
         
            +
                    shared_examples 'it handles not-to-spec in-value Azure/Entra dotted attribute paths' do | operation |
         
     | 
| 
      
 894 
     | 
    
         
            +
                      it "and performs operation" do
         
     | 
| 
      
 895 
     | 
    
         
            +
                        payload = {
         
     | 
| 
      
 896 
     | 
    
         
            +
                          Operations: [
         
     | 
| 
      
 897 
     | 
    
         
            +
                            {
         
     | 
| 
      
 898 
     | 
    
         
            +
                              op: 'add',
         
     | 
| 
      
 899 
     | 
    
         
            +
                              value: {
         
     | 
| 
      
 900 
     | 
    
         
            +
                                'name.givenName'  => 'Foo!',
         
     | 
| 
      
 901 
     | 
    
         
            +
                                'name.familyName' => 'Bar!',
         
     | 
| 
      
 902 
     | 
    
         
            +
                                'name.formatted'  => 'Foo! Bar!' # Unrecognised; should be ignored
         
     | 
| 
      
 903 
     | 
    
         
            +
                              },
         
     | 
| 
      
 904 
     | 
    
         
            +
                            },
         
     | 
| 
      
 905 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 906 
     | 
    
         
            +
                        }
         
     | 
| 
      
 907 
     | 
    
         
            +
             
     | 
| 
      
 908 
     | 
    
         
            +
                        payload = spec_helper_hupcase(payload) if force_upper_case
         
     | 
| 
      
 909 
     | 
    
         
            +
                        patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
         
     | 
| 
      
 910 
     | 
    
         
            +
             
     | 
| 
      
 911 
     | 
    
         
            +
                        expect(response.status                 ).to eql(200)
         
     | 
| 
      
 912 
     | 
    
         
            +
                        expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 913 
     | 
    
         
            +
             
     | 
| 
      
 914 
     | 
    
         
            +
                        @u2.reload
         
     | 
| 
      
 915 
     | 
    
         
            +
                        result = JSON.parse(response.body)
         
     | 
| 
      
 916 
     | 
    
         
            +
             
     | 
| 
      
 917 
     | 
    
         
            +
                        expect(@u2.first_name).to eql('Foo!')
         
     | 
| 
      
 918 
     | 
    
         
            +
                        expect(@u2.last_name ).to eql('Bar!')
         
     | 
| 
      
 919 
     | 
    
         
            +
                      end
         
     | 
| 
      
 920 
     | 
    
         
            +
                    end
         
     | 
| 
      
 921 
     | 
    
         
            +
             
     | 
| 
      
 922 
     | 
    
         
            +
                    it_behaves_like 'it handles not-to-spec in-value Azure/Entra dotted attribute paths', 'add'
         
     | 
| 
      
 923 
     | 
    
         
            +
                    it_behaves_like 'it handles not-to-spec in-value Azure/Entra dotted attribute paths', 'replace'
         
     | 
| 
      
 924 
     | 
    
         
            +
             
     | 
| 
      
 925 
     | 
    
         
            +
                    shared_examples 'it handles schema ID value keys without inline attributes' do | operation |
         
     | 
| 
      
 926 
     | 
    
         
            +
                      it "and performs operation" do
         
     | 
| 
      
 927 
     | 
    
         
            +
                        payload = {
         
     | 
| 
      
 928 
     | 
    
         
            +
                          Operations: [
         
     | 
| 
      
 929 
     | 
    
         
            +
                            {
         
     | 
| 
      
 930 
     | 
    
         
            +
                              op: operation,
         
     | 
| 
      
 931 
     | 
    
         
            +
                              value: {
         
     | 
| 
      
 932 
     | 
    
         
            +
                                'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User': {
         
     | 
| 
      
 933 
     | 
    
         
            +
                                  'organization' => 'Foo Bar!',
         
     | 
| 
      
 934 
     | 
    
         
            +
                                  'department'   => 'Bar Foo!'
         
     | 
| 
      
 935 
     | 
    
         
            +
                                },
         
     | 
| 
      
 936 
     | 
    
         
            +
                                'urn:ietf:params:scim:schemas:extension:manager:1.0:User': {
         
     | 
| 
      
 937 
     | 
    
         
            +
                                  'manager' => 'Foo Baz!'
         
     | 
| 
      
 938 
     | 
    
         
            +
                                }
         
     | 
| 
      
 939 
     | 
    
         
            +
                              },
         
     | 
| 
      
 940 
     | 
    
         
            +
                            },
         
     | 
| 
      
 941 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 942 
     | 
    
         
            +
                        }
         
     | 
| 
      
 943 
     | 
    
         
            +
             
     | 
| 
      
 944 
     | 
    
         
            +
                        @u2.update!(organization: 'Old org')
         
     | 
| 
      
 945 
     | 
    
         
            +
                        payload = spec_helper_hupcase(payload) if force_upper_case
         
     | 
| 
      
 946 
     | 
    
         
            +
                        patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
         
     | 
| 
      
 947 
     | 
    
         
            +
             
     | 
| 
      
 948 
     | 
    
         
            +
                        expect(response.status                 ).to eql(200)
         
     | 
| 
      
 949 
     | 
    
         
            +
                        expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 950 
     | 
    
         
            +
             
     | 
| 
      
 951 
     | 
    
         
            +
                        @u2.reload
         
     | 
| 
      
 952 
     | 
    
         
            +
                        result = JSON.parse(response.body)
         
     | 
| 
      
 953 
     | 
    
         
            +
             
     | 
| 
      
 954 
     | 
    
         
            +
                        expect(@u2.organization).to eql('Foo Bar!')
         
     | 
| 
      
 955 
     | 
    
         
            +
                        expect(@u2.department  ).to eql('Bar Foo!')
         
     | 
| 
      
 956 
     | 
    
         
            +
                        expect(@u2.manager     ).to eql('Foo Baz!')
         
     | 
| 
      
 957 
     | 
    
         
            +
                      end
         
     | 
| 
      
 958 
     | 
    
         
            +
                    end
         
     | 
| 
      
 959 
     | 
    
         
            +
             
     | 
| 
      
 960 
     | 
    
         
            +
                    it_behaves_like 'it handles schema ID value keys without inline attributes', 'add'
         
     | 
| 
      
 961 
     | 
    
         
            +
                    it_behaves_like 'it handles schema ID value keys without inline attributes', 'replace'
         
     | 
| 
      
 962 
     | 
    
         
            +
             
     | 
| 
      
 963 
     | 
    
         
            +
                    shared_examples 'it handles schema ID value keys with inline attributes' do
         
     | 
| 
      
 964 
     | 
    
         
            +
                      it "and performs operation" do
         
     | 
| 
      
 965 
     | 
    
         
            +
                        payload = {
         
     | 
| 
      
 966 
     | 
    
         
            +
                          Operations: [
         
     | 
| 
      
 967 
     | 
    
         
            +
                            {
         
     | 
| 
      
 968 
     | 
    
         
            +
                              op: 'add',
         
     | 
| 
      
 969 
     | 
    
         
            +
                              value: {
         
     | 
| 
      
 970 
     | 
    
         
            +
                                'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization' => 'Foo Bar!',
         
     | 
| 
      
 971 
     | 
    
         
            +
                                'urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:department'   => 'Bar Foo!',
         
     | 
| 
      
 972 
     | 
    
         
            +
                                'urn:ietf:params:scim:schemas:extension:manager:1.0:User:manager'         => 'Foo Baz!'
         
     | 
| 
      
 973 
     | 
    
         
            +
                              },
         
     | 
| 
      
 974 
     | 
    
         
            +
                            },
         
     | 
| 
      
 975 
     | 
    
         
            +
                          ]
         
     | 
| 
      
 976 
     | 
    
         
            +
                        }
         
     | 
| 
      
 977 
     | 
    
         
            +
             
     | 
| 
      
 978 
     | 
    
         
            +
                        @u2.update!(organization: 'Old org')
         
     | 
| 
      
 979 
     | 
    
         
            +
                        payload = spec_helper_hupcase(payload) if force_upper_case
         
     | 
| 
      
 980 
     | 
    
         
            +
                        patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
         
     | 
| 
      
 981 
     | 
    
         
            +
             
     | 
| 
      
 982 
     | 
    
         
            +
                        expect(response.status                 ).to eql(200)
         
     | 
| 
      
 983 
     | 
    
         
            +
                        expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 984 
     | 
    
         
            +
             
     | 
| 
      
 985 
     | 
    
         
            +
                        @u2.reload
         
     | 
| 
      
 986 
     | 
    
         
            +
                        result = JSON.parse(response.body)
         
     | 
| 
      
 987 
     | 
    
         
            +
             
     | 
| 
      
 988 
     | 
    
         
            +
                        expect(@u2.organization).to eql('Foo Bar!')
         
     | 
| 
      
 989 
     | 
    
         
            +
                        expect(@u2.department  ).to eql('Bar Foo!')
         
     | 
| 
      
 990 
     | 
    
         
            +
                        expect(@u2.manager     ).to eql('Foo Baz!')
         
     | 
| 
      
 991 
     | 
    
         
            +
                      end
         
     | 
| 
      
 992 
     | 
    
         
            +
                    end
         
     | 
| 
      
 993 
     | 
    
         
            +
             
     | 
| 
      
 994 
     | 
    
         
            +
                    it_behaves_like 'it handles schema ID value keys with inline attributes', 'add'
         
     | 
| 
      
 995 
     | 
    
         
            +
                    it_behaves_like 'it handles schema ID value keys with inline attributes', 'replace'
         
     | 
| 
      
 996 
     | 
    
         
            +
                  end
         
     | 
| 
      
 997 
     | 
    
         
            +
             
     | 
| 
      
 998 
     | 
    
         
            +
                  it 'which patches "returned: \'never\'" fields' do
         
     | 
| 
      
 999 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 1000 
     | 
    
         
            +
                      Operations: [
         
     | 
| 
      
 1001 
     | 
    
         
            +
                        {
         
     | 
| 
      
 1002 
     | 
    
         
            +
                          op: 'replace',
         
     | 
| 
      
 1003 
     | 
    
         
            +
                          path: 'password',
         
     | 
| 
      
 1004 
     | 
    
         
            +
                          value: 'correcthorsebatterystaple'
         
     | 
| 
      
 1005 
     | 
    
         
            +
                        }
         
     | 
| 
      
 1006 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 1007 
     | 
    
         
            +
                    }
         
     | 
| 
      
 1008 
     | 
    
         
            +
             
     | 
| 
      
 1009 
     | 
    
         
            +
                    payload = spec_helper_hupcase(payload) if force_upper_case
         
     | 
| 
      
 1010 
     | 
    
         
            +
             
     | 
| 
      
 1011 
     | 
    
         
            +
                    expect {
         
     | 
| 
      
 1012 
     | 
    
         
            +
                      patch "/Users/#{@u2.primary_key}", params: payload.merge(format: :scim)
         
     | 
| 
      
 1013 
     | 
    
         
            +
                    }.to_not change { MockUser.count }
         
     | 
| 
      
 1014 
     | 
    
         
            +
             
     | 
| 
      
 1015 
     | 
    
         
            +
                    expect(response.status                 ).to eql(200)
         
     | 
| 
      
 1016 
     | 
    
         
            +
                    expect(response.headers['Content-Type']).to eql('application/scim+json; charset=utf-8')
         
     | 
| 
      
 1017 
     | 
    
         
            +
             
     | 
| 
      
 1018 
     | 
    
         
            +
                    result = JSON.parse(response.body)
         
     | 
| 
      
 1019 
     | 
    
         
            +
             
     | 
| 
      
 1020 
     | 
    
         
            +
                    expect(result['id']).to eql(@u2.primary_key.to_s)
         
     | 
| 
      
 1021 
     | 
    
         
            +
                    expect(result['meta']['resourceType']).to eql('User')
         
     | 
| 
      
 1022 
     | 
    
         
            +
             
     | 
| 
      
 1023 
     | 
    
         
            +
                    expect(result).to     have_key('name')
         
     | 
| 
      
 1024 
     | 
    
         
            +
                    expect(result).to_not have_key('password')
         
     | 
| 
      
 1025 
     | 
    
         
            +
             
     | 
| 
      
 1026 
     | 
    
         
            +
                    @u2.reload
         
     | 
| 
      
 1027 
     | 
    
         
            +
             
     | 
| 
      
 1028 
     | 
    
         
            +
                    expect(@u2.username).to eql('2')
         
     | 
| 
      
 1029 
     | 
    
         
            +
                    expect(@u2.first_name).to eql('Foo')
         
     | 
| 
      
 1030 
     | 
    
         
            +
                    expect(@u2.last_name).to eql('Bar')
         
     | 
| 
      
 1031 
     | 
    
         
            +
                    expect(@u2.home_email_address).to eql('home_2@test.com')
         
     | 
| 
      
 1032 
     | 
    
         
            +
                    expect(@u2.work_email_address).to be_nil
         
     | 
| 
      
 1033 
     | 
    
         
            +
                    expect(@u2.password).to eql('correcthorsebatterystaple')
         
     | 
| 
       727 
1034 
     | 
    
         
             
                  end
         
     | 
| 
       728 
1035 
     | 
    
         | 
| 
       729 
1036 
     | 
    
         
             
                  context 'which clears attributes' do
         
     | 
| 
         @@ -42,4 +42,79 @@ RSpec.describe Scimitar::Engine do 
     | 
|
| 
       42 
42 
     | 
    
         
             
                  expect(JSON.parse(response.body)['name']['familyName']).to eql('baz')
         
     | 
| 
       43 
43 
     | 
    
         
             
                end
         
     | 
| 
       44 
44 
     | 
    
         
             
              end # "context 'parameter parser' do"
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              # These are unit tests rather than request tests; seems like a reasonable
         
     | 
| 
      
 47 
     | 
    
         
            +
              # place to put them in the absence of a standardised RSpec "engine" location.
         
     | 
| 
      
 48 
     | 
    
         
            +
              #
         
     | 
| 
      
 49 
     | 
    
         
            +
              context 'engine unit tests' do
         
     | 
| 
      
 50 
     | 
    
         
            +
                around :each do | example |
         
     | 
| 
      
 51 
     | 
    
         
            +
                  license_schema = Class.new(Scimitar::Schema::Base) do
         
     | 
| 
      
 52 
     | 
    
         
            +
                    def initialize(options = {})
         
     | 
| 
      
 53 
     | 
    
         
            +
                      super(name: 'License', id: self.class.id(), description: 'Represents a License')
         
     | 
| 
      
 54 
     | 
    
         
            +
                    end
         
     | 
| 
      
 55 
     | 
    
         
            +
                    def self.id; 'urn:ietf:params:scim:schemas:license'; end
         
     | 
| 
      
 56 
     | 
    
         
            +
                    def self.scim_attributes; []; end
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  @license_resource = Class.new(Scimitar::Resources::Base) do
         
     | 
| 
      
 60 
     | 
    
         
            +
                    self.set_schema(license_schema)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    def self.endpoint; '/License'; end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  example.run()
         
     | 
| 
      
 65 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 66 
     | 
    
         
            +
                  Scimitar::Engine.reset_default_resources()
         
     | 
| 
      
 67 
     | 
    
         
            +
                  Scimitar::Engine.reset_custom_resources()
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                context '::resources, :add_custom_resource, ::set_default_resources' do
         
     | 
| 
      
 71 
     | 
    
         
            +
                  it 'returns default resources' do
         
     | 
| 
      
 72 
     | 
    
         
            +
                    expect(Scimitar::Engine.resources()).to match_array([Scimitar::Resources::User, Scimitar::Resources::Group])
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  it 'includes custom resources' do
         
     | 
| 
      
 76 
     | 
    
         
            +
                    Scimitar::Engine.add_custom_resource(@license_resource)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    expect(Scimitar::Engine.resources()).to match_array([Scimitar::Resources::User, Scimitar::Resources::Group, @license_resource])
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  it 'notes changes to defaults' do
         
     | 
| 
      
 81 
     | 
    
         
            +
                    Scimitar::Engine::set_default_resources([Scimitar::Resources::User])
         
     | 
| 
      
 82 
     | 
    
         
            +
                    expect(Scimitar::Engine.resources()).to match_array([Scimitar::Resources::User])
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  it 'notes changes to defaults with custom resources added' do
         
     | 
| 
      
 86 
     | 
    
         
            +
                    Scimitar::Engine::set_default_resources([Scimitar::Resources::User])
         
     | 
| 
      
 87 
     | 
    
         
            +
                    Scimitar::Engine.add_custom_resource(@license_resource)
         
     | 
| 
      
 88 
     | 
    
         
            +
                    expect(Scimitar::Engine.resources()).to match_array([Scimitar::Resources::User, @license_resource])
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  it 'rejects bad defaults' do
         
     | 
| 
      
 92 
     | 
    
         
            +
                    expect {
         
     | 
| 
      
 93 
     | 
    
         
            +
                      Scimitar::Engine::set_default_resources([@license_resource])
         
     | 
| 
      
 94 
     | 
    
         
            +
                    }.to raise_error('Scimitar::Engine::set_default_resources: Only Scimitar::Resources::User, Scimitar::Resources::Group are supported')
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  it 'rejects empty defaults' do
         
     | 
| 
      
 98 
     | 
    
         
            +
                    expect {
         
     | 
| 
      
 99 
     | 
    
         
            +
                      Scimitar::Engine::set_default_resources([])
         
     | 
| 
      
 100 
     | 
    
         
            +
                    }.to raise_error('Scimitar::Engine::set_default_resources: At least one resource must be given')
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
                end # "context '::resources, :add_custom_resource, ::set_default_resources' do"
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                context '#schemas' do
         
     | 
| 
      
 105 
     | 
    
         
            +
                  it 'returns schema instances from ::resources' do
         
     | 
| 
      
 106 
     | 
    
         
            +
                    expect(Scimitar::Engine).to receive(:resources).and_return([Scimitar::Resources::User, @license_resource])
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                    schema_instances = Scimitar::Engine.schemas()
         
     | 
| 
      
 109 
     | 
    
         
            +
                    schema_classes   = schema_instances.map(&:class)
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                    expect(schema_classes).to match_array([
         
     | 
| 
      
 112 
     | 
    
         
            +
                      Scimitar::Schema::User,
         
     | 
| 
      
 113 
     | 
    
         
            +
                      ScimSchemaExtensions::User::Enterprise,
         
     | 
| 
      
 114 
     | 
    
         
            +
                      ScimSchemaExtensions::User::Manager,
         
     | 
| 
      
 115 
     | 
    
         
            +
                      @license_resource.schemas.first
         
     | 
| 
      
 116 
     | 
    
         
            +
                    ])
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
                end # "context '#schemas' do"
         
     | 
| 
      
 119 
     | 
    
         
            +
              end # "context 'engine unit tests' do"
         
     | 
| 
       45 
120 
     | 
    
         
             
            end
         
     | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | 
         @@ -19,7 +19,7 @@ require File.expand_path('../apps/dummy/config/environment', __FILE__) 
     | 
|
| 
       19 
19 
     | 
    
         
             
            abort("The Rails environment is running in production mode!") if Rails.env.production?
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            require 'rspec/rails'
         
     | 
| 
       22 
     | 
    
         
            -
            require ' 
     | 
| 
      
 22 
     | 
    
         
            +
            require 'debug'
         
     | 
| 
       23 
23 
     | 
    
         
             
            require 'scimitar'
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
            # ============================================================================
         
     | 
| 
         @@ -37,6 +37,114 @@ RSpec.describe Scimitar::Support::HashWithIndifferentCaseInsensitiveAccess do 
     | 
|
| 
       37 
37 
     | 
    
         
             
                    expect(subject()).to_not have_key('bar')
         
     | 
| 
       38 
38 
     | 
    
         
             
                  end
         
     | 
| 
       39 
39 
     | 
    
         
             
                end # "context 'where keys set as symbols' do"
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                context 'access and merging' do
         
     | 
| 
      
 42 
     | 
    
         
            +
                  before :each do
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @original_subject = subject().to_h().dup()
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  it 'returns keys as Strings' do
         
     | 
| 
      
 47 
     | 
    
         
            +
                    subject()[:foo] = 1
         
     | 
| 
      
 48 
     | 
    
         
            +
                    subject()[:BAR] = 2
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                    expect(subject().keys).to match_array(@original_subject.keys + ['foo', 'BAR'])
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  it 'retains original case of keys' do
         
     | 
| 
      
 54 
     | 
    
         
            +
                    subject()[:foo ] = 1
         
     | 
| 
      
 55 
     | 
    
         
            +
                    subject()['FoO'] = 40 # (first-time-set case preservation test in passing)
         
     | 
| 
      
 56 
     | 
    
         
            +
                    subject()[:BAR ] = 2
         
     | 
| 
      
 57 
     | 
    
         
            +
                    subject()['Baz'] = 3
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                    expectation = @original_subject.merge({
         
     | 
| 
      
 60 
     | 
    
         
            +
                      'foo' => 40,
         
     | 
| 
      
 61 
     | 
    
         
            +
                      'BAR' => 2,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      'Baz' => 3
         
     | 
| 
      
 63 
     | 
    
         
            +
                    })
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                    expect(subject()).to eql(expectation)
         
     | 
| 
      
 66 
     | 
    
         
            +
                  end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
                  it '#merge does not mutate the receiver and retains case of first-set keys' do
         
     | 
| 
      
 69 
     | 
    
         
            +
                    subject()[:foo] = 1
         
     | 
| 
      
 70 
     | 
    
         
            +
                    subject()[:BAR] = 2
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    pre_merge_subject = subject().dup()
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                    result      = subject().merge({:FOO => { 'onE' => 40 }, :Baz => 3})
         
     | 
| 
      
 75 
     | 
    
         
            +
                    expectation = @original_subject.merge({
         
     | 
| 
      
 76 
     | 
    
         
            +
                      'foo' => { 'onE' => 40 },
         
     | 
| 
      
 77 
     | 
    
         
            +
                      'BAR' => 2,
         
     | 
| 
      
 78 
     | 
    
         
            +
                      'Baz' => 3
         
     | 
| 
      
 79 
     | 
    
         
            +
                    })
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                    expect(subject()).to eql(pre_merge_subject)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    expect(result).to eql(expectation)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  it '#merge! mutates the receiver retains case of first-set keys' do
         
     | 
| 
      
 86 
     | 
    
         
            +
                    subject()[:foo] = 1
         
     | 
| 
      
 87 
     | 
    
         
            +
                    subject()[:BAR] = 2
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                    subject().merge!({:FOO => { 'onE' => 40 }, :Baz => 3})
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    expectation = @original_subject.merge({
         
     | 
| 
      
 92 
     | 
    
         
            +
                      'foo' => { 'onE' => 40 },
         
     | 
| 
      
 93 
     | 
    
         
            +
                      'BAR' => 2,
         
     | 
| 
      
 94 
     | 
    
         
            +
                      'Baz' => 3
         
     | 
| 
      
 95 
     | 
    
         
            +
                    })
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    expect(subject()).to eql(expectation)
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  it '#deep_merge does not mutate the receiver and retains nested key cases' do
         
     | 
| 
      
 101 
     | 
    
         
            +
                    subject()[:foo] = { :one => 10 }
         
     | 
| 
      
 102 
     | 
    
         
            +
                    subject()[:BAR] = 2
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    pre_merge_subject = subject().dup()
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    result      = subject().deep_merge({:FOO => { 'ONE' => 40, :TWO => 20 }, :Baz => 3})
         
     | 
| 
      
 107 
     | 
    
         
            +
                    expectation = @original_subject.merge({
         
     | 
| 
      
 108 
     | 
    
         
            +
                      'foo' => { 'one' => 40, 'TWO' => 20 },
         
     | 
| 
      
 109 
     | 
    
         
            +
                      'BAR' => 2,
         
     | 
| 
      
 110 
     | 
    
         
            +
                      'Baz' => 3
         
     | 
| 
      
 111 
     | 
    
         
            +
                    })
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                    expect(subject()).to eql(pre_merge_subject)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    expect(result).to eql(expectation)
         
     | 
| 
      
 115 
     | 
    
         
            +
                  end
         
     | 
| 
      
 116 
     | 
    
         
            +
             
     | 
| 
      
 117 
     | 
    
         
            +
                  it '#deep_merge! mutates the receiver and retains nested key cases' do
         
     | 
| 
      
 118 
     | 
    
         
            +
                    subject()[:foo] = { :one => 10 }
         
     | 
| 
      
 119 
     | 
    
         
            +
                    subject()[:BAR] = 2
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    subject().deep_merge!({:FOO => { 'ONE' => 40, :TWO => 20 }, :Baz => 3})
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    expectation = @original_subject.merge({
         
     | 
| 
      
 124 
     | 
    
         
            +
                      'foo' => { 'one' => 40, 'TWO' => 20 },
         
     | 
| 
      
 125 
     | 
    
         
            +
                      'BAR' => 2,
         
     | 
| 
      
 126 
     | 
    
         
            +
                      'Baz' => 3
         
     | 
| 
      
 127 
     | 
    
         
            +
                    })
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                    expect(subject()).to eql(expectation)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  it 'retains indifferent behaviour after duplication' do
         
     | 
| 
      
 133 
     | 
    
         
            +
                    subject()[:foo] = { 'onE' => 40 }
         
     | 
| 
      
 134 
     | 
    
         
            +
                    subject()[:BAR] = 2
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                    duplicate = subject().dup()
         
     | 
| 
      
 137 
     | 
    
         
            +
                    duplicate.merge!({ 'FOO' => true, 'baz' => 3 })
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                    expectation = @original_subject.merge({
         
     | 
| 
      
 140 
     | 
    
         
            +
                      'foo' => true,
         
     | 
| 
      
 141 
     | 
    
         
            +
                      'BAR' => 2,
         
     | 
| 
      
 142 
     | 
    
         
            +
                      'baz' => 3
         
     | 
| 
      
 143 
     | 
    
         
            +
                    })
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                    expect(duplicate.to_h).to eql(expectation.to_h)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
      
 147 
     | 
    
         
            +
                end # "context 'access and merging' do"
         
     | 
| 
       40 
148 
     | 
    
         
             
              end # "shared_examples 'an indifferent access, case insensitive Hash' do"
         
     | 
| 
       41 
149 
     | 
    
         | 
| 
       42 
150 
     | 
    
         
             
              context 'when created directly' do
         
     |