diff options
Diffstat (limited to 'spec/lib/gitlab/json_cache_spec.rb')
-rw-r--r-- | spec/lib/gitlab/json_cache_spec.rb | 551 |
1 files changed, 0 insertions, 551 deletions
diff --git a/spec/lib/gitlab/json_cache_spec.rb b/spec/lib/gitlab/json_cache_spec.rb deleted file mode 100644 index f4f6624bae9..00000000000 --- a/spec/lib/gitlab/json_cache_spec.rb +++ /dev/null @@ -1,551 +0,0 @@ -# frozen_string_literal: true -# rubocop:disable Style/RedundantFetchBlock - -require 'spec_helper' - -RSpec.describe Gitlab::JsonCache do - let_it_be(:broadcast_message) { create(:broadcast_message) } - - let(:backend) { double('backend').as_null_object } - let(:namespace) { 'geo' } - let(:key) { 'foo' } - let(:expanded_key) { "#{namespace}:#{key}:#{Gitlab.revision}" } - - subject(:cache) { described_class.new(namespace: namespace, backend: backend) } - - describe '#active?' do - context 'when backend respond to active? method' do - it 'delegates to the underlying cache implementation' do - backend = double('backend', active?: false) - - cache = described_class.new(namespace: namespace, backend: backend) - - expect(cache.active?).to eq(false) - end - end - - context 'when backend does not respond to active? method' do - it 'returns true' do - backend = double('backend') - - cache = described_class.new(namespace: namespace, backend: backend) - - expect(cache.active?).to eq(true) - end - end - end - - describe '#cache_key' do - using RSpec::Parameterized::TableSyntax - - where(:namespace, :cache_key_strategy, :expanded_key) do - nil | :revision | "#{key}:#{Gitlab.revision}" - nil | :version | "#{key}:#{Gitlab::VERSION}:#{Rails.version}" - namespace | :revision | "#{namespace}:#{key}:#{Gitlab.revision}" - namespace | :version | "#{namespace}:#{key}:#{Gitlab::VERSION}:#{Rails.version}" - end - - with_them do - let(:cache) { described_class.new(namespace: namespace, cache_key_strategy: cache_key_strategy) } - - subject { cache.cache_key(key) } - - it { is_expected.to eq expanded_key } - end - - context 'when cache_key_strategy is unknown' do - let(:cache) { described_class.new(namespace: namespace, cache_key_strategy: 'unknown') } - - it 'raises KeyError' do - expect { cache.cache_key('key') }.to raise_error(KeyError) - end - end - end - - describe '#namespace' do - it 'defaults to nil' do - cache = described_class.new - expect(cache.namespace).to be_nil - end - end - - describe '#strategy_key_component' do - subject { cache.strategy_key_component } - - it 'defaults to Gitlab.revision' do - expect(described_class.new.strategy_key_component).to eq Gitlab.revision - end - - context 'when cache_key_strategy is :revision' do - let(:cache) { described_class.new(cache_key_strategy: :revision) } - - it { is_expected.to eq Gitlab.revision } - end - - context 'when cache_key_strategy is :version' do - let(:cache) { described_class.new(cache_key_strategy: :version) } - - it { is_expected.to eq [Gitlab::VERSION, Rails.version] } - end - - context 'when cache_key_strategy is invalid' do - let(:cache) { described_class.new(cache_key_strategy: 'unknown') } - - it 'raises KeyError' do - expect { subject }.to raise_error(KeyError) - end - end - end - - describe '#expire' do - it 'expires the given key from the cache' do - cache.expire(key) - - expect(backend).to have_received(:delete).with(expanded_key) - end - end - - describe '#read' do - it 'reads the given key from the cache' do - cache.read(key) - - expect(backend).to have_received(:read).with(expanded_key) - end - - it 'returns the cached value when there is data in the cache with the given key' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return("true") - - expect(cache.read(key)).to eq(true) - end - - it 'returns nil when there is no data in the cache with the given key' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(nil) - - expect(Gitlab::Json).not_to receive(:parse) - expect(cache.read(key)).to be_nil - end - - context 'when the cached value is true' do - it 'parses the cached value' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(true) - - expect(Gitlab::Json).to receive(:parse).with("true").and_call_original - expect(cache.read(key, BroadcastMessage)).to eq(true) - end - end - - context 'when the cached value is false' do - it 'parses the cached value' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(false) - - expect(Gitlab::Json).to receive(:parse).with("false").and_call_original - expect(cache.read(key, BroadcastMessage)).to eq(false) - end - end - - context 'when the cached value is a JSON true value' do - it 'parses the cached value' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return("true") - - expect(cache.read(key, BroadcastMessage)).to eq(true) - end - end - - context 'when the cached value is a JSON false value' do - it 'parses the cached value' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return("false") - - expect(cache.read(key, BroadcastMessage)).to eq(false) - end - end - - context 'when the cached value is a hash' do - it 'parses the cached value' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(broadcast_message.to_json) - - expect(cache.read(key, BroadcastMessage)).to eq(broadcast_message) - end - - it 'returns nil when klass is nil' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(broadcast_message.to_json) - - expect(cache.read(key)).to be_nil - end - - it 'gracefully handles bad cached entry' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return('{') - - expect(cache.read(key, BroadcastMessage)).to be_nil - end - - it 'gracefully handles an empty hash' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return('{}') - - expect(cache.read(key, BroadcastMessage)).to be_a(BroadcastMessage) - end - - it 'gracefully handles unknown attributes' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(broadcast_message.attributes.merge(unknown_attribute: 1).to_json) - - expect(cache.read(key, BroadcastMessage)).to be_nil - end - - it 'gracefully handles excluded fields from attributes during serialization' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(broadcast_message.attributes.except("message_html").to_json) - - result = cache.read(key, BroadcastMessage) - - BroadcastMessage.cached_markdown_fields.html_fields.each do |field| - expect(result.public_send(field)).to be_nil - end - end - end - - context 'when the cached value is an array' do - it 'parses the cached value' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return([broadcast_message].to_json) - - expect(cache.read(key, BroadcastMessage)).to eq([broadcast_message]) - end - - it 'returns an empty array when klass is nil' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return([broadcast_message].to_json) - - expect(cache.read(key)).to eq([]) - end - - it 'gracefully handles bad cached entry' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return('[') - - expect(cache.read(key, BroadcastMessage)).to be_nil - end - - it 'gracefully handles an empty array' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return('[]') - - expect(cache.read(key, BroadcastMessage)).to eq([]) - end - - it 'gracefully handles unknown attributes' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return([{ unknown_attribute: 1 }, broadcast_message.attributes].to_json) - - expect(cache.read(key, BroadcastMessage)).to eq([broadcast_message]) - end - end - end - - describe '#write' do - it 'writes value to the cache with the given key' do - cache.write(key, true) - - expect(backend).to have_received(:write).with(expanded_key, "true", nil) - end - - it 'writes a string containing a JSON representation of the value to the cache' do - cache.write(key, broadcast_message) - - expect(backend).to have_received(:write) - .with(expanded_key, broadcast_message.to_json, nil) - end - - it 'passes options the underlying cache implementation' do - cache.write(key, true, expires_in: 15.seconds) - - expect(backend).to have_received(:write) - .with(expanded_key, "true", expires_in: 15.seconds) - end - - it 'passes options the underlying cache implementation when options is empty' do - cache.write(key, true, {}) - - expect(backend).to have_received(:write) - .with(expanded_key, "true", {}) - end - - it 'passes options the underlying cache implementation when options is nil' do - cache.write(key, true, nil) - - expect(backend).to have_received(:write) - .with(expanded_key, "true", nil) - end - end - - describe '#fetch', :use_clean_rails_memory_store_caching do - let(:backend) { Rails.cache } - - it 'requires a block' do - expect { cache.fetch(key) }.to raise_error(LocalJumpError) - end - - it 'passes options the underlying cache implementation' do - expect(backend).to receive(:write) - .with(expanded_key, "true", { expires_in: 15.seconds }) - - cache.fetch(key, { expires_in: 15.seconds }) { true } - end - - context 'when the given key does not exist in the cache' do - context 'when the result of the block is truthy' do - it 'returns the result of the block' do - result = cache.fetch(key) { true } - - expect(result).to eq(true) - end - - it 'caches the value' do - expect(backend).to receive(:write).with(expanded_key, "true", {}) - - cache.fetch(key) { true } - end - end - - context 'when the result of the block is false' do - it 'returns the result of the block' do - result = cache.fetch(key) { false } - - expect(result).to eq(false) - end - - it 'caches the value' do - expect(backend).to receive(:write).with(expanded_key, "false", {}) - - cache.fetch(key) { false } - end - end - - context 'when the result of the block is nil' do - it 'returns the result of the block' do - result = cache.fetch(key) { nil } - - expect(result).to eq(nil) - end - - it 'caches the value' do - expect(backend).to receive(:write).with(expanded_key, "null", {}) - - cache.fetch(key) { nil } - end - end - end - - context 'when the given key exists in the cache' do - context 'when the cached value is a hash' do - before do - backend.write(expanded_key, broadcast_message.to_json) - end - - it 'parses the cached value' do - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to eq(broadcast_message) - end - - it 'decodes enums correctly' do - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result.broadcast_type).to eq(broadcast_message.broadcast_type) - end - - context 'when the cached value is an instance of ActiveRecord::Base' do - it 'returns a persisted record when id is set' do - backend.write(expanded_key, broadcast_message.to_json) - - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to be_persisted - end - - it 'returns a new record when id is nil' do - backend.write(expanded_key, build(:broadcast_message).to_json) - - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to be_new_record - end - - it 'returns a new record when id is missing' do - backend.write(expanded_key, build(:broadcast_message).attributes.except('id').to_json) - - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to be_new_record - end - - it 'gracefully handles bad cached entry' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return('{') - - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to eq 'block result' - end - - it 'gracefully handles an empty hash' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return('{}') - - expect(cache.fetch(key, as: BroadcastMessage)).to be_a(BroadcastMessage) - end - - it 'gracefully handles unknown attributes' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(broadcast_message.attributes.merge(unknown_attribute: 1).to_json) - - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to eq 'block result' - end - - it 'gracefully handles excluded fields from attributes during serialization' do - allow(backend).to receive(:read) - .with(expanded_key) - .and_return(broadcast_message.attributes.except("message_html").to_json) - - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - BroadcastMessage.cached_markdown_fields.html_fields.each do |field| - expect(result.public_send(field)).to be_nil - end - end - end - - it "returns the result of the block when 'as' option is nil" do - result = cache.fetch(key, as: nil) { 'block result' } - - expect(result).to eq('block result') - end - - it "returns the result of the block when 'as' option is missing" do - result = cache.fetch(key) { 'block result' } - - expect(result).to eq('block result') - end - end - - context 'when the cached value is a array' do - before do - backend.write(expanded_key, [broadcast_message].to_json) - end - - it 'parses the cached value' do - result = cache.fetch(key, as: BroadcastMessage) { 'block result' } - - expect(result).to eq([broadcast_message]) - end - - it "returns an empty array when 'as' option is nil" do - result = cache.fetch(key, as: nil) { 'block result' } - - expect(result).to eq([]) - end - - it "returns an empty array when 'as' option is not informed" do - result = cache.fetch(key) { 'block result' } - - expect(result).to eq([]) - end - end - - context 'when the cached value is true' do - before do - backend.write(expanded_key, "true") - end - - it 'returns the cached value' do - result = cache.fetch(key) { 'block result' } - - expect(result).to eq(true) - end - - it 'does not execute the block' do - expect { |block| cache.fetch(key, &block) }.not_to yield_control - end - - it 'does not write to the cache' do - expect(backend).not_to receive(:write) - - cache.fetch(key) { 'block result' } - end - end - - context 'when the cached value is false' do - before do - backend.write(expanded_key, "false") - end - - it 'returns the cached value' do - result = cache.fetch(key) { 'block result' } - - expect(result).to eq(false) - end - - it 'does not execute the block' do - expect { |block| cache.fetch(key, &block) }.not_to yield_control - end - - it 'does not write to the cache' do - expect(backend).not_to receive(:write) - - cache.fetch(key) { 'block result' } - end - end - - context 'when the cached value is nil' do - before do - backend.write(expanded_key, "null") - end - - it 'returns the result of the block' do - result = cache.fetch(key) { 'block result' } - - expect(result).to eq('block result') - end - - it 'writes the result of the block to the cache' do - expect(backend).to receive(:write) - .with(expanded_key, 'block result'.to_json, {}) - - cache.fetch(key) { 'block result' } - end - end - end - end -end -# rubocop:enable Style/RedundantFetchBlock |