diff options
Diffstat (limited to 'actionpack/test/dispatch/response_test.rb')
-rw-r--r-- | actionpack/test/dispatch/response_test.rb | 360 |
1 files changed, 284 insertions, 76 deletions
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 5fbd19acdf..400af42bac 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -1,10 +1,11 @@ -require 'abstract_unit' -require 'timeout' -require 'rack/content_length' +require "abstract_unit" +require "timeout" +require "rack/content_length" class ResponseTest < ActiveSupport::TestCase def setup - @response = ActionDispatch::Response.new + @response = ActionDispatch::Response.create + @response.request = ActionDispatch::Request.empty end def test_can_wait_until_commit @@ -36,12 +37,79 @@ class ResponseTest < ActiveSupport::TestCase assert_equal "closed stream", e.message end + def test_each_isnt_called_if_str_body_is_written + # Controller writes and reads response body + each_counter = 0 + @response.body = Object.new.tap { |o| o.singleton_class.send(:define_method, :each) { |&block| each_counter += 1; block.call "foo" } } + @response["X-Foo"] = @response.body + + assert_equal 1, each_counter, "#each was not called once" + + # Build response + status, headers, body = @response.to_a + + assert_equal 200, status + assert_equal "foo", headers["X-Foo"] + assert_equal "foo", body.each.to_a.join + + # Show that #each was not called twice + assert_equal 1, each_counter, "#each was not called once" + end + + def test_set_header_after_read_body_during_action + @response.body + + # set header after the action reads back @response.body + @response["x-header"] = "Best of all possible worlds." + + # the response can be built. + status, headers, body = @response.to_a + assert_equal 200, status + assert_equal "", body.body + + assert_equal "Best of all possible worlds.", headers["x-header"] + end + + def test_read_body_during_action + @response.body = "Hello, World!" + + # even though there's no explicitly set content-type, + assert_equal nil, @response.content_type + + # after the action reads back @response.body, + assert_equal "Hello, World!", @response.body + + # the response can be built. + status, headers, body = @response.to_a + assert_equal 200, status + assert_equal({ + "Content-Type" => "text/html; charset=utf-8" + }, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["Hello, World!"], parts + end + def test_response_body_encoding body = ["hello".encode(Encoding::UTF_8)] response = ActionDispatch::Response.new 200, {}, body + response.request = ActionDispatch::Request.empty assert_equal Encoding::UTF_8, response.body.encoding end + def test_response_charset_writer + @response.charset = "utf-16" + assert_equal "utf-16", @response.charset + @response.charset = nil + assert_equal "utf-8", @response.charset + end + + def test_setting_content_type_header_impacts_content_type_method + @response.headers["Content-Type"] = "application/aaron" + assert_equal "application/aaron", @response.content_type + end + test "simple output" do @response.body = "Hello, World!" @@ -57,7 +125,14 @@ class ResponseTest < ActiveSupport::TestCase end test "status handled properly in initialize" do - assert_equal 200, ActionDispatch::Response.new('200 OK').status + assert_equal 200, ActionDispatch::Response.new("200 OK").status + end + + def test_only_set_charset_still_defaults_to_text_html + response = ActionDispatch::Response.new + response.charset = "utf-16" + _, headers, _ = response.to_a + assert_equal "text/html; charset=utf-16", headers["Content-Type"] end test "utf8 output" do @@ -70,14 +145,36 @@ class ResponseTest < ActiveSupport::TestCase }, headers) end + test "content length" do + [100, 101, 102, 204].each do |c| + @response = ActionDispatch::Response.new + @response.status = c.to_s + @response.set_header "Content-Length", "0" + _, headers, _ = @response.to_a + assert !headers.has_key?("Content-Length"), "#{c} must not have a Content-Length header field" + end + end + + test "does not contain a message-body" do + [100, 101, 102, 204, 304].each do |c| + @response = ActionDispatch::Response.new + @response.status = c.to_s + @response.body = "Body must not be included" + _, _, body = @response.to_a + assert_empty body, "#{c} must not have a message-body but actually contains #{body}" + end + end + test "content type" do [204, 304].each do |c| + @response = ActionDispatch::Response.new @response.status = c.to_s _, headers, _ = @response.to_a assert !headers.has_key?("Content-Type"), "#{c} should not have Content-Type header" end [200, 302, 404, 500].each do |c| + @response = ActionDispatch::Response.new @response.status = c.to_s _, headers, _ = @response.to_a assert headers.has_key?("Content-Type"), "#{c} did not have Content-Type header" @@ -87,7 +184,7 @@ class ResponseTest < ActiveSupport::TestCase test "does not include Status header" do @response.status = "200 OK" _, headers, _ = @response.to_a - assert !headers.has_key?('Status') + assert !headers.has_key?("Status") end test "response code" do @@ -124,56 +221,89 @@ class ResponseTest < ActiveSupport::TestCase end test "cookies" do - @response.set_cookie("user_name", :value => "david", :path => "/") - status, headers, body = @response.to_a + @response.set_cookie("user_name", value: "david", path: "/") + _status, headers, _body = @response.to_a assert_equal "user_name=david; path=/", headers["Set-Cookie"] - assert_equal({"user_name" => "david"}, @response.cookies) + assert_equal({ "user_name" => "david" }, @response.cookies) + end - @response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5)) - status, headers, body = @response.to_a + test "multiple cookies" do + @response.set_cookie("user_name", value: "david", path: "/") + @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10, 5)) + _status, headers, _body = @response.to_a assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000", headers["Set-Cookie"] - assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies) + assert_equal({ "login" => "foo&bar", "user_name" => "david" }, @response.cookies) + end + test "delete cookies" do + @response.set_cookie("user_name", value: "david", path: "/") + @response.set_cookie("login", value: "foo&bar", path: "/", expires: Time.utc(2005, 10, 10, 5)) @response.delete_cookie("login") - status, headers, body = @response.to_a - assert_equal({"user_name" => "david", "login" => nil}, @response.cookies) + assert_equal({ "user_name" => "david", "login" => nil }, @response.cookies) end - test "read cache control" do + test "read ETag and Cache-Control" do resp = ActionDispatch::Response.new.tap { |response| response.cache_control[:public] = true - response.etag = '123' - response.body = 'Hello' + response.etag = "123" + response.body = "Hello" } resp.to_a - assert_equal('"202cb962ac59075b964b07152d234b70"', resp.etag) - assert_equal({:public => true}, resp.cache_control) + assert resp.etag? + assert resp.weak_etag? + assert_not resp.strong_etag? + assert_equal('W/"202cb962ac59075b964b07152d234b70"', resp.etag) + assert_equal({ public: true }, resp.cache_control) + + assert_equal("public", resp.headers["Cache-Control"]) + assert_equal('W/"202cb962ac59075b964b07152d234b70"', resp.headers["ETag"]) + end + + test "read strong ETag" do + resp = ActionDispatch::Response.new.tap { |response| + response.cache_control[:public] = true + response.strong_etag = "123" + response.body = "Hello" + } + resp.to_a - assert_equal('public', resp.headers['Cache-Control']) - assert_equal('"202cb962ac59075b964b07152d234b70"', resp.headers['ETag']) + assert resp.etag? + assert_not resp.weak_etag? + assert resp.strong_etag? + assert_equal('"202cb962ac59075b964b07152d234b70"', resp.etag) end test "read charset and content type" do resp = ActionDispatch::Response.new.tap { |response| - response.charset = 'utf-16' - response.content_type = Mime::XML - response.body = 'Hello' + response.charset = "utf-16" + response.content_type = Mime[:xml] + response.body = "Hello" } resp.to_a - assert_equal('utf-16', resp.charset) - assert_equal(Mime::XML, resp.content_type) + assert_equal("utf-16", resp.charset) + assert_equal(Mime[:xml], resp.content_type) + + assert_equal("application/xml; charset=utf-16", resp.headers["Content-Type"]) + end - assert_equal('application/xml; charset=utf-16', resp.headers['Content-Type']) + test "read content type with default charset utf-8" do + original = ActionDispatch::Response.default_charset + begin + resp = ActionDispatch::Response.new(200, "Content-Type" => "text/xml") + assert_equal("utf-8", resp.charset) + ensure + ActionDispatch::Response.default_charset = original + end end - test "read content type without charset" do + test "read content type with charset utf-16" do original = ActionDispatch::Response.default_charset begin - ActionDispatch::Response.default_charset = 'utf-16' - resp = ActionDispatch::Response.new(200, { "Content-Type" => "text/xml" }) - assert_equal('utf-16', resp.charset) + ActionDispatch::Response.default_charset = "utf-16" + resp = ActionDispatch::Response.new(200, "Content-Type" => "text/xml") + assert_equal("utf-16", resp.charset) ensure ActionDispatch::Response.default_charset = original end @@ -183,18 +313,18 @@ class ResponseTest < ActiveSupport::TestCase original_default_headers = ActionDispatch::Response.default_headers begin ActionDispatch::Response.default_headers = { - 'X-Frame-Options' => 'DENY', - 'X-Content-Type-Options' => 'nosniff', - 'X-XSS-Protection' => '1;' + "X-Frame-Options" => "DENY", + "X-Content-Type-Options" => "nosniff", + "X-XSS-Protection" => "1;" } - resp = ActionDispatch::Response.new.tap { |response| - response.body = 'Hello' + resp = ActionDispatch::Response.create.tap { |response| + response.body = "Hello" } resp.to_a - assert_equal('DENY', resp.headers['X-Frame-Options']) - assert_equal('nosniff', resp.headers['X-Content-Type-Options']) - assert_equal('1;', resp.headers['X-XSS-Protection']) + assert_equal("DENY", resp.headers["X-Frame-Options"]) + assert_equal("nosniff", resp.headers["X-Content-Type-Options"]) + assert_equal("1;", resp.headers["X-XSS-Protection"]) ensure ActionDispatch::Response.default_headers = original_default_headers end @@ -204,14 +334,14 @@ class ResponseTest < ActiveSupport::TestCase original_default_headers = ActionDispatch::Response.default_headers begin ActionDispatch::Response.default_headers = { - 'X-XX-XXXX' => 'Here is my phone number' + "X-XX-XXXX" => "Here is my phone number" } - resp = ActionDispatch::Response.new.tap { |response| - response.body = 'Hello' + resp = ActionDispatch::Response.create.tap { |response| + response.body = "Hello" } resp.to_a - assert_equal('Here is my phone number', resp.headers['X-XX-XXXX']) + assert_equal("Here is my phone number", resp.headers["X-XX-XXXX"]) ensure ActionDispatch::Response.default_headers = original_default_headers end @@ -223,12 +353,13 @@ class ResponseTest < ActiveSupport::TestCase end test "can be explicitly destructured into status, headers and an enumerable body" do - response = ActionDispatch::Response.new(404, { 'Content-Type' => 'text/plain' }, ['Not Found']) + response = ActionDispatch::Response.new(404, { "Content-Type" => "text/plain" }, ["Not Found"]) + response.request = ActionDispatch::Request.empty status, headers, body = *response assert_equal 404, status - assert_equal({ 'Content-Type' => 'text/plain' }, headers) - assert_equal ['Not Found'], body.each.to_a + assert_equal({ "Content-Type" => "text/plain" }, headers) + assert_equal ["Not Found"], body.each.to_a end test "[response.to_a].flatten does not recurse infinitely" do @@ -241,15 +372,74 @@ class ResponseTest < ActiveSupport::TestCase end test "compatibility with Rack::ContentLength" do - @response.body = 'Hello' + @response.body = "Hello" app = lambda { |env| @response.to_a } env = Rack::MockRequest.env_for("/") status, headers, body = app.call(env) - assert_nil headers['Content-Length'] + assert_nil headers["Content-Length"] status, headers, body = Rack::ContentLength.new(app).call(env) - assert_equal '5', headers['Content-Length'] + assert_equal "5", headers["Content-Length"] + end +end + +class ResponseHeadersTest < ActiveSupport::TestCase + def setup + @response = ActionDispatch::Response.create + @response.set_header "Foo", "1" + end + + test "has_header?" do + assert @response.has_header? "Foo" + assert_not @response.has_header? "foo" + assert_not @response.has_header? nil + end + + test "get_header" do + assert_equal "1", @response.get_header("Foo") + assert_nil @response.get_header("foo") + assert_nil @response.get_header(nil) + end + + test "set_header" do + assert_equal "2", @response.set_header("Foo", "2") + assert @response.has_header?("Foo") + assert_equal "2", @response.get_header("Foo") + + assert_nil @response.set_header("Foo", nil) + assert @response.has_header?("Foo") + assert_nil @response.get_header("Foo") + end + + test "delete_header" do + assert_nil @response.delete_header(nil) + + assert_nil @response.delete_header("foo") + assert @response.has_header?("Foo") + + assert_equal "1", @response.delete_header("Foo") + assert_not @response.has_header?("Foo") + end + + test "add_header" do + # Add a value to an existing header + assert_equal "1,2", @response.add_header("Foo", "2") + assert_equal "1,2", @response.get_header("Foo") + + # Add nil to an existing header + assert_equal "1,2", @response.add_header("Foo", nil) + assert_equal "1,2", @response.get_header("Foo") + + # Add nil to a nonexistent header + assert_nil @response.add_header("Bar", nil) + assert_not @response.has_header?("Bar") + assert_nil @response.get_header("Bar") + + # Add a value to a nonexistent header + assert_equal "1", @response.add_header("Bar", "1") + assert @response.has_header?("Bar") + assert_equal "1", @response.get_header("Bar") end end @@ -258,69 +448,87 @@ class ResponseIntegrationTest < ActionDispatch::IntegrationTest @app = lambda { |env| ActionDispatch::Response.new.tap { |resp| resp.cache_control[:public] = true - resp.etag = '123' - resp.body = 'Hello' + resp.etag = "123" + resp.body = "Hello" + resp.request = ActionDispatch::Request.empty }.to_a } - get '/' + get "/" assert_response :success - assert_equal('public', @response.headers['Cache-Control']) - assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag']) + assert_equal("public", @response.headers["Cache-Control"]) + assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.headers["ETag"]) - assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) - assert_equal({:public => true}, @response.cache_control) + assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.etag) + assert_equal({ public: true }, @response.cache_control) end test "response cache control from rackish app" do @app = lambda { |env| [200, - {'ETag' => '"202cb962ac59075b964b07152d234b70"', - 'Cache-Control' => 'public'}, ['Hello']] + { "ETag" => 'W/"202cb962ac59075b964b07152d234b70"', + "Cache-Control" => "public" }, ["Hello"]] } - get '/' + get "/" assert_response :success - assert_equal('public', @response.headers['Cache-Control']) - assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers['ETag']) + assert_equal("public", @response.headers["Cache-Control"]) + assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.headers["ETag"]) - assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) - assert_equal({:public => true}, @response.cache_control) + assert_equal('W/"202cb962ac59075b964b07152d234b70"', @response.etag) + assert_equal({ public: true }, @response.cache_control) end test "response charset and content type from railsish app" do @app = lambda { |env| ActionDispatch::Response.new.tap { |resp| - resp.charset = 'utf-16' - resp.content_type = Mime::XML - resp.body = 'Hello' + resp.charset = "utf-16" + resp.content_type = Mime[:xml] + resp.body = "Hello" + resp.request = ActionDispatch::Request.empty }.to_a } - get '/' + get "/" assert_response :success - assert_equal('utf-16', @response.charset) - assert_equal(Mime::XML, @response.content_type) + assert_equal("utf-16", @response.charset) + assert_equal(Mime[:xml], @response.content_type) - assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type']) + assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"]) end test "response charset and content type from rackish app" do @app = lambda { |env| [200, - {'Content-Type' => 'application/xml; charset=utf-16'}, - ['Hello']] + { "Content-Type" => "application/xml; charset=utf-16" }, + ["Hello"]] } - get '/' + get "/" assert_response :success - assert_equal('utf-16', @response.charset) - assert_equal(Mime::XML, @response.content_type) + assert_equal("utf-16", @response.charset) + assert_equal(Mime[:xml], @response.content_type) + + assert_equal("application/xml; charset=utf-16", @response.headers["Content-Type"]) + end + + test "strong ETag validator" do + @app = lambda { |env| + ActionDispatch::Response.new.tap { |resp| + resp.strong_etag = "123" + resp.body = "Hello" + resp.request = ActionDispatch::Request.empty + }.to_a + } + + get "/" + assert_response :ok - assert_equal('application/xml; charset=utf-16', @response.headers['Content-Type']) + assert_equal('"202cb962ac59075b964b07152d234b70"', @response.headers["ETag"]) + assert_equal('"202cb962ac59075b964b07152d234b70"', @response.etag) end end |