|
@@ -5,197 +5,268 @@ require 'models/concerns/has_groups_permissions_examples'
|
|
|
require 'models/concerns/can_lookup_examples'
|
|
|
|
|
|
RSpec.describe User do
|
|
|
- let(:subject) { create(:user, user_attrs || {}) }
|
|
|
- let(:group_access_instance) { create(:agent_user) }
|
|
|
- let(:new_group_access_instance) { build(:agent_user) }
|
|
|
- let(:group_access_no_permission_instance) { build(:user) }
|
|
|
-
|
|
|
- include_examples 'HasGroups'
|
|
|
- include_examples 'HasRoles'
|
|
|
- include_examples 'HasGroups and Permissions'
|
|
|
+ include_examples 'HasGroups', group_access_factory: :agent_user
|
|
|
+ include_examples 'HasRoles', group_access_factory: :agent_user
|
|
|
+ include_examples 'HasGroups and Permissions', group_access_no_permission_factory: :user
|
|
|
include_examples 'CanLookup'
|
|
|
|
|
|
- let(:new_password) { 'N3W54V3PW!' }
|
|
|
+ subject(:user) { create(:user) }
|
|
|
|
|
|
- context 'password' do
|
|
|
+ describe 'attributes' do
|
|
|
+ describe '#login_failed' do
|
|
|
+ before { user.update(login_failed: 1) }
|
|
|
|
|
|
- it 'resets login_failed on password change' do
|
|
|
+ it 'resets failed login count when password is changed' do
|
|
|
+ expect { user.update(password: Faker::Internet.password) }
|
|
|
+ .to change { user.login_failed }.to(0)
|
|
|
+ end
|
|
|
+ end
|
|
|
|
|
|
- failed_logins = (Setting.get('password_max_login_failed').to_i || 10) + 1
|
|
|
- user = create(:user, login_failed: failed_logins)
|
|
|
+ describe '#password' do
|
|
|
+ context 'when set to plaintext password' do
|
|
|
+ it 'hashes password before saving to DB' do
|
|
|
+ user.password = 'password'
|
|
|
|
|
|
- expect do
|
|
|
- user.password = new_password
|
|
|
- user.save
|
|
|
- end.to change { user.login_failed }.to(0)
|
|
|
- end
|
|
|
- end
|
|
|
+ expect { user.save }
|
|
|
+ .to change { user.password }.to(PasswordHash.crypt('password'))
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ context 'when set to SHA2 digest (to facilitate OTRS imports)' do
|
|
|
+ it 'does not re-hash before saving' do
|
|
|
+ user.password = "{sha2}#{Digest::SHA2.hexdigest('password')}"
|
|
|
+
|
|
|
+ expect { user.save }.not_to change { user.password }
|
|
|
+ end
|
|
|
+ end
|
|
|
|
|
|
- context '#out_of_office_agent' do
|
|
|
+ context 'when set to Argon2 digest' do
|
|
|
+ it 'does not re-hash before saving' do
|
|
|
+ user.password = PasswordHash.crypt('password')
|
|
|
|
|
|
- it 'responds to out_of_office_agent' do
|
|
|
- user = create(:user)
|
|
|
- expect(user).to respond_to(:out_of_office_agent)
|
|
|
+ expect { user.save }.not_to change { user.password }
|
|
|
+ end
|
|
|
+ end
|
|
|
end
|
|
|
|
|
|
- context 'replacement' do
|
|
|
+ describe '#phone' do
|
|
|
+ subject(:user) { create(:user, phone: orig_number) }
|
|
|
+
|
|
|
+ context 'when included on create' do
|
|
|
+ let(:orig_number) { '1234567890' }
|
|
|
+
|
|
|
+ it 'adds corresponding CallerId record' do
|
|
|
+ expect { user }
|
|
|
+ .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ context 'when added on update' do
|
|
|
+ let(:orig_number) { nil }
|
|
|
+ let(:new_number) { '1234567890' }
|
|
|
+
|
|
|
+ before { user } # create user
|
|
|
|
|
|
- it 'finds assigned' do
|
|
|
- user_replacement = create(:user)
|
|
|
+ it 'adds corresponding CallerId record' do
|
|
|
+ expect { user.update(phone: new_number) }
|
|
|
+ .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ context 'when falsely added on update (change: [nil, ""])' do
|
|
|
+ let(:orig_number) { nil }
|
|
|
+ let(:new_number) { '' }
|
|
|
+
|
|
|
+ before { user } # create user
|
|
|
+
|
|
|
+ it 'does not attempt to update CallerId record' do
|
|
|
+ allow(Cti::CallerId).to receive(:build).with(any_args)
|
|
|
+
|
|
|
+ expect(Cti::CallerId.where(object: 'User', o_id: user.id).count)
|
|
|
+ .to eq(0)
|
|
|
+
|
|
|
+ expect { user.update(phone: new_number) }
|
|
|
+ .to change { Cti::CallerId.where(object: 'User', o_id: user.id).count }.by(0)
|
|
|
+
|
|
|
+ expect(Cti::CallerId).not_to have_received(:build)
|
|
|
+ end
|
|
|
+ end
|
|
|
+
|
|
|
+ context 'when removed on update' do
|
|
|
+ let(:orig_number) { '1234567890' }
|
|
|
+ let(:new_number) { nil }
|
|
|
|
|
|
- user_ooo = create(:user,
|
|
|
- out_of_office: true,
|
|
|
- out_of_office_start_at: Time.zone.yesterday,
|
|
|
- out_of_office_end_at: Time.zone.tomorrow,
|
|
|
- out_of_office_replacement_id: user_replacement.id,)
|
|
|
+ before { user } # create user
|
|
|
|
|
|
- expect(user_ooo.out_of_office_agent).to eq user_replacement
|
|
|
+ it 'removes corresponding CallerId record' do
|
|
|
+ expect { user.update(phone: nil) }
|
|
|
+ .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
|
|
|
+ end
|
|
|
end
|
|
|
|
|
|
- it 'finds none for available users' do
|
|
|
- user = create(:user)
|
|
|
- expect(user.out_of_office_agent).to be nil
|
|
|
+ context 'when changed on update' do
|
|
|
+ let(:orig_number) { '1234567890' }
|
|
|
+ let(:new_number) { orig_number.next }
|
|
|
+
|
|
|
+ before { user } # create user
|
|
|
+
|
|
|
+ it 'replaces CallerId record' do
|
|
|
+ # rubocop:disable Layout/MultilineMethodCallIndentation
|
|
|
+ expect { user.update(phone: new_number) }
|
|
|
+ .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
|
|
|
+ .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
|
|
|
+ # rubocop:enable Layout/MultilineMethodCallIndentation
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '#max_login_failed?' do
|
|
|
+ describe '#max_login_failed?' do
|
|
|
+ it { is_expected.to respond_to(:max_login_failed?) }
|
|
|
|
|
|
- it 'responds to max_login_failed?' do
|
|
|
- user = create(:user)
|
|
|
- expect(user).to respond_to(:max_login_failed?)
|
|
|
- end
|
|
|
+ context 'with password_max_login_failed setting' do
|
|
|
+ before { Setting.set('password_max_login_failed', 5) }
|
|
|
+ before { user.update(login_failed: 5) }
|
|
|
|
|
|
- it 'checks if a user has reached the maximum of failed logins' do
|
|
|
+ it 'returns true once user’s #login_failed count exceeds the setting' do
|
|
|
+ expect { user.update(login_failed: 6) }
|
|
|
+ .to change { user.max_login_failed? }.to(true)
|
|
|
+ end
|
|
|
+ end
|
|
|
|
|
|
- user = create(:user)
|
|
|
- expect(user.max_login_failed?).to be false
|
|
|
+ context 'without password_max_login_failed setting' do
|
|
|
+ before { Setting.set('password_max_login_failed', nil) }
|
|
|
+ before { user.update(login_failed: 0) }
|
|
|
|
|
|
- user.login_failed = 999
|
|
|
- user.save
|
|
|
- expect(user.max_login_failed?).to be true
|
|
|
+ it 'defaults to 0' do
|
|
|
+ expect { user.update(login_failed: 1) }
|
|
|
+ .to change { user.max_login_failed? }.to(true)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '.identify' do
|
|
|
+ describe '#out_of_office_agent' do
|
|
|
+ it { is_expected.to respond_to(:out_of_office_agent) }
|
|
|
|
|
|
- it 'returns users found by login' do
|
|
|
- user = create(:user)
|
|
|
- found_user = User.identify(user.login)
|
|
|
- expect(found_user).to be_an(User)
|
|
|
- expect(found_user.id).to eq user.id
|
|
|
+ context 'when user has no designated substitute' do
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(user.out_of_office_agent).to be(nil)
|
|
|
+ end
|
|
|
end
|
|
|
|
|
|
- it 'returns users found by email' do
|
|
|
- user = create(:user)
|
|
|
- found_user = User.identify(user.email)
|
|
|
- expect(found_user).to be_an(User)
|
|
|
- expect(found_user.id).to eq user.id
|
|
|
+ context 'when user has designated substitute, and is out of office' do
|
|
|
+ let(:substitute) { create(:user) }
|
|
|
+ subject(:user) do
|
|
|
+ create(:user,
|
|
|
+ out_of_office: true,
|
|
|
+ out_of_office_start_at: Time.zone.yesterday,
|
|
|
+ out_of_office_end_at: Time.zone.tomorrow,
|
|
|
+ out_of_office_replacement_id: substitute.id,)
|
|
|
+ end
|
|
|
+
|
|
|
+ it 'returns the designated substitute' do
|
|
|
+ expect(user.out_of_office_agent).to eq(substitute)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '.authenticate' do
|
|
|
+ describe '.authenticate' do
|
|
|
+ subject(:user) { create(:user, password: password) }
|
|
|
+ let(:password) { Faker::Internet.password }
|
|
|
|
|
|
- it 'authenticates by username and password' do
|
|
|
- password = 'zammad'
|
|
|
- user = create(:user, password: password)
|
|
|
- result = described_class.authenticate(user.login, password)
|
|
|
- expect(result).to be_an(User)
|
|
|
+ context 'with valid credentials' do
|
|
|
+ it 'returns the matching user' do
|
|
|
+ expect(described_class.authenticate(user.login, password))
|
|
|
+ .to eq(user)
|
|
|
+ end
|
|
|
end
|
|
|
|
|
|
- context 'failure' do
|
|
|
+ context 'with valid credentials, but exceeding failed login limit' do
|
|
|
+ before { user.update(login_failed: 999) }
|
|
|
|
|
|
- it 'increases login_failed on failed logins' do
|
|
|
- user = create(:user)
|
|
|
- expect do
|
|
|
- described_class.authenticate(user.login, 'wrongpw')
|
|
|
- user.reload
|
|
|
- end
|
|
|
- .to change { user.login_failed }.by(1)
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.authenticate(user.login, password))
|
|
|
+ .to be(nil)
|
|
|
end
|
|
|
+ end
|
|
|
|
|
|
- it 'fails for unknown users' do
|
|
|
- result = described_class.authenticate('john.doe', 'zammad')
|
|
|
- expect(result).to be nil
|
|
|
+ context 'with valid user and invalid password' do
|
|
|
+ it 'increments failed login count' do
|
|
|
+ expect { described_class.authenticate(user.login, password.next) }
|
|
|
+ .to change { user.reload.login_failed }.by(1)
|
|
|
end
|
|
|
|
|
|
- it 'fails for inactive users' do
|
|
|
- user = create(:user, active: false)
|
|
|
- result = described_class.authenticate(user.login, 'zammad')
|
|
|
- expect(result).to be nil
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.authenticate(user.login, password.next)).to be(nil)
|
|
|
end
|
|
|
+ end
|
|
|
|
|
|
- it 'fails for users with too many failed logins' do
|
|
|
- user = create(:user, login_failed: 999)
|
|
|
- result = described_class.authenticate(user.login, 'zammad')
|
|
|
- expect(result).to be nil
|
|
|
+ context 'with inactive user’s login' do
|
|
|
+ before { user.update(active: false) }
|
|
|
+
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.authenticate(user.login, password)).to be(nil)
|
|
|
end
|
|
|
+ end
|
|
|
|
|
|
- it 'fails for wrong passwords' do
|
|
|
- user = create(:user)
|
|
|
- result = described_class.authenticate(user.login, 'wrongpw')
|
|
|
- expect(result).to be nil
|
|
|
+ context 'with non-existent user login' do
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.authenticate('john.doe', password)).to be(nil)
|
|
|
end
|
|
|
+ end
|
|
|
|
|
|
- it 'fails for empty username parameter' do
|
|
|
- result = described_class.authenticate('', 'zammad')
|
|
|
- expect(result).to be nil
|
|
|
+ context 'with empty login string' do
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.authenticate('', password)).to be(nil)
|
|
|
end
|
|
|
+ end
|
|
|
|
|
|
- it 'fails for empty password parameter' do
|
|
|
- result = described_class.authenticate('username', '')
|
|
|
- expect(result).to be nil
|
|
|
+ context 'with empty password string' do
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.authenticate(user.login, '')).to be(nil)
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '#by_reset_token' do
|
|
|
+ describe '#by_reset_token' do
|
|
|
+ let(:token) { create(:token_password_reset) }
|
|
|
+ subject(:user) { token.user }
|
|
|
|
|
|
- it 'returns a User instance for existing tokens' do
|
|
|
- token = create(:token_password_reset)
|
|
|
- expect(described_class.by_reset_token(token.name)).to be_instance_of(described_class)
|
|
|
+ context 'with a valid token' do
|
|
|
+ it 'returns the matching user' do
|
|
|
+ expect(described_class.by_reset_token(token.name)).to eq(user)
|
|
|
+ end
|
|
|
end
|
|
|
|
|
|
- it 'returns nil for not existing tokens' do
|
|
|
- expect(described_class.by_reset_token('not-existing')).to be nil
|
|
|
+ context 'with an invalid token' do
|
|
|
+ it 'returns nil' do
|
|
|
+ expect(described_class.by_reset_token('not-existing')).to be(nil)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '#password_reset_via_token' do
|
|
|
+ describe '#password_reset_via_token' do
|
|
|
+ let!(:token) { create(:token_password_reset) }
|
|
|
+ subject(:user) { token.user }
|
|
|
|
|
|
it 'changes the password of the token user and destroys the token' do
|
|
|
- token = create(:token_password_reset)
|
|
|
- user = User.find(token.user_id)
|
|
|
-
|
|
|
- expect do
|
|
|
- described_class.password_reset_via_token(token.name, new_password)
|
|
|
- user.reload
|
|
|
- end.to change {
|
|
|
- user.password
|
|
|
- }.and change {
|
|
|
- Token.count
|
|
|
- }.by(-1)
|
|
|
+ expect { described_class.password_reset_via_token(token.name, Faker::Internet.password) }
|
|
|
+ .to change { user.reload.password }
|
|
|
+ .and change { Token.count }.by(-1)
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context 'import' do
|
|
|
-
|
|
|
- it "doesn't change imported passwords" do
|
|
|
-
|
|
|
- # mock settings calls
|
|
|
- expect(Setting).to receive(:get).with('import_mode').and_return(true)
|
|
|
- allow(Setting).to receive(:get)
|
|
|
+ describe '.identify' do
|
|
|
+ it 'returns users by given login' do
|
|
|
+ expect(User.identify(user.login)).to eq(user)
|
|
|
+ end
|
|
|
|
|
|
- user = build(:user, password: '{sha2}dd9c764fa7ea18cd992c8600006d3dc3ac983d1ba22e9ba2d71f6207456be0ba') # zammad
|
|
|
- expect do
|
|
|
- user.save
|
|
|
- end.to_not change {
|
|
|
- user.password
|
|
|
- }
|
|
|
+ it 'returns users by given email' do
|
|
|
+ expect(User.identify(user.email)).to eq(user)
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '.access?' do
|
|
|
+ describe '#access?' do
|
|
|
|
|
|
let(:role_with_admin_user_permissions) do
|
|
|
create(:role).tap do |role|
|
|
@@ -533,7 +604,7 @@ RSpec.describe User do
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context 'agent limit' do
|
|
|
+ describe 'system-wide agent limit' do
|
|
|
|
|
|
def current_agent_count
|
|
|
User.with_permissions('ticket.agent').count
|
|
@@ -542,323 +613,153 @@ RSpec.describe User do
|
|
|
let(:agent_role) { Role.lookup(name: 'Agent') }
|
|
|
let(:admin_role) { Role.lookup(name: 'Admin') }
|
|
|
|
|
|
- context '#validate_agent_limit_by_role' do
|
|
|
+ describe '#validate_agent_limit_by_role' do
|
|
|
+ context 'for Integer value of system_agent_limit' do
|
|
|
+ context 'before exceeding the agent limit' do
|
|
|
+ before { Setting.set('system_agent_limit', current_agent_count + 1) }
|
|
|
|
|
|
- context 'agent creation limit not reached' do
|
|
|
-
|
|
|
- it 'grants agent creation' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count + 1)
|
|
|
-
|
|
|
- expect do
|
|
|
- create(:agent_user)
|
|
|
- end.to change {
|
|
|
- current_agent_count
|
|
|
- }.by(1)
|
|
|
- end
|
|
|
-
|
|
|
- it 'grants role change' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count + 1)
|
|
|
-
|
|
|
- future_agent = create(:customer_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- future_agent.roles = [agent_role]
|
|
|
- end.to change {
|
|
|
- current_agent_count
|
|
|
- }.by(1)
|
|
|
- end
|
|
|
-
|
|
|
- context 'role updates' do
|
|
|
-
|
|
|
- it 'grants update by instances' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count + 1)
|
|
|
-
|
|
|
- agent = create(:agent_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- agent.roles = [
|
|
|
- admin_role,
|
|
|
- agent_role
|
|
|
- ]
|
|
|
- agent.save!
|
|
|
- end.not_to raise_error
|
|
|
- end
|
|
|
-
|
|
|
- it 'grants update by id (Integer)' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count + 1)
|
|
|
-
|
|
|
- agent = create(:agent_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- agent.role_ids = [
|
|
|
- admin_role.id,
|
|
|
- agent_role.id
|
|
|
- ]
|
|
|
- agent.save!
|
|
|
- end.not_to raise_error
|
|
|
+ it 'grants agent creation' do
|
|
|
+ expect { create(:agent_user) }
|
|
|
+ .to change { current_agent_count }.by(1)
|
|
|
end
|
|
|
|
|
|
- it 'grants update by id (String)' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count + 1)
|
|
|
+ it 'grants role change' do
|
|
|
+ future_agent = create(:customer_user)
|
|
|
|
|
|
- agent = create(:agent_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- agent.role_ids = [
|
|
|
- admin_role.id.to_s,
|
|
|
- agent_role.id.to_s
|
|
|
- ]
|
|
|
- agent.save!
|
|
|
- end.not_to raise_error
|
|
|
+ expect { future_agent.roles = [agent_role] }
|
|
|
+ .to change { current_agent_count }.by(1)
|
|
|
end
|
|
|
- end
|
|
|
- end
|
|
|
-
|
|
|
- context 'agent creation limit surpassing prevention' do
|
|
|
-
|
|
|
- it 'creation of new agents' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count + 2)
|
|
|
-
|
|
|
- create_list(:agent_user, 2)
|
|
|
-
|
|
|
- initial_agent_count = current_agent_count
|
|
|
-
|
|
|
- expect do
|
|
|
- create(:agent_user)
|
|
|
- end.to raise_error(Exceptions::UnprocessableEntity)
|
|
|
-
|
|
|
- expect(current_agent_count).to eq(initial_agent_count)
|
|
|
- end
|
|
|
-
|
|
|
- it 'prevents role change' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count)
|
|
|
|
|
|
- future_agent = create(:customer_user)
|
|
|
+ describe 'role updates' do
|
|
|
+ let(:agent) { create(:agent_user) }
|
|
|
|
|
|
- initial_agent_count = current_agent_count
|
|
|
+ it 'grants update by instances' do
|
|
|
+ expect { agent.roles = [admin_role, agent_role] }
|
|
|
+ .not_to raise_error
|
|
|
+ end
|
|
|
|
|
|
- expect do
|
|
|
- future_agent.roles = [agent_role]
|
|
|
- end.to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ it 'grants update by id (Integer)' do
|
|
|
+ expect { agent.role_ids = [admin_role.id, agent_role.id] }
|
|
|
+ .not_to raise_error
|
|
|
+ end
|
|
|
|
|
|
- expect(current_agent_count).to eq(initial_agent_count)
|
|
|
+ it 'grants update by id (String)' do
|
|
|
+ expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
|
|
|
+ .not_to raise_error
|
|
|
+ end
|
|
|
+ end
|
|
|
end
|
|
|
- end
|
|
|
- end
|
|
|
|
|
|
- context '#validate_agent_limit_by_attributes' do
|
|
|
+ context 'when exceeding the agent limit' do
|
|
|
+ it 'creation of new agents' do
|
|
|
+ Setting.set('system_agent_limit', current_agent_count + 2)
|
|
|
|
|
|
- context 'agent creation limit surpassing prevention' do
|
|
|
+ create_list(:agent_user, 2)
|
|
|
|
|
|
- it 'prevents re-activation of agents' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count)
|
|
|
-
|
|
|
- inactive_agent = create(:agent_user, active: false)
|
|
|
+ expect { create(:agent_user) }
|
|
|
+ .to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ .and change { current_agent_count }.by(0)
|
|
|
+ end
|
|
|
|
|
|
- initial_agent_count = current_agent_count
|
|
|
+ it 'prevents role change' do
|
|
|
+ Setting.set('system_agent_limit', current_agent_count)
|
|
|
|
|
|
- expect do
|
|
|
- inactive_agent.update!(active: true)
|
|
|
- end.to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ future_agent = create(:customer_user)
|
|
|
|
|
|
- expect(current_agent_count).to eq(initial_agent_count)
|
|
|
+ expect { future_agent.roles = [agent_role] }
|
|
|
+ .to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ .and change { current_agent_count }.by(0)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
- end
|
|
|
-
|
|
|
- context '#validate_agent_limit_by_role by string' do
|
|
|
-
|
|
|
- context 'agent creation limit not reached' do
|
|
|
|
|
|
- it 'grants agent creation' do
|
|
|
- Setting.set('system_agent_limit', (current_agent_count + 1).to_s)
|
|
|
+ context 'for String value of system_agent_limit' do
|
|
|
+ context 'before exceeding the agent limit' do
|
|
|
+ before { Setting.set('system_agent_limit', (current_agent_count + 1).to_s) }
|
|
|
|
|
|
- expect do
|
|
|
- create(:agent_user)
|
|
|
- end.to change {
|
|
|
- current_agent_count
|
|
|
- }.by(1)
|
|
|
- end
|
|
|
-
|
|
|
- it 'grants role change' do
|
|
|
- Setting.set('system_agent_limit', (current_agent_count + 1).to_s)
|
|
|
-
|
|
|
- future_agent = create(:customer_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- future_agent.roles = [agent_role]
|
|
|
- end.to change {
|
|
|
- current_agent_count
|
|
|
- }.by(1)
|
|
|
- end
|
|
|
-
|
|
|
- context 'role updates' do
|
|
|
-
|
|
|
- it 'grants update by instances' do
|
|
|
- Setting.set('system_agent_limit', (current_agent_count + 1).to_s)
|
|
|
-
|
|
|
- agent = create(:agent_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- agent.roles = [
|
|
|
- admin_role,
|
|
|
- agent_role
|
|
|
- ]
|
|
|
- agent.save!
|
|
|
- end.not_to raise_error
|
|
|
+ it 'grants agent creation' do
|
|
|
+ expect { create(:agent_user) }
|
|
|
+ .to change { current_agent_count }.by(1)
|
|
|
end
|
|
|
|
|
|
- it 'grants update by id (Integer)' do
|
|
|
- Setting.set('system_agent_limit', (current_agent_count + 1).to_s)
|
|
|
+ it 'grants role change' do
|
|
|
+ future_agent = create(:customer_user)
|
|
|
|
|
|
- agent = create(:agent_user)
|
|
|
-
|
|
|
- expect do
|
|
|
- agent.role_ids = [
|
|
|
- admin_role.id,
|
|
|
- agent_role.id
|
|
|
- ]
|
|
|
- agent.save!
|
|
|
- end.not_to raise_error
|
|
|
+ expect { future_agent.roles = [agent_role] }
|
|
|
+ .to change { current_agent_count }.by(1)
|
|
|
end
|
|
|
|
|
|
- it 'grants update by id (String)' do
|
|
|
- Setting.set('system_agent_limit', (current_agent_count + 1).to_s)
|
|
|
+ describe 'role updates' do
|
|
|
+ let(:agent) { create(:agent_user) }
|
|
|
+
|
|
|
+ it 'grants update by instances' do
|
|
|
+ expect { agent.roles = [admin_role, agent_role] }
|
|
|
+ .not_to raise_error
|
|
|
+ end
|
|
|
|
|
|
- agent = create(:agent_user)
|
|
|
+ it 'grants update by id (Integer)' do
|
|
|
+ expect { agent.role_ids = [admin_role.id, agent_role.id] }
|
|
|
+ .not_to raise_error
|
|
|
+ end
|
|
|
|
|
|
- expect do
|
|
|
- agent.role_ids = [
|
|
|
- admin_role.id.to_s,
|
|
|
- agent_role.id.to_s
|
|
|
- ]
|
|
|
- agent.save!
|
|
|
- end.not_to raise_error
|
|
|
+ it 'grants update by id (String)' do
|
|
|
+ expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
|
|
|
+ .not_to raise_error
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
- end
|
|
|
-
|
|
|
- context 'agent creation limit surpassing prevention' do
|
|
|
-
|
|
|
- it 'creation of new agents' do
|
|
|
- Setting.set('system_agent_limit', (current_agent_count + 2).to_s)
|
|
|
|
|
|
- create_list(:agent_user, 2)
|
|
|
-
|
|
|
- initial_agent_count = current_agent_count
|
|
|
-
|
|
|
- expect do
|
|
|
- create(:agent_user)
|
|
|
- end.to raise_error(Exceptions::UnprocessableEntity)
|
|
|
-
|
|
|
- expect(current_agent_count).to eq(initial_agent_count)
|
|
|
- end
|
|
|
+ context 'when exceeding the agent limit' do
|
|
|
+ it 'creation of new agents' do
|
|
|
+ Setting.set('system_agent_limit', (current_agent_count + 2).to_s)
|
|
|
|
|
|
- it 'prevents role change' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count.to_s)
|
|
|
+ create_list(:agent_user, 2)
|
|
|
|
|
|
- future_agent = create(:customer_user)
|
|
|
+ expect { create(:agent_user) }
|
|
|
+ .to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ .and change { current_agent_count }.by(0)
|
|
|
+ end
|
|
|
|
|
|
- initial_agent_count = current_agent_count
|
|
|
+ it 'prevents role change' do
|
|
|
+ Setting.set('system_agent_limit', current_agent_count.to_s)
|
|
|
|
|
|
- expect do
|
|
|
- future_agent.roles = [agent_role]
|
|
|
- end.to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ future_agent = create(:customer_user)
|
|
|
|
|
|
- expect(current_agent_count).to eq(initial_agent_count)
|
|
|
+ expect { future_agent.roles = [agent_role] }
|
|
|
+ .to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ .and change { current_agent_count }.by(0)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
end
|
|
|
|
|
|
- context '#validate_agent_limit_by_attributes' do
|
|
|
-
|
|
|
- context 'agent creation limit surpassing prevention' do
|
|
|
-
|
|
|
- it 'prevents re-activation of agents' do
|
|
|
- Setting.set('system_agent_limit', current_agent_count.to_s)
|
|
|
-
|
|
|
- inactive_agent = create(:agent_user, active: false)
|
|
|
+ describe '#validate_agent_limit_by_attributes' do
|
|
|
+ context 'for Integer value of system_agent_limit' do
|
|
|
+ before { Setting.set('system_agent_limit', current_agent_count) }
|
|
|
|
|
|
- initial_agent_count = current_agent_count
|
|
|
+ context 'when exceeding the agent limit' do
|
|
|
+ it 'prevents re-activation of agents' do
|
|
|
+ inactive_agent = create(:agent_user, active: false)
|
|
|
|
|
|
- expect do
|
|
|
- inactive_agent.update!(active: true)
|
|
|
- end.to raise_error(Exceptions::UnprocessableEntity)
|
|
|
-
|
|
|
- expect(current_agent_count).to eq(initial_agent_count)
|
|
|
+ expect { inactive_agent.update!(active: true) }
|
|
|
+ .to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ .and change { current_agent_count }.by(0)
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
- end
|
|
|
-
|
|
|
- end
|
|
|
-
|
|
|
- context 'when phone attribute' do
|
|
|
- let(:user_attrs) { { phone: orig_number } }
|
|
|
-
|
|
|
- context 'included on create' do
|
|
|
- let(:orig_number) { '1234567890' }
|
|
|
-
|
|
|
- it 'adds corresponding CallerId record' do
|
|
|
- expect { subject }
|
|
|
- .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
|
|
|
- end
|
|
|
- end
|
|
|
-
|
|
|
- context 'added on update' do
|
|
|
- let(:orig_number) { nil }
|
|
|
- let(:new_number) { '1234567890' }
|
|
|
|
|
|
- before { subject } # create user
|
|
|
+ context 'for String value of system_agent_limit' do
|
|
|
+ before { Setting.set('system_agent_limit', current_agent_count.to_s) }
|
|
|
|
|
|
- it 'adds corresponding CallerId record' do
|
|
|
- expect { subject.update(phone: new_number) }
|
|
|
- .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
|
|
|
- end
|
|
|
- end
|
|
|
+ context 'when exceeding the agent limit' do
|
|
|
+ it 'prevents re-activation of agents' do
|
|
|
+ inactive_agent = create(:agent_user, active: false)
|
|
|
|
|
|
- context 'falsely added on update (change: [nil, ""])' do
|
|
|
- let(:orig_number) { nil }
|
|
|
- let(:new_number) { '' }
|
|
|
-
|
|
|
- before { subject } # create user
|
|
|
-
|
|
|
- it 'does not attempt to update CallerId record' do
|
|
|
- allow(Cti::CallerId).to receive(:build).with(any_args)
|
|
|
-
|
|
|
- expect(Cti::CallerId.where(object: 'User', o_id: subject.id).count)
|
|
|
- .to eq(0)
|
|
|
-
|
|
|
- expect { subject.update(phone: new_number) }
|
|
|
- .to change { Cti::CallerId.where(object: 'User', o_id: subject.id).count }.by(0)
|
|
|
-
|
|
|
- expect(Cti::CallerId).not_to have_received(:build)
|
|
|
- end
|
|
|
- end
|
|
|
-
|
|
|
- context 'removed on update' do
|
|
|
- let(:orig_number) { '1234567890' }
|
|
|
- let(:new_number) { nil }
|
|
|
-
|
|
|
- before { subject } # create user
|
|
|
-
|
|
|
- it 'removes corresponding CallerId record' do
|
|
|
- expect { subject.update(phone: nil) }
|
|
|
- .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
|
|
|
- end
|
|
|
- end
|
|
|
-
|
|
|
- context 'changed on update' do
|
|
|
- let(:orig_number) { '1234567890' }
|
|
|
- let(:new_number) { orig_number.next }
|
|
|
-
|
|
|
- before { subject } # create user
|
|
|
-
|
|
|
- it 'replaces CallerId record' do
|
|
|
- # rubocop:disable Layout/MultilineMethodCallIndentation
|
|
|
- expect { subject.update(phone: new_number) }
|
|
|
- .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
|
|
|
- .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
|
|
|
- # rubocop:enable Layout/MultilineMethodCallIndentation
|
|
|
+ expect { inactive_agent.update!(active: true) }
|
|
|
+ .to raise_error(Exceptions::UnprocessableEntity)
|
|
|
+ .and change { current_agent_count }.by(0)
|
|
|
+ end
|
|
|
+ end
|
|
|
end
|
|
|
end
|
|
|
end
|