faraday-http-cache 2.1.0 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday/http_cache/strategies/by_url'
4
+ require 'faraday/http_cache/strategies/by_vary'
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'faraday'
3
4
 
4
5
  require 'faraday/http_cache/storage'
5
6
  require 'faraday/http_cache/request'
6
7
  require 'faraday/http_cache/response'
8
+ require 'faraday/http_cache/strategies'
7
9
 
8
10
  module Faraday
9
11
  # Public: The middleware responsible for caching and serving responses.
10
- # The middleware use the provided configuration options to establish a
11
- # 'Faraday::HttpCache::Storage' to cache responses retrieved by the stack
12
+ # The middleware use the provided configuration options to establish on of
13
+ # 'Faraday::HttpCache::Strategies' to cache responses retrieved by the stack
12
14
  # adapter. If a stored response can be served again for a subsequent
13
15
  # request, the middleware will return the response instead of issuing a new
14
16
  # request to it's server. This middleware should be the last attached handler
@@ -44,7 +46,7 @@ module Faraday
44
46
  # builder.use :http_cache, store: Rails.cache, instrumenter: ActiveSupport::Notifications
45
47
  # end
46
48
  class HttpCache < Faraday::Middleware
47
- UNSAFE_METHODS = [:post, :put, :delete, :patch].freeze
49
+ UNSAFE_METHODS = %i[post put delete patch].freeze
48
50
 
49
51
  ERROR_STATUSES = (400..499).freeze
50
52
 
@@ -71,7 +73,7 @@ module Faraday
71
73
  :uncacheable,
72
74
 
73
75
  # The request was cached but need to be revalidated by the server.
74
- :must_revalidate,
76
+ :must_revalidate
75
77
  ].freeze
76
78
 
77
79
  # Public: Initializes a new HttpCache middleware.
@@ -90,7 +92,7 @@ module Faraday
90
92
  # Faraday::HttpCache.new(app, logger: my_logger)
91
93
  #
92
94
  # # Initialize the middleware with a logger and Marshal as a serializer
93
- # Faraday:HttpCache.new(app, logger: my_logger, serializer: Marshal)
95
+ # Faraday::HttpCache.new(app, logger: my_logger, serializer: Marshal)
94
96
  #
95
97
  # # Initialize the middleware with a FileStore at the 'tmp' dir.
96
98
  # store = ActiveSupport::Cache.lookup_store(:file_store, ['tmp'])
@@ -99,14 +101,18 @@ module Faraday
99
101
  # # Initialize the middleware with a MemoryStore and logger
100
102
  # store = ActiveSupport::Cache.lookup_store
101
103
  # Faraday::HttpCache.new(app, store: store, logger: my_logger)
102
- def initialize(app, store: nil, serializer: nil, shared_cache: true, instrumenter: nil, instrument_name: EVENT_NAME, logger: nil) # rubocop:disable Metrics/ParameterLists
104
+ def initialize(app, options = {})
103
105
  super(app)
104
106
 
105
- @logger = logger
106
- @shared_cache = shared_cache
107
- @instrumenter = instrumenter
108
- @instrument_name = instrument_name
109
- @storage = Storage.new(store: store, serializer: serializer, logger: logger)
107
+ options = options.dup
108
+ @logger = options[:logger]
109
+ @shared_cache = options.delete(:shared_cache) { true }
110
+ @instrumenter = options.delete(:instrumenter)
111
+ @instrument_name = options.delete(:instrument_name) { EVENT_NAME }
112
+
113
+ strategy = options.delete(:strategy) { Strategies::ByUrl }
114
+
115
+ @strategy = strategy.new(**options)
110
116
  end
111
117
 
112
118
  # Public: Process the request into a duplicate of this instance to
@@ -151,9 +157,6 @@ module Faraday
151
157
  # Internal: Gets the request object created from the Faraday env Hash.
152
158
  attr_reader :request
153
159
 
154
- # Internal: Gets the storage instance associated with the middleware.
155
- attr_reader :storage
156
-
157
160
  private
158
161
 
159
162
  # Internal: Should this cache instance act like a "shared cache" according
@@ -182,7 +185,7 @@ module Faraday
182
185
  #
183
186
  # Returns the 'Faraday::Response' instance to be served.
184
187
  def process(env)
185
- entry = @storage.read(@request)
188
+ entry = @strategy.read(@request)
186
189
 
187
190
  return fetch(env) if entry.nil?
188
191
 
@@ -256,20 +259,20 @@ module Faraday
256
259
  def store(response)
257
260
  if shared_cache? ? response.cacheable_in_shared_cache? : response.cacheable_in_private_cache?
258
261
  trace :store
259
- @storage.write(@request, response)
262
+ @strategy.write(@request, response)
260
263
  else
261
264
  trace :uncacheable
262
265
  end
263
266
  end
264
267
 
265
268
  def delete(request, response)
266
- headers = %w(Location Content-Location)
269
+ headers = %w[Location Content-Location]
267
270
  headers.each do |header|
268
271
  url = response.headers[header]
269
- @storage.delete(url) if url
272
+ @strategy.delete(url) if url
270
273
  end
271
274
 
272
- @storage.delete(request.url)
275
+ @strategy.delete(request.url)
273
276
  trace :delete
274
277
  end
275
278
 
@@ -297,7 +300,7 @@ module Faraday
297
300
 
298
301
  {
299
302
  status: hash[:status],
300
- body: hash[:body],
303
+ body: hash[:body] || hash[:response_body],
301
304
  response_headers: hash[:response_headers]
302
305
  }
303
306
  end
data/spec/binary_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache do
@@ -10,7 +11,7 @@ describe Faraday::HttpCache do
10
11
  stack.adapter adapter.to_sym
11
12
  end
12
13
  end
13
- let(:data) { IO.binread File.expand_path('../support/empty.png', __FILE__) }
14
+ let(:data) { IO.binread File.expand_path('support/empty.png', __dir__) }
14
15
 
15
16
  it 'works fine with binary data' do
16
17
  expect(client.get('image').body).to eq data
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache::CacheControl do
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache do
@@ -36,7 +37,7 @@ describe Faraday::HttpCache do
36
37
 
37
38
  it 'adds a trace of the actions performed to the env' do
38
39
  response = client.post('post')
39
- expect(response.env[:http_cache_trace]).to eq([:unacceptable, :delete])
40
+ expect(response.env[:http_cache_trace]).to eq(%i[unacceptable delete])
40
41
  end
41
42
 
42
43
  describe 'cache invalidation' do
@@ -282,23 +283,13 @@ describe Faraday::HttpCache do
282
283
  expect(client.get('must-revalidate').body).to eq('1')
283
284
  end
284
285
 
285
- it 'raises an error when misconfigured' do
286
- expect {
287
- client = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
288
- stack.use Faraday::HttpCache, i_have_no_idea: true
289
- end
290
-
291
- client.get('get')
292
- }.to raise_error(ArgumentError)
293
- end
294
-
295
286
  describe 'Configuration options' do
296
287
  let(:app) { double('it is an app!') }
297
288
 
298
289
  it 'uses the options to create a Cache Store' do
299
290
  store = double(read: nil, write: nil)
300
291
 
301
- expect(Faraday::HttpCache::Storage).to receive(:new).with(hash_including(store: store))
292
+ expect(Faraday::HttpCache::Strategies::ByUrl).to receive(:new).with(hash_including(store: store))
302
293
  Faraday::HttpCache.new(app, store: store)
303
294
  end
304
295
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
  require 'active_support'
4
5
  require 'active_support/notifications'
data/spec/json_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache do
data/spec/request_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache::Request do
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache::Response do
@@ -228,11 +229,11 @@ describe Faraday::HttpCache::Response do
228
229
  describe 'remove age before caching and normalize max-age if non-zero age present' do
229
230
  it 'is fresh if the response still has some time to live' do
230
231
  headers = {
231
- 'Age' => 6,
232
- 'Cache-Control' => 'public, max-age=40',
233
- 'Date' => (Time.now - 38).httpdate,
234
- 'Expires' => (Time.now - 37).httpdate,
235
- 'Last-Modified' => (Time.now - 300).httpdate
232
+ 'Age' => 6,
233
+ 'Cache-Control' => 'public, max-age=40',
234
+ 'Date' => (Time.now - 38).httpdate,
235
+ 'Expires' => (Time.now - 37).httpdate,
236
+ 'Last-Modified' => (Time.now - 300).httpdate
236
237
  }
237
238
  response = Faraday::HttpCache::Response.new(response_headers: headers)
238
239
  expect(response).to be_fresh
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,17 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'uri'
3
4
  require 'socket'
4
5
 
5
6
  require 'faraday-http-cache'
6
- require 'faraday_middleware'
7
7
 
8
- # https://github.com/rails/rails/pull/14667
9
- require 'active_support/per_thread_registry'
8
+ if Gem::Version.new(Faraday::VERSION) < Gem::Version.new('1.0')
9
+ require 'faraday_middleware'
10
+ elsif ENV['FARADAY_ADAPTER'] == 'em_http'
11
+ require 'faraday/em_http'
12
+ end
13
+
14
+ require 'active_support'
10
15
  require 'active_support/cache'
11
16
 
12
17
  require 'support/test_app'
data/spec/storage_spec.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'spec_helper'
3
4
 
4
5
  describe Faraday::HttpCache::Storage do
@@ -15,6 +16,21 @@ describe Faraday::HttpCache::Storage do
15
16
  let(:storage) { Faraday::HttpCache::Storage.new(store: cache) }
16
17
  subject { storage }
17
18
 
19
+ before do
20
+ allow(Kernel).to receive(:warn).with(
21
+ 'Deprecated: Faraday::HttpCache::Storage is deprecated and will be removed '\
22
+ 'in the next major release. Use Faraday::HttpCache::Strategies::ByUrl instead.'
23
+ )
24
+ end
25
+
26
+ it 'creates strategy and warns about deprecation' do
27
+ expect(Kernel).to receive(:warn).with(
28
+ 'Deprecated: Faraday::HttpCache::Storage is deprecated and will be removed '\
29
+ 'in the next major release. Use Faraday::HttpCache::Strategies::ByUrl instead.'
30
+ )
31
+ is_expected.to be_a_kind_of(Faraday::HttpCache::Strategies::ByUrl)
32
+ end
33
+
18
34
  describe 'Cache configuration' do
19
35
  it 'uses a MemoryStore by default' do
20
36
  expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original
@@ -43,7 +59,7 @@ describe Faraday::HttpCache::Storage do
43
59
  let(:serializer) { JSON }
44
60
  it_behaves_like 'A storage with serialization'
45
61
 
46
- context 'when ASCII characters in response cannot be converted to UTF-8' do
62
+ context 'when ASCII characters in response cannot be converted to UTF-8', if: Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1') do
47
63
  let(:response) do
48
64
  body = String.new("\u2665").force_encoding('ASCII-8BIT')
49
65
  double(:response, serializable_hash: { 'body' => body })
@@ -111,11 +127,11 @@ describe Faraday::HttpCache::Storage do
111
127
  describe 'remove age before caching and normalize max-age if non-zero age present' do
112
128
  it 'is fresh if the response still has some time to live' do
113
129
  headers = {
114
- 'Age' => 6,
115
- 'Cache-Control' => 'public, max-age=40',
116
- 'Date' => (Time.now - 38).httpdate,
117
- 'Expires' => (Time.now - 37).httpdate,
118
- 'Last-Modified' => (Time.now - 300).httpdate
130
+ 'Age' => 6,
131
+ 'Cache-Control' => 'public, max-age=40',
132
+ 'Date' => (Time.now - 38).httpdate,
133
+ 'Expires' => (Time.now - 37).httpdate,
134
+ 'Last-Modified' => (Time.now - 300).httpdate
119
135
  }
120
136
  response = Faraday::HttpCache::Response.new(response_headers: headers)
121
137
  expect(response).to be_fresh
@@ -127,9 +143,10 @@ describe Faraday::HttpCache::Storage do
127
143
  end
128
144
 
129
145
  it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do
146
+ current_time = Time.now
130
147
  headers = {
131
- 'Date' => (Time.now - 39).httpdate,
132
- 'Expires' => (Time.now + 40).httpdate
148
+ 'Date' => (current_time - 39).httpdate,
149
+ 'Expires' => (current_time + 40).httpdate
133
150
  }
134
151
 
135
152
  response = Faraday::HttpCache::Response.new(response_headers: headers)
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Faraday::HttpCache::Strategies::BaseStrategy do
6
+ subject(:strategy) { described_class.new }
7
+
8
+ it 'uses a MemoryStore as a default store' do
9
+ expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original
10
+ strategy
11
+ end
12
+
13
+ context 'when the given store is not valid' do
14
+ let(:store) { double(:wrong_store) }
15
+ subject(:strategy) { described_class.new(store: store) }
16
+
17
+ it 'raises an error' do
18
+ expect { strategy }.to raise_error(ArgumentError)
19
+ end
20
+ end
21
+
22
+ it 'raises an error when abstract methods are called' do
23
+ expect { strategy.write(nil, nil) }.to raise_error(NotImplementedError)
24
+ expect { strategy.read(nil) }.to raise_error(NotImplementedError)
25
+ expect { strategy.delete(nil) }.to raise_error(NotImplementedError)
26
+ end
27
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Faraday::HttpCache::Strategies::ByUrl do
6
+ let(:cache_key) { '6e3b941d0f7572291c777b3e48c04b74124a55d0' }
7
+ let(:request) do
8
+ env = { method: :get, url: 'http://test/index' }
9
+ double(env.merge(serializable_hash: env))
10
+ end
11
+
12
+ let(:response) { double(serializable_hash: { response_headers: {} }) }
13
+
14
+ let(:cache) { Faraday::HttpCache::MemoryStore.new }
15
+
16
+ let(:strategy) { described_class.new(store: cache) }
17
+ subject { strategy }
18
+
19
+ describe 'Cache configuration' do
20
+ it 'uses a MemoryStore by default' do
21
+ expect(Faraday::HttpCache::MemoryStore).to receive(:new).and_call_original
22
+ described_class.new
23
+ end
24
+
25
+ it 'raises an error when the given store is not valid' do
26
+ wrong = double
27
+
28
+ expect {
29
+ described_class.new(store: wrong)
30
+ }.to raise_error(ArgumentError)
31
+ end
32
+ end
33
+
34
+ describe 'storing responses' do
35
+ shared_examples 'A strategy with serialization' do
36
+ it 'writes the response object to the underlying cache' do
37
+ entry = [serializer.dump(request.serializable_hash), serializer.dump(response.serializable_hash)]
38
+ expect(cache).to receive(:write).with(cache_key, [entry])
39
+ subject.write(request, response)
40
+ end
41
+ end
42
+
43
+ context 'with the JSON serializer' do
44
+ let(:serializer) { JSON }
45
+ it_behaves_like 'A strategy with serialization'
46
+
47
+ context 'when ASCII characters in response cannot be converted to UTF-8', if: Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1') do
48
+ let(:response) do
49
+ body = String.new("\u2665").force_encoding('ASCII-8BIT')
50
+ double(:response, serializable_hash: { 'body' => body })
51
+ end
52
+
53
+ it 'raises and logs a warning' do
54
+ logger = double(:logger, warn: nil)
55
+ strategy = described_class.new(logger: logger)
56
+
57
+ expect {
58
+ strategy.write(request, response)
59
+ }.to raise_error(::Encoding::UndefinedConversionError)
60
+ expect(logger).to have_received(:warn).with(
61
+ 'Response could not be serialized: "\xE2" from ASCII-8BIT to UTF-8. Try using Marshal to serialize.'
62
+ )
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'with the Marshal serializer' do
68
+ let(:cache_key) { '337d1e9c6c92423dd1c48a23054139058f97be40' }
69
+ let(:serializer) { Marshal }
70
+ let(:strategy) { described_class.new(store: cache, serializer: Marshal) }
71
+
72
+ it_behaves_like 'A strategy with serialization'
73
+ end
74
+ end
75
+
76
+ describe 'reading responses' do
77
+ let(:strategy) { described_class.new(store: cache, serializer: serializer) }
78
+
79
+ shared_examples 'A strategy with serialization' do
80
+ it 'returns nil if the response is not cached' do
81
+ expect(subject.read(request)).to be_nil
82
+ end
83
+
84
+ it 'decodes a stored response' do
85
+ subject.write(request, response)
86
+
87
+ expect(subject.read(request)).to be_a(Faraday::HttpCache::Response)
88
+ end
89
+ end
90
+
91
+ context 'with the JSON serializer' do
92
+ let(:serializer) { JSON }
93
+
94
+ it_behaves_like 'A strategy with serialization'
95
+ end
96
+
97
+ context 'with the Marshal serializer' do
98
+ let(:serializer) { Marshal }
99
+
100
+ it_behaves_like 'A strategy with serialization'
101
+ end
102
+ end
103
+
104
+ describe 'deleting responses' do
105
+ it 'removes the entries from the cache of the given URL' do
106
+ subject.write(request, response)
107
+ subject.delete(request.url)
108
+ expect(subject.read(request)).to be_nil
109
+ end
110
+ end
111
+
112
+ describe 'remove age before caching and normalize max-age if non-zero age present' do
113
+ it 'is fresh if the response still has some time to live' do
114
+ headers = {
115
+ 'Age' => 6,
116
+ 'Cache-Control' => 'public, max-age=40',
117
+ 'Date' => (Time.now - 38).httpdate,
118
+ 'Expires' => (Time.now - 37).httpdate,
119
+ 'Last-Modified' => (Time.now - 300).httpdate
120
+ }
121
+ response = Faraday::HttpCache::Response.new(response_headers: headers)
122
+ expect(response).to be_fresh
123
+ subject.write(request, response)
124
+
125
+ cached_response = subject.read(request)
126
+ expect(cached_response.max_age).to eq(34)
127
+ expect(cached_response).not_to be_fresh
128
+ end
129
+
130
+ it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do
131
+ headers = {
132
+ 'Date' => (Time.now - 39).httpdate,
133
+ 'Expires' => (Time.now + 40).httpdate
134
+ }
135
+
136
+ response = Faraday::HttpCache::Response.new(response_headers: headers)
137
+ expect(response).to be_fresh
138
+ subject.write(request, response)
139
+
140
+ sleep(1)
141
+ cached_response = subject.read(request)
142
+ expect(cached_response).not_to be_fresh
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Faraday::HttpCache::Strategies::ByVary do
6
+ let(:vary_index_cache_key) { '64896419583e8022efeb21d0ece6e266c0e58b59' }
7
+ let(:cache_key) { '25230d75622fffc4f4de8a6af69e6e3764f7eb6f' }
8
+ let(:vary) { '' }
9
+ let(:request) do
10
+ env = {method: :get, url: 'http://test/index'}
11
+ double(env.merge(serializable_hash: env))
12
+ end
13
+
14
+ let(:response_payload) { {response_headers: {'Vary' => vary}} }
15
+
16
+ let(:response) do
17
+ instance_double(Faraday::HttpCache::Response, payload: response_payload, serializable_hash: response_payload)
18
+ end
19
+
20
+ let(:cache) { Faraday::HttpCache::MemoryStore.new }
21
+
22
+ let(:strategy) { described_class.new(store: cache) }
23
+ subject { strategy }
24
+
25
+ describe 'storing responses' do
26
+ shared_examples 'A strategy with serialization' do
27
+ it 'writes the response object to the underlying cache' do
28
+ entry = serializer.dump(response.serializable_hash)
29
+ expect(cache).to receive(:write).with(vary_index_cache_key, vary)
30
+ expect(cache).to receive(:write).with(cache_key, entry)
31
+ subject.write(request, response)
32
+ end
33
+ end
34
+
35
+ context 'with the JSON serializer' do
36
+ let(:serializer) { JSON }
37
+ it_behaves_like 'A strategy with serialization'
38
+
39
+ context 'when ASCII characters in response cannot be converted to UTF-8', if: Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.1') do
40
+ let(:response_payload) do
41
+ body = String.new("\u2665").force_encoding('ASCII-8BIT')
42
+ super().merge('body' => body)
43
+ end
44
+
45
+ it 'raises and logs a warning' do
46
+ logger = double(:logger, warn: nil)
47
+ strategy = described_class.new(logger: logger)
48
+
49
+ expect {
50
+ strategy.write(request, response)
51
+ }.to raise_error(::Encoding::UndefinedConversionError)
52
+ expect(logger).to have_received(:warn).with(
53
+ 'Response could not be serialized: "\xE2" from ASCII-8BIT to UTF-8. Try using Marshal to serialize.'
54
+ )
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'with the Marshal serializer' do
60
+ let(:vary_index_cache_key) { '6a7cb42440c10ef6edeb1826086a4d90b04103f0' }
61
+ let(:cache_key) { '45e0efd1a60d29ed69d6c6018dfcb96f58db89e0' }
62
+ let(:serializer) { Marshal }
63
+ let(:strategy) { described_class.new(store: cache, serializer: Marshal) }
64
+
65
+ it_behaves_like 'A strategy with serialization'
66
+ end
67
+ end
68
+
69
+ describe 'reading responses' do
70
+ let(:strategy) { described_class.new(store: cache, serializer: serializer) }
71
+
72
+ shared_examples 'A strategy with serialization' do
73
+ it 'returns nil if the response is not cached' do
74
+ expect(subject.read(request)).to be_nil
75
+ end
76
+
77
+ it 'decodes a stored response' do
78
+ subject.write(request, response)
79
+
80
+ expect(subject.read(request)).to be_a(Faraday::HttpCache::Response)
81
+ end
82
+ end
83
+
84
+ context 'with the JSON serializer' do
85
+ let(:serializer) { JSON }
86
+
87
+ it_behaves_like 'A strategy with serialization'
88
+ end
89
+
90
+ context 'with the Marshal serializer' do
91
+ let(:serializer) { Marshal }
92
+
93
+ it_behaves_like 'A strategy with serialization'
94
+ end
95
+ end
96
+
97
+ describe 'deleting responses' do
98
+ it 'ignores delete method' do
99
+ subject.write(request, response)
100
+ subject.delete(request.url)
101
+ expect(subject.read(request)).not_to be_nil
102
+ end
103
+ end
104
+
105
+ describe 'remove age before caching and normalize max-age if non-zero age present' do
106
+ it 'is fresh if the response still has some time to live' do
107
+ headers = {
108
+ 'Age' => 6,
109
+ 'Cache-Control' => 'public, max-age=40',
110
+ 'Date' => (Time.now - 38).httpdate,
111
+ 'Expires' => (Time.now - 37).httpdate,
112
+ 'Last-Modified' => (Time.now - 300).httpdate
113
+ }
114
+ response = Faraday::HttpCache::Response.new(response_headers: headers)
115
+ expect(response).to be_fresh
116
+ subject.write(request, response)
117
+
118
+ cached_response = subject.read(request)
119
+ expect(cached_response.max_age).to eq(34)
120
+ expect(cached_response).not_to be_fresh
121
+ end
122
+
123
+ it 'is fresh until cached and that 1 second elapses then the response is no longer fresh' do
124
+ headers = {
125
+ 'Date' => (Time.now - 39).httpdate,
126
+ 'Expires' => (Time.now + 40).httpdate
127
+ }
128
+
129
+ response = Faraday::HttpCache::Response.new(response_headers: headers)
130
+ expect(response).to be_fresh
131
+ subject.write(request, response)
132
+
133
+ sleep(1)
134
+ cached_response = subject.read(request)
135
+ expect(cached_response).not_to be_fresh
136
+ end
137
+ end
138
+ end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'sinatra/base'
3
4
  require 'json'
4
5
 
@@ -27,7 +28,7 @@ class TestApp < Sinatra::Base
27
28
  end
28
29
 
29
30
  get '/image' do
30
- image = File.expand_path('../empty.png', __FILE__)
31
+ image = File.expand_path('empty.png', __dir__)
31
32
  data = IO.binread(image)
32
33
  [200, { 'Cache-Control' => 'max-age=400', 'Content-Type' => 'image/png' }, data]
33
34
  end