faraday-http-cache 2.1.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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