test/helpers_test.rb in sinatra-1.3.0.f vs test/helpers_test.rb in sinatra-1.3.0.g

- old
+ new

@@ -856,10 +856,24 @@ get '/' assert ! response['Last-Modified'] end + it 'does not change a status other than 200' do + mock_app do + get '/' do + status 299 + last_modified Time.at(0) + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT') + assert_status 299 + assert_body 'ok' + end + [Time.now, DateTime.now, Date.today, Time.now.to_i, Struct.new(:to_time).new(Time.now) ].each do |last_modified_time| describe "with #{last_modified_time.class.name}" do setup do mock_app do @@ -953,77 +967,644 @@ get '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time + 1).httpdate } assert_equal 304, status assert_equal '', body end end + + context "If-Unmodified-Since" do + it 'results in 200 if resource has not been modified' do + get '/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' } + assert_equal 200, status + assert_equal 'Boo!', body + end + + it 'results in 412 if resource has been modified' do + get '/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => Time.at(0).httpdate } + assert_equal 412, status + assert_equal '', body + end + end end end end describe 'etag' do - setup do - mock_app { - get '/' do - body { 'Hello World' } - etag 'FOO' - 'Boo!' + context "safe requests" do + it 'returns 200 for normal requests' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end end - post '/' do - etag 'FOO' - 'Matches!' + get('/') + assert_status 200 + assert_body 'ok' + end + + context "If-None-Match" do + it 'returns 304 when If-None-Match is *' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 304 + assert_body '' end - } - end - it 'sets the ETag header' do - get '/' - assert_equal '"FOO"', response['ETag'] - end + it 'returns 200 when If-None-Match is * for new resources' do + mock_app do + get '/' do + etag 'foo', :new_resource => true + 'ok' + end + end - it 'returns a body when conditional get misses' do - get '/' - assert_equal 200, status - assert_equal 'Boo!', body - end + get('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end - it 'returns a body when posting with no If-None-Match header' do - post '/' - assert_equal 200, status - assert_equal 'Matches!', body - end + it 'returns 304 when If-None-Match is * for existing resources' do + mock_app do + get '/' do + etag 'foo', :new_resource => false + 'ok' + end + end - it 'returns a body when conditional post matches' do - post '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' } - assert_equal 200, status - assert_equal 'Matches!', body - end + get('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 304 + assert_body '' + end - it 'halts with 412 when conditional post misses' do - post '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR"' } - assert_equal 412, status - assert_equal '', body + it 'returns 304 when If-None-Match is the etag' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"') + assert_status 304 + assert_body '' + end + + it 'returns 304 when If-None-Match includes the etag' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"') + assert_status 304 + assert_body '' + end + + it 'returns 200 when If-None-Match does not include the etag' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"') + assert_status 200 + assert_body 'ok' + end + + it 'ignores If-Modified-Since if If-None-Match does not match' do + mock_app do + get '/' do + etag 'foo' + last_modified Time.at(0) + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"') + assert_status 200 + assert_body 'ok' + end + + it 'does not change a status code other than 2xx or 304' do + mock_app do + get '/' do + status 499 + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"') + assert_status 499 + assert_body 'ok' + end + + it 'does change 2xx status codes' do + mock_app do + get '/' do + status 299 + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"') + assert_status 304 + assert_body '' + end + + it 'does not send a body on 304 status codes' do + mock_app do + get '/' do + status 304 + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"') + assert_status 304 + assert_body '' + end + end + + context "If-Match" do + it 'returns 200 when If-Match is the etag' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MATCH' => '"foo"') + assert_status 200 + assert_body 'ok' + end + + it 'returns 200 when If-Match includes the etag' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"') + assert_status 200 + assert_body 'ok' + end + + it 'returns 200 when If-Match is *' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-Match is * for new resources' do + mock_app do + get '/' do + etag 'foo', :new_resource => true + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 200 when If-Match is * for existing resources' do + mock_app do + get '/' do + etag 'foo', :new_resource => false + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-Match does not include the etag' do + mock_app do + get '/' do + etag 'foo' + 'ok' + end + end + + get('/', {}, 'HTTP_IF_MATCH' => '"bar"') + assert_status 412 + assert_body '' + end + end end - it 'halts when a conditional GET matches' do - get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"FOO"' } - assert_equal 304, status - assert_equal '', body + context "idempotent requests" do + it 'returns 200 for normal requests' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/') + assert_status 200 + assert_body 'ok' + end + + context "If-None-Match" do + it 'returns 412 when If-None-Match is *' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 200 when If-None-Match is * for new resources' do + mock_app do + put '/' do + etag 'foo', :new_resource => true + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-None-Match is * for existing resources' do + mock_app do + put '/' do + etag 'foo', :new_resource => false + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 412 when If-None-Match is the etag' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"') + assert_status 412 + assert_body '' + end + + it 'returns 412 when If-None-Match includes the etag' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"') + assert_status 412 + assert_body '' + end + + it 'returns 200 when If-None-Match does not include the etag' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"') + assert_status 200 + assert_body 'ok' + end + + it 'ignores If-Modified-Since if If-None-Match does not match' do + mock_app do + put '/' do + etag 'foo' + last_modified Time.at(0) + 'ok' + end + end + + put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"') + assert_status 200 + assert_body 'ok' + end + end + + context "If-Match" do + it 'returns 200 when If-Match is the etag' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_MATCH' => '"foo"') + assert_status 200 + assert_body 'ok' + end + + it 'returns 200 when If-Match includes the etag' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"') + assert_status 200 + assert_body 'ok' + end + + it 'returns 200 when If-Match is *' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-Match is * for new resources' do + mock_app do + put '/' do + etag 'foo', :new_resource => true + 'ok' + end + end + + put('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 200 when If-Match is * for existing resources' do + mock_app do + put '/' do + etag 'foo', :new_resource => false + 'ok' + end + end + + put('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-Match does not include the etag' do + mock_app do + put '/' do + etag 'foo' + 'ok' + end + end + + put('/', {}, 'HTTP_IF_MATCH' => '"bar"') + assert_status 412 + assert_body '' + end + end end - it 'should handle multiple ETag values in If-None-Match header' do - get '/', {}, { 'HTTP_IF_NONE_MATCH' => '"BAR", *' } - assert_equal 304, status - assert_equal '', body + context "post requests" do + it 'returns 200 for normal requests' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/') + assert_status 200 + assert_body 'ok' + end + + context "If-None-Match" do + it 'returns 200 when If-None-Match is *' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 200 when If-None-Match is * for new resources' do + mock_app do + post '/' do + etag 'foo', :new_resource => true + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-None-Match is * for existing resources' do + mock_app do + post '/' do + etag 'foo', :new_resource => false + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 412 when If-None-Match is the etag' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"') + assert_status 412 + assert_body '' + end + + it 'returns 412 when If-None-Match includes the etag' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"') + assert_status 412 + assert_body '' + end + + it 'returns 200 when If-None-Match does not include the etag' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"') + assert_status 200 + assert_body 'ok' + end + + it 'ignores If-Modified-Since if If-None-Match does not match' do + mock_app do + post '/' do + etag 'foo' + last_modified Time.at(0) + 'ok' + end + end + + post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"') + assert_status 200 + assert_body 'ok' + end + end + + context "If-Match" do + it 'returns 200 when If-Match is the etag' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_MATCH' => '"foo"') + assert_status 200 + assert_body 'ok' + end + + it 'returns 200 when If-Match includes the etag' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-Match is *' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 412 when If-Match is * for new resources' do + mock_app do + post '/' do + etag 'foo', :new_resource => true + 'ok' + end + end + + post('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 412 + assert_body '' + end + + it 'returns 200 when If-Match is * for existing resources' do + mock_app do + post '/' do + etag 'foo', :new_resource => false + 'ok' + end + end + + post('/', {}, 'HTTP_IF_MATCH' => '*') + assert_status 200 + assert_body 'ok' + end + + it 'returns 412 when If-Match does not include the etag' do + mock_app do + post '/' do + etag 'foo' + 'ok' + end + end + + post('/', {}, 'HTTP_IF_MATCH' => '"bar"') + assert_status 412 + assert_body '' + end + end end it 'uses a weak etag with the :weak option' do - mock_app { + mock_app do get '/' do etag 'FOO', :weak "that's weak, dude." end - } + end get '/' assert_equal 'W/"FOO"', response['ETag'] end it 'raises an ArgumentError for an invalid strength' do