response_test.rb 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. require "#{File.dirname(__FILE__)}/spec_setup"
  2. describe 'Rack::Cache::Response' do
  3. before do
  4. @now = Time.httpdate(Time.now.httpdate)
  5. @one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
  6. @one_hour_later = Time.httpdate((Time.now + (60**2)).httpdate)
  7. @res = Rack::Cache::Response.new(200, {'Date' => @now.httpdate}, [])
  8. end
  9. after do
  10. @now, @res, @one_hour_ago = nil
  11. end
  12. it 'marks Rack tuples with string typed statuses as cacheable' do
  13. @res = Rack::Cache::Response.new('200',{'Date' => @now.httpdate},[])
  14. @res.headers['Expires'] = @one_hour_later.httpdate
  15. @res.should.be.cacheable
  16. end
  17. it 'responds to #to_a with a Rack response tuple' do
  18. @res.should.respond_to :to_a
  19. @res.to_a.should.equal [200, {'Date' => @now.httpdate}, []]
  20. end
  21. describe '#cache_control' do
  22. it 'handles multiple name=value pairs' do
  23. @res.headers['Cache-Control'] = 'max-age=600, max-stale=300, min-fresh=570'
  24. @res.cache_control['max-age'].should.equal '600'
  25. @res.cache_control['max-stale'].should.equal '300'
  26. @res.cache_control['min-fresh'].should.equal '570'
  27. end
  28. it 'removes the header when given an empty hash' do
  29. @res.headers['Cache-Control'] = 'max-age=600, must-revalidate'
  30. @res.cache_control['max-age'].should.equal '600'
  31. @res.cache_control = {}
  32. @res.headers.should.not.include 'Cache-Control'
  33. end
  34. end
  35. describe '#validateable?' do
  36. it 'is true when Last-Modified header present' do
  37. @res = Rack::Cache::Response.new(200, {'Last-Modified' => @one_hour_ago.httpdate}, [])
  38. @res.should.be.validateable
  39. end
  40. it 'is true when ETag header present' do
  41. @res = Rack::Cache::Response.new(200, {'ETag' => '"12345"'}, [])
  42. @res.should.be.validateable
  43. end
  44. it 'is false when no validator is present' do
  45. @res = Rack::Cache::Response.new(200, {}, [])
  46. @res.should.not.be.validateable
  47. end
  48. end
  49. describe '#date' do
  50. it 'uses the Date header if present' do
  51. @res = Rack::Cache::Response.new(200, {'Date' => @one_hour_ago.httpdate}, [])
  52. @res.date.should.equal @one_hour_ago
  53. end
  54. it 'uses the current time when no Date header present' do
  55. @res = Rack::Cache::Response.new(200, {}, [])
  56. @res.date.to_i.should.be.close Time.now.to_i, 1
  57. end
  58. it 'returns the correct date when the header is modified directly' do
  59. @res = Rack::Cache::Response.new(200, { 'Date' => @one_hour_ago.httpdate }, [])
  60. @res.date.should.equal @one_hour_ago
  61. @res.headers['Date'] = @now.httpdate
  62. @res.date.should.equal @now
  63. end
  64. end
  65. describe '#max_age' do
  66. it 'uses s-maxage cache control directive when present' do
  67. @res.headers['Cache-Control'] = 's-maxage=600, max-age=0'
  68. @res.max_age.should.equal 600
  69. end
  70. it 'falls back to max-age when no s-maxage directive present' do
  71. @res.headers['Cache-Control'] = 'max-age=600'
  72. @res.max_age.should.equal 600
  73. end
  74. it 'falls back to Expires when no max-age or s-maxage directive present' do
  75. @res.headers['Cache-Control'] = 'must-revalidate'
  76. @res.headers['Expires'] = @one_hour_later.httpdate
  77. @res.max_age.should.equal 60 ** 2
  78. end
  79. it 'gives a #max_age of nil when no freshness information available' do
  80. @res.max_age.should.be.nil
  81. end
  82. end
  83. describe '#private=' do
  84. it 'adds the private Cache-Control directive when set true' do
  85. @res.headers['Cache-Control'] = 'max-age=100'
  86. @res.private = true
  87. @res.headers['Cache-Control'].split(', ').sort.
  88. should.equal ['max-age=100', 'private']
  89. end
  90. it 'removes the public Cache-Control directive' do
  91. @res.headers['Cache-Control'] = 'public, max-age=100'
  92. @res.private = true
  93. @res.headers['Cache-Control'].split(', ').sort.
  94. should.equal ['max-age=100', 'private']
  95. end
  96. end
  97. describe '#expire!' do
  98. it 'sets the Age to be equal to the max-age' do
  99. @res.headers['Cache-Control'] = 'max-age=100'
  100. @res.expire!
  101. @res.headers['Age'].should.equal '100'
  102. end
  103. it 'sets the Age to be equal to the s-maxage when both max-age and s-maxage present' do
  104. @res.headers['Cache-Control'] = 'max-age=100, s-maxage=500'
  105. @res.expire!
  106. @res.headers['Age'].should.equal '500'
  107. end
  108. it 'does nothing when the response is already stale/expired' do
  109. @res.headers['Cache-Control'] = 'max-age=5, s-maxage=500'
  110. @res.headers['Age'] = '1000'
  111. @res.expire!
  112. @res.headers['Age'].should.equal '1000'
  113. end
  114. it 'does nothing when the response does not include freshness information' do
  115. @res.expire!
  116. @res.headers.should.not.include 'Age'
  117. end
  118. end
  119. describe '#ttl' do
  120. it 'is nil when no Expires or Cache-Control headers present' do
  121. @res.ttl.should.be.nil
  122. end
  123. it 'uses the Expires header when no max-age is present' do
  124. @res.headers['Expires'] = (@res.now + (60**2)).httpdate
  125. @res.ttl.should.be.close(60**2, 1)
  126. end
  127. it 'returns negative values when Expires is in part' do
  128. @res.ttl.should.be.nil
  129. @res.headers['Expires'] = @one_hour_ago.httpdate
  130. @res.ttl.should.be < 0
  131. end
  132. it 'uses the Cache-Control max-age value when present' do
  133. @res.headers['Cache-Control'] = 'max-age=60'
  134. @res.ttl.should.be.close(60, 1)
  135. end
  136. end
  137. describe '#vary' do
  138. it 'is nil when no Vary header is present' do
  139. @res.vary.should.be.nil
  140. end
  141. it 'returns the literal value of the Vary header' do
  142. @res.headers['Vary'] = 'Foo Bar Baz'
  143. @res.vary.should.equal 'Foo Bar Baz'
  144. end
  145. it 'can be checked for existence using the #vary? method' do
  146. @res.should.respond_to :vary?
  147. @res.should.not.vary
  148. @res.headers['Vary'] = '*'
  149. @res.should.vary
  150. end
  151. end
  152. describe '#vary_header_names' do
  153. it 'returns an empty Array when no Vary header is present' do
  154. @res.vary_header_names.should.be.empty
  155. end
  156. it 'parses a single header name value' do
  157. @res.headers['Vary'] = 'Accept-Language'
  158. @res.vary_header_names.should.equal ['Accept-Language']
  159. end
  160. it 'parses multiple header name values separated by spaces' do
  161. @res.headers['Vary'] = 'Accept-Language User-Agent X-Foo'
  162. @res.vary_header_names.should.equal \
  163. ['Accept-Language', 'User-Agent', 'X-Foo']
  164. end
  165. it 'parses multiple header name values separated by commas' do
  166. @res.headers['Vary'] = 'Accept-Language,User-Agent, X-Foo'
  167. @res.vary_header_names.should.equal \
  168. ['Accept-Language', 'User-Agent', 'X-Foo']
  169. end
  170. end
  171. end