Refactor user_spec.rb

This commit is contained in:
Ryan Lue 2018-12-13 10:10:32 +01:00
parent d056feae94
commit ad19b532a9
6 changed files with 501 additions and 604 deletions

View file

@ -1,6 +1,6 @@
FactoryBot.define do FactoryBot.define do
factory :token do factory :token do
user_id { FactoryBot.create(:user).id } user
end end
factory :token_password_reset, parent: :token do factory :token_password_reset, parent: :token do

View file

@ -1,12 +1,6 @@
# Requires: let(:group_access_instance) { ... } RSpec.shared_examples 'HasGroups' do |group_access_factory:|
# Requires: let(:new_group_access_instance) { ... }
RSpec.shared_examples 'HasGroups' do
context 'group' do context 'group' do
let(:group_access_instance_inactive) do subject { create(group_access_factory) }
group_access_instance.update!(active: false)
group_access_instance
end
let(:group_full) { create(:group) } let(:group_full) { create(:group) }
let(:group_read) { create(:group) } let(:group_read) { create(:group) }
let(:group_inactive) { create(:group, active: false) } let(:group_inactive) { create(:group, active: false) }
@ -22,7 +16,7 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'instance responds to group_through_identifier method' do it 'instance responds to group_through_identifier method' do
expect(group_access_instance).to respond_to(described_class.group_through_identifier) expect(subject).to respond_to(described_class.group_through_identifier)
end end
end end
@ -40,19 +34,19 @@ RSpec.shared_examples 'HasGroups' do
context '#groups' do context '#groups' do
it 'responds to groups' do it 'responds to groups' do
expect(group_access_instance).to respond_to(:groups) expect(subject).to respond_to(:groups)
end end
context '#groups.access' do context '#groups.access' do
it 'responds to groups.access' do it 'responds to groups.access' do
expect(group_access_instance.groups).to respond_to(:access) expect(subject.groups).to respond_to(:access)
end end
context 'result' do context 'result' do
before(:each) do before(:each) do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
group_inactive.name => 'change', group_inactive.name => 'change',
@ -60,23 +54,23 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'returns all related Groups' do it 'returns all related Groups' do
expect(group_access_instance.groups.access.size).to eq(3) expect(subject.groups.access.size).to eq(3)
end end
it 'adds join table attribute(s like) access' do it 'adds join table attribute(s like) access' do
expect(group_access_instance.groups.access.first).to respond_to(:access) expect(subject.groups.access.first).to respond_to(:access)
end end
it 'filters for given access parameter' do it 'filters for given access parameter' do
expect(group_access_instance.groups.access('read')).to include(group_read) expect(subject.groups.access('read')).to include(group_read)
end end
it 'filters for given access list parameter' do it 'filters for given access list parameter' do
expect(group_access_instance.groups.access('read', 'change')).to include(group_read, group_inactive) expect(subject.groups.access('read', 'change')).to include(group_read, group_inactive)
end end
it 'always includes full access groups' do it 'always includes full access groups' do
expect(group_access_instance.groups.access('read')).to include(group_full) expect(subject.groups.access('read')).to include(group_full)
end end
end end
end end
@ -85,11 +79,11 @@ RSpec.shared_examples 'HasGroups' do
context '#group_access?' do context '#group_access?' do
it 'responds to group_access?' do it 'responds to group_access?' do
expect(group_access_instance).to respond_to(:group_access?) expect(subject).to respond_to(:group_access?)
end end
before(:each) do before(:each) do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
end end
@ -107,61 +101,65 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'prevents inactive Group' do it 'prevents inactive Group' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_inactive.name => 'read', group_inactive.name => 'read',
} }
expect(group_access_instance.group_access?(group_inactive.id, 'read')).to be false expect(subject.group_access?(group_inactive.id, 'read')).to be false
end end
it 'prevents inactive instances' do it 'prevents inactive instances' do
group_access_instance_inactive.group_names_access_map = { subject.update!(active: false)
subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
expect(group_access_instance_inactive.group_access?(group_read.id, 'read')).to be false expect(subject.group_access?(group_read.id, 'read')).to be false
end end
end end
context '#group_ids_access' do context '#group_ids_access' do
it 'responds to group_ids_access' do it 'responds to group_ids_access' do
expect(group_access_instance).to respond_to(:group_ids_access) expect(subject).to respond_to(:group_ids_access)
end end
before(:each) do before(:each) do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
end end
it 'lists only active Group IDs' do it 'lists only active Group IDs' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
group_inactive.name => 'read', group_inactive.name => 'read',
} }
result = group_access_instance.group_ids_access('read') result = subject.group_ids_access('read')
expect(result).not_to include(group_inactive.id) expect(result).not_to include(group_inactive.id)
end end
it "doesn't list for inactive instances" do it "doesn't list for inactive instances" do
group_access_instance_inactive.group_names_access_map = { subject.update!(active: false)
subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
expect(group_access_instance_inactive.group_ids_access('read')).to be_empty expect(subject.group_ids_access('read')).to be_empty
end end
context 'single access' do context 'single access' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = group_access_instance.group_ids_access('read') result = subject.group_ids_access('read')
expect(result).to include(group_read.id) expect(result).to include(group_read.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = group_access_instance.group_ids_access('change') result = subject.group_ids_access('change')
expect(result).not_to include(group_read.id) expect(result).not_to include(group_read.id)
end end
end end
@ -169,12 +167,12 @@ RSpec.shared_examples 'HasGroups' do
context 'access list' do context 'access list' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = group_access_instance.group_ids_access(%w[read change]) result = subject.group_ids_access(%w[read change])
expect(result).to include(group_read.id) expect(result).to include(group_read.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = group_access_instance.group_ids_access(%w[change create]) result = subject.group_ids_access(%w[change create])
expect(result).not_to include(group_read.id) expect(result).not_to include(group_read.id)
end end
end end
@ -183,19 +181,19 @@ RSpec.shared_examples 'HasGroups' do
context '#groups_access' do context '#groups_access' do
it 'responds to groups_access' do it 'responds to groups_access' do
expect(group_access_instance).to respond_to(:groups_access) expect(subject).to respond_to(:groups_access)
end end
it 'wraps #group_ids_access' do it 'wraps #group_ids_access' do
expect(group_access_instance).to receive(:group_ids_access) expect(subject).to receive(:group_ids_access)
group_access_instance.groups_access('read') subject.groups_access('read')
end end
it 'returns Groups' do it 'returns Groups' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
result = group_access_instance.groups_access('read') result = subject.groups_access('read')
expect(result).to include(group_read) expect(result).to include(group_read)
end end
end end
@ -203,14 +201,14 @@ RSpec.shared_examples 'HasGroups' do
context '#group_names_access_map=' do context '#group_names_access_map=' do
it 'responds to group_names_access_map=' do it 'responds to group_names_access_map=' do
expect(group_access_instance).to respond_to(:group_names_access_map=) expect(subject).to respond_to(:group_names_access_map=)
end end
context 'existing instance' do context 'existing instance' do
it 'stores Hash with String values' do it 'stores Hash with String values' do
expect do expect do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
} }
@ -221,7 +219,7 @@ RSpec.shared_examples 'HasGroups' do
it 'stores Hash with Array<String> values' do it 'stores Hash with Array<String> values' do
expect do expect do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => %w[read change], group_read.name => %w[read change],
} }
@ -231,13 +229,13 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'allows empty Hash value' do it 'allows empty Hash value' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => %w[read change], group_read.name => %w[read change],
} }
expect do expect do
group_access_instance.group_names_access_map = {} subject.group_names_access_map = {}
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(-3) }.by(-3)
@ -249,13 +247,13 @@ RSpec.shared_examples 'HasGroups' do
exception = ActiveRecord::RecordInvalid exception = ActiveRecord::RecordInvalid
expect do expect do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => invalid_combination, group_full.name => invalid_combination,
} }
end.to raise_error(exception) end.to raise_error(exception)
expect do expect do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => invalid_combination.reverse, group_full.name => invalid_combination.reverse,
} }
end.to raise_error(exception) end.to raise_error(exception)
@ -263,10 +261,11 @@ RSpec.shared_examples 'HasGroups' do
end end
context 'new instance' do context 'new instance' do
subject { build(group_access_factory) }
it "doesn't store directly" do it "doesn't store directly" do
expect do expect do
new_group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
} }
@ -277,12 +276,12 @@ RSpec.shared_examples 'HasGroups' do
it 'stores after save' do it 'stores after save' do
expect do expect do
new_group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
} }
new_group_access_instance.save subject.save
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(2) }.by(2)
@ -290,9 +289,9 @@ RSpec.shared_examples 'HasGroups' do
it 'allows empty Hash value' do it 'allows empty Hash value' do
expect do expect do
new_group_access_instance.group_names_access_map = {} subject.group_names_access_map = {}
new_group_access_instance.save subject.save
end.not_to change { end.not_to change {
described_class.group_through.klass.count described_class.group_through.klass.count
} }
@ -303,7 +302,7 @@ RSpec.shared_examples 'HasGroups' do
context '#group_names_access_map' do context '#group_names_access_map' do
it 'responds to group_names_access_map' do it 'responds to group_names_access_map' do
expect(group_access_instance).to respond_to(:group_names_access_map) expect(subject).to respond_to(:group_names_access_map)
end end
it 'returns instance Group name => access relations as Hash' do it 'returns instance Group name => access relations as Hash' do
@ -312,44 +311,46 @@ RSpec.shared_examples 'HasGroups' do
group_read.name => ['read'], group_read.name => ['read'],
} }
group_access_instance.group_names_access_map = expected subject.group_names_access_map = expected
expect(group_access_instance.group_names_access_map).to eq(expected) expect(subject.group_names_access_map).to eq(expected)
end end
it "doesn't map for inactive instances" do it "doesn't map for inactive instances" do
group_access_instance_inactive.group_names_access_map = { subject.update!(active: false)
subject.group_names_access_map = {
group_full.name => ['full'], group_full.name => ['full'],
group_read.name => ['read'], group_read.name => ['read'],
} }
expect(group_access_instance_inactive.group_names_access_map).to be_empty expect(subject.group_names_access_map).to be_empty
end end
it 'returns empty map if none is stored' do it 'returns empty map if none is stored' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
} }
group_access_instance.group_names_access_map = {} subject.group_names_access_map = {}
expect(group_access_instance.group_names_access_map).to be_blank expect(subject.group_names_access_map).to be_blank
end end
end end
context '#group_ids_access_map=' do context '#group_ids_access_map=' do
it 'responds to group_ids_access_map=' do it 'responds to group_ids_access_map=' do
expect(group_access_instance).to respond_to(:group_ids_access_map=) expect(subject).to respond_to(:group_ids_access_map=)
end end
context 'existing instance' do context 'existing instance' do
it 'stores Hash with String values' do it 'stores Hash with String values' do
expect do expect do
group_access_instance.group_ids_access_map = { subject.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
} }
@ -360,7 +361,7 @@ RSpec.shared_examples 'HasGroups' do
it 'stores Hash with Array<String> values' do it 'stores Hash with Array<String> values' do
expect do expect do
group_access_instance.group_ids_access_map = { subject.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => %w[read change], group_read.id => %w[read change],
} }
@ -370,13 +371,13 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'allows empty Hash value' do it 'allows empty Hash value' do
group_access_instance.group_ids_access_map = { subject.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => %w[read change], group_read.id => %w[read change],
} }
expect do expect do
group_access_instance.group_ids_access_map = {} subject.group_ids_access_map = {}
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(-3) }.by(-3)
@ -384,10 +385,11 @@ RSpec.shared_examples 'HasGroups' do
end end
context 'new instance' do context 'new instance' do
subject { build(group_access_factory) }
it "doesn't store directly" do it "doesn't store directly" do
expect do expect do
new_group_access_instance.group_ids_access_map = { subject.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
} }
@ -398,12 +400,12 @@ RSpec.shared_examples 'HasGroups' do
it 'stores after save' do it 'stores after save' do
expect do expect do
new_group_access_instance.group_ids_access_map = { subject.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
} }
new_group_access_instance.save subject.save
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(2) }.by(2)
@ -411,9 +413,9 @@ RSpec.shared_examples 'HasGroups' do
it 'allows empty Hash value' do it 'allows empty Hash value' do
expect do expect do
new_group_access_instance.group_ids_access_map = {} subject.group_ids_access_map = {}
new_group_access_instance.save subject.save
end.not_to change { end.not_to change {
described_class.group_through.klass.count described_class.group_through.klass.count
} }
@ -424,7 +426,7 @@ RSpec.shared_examples 'HasGroups' do
context '#group_ids_access_map' do context '#group_ids_access_map' do
it 'responds to group_ids_access_map' do it 'responds to group_ids_access_map' do
expect(group_access_instance).to respond_to(:group_ids_access_map) expect(subject).to respond_to(:group_ids_access_map)
end end
it 'returns instance Group ID => access relations as Hash' do it 'returns instance Group ID => access relations as Hash' do
@ -433,30 +435,32 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
group_access_instance.group_ids_access_map = expected subject.group_ids_access_map = expected
expect(group_access_instance.group_ids_access_map).to eq(expected) expect(subject.group_ids_access_map).to eq(expected)
end end
it "doesn't map for inactive instances" do it "doesn't map for inactive instances" do
group_access_instance_inactive.group_ids_access_map = { subject.update!(active: false)
subject.group_ids_access_map = {
group_full.id => ['full'], group_full.id => ['full'],
group_read.id => ['read'], group_read.id => ['read'],
} }
expect(group_access_instance_inactive.group_ids_access_map).to be_empty expect(subject.group_ids_access_map).to be_empty
end end
it 'returns empty map if none is stored' do it 'returns empty map if none is stored' do
group_access_instance.group_ids_access_map = { subject.group_ids_access_map = {
group_full.id => 'full', group_full.id => 'full',
group_read.id => 'read', group_read.id => 'read',
} }
group_access_instance.group_ids_access_map = {} subject.group_ids_access_map = {}
expect(group_access_instance.group_ids_access_map).to be_blank expect(subject.group_ids_access_map).to be_blank
end end
end end
@ -468,8 +472,8 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
group_access_instance.associations_from_param(group_ids: expected) subject.associations_from_param(group_ids: expected)
expect(group_access_instance.group_ids_access_map).to eq(expected) expect(subject.group_ids_access_map).to eq(expected)
end end
it 'handles groups parameter as group_names_access_map' do it 'handles groups parameter as group_names_access_map' do
@ -478,8 +482,8 @@ RSpec.shared_examples 'HasGroups' do
group_read.name => ['read'], group_read.name => ['read'],
} }
group_access_instance.associations_from_param(groups: expected) subject.associations_from_param(groups: expected)
expect(group_access_instance.group_names_access_map).to eq(expected) expect(subject.group_names_access_map).to eq(expected)
end end
end end
@ -491,9 +495,9 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
group_access_instance.group_ids_access_map = expected subject.group_ids_access_map = expected
result = group_access_instance.attributes_with_association_ids result = subject.attributes_with_association_ids
expect(result['group_ids']).to eq(expected) expect(result['group_ids']).to eq(expected)
end end
end end
@ -506,9 +510,9 @@ RSpec.shared_examples 'HasGroups' do
group_read.id => ['read'], group_read.id => ['read'],
} }
group_access_instance.group_ids_access_map = expected subject.group_ids_access_map = expected
result = group_access_instance.attributes_with_association_names result = subject.attributes_with_association_names
expect(result['group_ids']).to eq(expected) expect(result['group_ids']).to eq(expected)
end end
@ -518,9 +522,9 @@ RSpec.shared_examples 'HasGroups' do
group_read.name => ['read'], group_read.name => ['read'],
} }
group_access_instance.group_names_access_map = expected subject.group_names_access_map = expected
result = group_access_instance.attributes_with_association_names result = subject.attributes_with_association_names
expect(result['groups']).to eq(expected) expect(result['groups']).to eq(expected)
end end
end end
@ -532,18 +536,20 @@ RSpec.shared_examples 'HasGroups' do
end end
before(:each) do before(:each) do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
end end
it 'lists only active instances' do it 'lists only active instances' do
group_access_instance_inactive.group_names_access_map = { subject.update!(active: false)
subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
result = described_class.group_access(group_read.id, 'read') result = described_class.group_access(group_read.id, 'read')
expect(result).not_to include(group_access_instance_inactive) expect(result).not_to include(subject)
end end
context 'Group ID parameter' do context 'Group ID parameter' do
@ -571,24 +577,24 @@ RSpec.shared_examples 'HasGroups' do
end end
it 'returns class instances' do it 'returns class instances' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
result = described_class.group_access_ids(group_read, 'read') result = described_class.group_access_ids(group_read, 'read')
expect(result).to include(group_access_instance.id) expect(result).to include(subject.id)
end end
end end
it 'destroys relations before instance gets destroyed' do it 'destroys relations before instance gets destroyed' do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_full.name => 'full', group_full.name => 'full',
group_read.name => 'read', group_read.name => 'read',
group_inactive.name => 'change', group_inactive.name => 'change',
} }
expect do expect do
group_access_instance.destroy subject.destroy
end.to change { end.to change {
described_class.group_through.klass.count described_class.group_through.klass.count
}.by(-3) }.by(-3)
@ -600,22 +606,22 @@ RSpec.shared_examples '#group_access? call' do
context 'single access' do context 'single access' do
it 'checks positive' do it 'checks positive' do
expect(group_access_instance.group_access?(group_parameter, 'read')).to be true expect(subject.group_access?(group_parameter, 'read')).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(group_access_instance.group_access?(group_parameter, 'change')).to be false expect(subject.group_access?(group_parameter, 'change')).to be false
end end
end end
context 'access list' do context 'access list' do
it 'checks positive' do it 'checks positive' do
expect(group_access_instance.group_access?(group_parameter, %w[read change])).to be true expect(subject.group_access?(group_parameter, %w[read change])).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(group_access_instance.group_access?(group_parameter, %w[change create])).to be false expect(subject.group_access?(group_parameter, %w[change create])).to be false
end end
end end
end end
@ -624,22 +630,22 @@ RSpec.shared_examples '.group_access call' do
context 'single access' do context 'single access' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.group_access(group_parameter, 'read')).to include(group_access_instance) expect(described_class.group_access(group_parameter, 'read')).to include(subject)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.group_access(group_parameter, 'change')).not_to include(group_access_instance) expect(described_class.group_access(group_parameter, 'change')).not_to include(subject)
end end
end end
context 'access list' do context 'access list' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.group_access(group_parameter, %w[read change])).to include(group_access_instance) expect(described_class.group_access(group_parameter, %w[read change])).to include(subject)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.group_access(group_parameter, %w[change create])).not_to include(group_access_instance) expect(described_class.group_access(group_parameter, %w[change create])).not_to include(subject)
end end
end end
end end

View file

@ -1,12 +1,10 @@
# Requires: let(:group_access_no_permission_instance) { ... } RSpec.shared_examples 'HasGroups and Permissions' do |group_access_no_permission_factory:|
RSpec.shared_examples 'HasGroups and Permissions' do
context 'group' do context 'group' do
subject { build(group_access_no_permission_factory) }
let(:group_read) { create(:group) } let(:group_read) { create(:group) }
before(:each) do before(:each) do
group_access_no_permission_instance.group_names_access_map = { subject.group_names_access_map = {
group_read.name => 'read', group_read.name => 'read',
} }
end end
@ -14,49 +12,49 @@ RSpec.shared_examples 'HasGroups and Permissions' do
context '#group_access?' do context '#group_access?' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_access?(group_read, 'read')).to be false expect(subject.group_access?(group_read, 'read')).to be false
end end
end end
context '#group_ids_access' do context '#group_ids_access' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_ids_access('read')).to be_empty expect(subject.group_ids_access('read')).to be_empty
end end
end end
context '#groups_access' do context '#groups_access' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.groups_access('read')).to be_empty expect(subject.groups_access('read')).to be_empty
end end
end end
context '#group_names_access_map' do context '#group_names_access_map' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_names_access_map).to be_empty expect(subject.group_names_access_map).to be_empty
end end
end end
context '#group_ids_access_map' do context '#group_ids_access_map' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.group_ids_access_map).to be_empty expect(subject.group_ids_access_map).to be_empty
end end
end end
context '#attributes_with_association_ids' do context '#attributes_with_association_ids' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.attributes_with_association_ids['group_ids']).to be_empty expect(subject.attributes_with_association_ids['group_ids']).to be_empty
end end
end end
context '#attributes_with_association_names' do context '#attributes_with_association_names' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
expect(group_access_no_permission_instance.attributes_with_association_names['group_ids']).to be_empty expect(subject.attributes_with_association_names['group_ids']).to be_empty
end end
end end
@ -64,7 +62,7 @@ RSpec.shared_examples 'HasGroups and Permissions' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
result = described_class.group_access(group_read.id, 'read') result = described_class.group_access(group_read.id, 'read')
expect(result).not_to include(group_access_no_permission_instance) expect(result).not_to include(subject)
end end
end end
@ -72,7 +70,7 @@ RSpec.shared_examples 'HasGroups and Permissions' do
it 'prevents instances without permissions' do it 'prevents instances without permissions' do
result = described_class.group_access(group_read.id, 'read') result = described_class.group_access(group_read.id, 'read')
expect(result).not_to include(group_access_no_permission_instance.id) expect(result).not_to include(subject.id)
end end
end end
end end

View file

@ -1,13 +1,6 @@
# Requires: let(:group_access_instance) { ... } RSpec.shared_examples 'HasRoles' do |group_access_factory:|
# Requires: let(:new_group_access_instance) { ... }
RSpec.shared_examples 'HasRoles' do
context 'role' do context 'role' do
subject { create(group_access_factory) }
let(:group_access_instance_inactive) do
group_access_instance.update!(active: false)
group_access_instance
end
let(:role) { create(:role) } let(:role) { create(:role) }
let(:group_instance) { create(:group) } let(:group_instance) { create(:group) }
let(:group_role) { create(:group) } let(:group_role) { create(:group) }
@ -16,7 +9,7 @@ RSpec.shared_examples 'HasRoles' do
context '#role_access?' do context '#role_access?' do
it 'responds to role_access?' do it 'responds to role_access?' do
expect(group_access_instance).to respond_to(:role_access?) expect(subject).to respond_to(:role_access?)
end end
context 'active Role' do context 'active Role' do
@ -25,8 +18,8 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
group_access_instance.roles.push(role) subject.roles.push(role)
group_access_instance.save subject.save
end end
context 'Group ID parameter' do context 'Group ID parameter' do
@ -46,7 +39,7 @@ RSpec.shared_examples 'HasRoles' do
group_inactive.name => 'read', group_inactive.name => 'read',
} }
expect(group_access_instance.group_access?(group_inactive.id, 'read')).to be false expect(subject.group_access?(group_inactive.id, 'read')).to be false
end end
end end
@ -56,10 +49,10 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
group_access_instance.roles.push(role_inactive) subject.roles.push(role_inactive)
group_access_instance.save subject.save
expect(group_access_instance.group_access?(group_role.id, 'read')).to be false expect(subject.group_access?(group_role.id, 'read')).to be false
end end
end end
@ -70,8 +63,8 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
group_access_instance.roles.push(role) subject.roles.push(role)
group_access_instance.save subject.save
end end
it 'responds to role_access_ids' do it 'responds to role_access_ids' do
@ -79,16 +72,18 @@ RSpec.shared_examples 'HasRoles' do
end end
it 'lists only active instance IDs' do it 'lists only active instance IDs' do
subject.update!(active: false)
role.group_names_access_map = { role.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
group_access_instance_inactive.roles.push(role) subject.roles.push(role)
group_access_instance_inactive.save subject.save
group_access_instance_inactive.save subject.save
result = described_class.role_access_ids(group_role.id, 'read') result = described_class.role_access_ids(group_role.id, 'read')
expect(result).not_to include(group_access_instance_inactive.id) expect(result).not_to include(subject.id)
end end
context 'Group ID parameter' do context 'Group ID parameter' do
@ -111,10 +106,10 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
group_access_instance.roles.push(role) subject.roles.push(role)
group_access_instance.save subject.save
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_instance.name => 'read', group_instance.name => 'read',
} }
end end
@ -122,13 +117,13 @@ RSpec.shared_examples 'HasRoles' do
context '#group_access?' do context '#group_access?' do
it 'falls back to #role_access?' do it 'falls back to #role_access?' do
expect(group_access_instance).to receive(:role_access?) expect(subject).to receive(:role_access?)
group_access_instance.group_access?(group_role, 'read') subject.group_access?(group_role, 'read')
end end
it "doesn't fall back to #role_access? if not needed" do it "doesn't fall back to #role_access? if not needed" do
expect(group_access_instance).not_to receive(:role_access?) expect(subject).not_to receive(:role_access?)
group_access_instance.group_access?(group_instance, 'read') subject.group_access?(group_instance, 'read')
end end
end end
@ -139,10 +134,10 @@ RSpec.shared_examples 'HasRoles' do
group_role.name => 'read', group_role.name => 'read',
} }
group_access_instance.roles.push(role) subject.roles.push(role)
group_access_instance.save subject.save
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_instance.name => 'read', group_instance.name => 'read',
} }
end end
@ -153,28 +148,28 @@ RSpec.shared_examples 'HasRoles' do
group_inactive.name => 'read', group_inactive.name => 'read',
} }
result = group_access_instance.group_ids_access('read') result = subject.group_ids_access('read')
expect(result).not_to include(group_inactive.id) expect(result).not_to include(group_inactive.id)
end end
context 'single access' do context 'single access' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = group_access_instance.group_ids_access('read') result = subject.group_ids_access('read')
expect(result).to include(group_role.id) expect(result).to include(group_role.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = group_access_instance.group_ids_access('change') result = subject.group_ids_access('change')
expect(result).not_to include(group_role.id) expect(result).not_to include(group_role.id)
end end
it "doesn't contain duplicate IDs" do it "doesn't contain duplicate IDs" do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
result = group_access_instance.group_ids_access('read') result = subject.group_ids_access('read')
expect(result.uniq).to eq(result) expect(result.uniq).to eq(result)
end end
end end
@ -182,21 +177,21 @@ RSpec.shared_examples 'HasRoles' do
context 'access list' do context 'access list' do
it 'lists access Group IDs' do it 'lists access Group IDs' do
result = group_access_instance.group_ids_access(%w[read change]) result = subject.group_ids_access(%w[read change])
expect(result).to include(group_role.id) expect(result).to include(group_role.id)
end end
it "doesn't list for no access" do it "doesn't list for no access" do
result = group_access_instance.group_ids_access(%w[change create]) result = subject.group_ids_access(%w[change create])
expect(result).not_to include(group_role.id) expect(result).not_to include(group_role.id)
end end
it "doesn't contain duplicate IDs" do it "doesn't contain duplicate IDs" do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
result = group_access_instance.group_ids_access(%w[read create]) result = subject.group_ids_access(%w[read create])
expect(result.uniq).to eq(result) expect(result.uniq).to eq(result)
end end
end end
@ -206,11 +201,11 @@ RSpec.shared_examples 'HasRoles' do
it 'includes the result of .role_access_ids' do it 'includes the result of .role_access_ids' do
result = described_class.group_access_ids(group_role, 'read') result = described_class.group_access_ids(group_role, 'read')
expect(result).to include(group_access_instance.id) expect(result).to include(subject.id)
end end
it "doesn't contain duplicate IDs" do it "doesn't contain duplicate IDs" do
group_access_instance.group_names_access_map = { subject.group_names_access_map = {
group_role.name => 'read', group_role.name => 'read',
} }
@ -226,22 +221,22 @@ RSpec.shared_examples '#role_access? call' do
context 'single access' do context 'single access' do
it 'checks positive' do it 'checks positive' do
expect(group_access_instance.role_access?(group_parameter, 'read')).to be true expect(subject.role_access?(group_parameter, 'read')).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(group_access_instance.role_access?(group_parameter, 'change')).to be false expect(subject.role_access?(group_parameter, 'change')).to be false
end end
end end
context 'access list' do context 'access list' do
it 'checks positive' do it 'checks positive' do
expect(group_access_instance.role_access?(group_parameter, %w[read change])).to be true expect(subject.role_access?(group_parameter, %w[read change])).to be true
end end
it 'checks negative' do it 'checks negative' do
expect(group_access_instance.role_access?(group_parameter, %w[change create])).to be false expect(subject.role_access?(group_parameter, %w[change create])).to be false
end end
end end
end end
@ -250,22 +245,22 @@ RSpec.shared_examples '.role_access_ids call' do
context 'single access' do context 'single access' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.role_access_ids(group_parameter, 'read')).to include(group_access_instance.id) expect(described_class.role_access_ids(group_parameter, 'read')).to include(subject.id)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.role_access_ids(group_parameter, 'change')).not_to include(group_access_instance.id) expect(described_class.role_access_ids(group_parameter, 'change')).not_to include(subject.id)
end end
end end
context 'access list' do context 'access list' do
it 'lists access IDs' do it 'lists access IDs' do
expect(described_class.role_access_ids(group_parameter, %w[read change])).to include(group_access_instance.id) expect(described_class.role_access_ids(group_parameter, %w[read change])).to include(subject.id)
end end
it 'excludes non access IDs' do it 'excludes non access IDs' do
expect(described_class.role_access_ids(group_parameter, %w[change create])).not_to include(group_access_instance.id) expect(described_class.role_access_ids(group_parameter, %w[change create])).not_to include(subject.id)
end end
end end
end end

View file

@ -2,10 +2,7 @@ require 'rails_helper'
require 'models/concerns/has_groups_examples' require 'models/concerns/has_groups_examples'
RSpec.describe Role do RSpec.describe Role do
let(:group_access_instance) { create(:role) } include_examples 'HasGroups', group_access_factory: :role
let(:new_group_access_instance) { build(:role) }
include_examples 'HasGroups'
context '#validate_agent_limit_by_attributes' do context '#validate_agent_limit_by_attributes' do

View file

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