summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/cache/request_cache_spec.rb
blob: 5b82c216a13fb1c34e5dd4a4e1cc782f305aa95d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
69
70
71
72
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
require 'spec_helper'

describe Gitlab::Cache::RequestCache do
  let(:klass) do
    Class.new do
      extend Gitlab::Cache::RequestCache

      attr_accessor :id, :name, :result, :extra

      def self.name
        'ExpensiveAlgorithm'
      end

      def initialize(id, name, result, extra = nil)
        self.id = id
        self.name = name
        self.result = result
        self.extra = nil
      end

      request_cache def compute(arg)
        result << arg
      end

      request_cache def repute(arg)
        result << arg
      end

      def dispute(arg)
        result << arg
      end
      request_cache(:dispute) { extra }
    end
  end

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

  shared_examples 'cache for the same instance' do
    it 'does not compute twice for the same argument' do
      algorithm.compute(true)
      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

    context 'when request_cache_key is provided' do
      before do
        klass.request_cache_key do
          [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
  end

  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])
    end

    it 'computes twice if RequestStore starts over' do
      algorithm.compute(true)
      RequestStore.end!
      RequestStore.clear!
      RequestStore.begin!
      result = algorithm.compute(true)

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

  context 'when RequestStore is inactive' do
    it_behaves_like 'cache for the same instance'

    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)

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