can_lookup_examples.rb 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. RSpec.shared_examples 'ApplicationModel::CanLookup' do
  2. describe '.lookup_keys' do
  3. it 'returns a subset of: id, name, login, email, number' do
  4. expect(described_class.lookup_keys)
  5. .to all(be_in(%i[id name login email number]))
  6. end
  7. it 'only includes attributes present on the model' do
  8. expect(described_class.lookup_keys)
  9. .to all(be_in(described_class.attribute_names.map(&:to_sym)))
  10. end
  11. end
  12. describe '.lookup' do
  13. around do |example|
  14. Rails.cache.clear
  15. example.run
  16. Rails.cache.clear
  17. end
  18. let!(:instance) { create(described_class.name.underscore) }
  19. let(:valid_lookup_key) { (described_class.lookup_keys - [:id]).sample }
  20. let(:invalid_lookup_key) { (described_class.attribute_names.map(&:to_sym) - described_class.lookup_keys).sample }
  21. it 'accepts exactly one attribute-value pair' do
  22. expect { described_class.lookup(id: instance.id, valid_lookup_key => 'foo') }
  23. .to raise_error(ArgumentError)
  24. end
  25. it 'only accepts attributes from .lookup_keys' do
  26. expect { described_class.lookup(invalid_lookup_key => 'foo') }
  27. .to raise_error(ArgumentError)
  28. end
  29. shared_examples 'per-attribute examples' do |attribute|
  30. it "finds records by #{attribute} (like .find_by)" do
  31. expect(described_class.lookup(attribute => instance.send(attribute))).to eq(instance)
  32. end
  33. describe "cache storage by #{attribute}" do
  34. context 'inside a DB transaction' do # provided by default RSpec config
  35. it 'leaves the cache untouched' do
  36. expect { described_class.lookup(attribute => instance.send(attribute)) }
  37. .not_to change { described_class.cache_get(instance.send(attribute)) }
  38. end
  39. end
  40. context 'outside a DB transaction' do
  41. before do
  42. allow(ActiveRecord::Base.connection)
  43. .to receive(:transaction_open?).and_return(false)
  44. end
  45. context 'when called for the first time' do
  46. it 'saves the value to the cache' do
  47. expect(Rails.cache)
  48. .to receive(:write)
  49. .with("#{described_class}::#{instance.send(attribute)}", instance, { expires_in: 4.hours })
  50. .and_call_original
  51. expect { described_class.lookup(attribute => instance.send(attribute)) }
  52. .to change { described_class.cache_get(instance.send(attribute)) }
  53. .to(instance)
  54. end
  55. end
  56. if described_class.type_for_attribute(attribute).type == :string
  57. # https://github.com/zammad/zammad/issues/3121
  58. it 'retrieves results from cache with value as symbol' do
  59. expect(described_class.lookup(attribute => instance.send(attribute).to_sym)).to be_present
  60. end
  61. end
  62. context 'when called a second time' do
  63. before { described_class.lookup(attribute => instance.send(attribute)) }
  64. it 'retrieves results from cache' do
  65. expect(Rails.cache)
  66. .to receive(:read)
  67. .with("#{described_class}::#{instance.send(attribute)}")
  68. described_class.lookup(attribute => instance.send(attribute))
  69. end
  70. if attribute != :id
  71. context 'after it has been updated' do
  72. let!(:old_attribute_val) { instance.send(attribute) }
  73. let!(:new_attribute_val) { instance.send(attribute).next }
  74. it 'moves the record to a new cache key' do
  75. expect { instance.update(attribute => new_attribute_val) }
  76. .to change { described_class.cache_get(old_attribute_val) }.to(nil)
  77. expect { described_class.lookup({ attribute => instance.send(attribute) }) }
  78. .to change { described_class.cache_get(new_attribute_val) }.to(instance)
  79. end
  80. end
  81. end
  82. context 'after it has been destroyed' do
  83. it 'returns nil' do
  84. expect { instance.destroy }
  85. .to change { described_class.cache_get(instance.send(attribute)) }
  86. .to(nil)
  87. end
  88. end
  89. end
  90. end
  91. end
  92. end
  93. described_class.lookup_keys.each do |key|
  94. include_examples 'per-attribute examples', key
  95. end
  96. end
  97. end