123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- RSpec.shared_examples 'HasGroups' do |group_access_factory:|
- describe 'group' do
- subject { create(group_access_factory) }
- let(:group_full) { create(:group) }
- let(:group_read) { create(:group) }
- let(:group_inactive) { create(:group, active: false) }
- describe '.group_through_identifier' do
- it 'responds to group_through_identifier' do
- expect(described_class).to respond_to(:group_through_identifier)
- end
- it 'returns a Symbol as identifier' do
- expect(described_class.group_through_identifier).to be_a(Symbol)
- end
- it 'instance responds to group_through_identifier method' do
- expect(subject).to respond_to(described_class.group_through_identifier)
- end
- end
- describe '.group_through' do
- it 'responds to group_through' do
- expect(described_class).to respond_to(:group_through)
- end
- it 'returns the Reflection instance of the has_many :through relation' do
- expect(described_class.group_through).to be_a(ActiveRecord::Reflection::HasManyReflection)
- end
- end
- describe '#groups' do
- it 'responds to groups' do
- expect(subject).to respond_to(:groups)
- end
- describe '#groups.access' do
- it 'responds to groups.access' do
- expect(subject.groups).to respond_to(:access)
- end
- describe 'result' do
- before do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => 'read',
- group_inactive.name => 'change',
- }
- end
- it 'returns all related Groups' do
- expect(subject.groups.access.size).to eq(3)
- end
- it 'adds join table attribute(s like) access' do
- expect(subject.groups.access.first).to respond_to(:access)
- end
- it 'filters for given access parameter' do
- expect(subject.groups.access('read')).to include(group_read)
- end
- it 'filters for given access list parameter' do
- expect(subject.groups.access('read', 'change')).to include(group_read, group_inactive)
- end
- it 'always includes full access groups' do
- expect(subject.groups.access('read')).to include(group_full)
- end
- end
- end
- end
- describe '#group_access?' do
- before do
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- end
- it 'responds to group_access?' do
- expect(subject).to respond_to(:group_access?)
- end
- context 'Group ID parameter' do
- include_examples '#group_access? call' do
- let(:group_parameter) { group_read.id }
- end
- end
- context 'Group parameter' do
- include_examples '#group_access? call' do
- let(:group_parameter) { group_read }
- end
- end
- it 'prevents inactive Group' do
- subject.group_names_access_map = {
- group_inactive.name => 'read',
- }
- expect(subject.group_access?(group_inactive.id, 'read')).to be false
- end
- it 'prevents inactive instances' do
- subject.update!(active: false)
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- expect(subject.group_access?(group_read.id, 'read')).to be false
- end
- end
- describe '#group_ids_access' do
- before do
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- end
- it 'responds to group_ids_access' do
- expect(subject).to respond_to(:group_ids_access)
- end
- it 'lists only active Group IDs' do
- subject.group_names_access_map = {
- group_read.name => 'read',
- group_inactive.name => 'read',
- }
- result = subject.group_ids_access('read')
- expect(result).not_to include(group_inactive.id)
- end
- it "doesn't list for inactive instances" do
- subject.update!(active: false)
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- expect(subject.group_ids_access('read')).to be_empty
- end
- describe 'single access' do
- it 'lists access Group IDs' do
- result = subject.group_ids_access('read')
- expect(result).to include(group_read.id)
- end
- it "doesn't list for no access" do
- result = subject.group_ids_access('change')
- expect(result).not_to include(group_read.id)
- end
- end
- describe 'access list' do
- it 'lists access Group IDs' do
- result = subject.group_ids_access(%w[read change])
- expect(result).to include(group_read.id)
- end
- it "doesn't list for no access" do
- result = subject.group_ids_access(%w[change create])
- expect(result).not_to include(group_read.id)
- end
- end
- end
- describe '#groups_access' do
- it 'responds to groups_access' do
- expect(subject).to respond_to(:groups_access)
- end
- it 'wraps #group_ids_access' do
- expect(subject).to receive(:group_ids_access)
- subject.groups_access('read')
- end
- it 'returns Groups' do
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- result = subject.groups_access('read')
- expect(result).to include(group_read)
- end
- end
- describe '#group_names_access_map=' do
- it 'responds to group_names_access_map=' do
- expect(subject).to respond_to(:group_names_access_map=)
- end
- context 'existing instance' do
- it 'stores Hash with String values' do
- expect do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => 'read',
- }
- end.to change {
- described_class.group_through.klass.count
- }.by(2)
- end
- it 'stores Hash with Array<String> values' do
- expect do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => %w[read change],
- }
- end.to change {
- described_class.group_through.klass.count
- }.by(3)
- end
- it 'allows empty Hash value' do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => %w[read change],
- }
- expect do
- subject.group_names_access_map = {}
- end.to change {
- described_class.group_through.klass.count
- }.by(-3)
- end
- it 'prevents having full and other privilege at the same time' do
- invalid_combination = %w[full read change]
- exception = ActiveRecord::RecordInvalid
- expect do
- subject.group_names_access_map = {
- group_full.name => invalid_combination,
- }
- end.to raise_error(exception)
- expect do
- subject.group_names_access_map = {
- group_full.name => invalid_combination.reverse,
- }
- end.to raise_error(exception)
- end
- end
- context 'new instance' do
- subject { build(group_access_factory) }
- it "doesn't store directly" do
- expect do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => 'read',
- }
- end.not_to change {
- described_class.group_through.klass.count
- }
- end
- it 'stores after save' do
- expect do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => 'read',
- }
- subject.save
- end.to change {
- described_class.group_through.klass.count
- }.by(2)
- end
- it 'allows empty Hash value' do
- expect do
- subject.group_names_access_map = {}
- subject.save
- end.not_to change {
- described_class.group_through.klass.count
- }
- end
- end
- end
- describe '#group_names_access_map' do
- it 'responds to group_names_access_map' do
- expect(subject).to respond_to(:group_names_access_map)
- end
- it 'returns instance Group name => access relations as Hash' do
- expected = {
- group_full.name => ['full'],
- group_read.name => ['read'],
- }
- subject.group_names_access_map = expected
- expect(subject.group_names_access_map).to eq(expected)
- end
- it "doesn't map for inactive instances" do
- subject.update!(active: false)
- subject.group_names_access_map = {
- group_full.name => ['full'],
- group_read.name => ['read'],
- }
- expect(subject.group_names_access_map).to be_empty
- end
- it 'returns empty map if none is stored' do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => 'read',
- }
- subject.group_names_access_map = {}
- expect(subject.group_names_access_map).to be_blank
- end
- end
- describe '#group_ids_access_map=' do
- it 'responds to group_ids_access_map=' do
- expect(subject).to respond_to(:group_ids_access_map=)
- end
- context 'existing instance' do
- it 'stores Hash with String values' do
- expect do
- subject.group_ids_access_map = {
- group_full.id => 'full',
- group_read.id => 'read',
- }
- end.to change {
- described_class.group_through.klass.count
- }.by(2)
- end
- it 'stores Hash with Array<String> values' do
- expect do
- subject.group_ids_access_map = {
- group_full.id => 'full',
- group_read.id => %w[read change],
- }
- end.to change {
- described_class.group_through.klass.count
- }.by(3)
- end
- it 'allows empty Hash value' do
- subject.group_ids_access_map = {
- group_full.id => 'full',
- group_read.id => %w[read change],
- }
- expect do
- subject.group_ids_access_map = {}
- end.to change {
- described_class.group_through.klass.count
- }.by(-3)
- end
- end
- context 'new instance' do
- subject { build(group_access_factory) }
- it "doesn't store directly" do
- expect do
- subject.group_ids_access_map = {
- group_full.id => 'full',
- group_read.id => 'read',
- }
- end.not_to change {
- described_class.group_through.klass.count
- }
- end
- it 'stores after save' do
- expect do
- subject.group_ids_access_map = {
- group_full.id => 'full',
- group_read.id => 'read',
- }
- subject.save
- end.to change {
- described_class.group_through.klass.count
- }.by(2)
- end
- it 'allows empty Hash value' do
- expect do
- subject.group_ids_access_map = {}
- subject.save
- end.not_to change {
- described_class.group_through.klass.count
- }
- end
- end
- end
- describe '#group_ids_access_map' do
- it 'responds to group_ids_access_map' do
- expect(subject).to respond_to(:group_ids_access_map)
- end
- it 'returns instance Group ID => access relations as Hash' do
- expected = {
- group_full.id => ['full'],
- group_read.id => ['read'],
- }
- subject.group_ids_access_map = expected
- expect(subject.group_ids_access_map).to eq(expected)
- end
- it "doesn't map for inactive instances" do
- subject.update!(active: false)
- subject.group_ids_access_map = {
- group_full.id => ['full'],
- group_read.id => ['read'],
- }
- expect(subject.group_ids_access_map).to be_empty
- end
- it 'returns empty map if none is stored' do
- subject.group_ids_access_map = {
- group_full.id => 'full',
- group_read.id => 'read',
- }
- subject.group_ids_access_map = {}
- expect(subject.group_ids_access_map).to be_blank
- end
- end
- describe '#associations_from_param' do
- it 'handles group_ids parameter as group_ids_access_map' do
- expected = {
- group_full.id => ['full'],
- group_read.id => ['read'],
- }
- subject.associations_from_param(group_ids: expected)
- expect(subject.group_ids_access_map).to eq(expected)
- end
- it 'handles groups parameter as group_names_access_map' do
- expected = {
- group_full.name => ['full'],
- group_read.name => ['read'],
- }
- subject.associations_from_param(groups: expected)
- expect(subject.group_names_access_map).to eq(expected)
- end
- end
- describe '#attributes_with_association_ids' do
- it 'includes group_ids as group_ids_access_map' do
- expected = {
- group_full.id => ['full'],
- group_read.id => ['read'],
- }
- subject.group_ids_access_map = expected
- result = subject.attributes_with_association_ids
- expect(result['group_ids']).to eq(expected)
- end
- end
- describe '#attributes_with_association_names' do
- it 'includes group_ids as group_ids_access_map' do
- expected = {
- group_full.id => ['full'],
- group_read.id => ['read'],
- }
- subject.group_ids_access_map = expected
- result = subject.attributes_with_association_names
- expect(result['group_ids']).to eq(expected)
- end
- it 'includes groups as group_names_access_map' do
- expected = {
- group_full.name => ['full'],
- group_read.name => ['read'],
- }
- subject.group_names_access_map = expected
- result = subject.attributes_with_association_names
- expect(result['groups']).to eq(expected)
- end
- end
- describe '.group_access' do
- before do
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- end
- it 'responds to group_access' do
- expect(described_class).to respond_to(:group_access)
- end
- it 'lists only active instances' do
- subject.update!(active: false)
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- result = described_class.group_access(group_read.id, 'read')
- expect(result).not_to include(subject)
- end
- context 'Group ID parameter' do
- include_examples '.group_access call' do
- let(:group_parameter) { group_read.id }
- end
- end
- context 'Group parameter' do
- include_examples '.group_access call' do
- let(:group_parameter) { group_read }
- end
- end
- end
- describe '.group_access_ids' do
- it 'responds to group_access_ids' do
- expect(described_class).to respond_to(:group_access_ids)
- end
- it 'wraps .group_access' do
- expect(described_class).to receive(:group_access).and_call_original
- described_class.group_access_ids(group_read, 'read')
- end
- it 'returns class instances' do
- subject.group_names_access_map = {
- group_read.name => 'read',
- }
- result = described_class.group_access_ids(group_read, 'read')
- expect(result).to include(subject.id)
- end
- end
- it 'destroys relations before instance gets destroyed' do
- subject.group_names_access_map = {
- group_full.name => 'full',
- group_read.name => 'read',
- group_inactive.name => 'change',
- }
- expect do
- subject.destroy
- end.to change {
- described_class.group_through.klass.count
- }.by(-3)
- end
- end
- end
- RSpec.shared_examples '#group_access? call' do
- context 'single access' do
- it 'checks positive' do
- expect(subject.group_access?(group_parameter, 'read')).to be true
- end
- it 'checks negative' do
- expect(subject.group_access?(group_parameter, 'change')).to be false
- end
- end
- context 'access list' do
- it 'checks positive' do
- expect(subject.group_access?(group_parameter, %w[read change])).to be true
- end
- it 'checks negative' do
- expect(subject.group_access?(group_parameter, %w[change create])).to be false
- end
- end
- end
- RSpec.shared_examples '.group_access call' do
- context 'single access' do
- it 'lists access IDs' do
- expect(described_class.group_access(group_parameter, 'read')).to include(subject)
- end
- it 'excludes non access IDs' do
- expect(described_class.group_access(group_parameter, 'change')).not_to include(subject)
- end
- end
- context 'access list' do
- it 'lists access IDs' do
- expect(described_class.group_access(group_parameter, %w[read change])).to include(subject)
- end
- it 'excludes non access IDs' do
- expect(described_class.group_access(group_parameter, %w[change create])).not_to include(subject)
- end
- end
- end
|