request_cache_spec.rb 3.25 KB
Newer Older
1 2
# frozen_string_literal: true

3 4
require 'spec_helper'

5
describe Gitlab::Cache::RequestCache do
6 7
  let(:klass) do
    Class.new do
8
      extend Gitlab::Cache::RequestCache
9

10
      attr_accessor :id, :name, :result, :extra
11 12 13 14 15

      def self.name
        'ExpensiveAlgorithm'
      end

16
      def initialize(id, name, result, extra = nil)
17 18 19
        self.id = id
        self.name = name
        self.result = result
20
        self.extra = nil
21 22
      end

23
      request_cache def compute(arg)
24 25 26
        result << arg
      end

27
      request_cache def repute(arg)
28 29
        result << arg
      end
30 31 32 33

      def dispute(arg)
        result << arg
      end
34
      request_cache(:dispute) { extra }
35 36 37 38 39
    end
  end

  let(:algorithm) { klass.new('id', 'name', []) }

40
  shared_examples 'cache for the same instance' do
41
    it 'does not compute twice for the same argument' do
42
      algorithm.compute(true)
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
      result = algorithm.compute(true)

      expect(result).to eq([true])
    end

    it 'computes twice for the different argument' do
      algorithm.compute(true)
      result = algorithm.compute(false)

      expect(result).to eq([true, false])
    end

    it 'computes twice for the different class name' do
      algorithm.compute(true)
      allow(klass).to receive(:name).and_return('CheapAlgo')
      result = algorithm.compute(true)

      expect(result).to eq([true, true])
    end

    it 'computes twice for the different method' do
      algorithm.compute(true)
      result = algorithm.repute(true)

      expect(result).to eq([true, true])
    end
69

70
    context 'when request_cache_key is provided' do
71
      before do
72
        klass.request_cache_key do
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
          [id, name]
        end
      end

      it 'computes twice for the different keys, id' do
        algorithm.compute(true)
        algorithm.id = 'ad'
        result = algorithm.compute(true)

        expect(result).to eq([true, true])
      end

      it 'computes twice for the different keys, name' do
        algorithm.compute(true)
        algorithm.name = 'same'
        result = algorithm.compute(true)

        expect(result).to eq([true, true])
      end

      it 'uses extra method cache key if provided' do
        algorithm.dispute(true) # miss
        algorithm.extra = true
        algorithm.dispute(true) # miss
        result = algorithm.dispute(true) # hit

        expect(result).to eq([true, true])
      end
    end
102 103
  end

104 105 106 107 108 109 110 111
  context 'when RequestStore is active', :request_store do
    it_behaves_like 'cache for the same instance'

    it 'computes once for different instances when keys are the same' do
      algorithm.compute(true)
      result = klass.new('id', 'name', algorithm.result).compute(true)

      expect(result).to eq([true])
112 113
    end

114
    it 'computes twice if RequestStore starts over' do
115
      algorithm.compute(true)
116 117 118
      RequestStore.end!
      RequestStore.clear!
      RequestStore.begin!
119 120
      result = algorithm.compute(true)

121
      expect(result).to eq([true, true])
122
    end
123 124 125 126
  end

  context 'when RequestStore is inactive' do
    it_behaves_like 'cache for the same instance'
127 128 129 130 131

    it 'computes twice for different instances even if keys are the same' do
      algorithm.compute(true)
      result = klass.new('id', 'name', algorithm.result).compute(true)

132 133 134 135
      expect(result).to eq([true, true])
    end
  end
end