2022-01-01 13:38:12 +00:00
|
|
|
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
2021-06-01 12:20:20 +00:00
|
|
|
|
2019-10-21 10:44:52 +00:00
|
|
|
RSpec.shared_examples 'ApplicationModel::CanLookup' do
|
|
|
|
describe '.lookup_keys' do
|
|
|
|
it 'returns a subset of: id, name, login, email, number' do
|
|
|
|
expect(described_class.lookup_keys)
|
|
|
|
.to all(be_in(%i[id name login email number]))
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'only includes attributes present on the model' do
|
|
|
|
expect(described_class.lookup_keys)
|
|
|
|
.to all(be_in(described_class.attribute_names.map(&:to_sym)))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
describe '.lookup' do
|
|
|
|
around do |example|
|
|
|
|
Rails.cache.clear
|
|
|
|
example.run
|
|
|
|
Rails.cache.clear
|
|
|
|
end
|
|
|
|
|
|
|
|
let!(:instance) { create(described_class.name.underscore) }
|
|
|
|
let(:valid_lookup_key) { (described_class.lookup_keys - [:id]).sample }
|
|
|
|
let(:invalid_lookup_key) { (described_class.attribute_names.map(&:to_sym) - described_class.lookup_keys).sample }
|
|
|
|
|
|
|
|
it 'accepts exactly one attribute-value pair' do
|
|
|
|
expect { described_class.lookup(id: instance.id, valid_lookup_key => 'foo') }
|
|
|
|
.to raise_error(ArgumentError)
|
|
|
|
end
|
|
|
|
|
|
|
|
it 'only accepts attributes from .lookup_keys' do
|
|
|
|
expect { described_class.lookup(invalid_lookup_key => 'foo') }
|
|
|
|
.to raise_error(ArgumentError)
|
|
|
|
end
|
|
|
|
|
|
|
|
shared_examples 'per-attribute examples' do |attribute|
|
|
|
|
it "finds records by #{attribute} (like .find_by)" do
|
|
|
|
expect(described_class.lookup(attribute => instance.send(attribute))).to eq(instance)
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "cache storage by #{attribute}" do
|
2020-06-22 09:57:45 +00:00
|
|
|
context 'inside a DB transaction' do # provided by default RSpec config
|
2019-10-21 10:44:52 +00:00
|
|
|
it 'leaves the cache untouched' do
|
|
|
|
expect { described_class.lookup(attribute => instance.send(attribute)) }
|
|
|
|
.not_to change { described_class.cache_get(instance.send(attribute)) }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'outside a DB transaction' do
|
|
|
|
before do
|
|
|
|
allow(ActiveRecord::Base.connection)
|
|
|
|
.to receive(:transaction_open?).and_return(false)
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'when called for the first time' do
|
|
|
|
it 'saves the value to the cache' do
|
|
|
|
expect(Rails.cache)
|
|
|
|
.to receive(:write)
|
2021-01-05 11:35:56 +00:00
|
|
|
.with("#{described_class}::#{instance.send(attribute)}", instance, { expires_in: 4.hours })
|
2019-10-21 10:44:52 +00:00
|
|
|
.and_call_original
|
|
|
|
|
|
|
|
expect { described_class.lookup(attribute => instance.send(attribute)) }
|
|
|
|
.to change { described_class.cache_get(instance.send(attribute)) }
|
|
|
|
.to(instance)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-16 17:50:05 +00:00
|
|
|
if described_class.type_for_attribute(attribute).type == :string
|
|
|
|
# https://github.com/zammad/zammad/issues/3121
|
|
|
|
it 'retrieves results from cache with value as symbol' do
|
|
|
|
expect(described_class.lookup(attribute => instance.send(attribute).to_sym)).to be_present
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-21 10:44:52 +00:00
|
|
|
context 'when called a second time' do
|
|
|
|
before { described_class.lookup(attribute => instance.send(attribute)) }
|
|
|
|
|
|
|
|
it 'retrieves results from cache' do
|
|
|
|
expect(Rails.cache)
|
|
|
|
.to receive(:read)
|
|
|
|
.with("#{described_class}::#{instance.send(attribute)}")
|
|
|
|
|
|
|
|
described_class.lookup(attribute => instance.send(attribute))
|
|
|
|
end
|
|
|
|
|
|
|
|
if attribute != :id
|
|
|
|
context 'after it has been updated' do
|
|
|
|
let!(:old_attribute_val) { instance.send(attribute) }
|
|
|
|
let!(:new_attribute_val) { instance.send(attribute).next }
|
|
|
|
|
|
|
|
it 'moves the record to a new cache key' do
|
|
|
|
expect { instance.update(attribute => new_attribute_val) }
|
|
|
|
.to change { described_class.cache_get(old_attribute_val) }.to(nil)
|
|
|
|
|
2021-07-06 07:52:22 +00:00
|
|
|
expect { described_class.lookup(attribute => instance.send(attribute)) }
|
2019-10-21 10:44:52 +00:00
|
|
|
.to change { described_class.cache_get(new_attribute_val) }.to(instance)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
context 'after it has been destroyed' do
|
|
|
|
it 'returns nil' do
|
|
|
|
expect { instance.destroy }
|
|
|
|
.to change { described_class.cache_get(instance.send(attribute)) }
|
|
|
|
.to(nil)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
described_class.lookup_keys.each do |key|
|
|
|
|
include_examples 'per-attribute examples', key
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|