From 5231c36b4fc030ade7acd4f2e69e8b18cecc0567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Celizna?= Date: Tue, 23 Apr 2024 12:33:44 +0200 Subject: [PATCH] Rack 3 compatibility (#682) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rack 3 compat * add appraisals to test on both Rack 2 & 3 • drop Ruby 2.3 test * install appraisals * remove lockfiles * Update .gitignore * downcase headers refactor to `headers = headers.transform_keys(&:downcase) if Rack.release >= "3"` * update `activerecord-jdbcsqlite3-adapter` --- .github/workflows/ci.yml | 48 +++---- .gitignore | 1 + Appraisals | 7 ++ Gemfile | 2 +- gemfiles/rack_2.gemfile | 10 ++ gemfiles/rack_3.gemfile | 10 ++ lib/shrine/plugins/derivation_endpoint.rb | 45 +++++-- lib/shrine/plugins/download_endpoint.rb | 15 ++- lib/shrine/plugins/presign_endpoint.rb | 15 ++- lib/shrine/plugins/rack_response.rb | 10 +- lib/shrine/plugins/upload_endpoint.rb | 15 ++- shrine.gemspec | 3 +- test/plugin/derivation_endpoint_test.rb | 146 +++++++++++----------- test/plugin/download_endpoint_test.rb | 34 ++--- test/plugin/presign_endpoint_test.rb | 26 ++-- test/plugin/rack_response_test.rb | 80 ++++++------ test/plugin/upload_endpoint_test.rb | 28 ++--- test/support/ext.rb | 16 +++ test/test_helper.rb | 9 ++ 19 files changed, 324 insertions(+), 196 deletions(-) create mode 100644 Appraisals create mode 100644 gemfiles/rack_2.gemfile create mode 100644 gemfiles/rack_3.gemfile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e763de5a..b6f9c7652 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,9 +2,9 @@ name: CI on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ '**' ] + branches: ["**"] env: RACK_ENV: development @@ -24,7 +24,6 @@ jobs: fail-fast: false matrix: ruby: - - "2.3" - "2.4" - "2.5" - "2.6" @@ -32,24 +31,31 @@ jobs: - "3.0" - "3.1" - "3.2" + - "3.3" - "jruby-9.4" + rack: + - "2" + - "3" steps: - - uses: actions/checkout@v3 - - - name: Set up Ruby - uses: ruby/setup-ruby@v1 - with: - ruby-version: ${{ matrix.ruby }} - bundler-cache: true - rubygems: ${{ matrix.ruby <= '2.5' && '3.3.26' || 'latest' }} - - - name: Set up Minio - run: | - mkdir -p "${GITHUB_WORKSPACE}"/minio/data/minio-bucket - wget -nc -O "${GITHUB_WORKSPACE}"/minio/minio https://dl.min.io/server/minio/release/linux-amd64/minio - chmod +x "${GITHUB_WORKSPACE}"/minio/minio - ${GITHUB_WORKSPACE}/minio/minio server ${GITHUB_WORKSPACE}/minio/data --address localhost:9000 &>${GITHUB_WORKSPACE}/minio/data/server.log & - - - name: Run tests - run: bundle exec rake test + - uses: actions/checkout@v3 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + rubygems: ${{ matrix.ruby <= '2.5' && '3.3.26' || 'latest' }} + + - name: Set up Minio + run: | + mkdir -p "${GITHUB_WORKSPACE}"/minio/data/minio-bucket + wget -nc -O "${GITHUB_WORKSPACE}"/minio/minio https://dl.min.io/server/minio/release/linux-amd64/minio + chmod +x "${GITHUB_WORKSPACE}"/minio/minio + ${GITHUB_WORKSPACE}/minio/minio server ${GITHUB_WORKSPACE}/minio/data --address localhost:9000 &>${GITHUB_WORKSPACE}/minio/data/server.log & + + - name: Install dependencies + run: bundle exec appraisal install + + - name: Run tests + run: bundle exec appraisal rack-${{ matrix.rack }} rake test diff --git a/.gitignore b/.gitignore index 313608364..cc041148f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ website/build coverage/ node_modules .docusaurus +gemfiles/*.lock diff --git a/Appraisals b/Appraisals new file mode 100644 index 000000000..f5b330cf7 --- /dev/null +++ b/Appraisals @@ -0,0 +1,7 @@ +appraise "rack-2" do + gem "rack", "~> 2" +end + +appraise "rack-3" do + gem "rack", "~> 3" +end diff --git a/Gemfile b/Gemfile index bae8c28c9..9b00365dd 100644 --- a/Gemfile +++ b/Gemfile @@ -7,4 +7,4 @@ gem "simplecov" gem "hanna", require: false -gem "activerecord-jdbcsqlite3-adapter", "~> 70.0", platform: :jruby if RUBY_ENGINE == "jruby" +gem "activerecord-jdbcsqlite3-adapter", "~> 51.0", platform: :jruby if RUBY_ENGINE == "jruby" diff --git a/gemfiles/rack_2.gemfile b/gemfiles/rack_2.gemfile new file mode 100644 index 000000000..0b47a3a30 --- /dev/null +++ b/gemfiles/rack_2.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "pry" +gem "simplecov" +gem "hanna", require: false +gem "rack", "~> 2" + +gemspec path: "../" diff --git a/gemfiles/rack_3.gemfile b/gemfiles/rack_3.gemfile new file mode 100644 index 000000000..c3394c647 --- /dev/null +++ b/gemfiles/rack_3.gemfile @@ -0,0 +1,10 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "pry" +gem "simplecov" +gem "hanna", require: false +gem "rack", "~> 3" + +gemspec path: "../" diff --git a/lib/shrine/plugins/derivation_endpoint.rb b/lib/shrine/plugins/derivation_endpoint.rb index d175a8660..27fef576a 100644 --- a/lib/shrine/plugins/derivation_endpoint.rb +++ b/lib/shrine/plugins/derivation_endpoint.rb @@ -365,7 +365,16 @@ def call(env) handle_request(request) end - headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + headers ||= {} + + if Rack.release >= "3" + headers["content-length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s : + body.map(&:bytesize).inject(0, :+).to_s + else + headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + end + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" [status, headers, body] end @@ -411,6 +420,8 @@ def handle_request(request) headers["Cache-Control"] = derivation.option(:cache_control) end + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" + [status, headers, body] end @@ -444,7 +455,11 @@ def expires_in(request) # Halts the request with the error message. def error!(status, message) - throw :halt, [status, { "Content-Type" => "text/plain" }, [message]] + headers = { "Content-Type" => "text/plain" } + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" + + throw :halt, [status, headers, [message]] end def secret_key @@ -485,18 +500,30 @@ def file_response(file, env) status = response[0] - headers = { - "Content-Type" => type || response[1]["Content-Type"], - "Content-Length" => response[1]["Content-Length"], - "Content-Disposition" => content_disposition(file), - "Content-Range" => response[1]["Content-Range"], - "Accept-Ranges" => "bytes", - }.compact + headers = if Rack.release >= "3" + { + "content-type" => type || response[1]["content-type"], + "content-length" => response[1]["content-length"], + "content-disposition" => content_disposition(file), + "content-range" => response[1]["content-range"], + "accept-ranges" => "bytes", + }.compact + else + { + "Content-Type" => type || response[1]["Content-Type"], + "Content-Length" => response[1]["Content-Length"], + "Content-Disposition" => content_disposition(file), + "Content-Range" => response[1]["Content-Range"], + "Accept-Ranges" => "bytes", + }.compact + end body = Rack::BodyProxy.new(response[2]) { File.delete(file.path) } file.close + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" + [status, headers, body] end diff --git a/lib/shrine/plugins/download_endpoint.rb b/lib/shrine/plugins/download_endpoint.rb index 722c47437..768376209 100644 --- a/lib/shrine/plugins/download_endpoint.rb +++ b/lib/shrine/plugins/download_endpoint.rb @@ -113,7 +113,14 @@ def call(env) handle_request(request) end - headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + if Rack.release >= "3" + headers["content-length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s : + body.map(&:bytesize).inject(0, :+).to_s + else + headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + end + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" [status, headers, body] end @@ -197,7 +204,11 @@ def bad_request!(message) # Halts the request with the error message. def error!(status, message) - throw :halt, [status, { "Content-Type" => "text/plain" }, [message]] + headers = { "Content-Type" => "text/plain" } + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" + + throw :halt, [status, headers, [message]] end end end diff --git a/lib/shrine/plugins/presign_endpoint.rb b/lib/shrine/plugins/presign_endpoint.rb index 34e72025e..c8330f96a 100644 --- a/lib/shrine/plugins/presign_endpoint.rb +++ b/lib/shrine/plugins/presign_endpoint.rb @@ -91,7 +91,14 @@ def call(env) end end - headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + if Rack.release >= "3" + headers["content-length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s : + body.map(&:bytesize).inject(0, :+).to_s + else + headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + end + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" [status, headers, body] end @@ -172,7 +179,11 @@ def make_response(object, request) # Used for early returning an error response. def error!(status, message) - throw :halt, [status, { "Content-Type" => CONTENT_TYPE_TEXT }, [message]] + headers = { "Content-Type" => CONTENT_TYPE_TEXT } + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" + + throw :halt, [status, headers, [message]] end # Returns the uploader around the specified storage. diff --git a/lib/shrine/plugins/rack_response.rb b/lib/shrine/plugins/rack_response.rb index e307fd64a..c5eb47301 100644 --- a/lib/shrine/plugins/rack_response.rb +++ b/lib/shrine/plugins/rack_response.rb @@ -32,7 +32,11 @@ def call(**options) headers = rack_headers(**options) body = rack_body(**options) - [status, headers, body] + if Rack.release >= "3" + [status, headers.transform_keys(&:downcase), body] + else + [status, headers, body] + end end private @@ -141,6 +145,10 @@ def close file.close end + def bytesize + each.inject(0) { |sum, chunk| sum += chunk.length } + end + # Rack::Sendfile is activated when response body responds to #to_path. def respond_to_missing?(name, include_private = false) name == :to_path && path diff --git a/lib/shrine/plugins/upload_endpoint.rb b/lib/shrine/plugins/upload_endpoint.rb index 84235fdcd..8e271af80 100644 --- a/lib/shrine/plugins/upload_endpoint.rb +++ b/lib/shrine/plugins/upload_endpoint.rb @@ -91,7 +91,14 @@ def call(env) handle_request(request) end - headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + if Rack.release >= "3" + headers["content-length"] ||= body.respond_to?(:bytesize) ? body.bytesize.to_s : + body.map(&:bytesize).inject(0, :+).to_s + else + headers["Content-Length"] ||= body.map(&:bytesize).inject(0, :+).to_s + end + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" [status, headers, body] end @@ -210,7 +217,11 @@ def verify_checksum!(file, request) # Used for early returning an error response. def error!(status, message) - throw :halt, [status, { "Content-Type" => CONTENT_TYPE_TEXT }, [message]] + headers = { "Content-Type" => CONTENT_TYPE_TEXT } + + headers = headers.transform_keys(&:downcase) if Rack.release >= "3" + + throw :halt, [status, headers, [message]] end # Returns the uploader around the specified storage. diff --git a/shrine.gemspec b/shrine.gemspec index 3878b93e6..26912822d 100644 --- a/shrine.gemspec +++ b/shrine.gemspec @@ -37,12 +37,13 @@ direct uploads for fully asynchronous user experience. gem.add_dependency "content_disposition", "~> 1.0" # general testing helpers + gem.add_development_dependency "appraisal", "~> 2.5" gem.add_development_dependency "rake", ">= 11.1" gem.add_development_dependency "minitest", "~> 5.8" gem.add_development_dependency "mocha", "~> 1.11" # for endpoint plugins - gem.add_development_dependency "rack", "~> 2.0" + gem.add_development_dependency "rack", ">= 2", "< 4" gem.add_development_dependency "http-form_data", "~> 2.2" gem.add_development_dependency "rack-test_app" diff --git a/test/plugin/derivation_endpoint_test.rb b/test/plugin/derivation_endpoint_test.rb index 1f6994f39..05cb77051 100644 --- a/test/plugin/derivation_endpoint_test.rb +++ b/test/plugin/derivation_endpoint_test.rb @@ -198,7 +198,7 @@ describe "Shrine.derivation_endpoint" do def app(*args, **options) - Rack::TestApp.wrap(Rack::Lint.new(endpoint(*args, **options))) + Rack::TestApp.wrap(Rack::Lint.new(endpoint(*args, **options)), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) end def endpoint(*args, **options) @@ -210,7 +210,7 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 200, response.status assert_equal "gray dark content", response.body_binary - assert_equal "17", response.headers["Content-Length"] + assert_equal "17", response.headers[CONTENT_LENGTH_HEADER] end it "handles Range requests" do @@ -218,8 +218,8 @@ def endpoint(*args, **options) response = app.get(derivation_url, headers: { "Range" => "bytes=0-3" }) assert_equal 206, response.status assert_equal "gray", response.body_binary - assert_equal "4", response.headers["Content-Length"] - assert_equal "bytes 0-3/12", response.headers["Content-Range"] + assert_equal "4", response.headers[CONTENT_LENGTH_HEADER] + assert_equal "bytes 0-3/12", response.headers[CONTENT_RANGE_HEADER] end it "applies plugin options" do @@ -227,7 +227,7 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray) response = app.get(derivation_url) assert_equal 200, response.status - assert_match "attachment", response.headers["Content-Disposition"] + assert_match "attachment", response.headers[CONTENT_DISPOSITION_HEADER] end it "applies app options" do @@ -235,7 +235,7 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray) response = app(disposition: "attachment").get(derivation_url) assert_equal 200, response.status - assert_match "attachment", response.headers["Content-Disposition"] + assert_match "attachment", response.headers[CONTENT_DISPOSITION_HEADER] end it "applies 'type' param" do @@ -243,7 +243,7 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray, type: "text/csv") response = app(type: "text/plain").get(derivation_url) assert_equal 200, response.status - assert_equal "text/csv", response.headers["Content-Type"] + assert_equal "text/csv", response.headers[CONTENT_TYPE_HEADER] end it "applies 'disposition' param" do @@ -251,7 +251,7 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray, disposition: "attachment") response = app(disposition: "inline").get(derivation_url) assert_equal 200, response.status - assert_match "attachment", response.headers["Content-Disposition"] + assert_match "attachment", response.headers[CONTENT_DISPOSITION_HEADER] end it "applies 'filename' param" do @@ -259,7 +259,7 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray, filename: "custom") response = app(filename: "default").get(derivation_url) assert_equal 200, response.status - assert_match "filename=\"custom\"", response.headers["Content-Disposition"] + assert_match "filename=\"custom\"", response.headers[CONTENT_DISPOSITION_HEADER] end it "applies 'version' param" do @@ -267,41 +267,41 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray, version: 2) response = app(version: 1).get(derivation_url) assert_equal 200, response.status - assert_match "filename=\"2\"", response.headers["Content-Disposition"] + assert_match "filename=\"2\"", response.headers[CONTENT_DISPOSITION_HEADER] end it "returns Cache-Control header on 2xx response" do derivation_url = @uploaded_file.derivation_url(:gray) response = app.get(derivation_url) assert_equal 200, response.status - assert_equal "public, max-age=31536000", response.headers["Cache-Control"] + assert_equal "public, max-age=31536000", response.headers[CACHE_CONTROL_HEADER] derivation_url = @uploaded_file.derivation_url(:gray) response = app.get(derivation_url, headers: { "Range" => "bytes=0-3" }) assert_equal 206, response.status - assert_equal "public, max-age=31536000", response.headers["Cache-Control"] + assert_equal "public, max-age=31536000", response.headers[CACHE_CONTROL_HEADER] derivation_url = @uploaded_file.derivation_url(:gray) response = app(upload: true, upload_redirect: true).get(derivation_url) assert_equal 302, response.status - refute response.headers.key?("Cache-Control") + refute response.headers.key?(CACHE_CONTROL_HEADER) end it "applies :cache_control" do @shrine.plugin :derivation_endpoint, cache_control: "public, max-age=10" derivation_url = @uploaded_file.derivation_url(:gray) response = app.get(derivation_url) - assert_equal "public, max-age=10", response.headers["Cache-Control"] + assert_equal "public, max-age=10", response.headers[CACHE_CONTROL_HEADER] response = app(cache_control: "public, max-age=20").get(derivation_url) - assert_equal "public, max-age=20", response.headers["Cache-Control"] + assert_equal "public, max-age=20", response.headers[CACHE_CONTROL_HEADER] response = app(cache_control: -> { "public, max-age=20" }).get(derivation_url) - assert_equal "public, max-age=20", response.headers["Cache-Control"] + assert_equal "public, max-age=20", response.headers[CACHE_CONTROL_HEADER] derivation_url = @uploaded_file.derivation_url(:gray, expires_in: 100) response = app.get(derivation_url) - assert_equal "public, max-age=10", response.headers["Cache-Control"] + assert_equal "public, max-age=10", response.headers[CACHE_CONTROL_HEADER] end it "returns 404 on unknown derivation" do @@ -309,7 +309,7 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 404, response.status assert_match "Unknown derivation", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] end it "returns 404 when source was not found" do @@ -318,17 +318,17 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 404, response.status assert_match "Source file not found", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] end it "successfully handles expiring links that have not yet expired" do derivation_url = @uploaded_file.derivation_url(:gray, expires_in: 100) response = app.get(derivation_url) assert_equal 200, response.status - assert_equal "12", response.headers["Content-Length"] + assert_equal "12", response.headers[CONTENT_LENGTH_HEADER] # caching duration is limited by the expiration date - max_age = Integer(response.headers["Cache-Control"][/max-age=(\d+)/, 1]) + max_age = Integer(response.headers[CACHE_CONTROL_HEADER][/max-age=(\d+)/, 1]) assert_operator max_age, :<=, 100 assert_operator 0, :<, max_age end @@ -339,8 +339,8 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 403, response.status assert_match "Request has expired", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] - refute response.headers.key?("Cache-Control") + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] + refute response.headers.key?(CACHE_CONTROL_HEADER) end it "returns 403 on invalid signature" do @@ -349,8 +349,8 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 403, response.status assert_match "signature does not match", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] - refute response.headers.key?("Cache-Control") + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] + refute response.headers.key?(CACHE_CONTROL_HEADER) end it "returns 403 on missing signature" do @@ -359,8 +359,8 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 403, response.status assert_match "Missing \"signature\" param", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] - refute response.headers.key?("Cache-Control") + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] + refute response.headers.key?(CACHE_CONTROL_HEADER) end it "includes request params when calculating signature" do @@ -368,7 +368,7 @@ def endpoint(*args, **options) response = app.get(derivation_url) assert_equal 403, response.status assert_match "signature does not match", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] end it "skips signature verifcation when secret_key is nil" do @@ -378,14 +378,14 @@ def endpoint(*args, **options) derivation_url = @uploaded_file.derivation_url(:gray).sub(/signature=\w+$/, "") response = app.get(derivation_url) assert_equal 200, response.status - assert_equal "12", response.headers["Content-Length"] + assert_equal "12", response.headers[CONTENT_LENGTH_HEADER] end it "accepts HEAD requests" do derivation_url = @uploaded_file.derivation_url(:gray) response = app.head(derivation_url) assert_equal 200, response.status - assert_equal "12", response.headers["Content-Length"] + assert_equal "12", response.headers[CONTENT_LENGTH_HEADER] end it "returns 405 on invalid request method" do @@ -393,7 +393,7 @@ def endpoint(*args, **options) response = app.post(derivation_url) assert_equal 405, response.status assert_equal "Method not allowed", response.body_binary - assert_equal response.body_binary.length.to_s, response.headers["Content-Length"] + assert_equal response.body_binary.length.to_s, response.headers[CONTENT_LENGTH_HEADER] end it "defines #inspect and #to_s" do @@ -419,7 +419,7 @@ def endpoint(*args, **options) status, headers, body = @shrine.derivation_response(env) assert_equal 200, status - assert_equal "12", headers["Content-Length"] + assert_equal "12", headers[CONTENT_LENGTH_HEADER] assert_equal "gray content", body.enum_for(:each).to_a.join assert_equal "", env["SCRIPT_NAME"] @@ -461,7 +461,7 @@ def endpoint(*args, **options) status, headers, body = @shrine.derivation_response(env, type: "text/plain") assert_equal 200, status - assert_equal "text/plain", headers["Content-Type"] + assert_equal "text/plain", headers[CONTENT_TYPE_HEADER] end it "fails when request path doesn't start with prefix" do @@ -489,26 +489,26 @@ def endpoint(*args, **options) response = @uploaded_file.derivation_response(:gray, env: {}) assert_equal 200, response[0] - assert_equal "12", response[1]["Content-Length"] + assert_equal "12", response[1][CONTENT_LENGTH_HEADER] assert_equal "gray content", response[2].enum_for(:each).to_a.join end it "returns Content-Disposition" do response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal ContentDisposition.inline("gray-#{@uploaded_file.id}"), response[1]["Content-Disposition"] + assert_equal ContentDisposition.inline("gray-#{@uploaded_file.id}"), response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, "dark", env: {}) - assert_equal ContentDisposition.inline("gray-dark-#{@uploaded_file.id}"), response[1]["Content-Disposition"] + assert_equal ContentDisposition.inline("gray-dark-#{@uploaded_file.id}"), response[1][CONTENT_DISPOSITION_HEADER] @shrine.derivation(:gray) { |file| Tempfile.new(["derivation", ".txt"]) } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal ContentDisposition.inline("gray-#{@uploaded_file.id}.txt"), response[1]["Content-Disposition"] + assert_equal ContentDisposition.inline("gray-#{@uploaded_file.id}.txt"), response[1][CONTENT_DISPOSITION_HEADER] end it "returns Content-Type" do @shrine.derivation(:gray) { |file| Tempfile.new(["derivation", ".jpg"]) } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal "image/jpeg", response[1]["Content-Type"] + assert_equal "image/jpeg", response[1][CONTENT_TYPE_HEADER] @shrine.derivation(:gray) { |file| Tempfile.new(["derivation"]) } response = @uploaded_file.derivation_response(:gray, env: {}) @@ -520,33 +520,33 @@ def endpoint(*args, **options) @shrine.plugin :derivation_endpoint, type: "text/plain" response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] @shrine.plugin :derivation_endpoint, type: -> { "text/plain" } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, type: "text/csv") - assert_equal "text/csv", response[1]["Content-Type"] + assert_equal "text/csv", response[1][CONTENT_TYPE_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, type: -> { "text/csv" }) - assert_equal "text/csv", response[1]["Content-Type"] + assert_equal "text/csv", response[1][CONTENT_TYPE_HEADER] end it "applies :disposition" do @shrine.plugin :derivation_endpoint, disposition: "attachment" response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "attachment; ", response[1]["Content-Disposition"] + assert_match "attachment; ", response[1][CONTENT_DISPOSITION_HEADER] @shrine.plugin :derivation_endpoint, disposition: -> { "attachment" } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "attachment; ", response[1]["Content-Disposition"] + assert_match "attachment; ", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, disposition: "inline") - assert_match "inline; ", response[1]["Content-Disposition"] + assert_match "inline; ", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, disposition: -> { "inline" }) - assert_match "inline; ", response[1]["Content-Disposition"] + assert_match "inline; ", response[1][CONTENT_DISPOSITION_HEADER] end it "applies :filename" do @@ -554,30 +554,30 @@ def endpoint(*args, **options) @shrine.plugin :derivation_endpoint, filename: "one" response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "inline; filename=\"one.txt\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"one.txt\"", response[1][CONTENT_DISPOSITION_HEADER] @shrine.plugin :derivation_endpoint, filename: -> { "one" } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "inline; filename=\"one.txt\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"one.txt\"", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, filename: "two.csv") - assert_match "inline; filename=\"two.csv\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"two.csv\"", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, filename: -> { "two.csv" }) - assert_match "inline; filename=\"two.csv\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"two.csv\"", response[1][CONTENT_DISPOSITION_HEADER] end it "handles Range requests" do response = @uploaded_file.derivation_response(:gray, env: { "HTTP_RANGE" => "bytes=0-3" }) assert_equal 206, response[0] - assert_equal "bytes 0-3/12", response[1]["Content-Range"] - assert_equal "bytes", response[1]["Accept-Ranges"] + assert_equal "bytes 0-3/12", response[1][CONTENT_RANGE_HEADER] + assert_equal "bytes", response[1][ACCEPT_RANGES_HEADER] assert_equal "gray", response[2].enum_for(:each).to_a.join response = @uploaded_file.derivation_response(:gray, env: {}) assert_equal 200, response[0] - assert_equal "bytes", response[1]["Accept-Ranges"] - refute response[1].key?("Content-Range") + assert_equal "bytes", response[1][ACCEPT_RANGES_HEADER] + refute response[1].key?(CONTENT_RANGE_HEADER) end it "closes and deletes derivation result" do @@ -605,7 +605,7 @@ def endpoint(*args, **options) response = @uploaded_file.derivation_response(:gray, env: {}) assert_equal 200, response[0] - assert_equal "12", response[1]["Content-Length"] + assert_equal "12", response[1][CONTENT_LENGTH_HEADER] assert_equal "gray content", response[2].enum_for(:each).to_a.join refute_instance_of Shrine::Plugins::RackResponse::FileBody, response[2] @@ -618,7 +618,7 @@ def endpoint(*args, **options) response = @uploaded_file.derivation_response(:gray, env: {}) assert_equal 200, response[0] - assert_equal "12", response[1]["Content-Length"] + assert_equal "12", response[1][CONTENT_LENGTH_HEADER] assert_equal "gray content", response[2].enum_for(:each).to_a.join end @@ -627,17 +627,17 @@ def endpoint(*args, **options) @shrine.plugin :derivation_endpoint, type: "text/plain" response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] @shrine.plugin :derivation_endpoint, type: -> { "text/plain" } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, type: "text/csv") - assert_equal "text/csv", response[1]["Content-Type"] + assert_equal "text/csv", response[1][CONTENT_TYPE_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, type: -> { "text/csv" }) - assert_equal "text/csv", response[1]["Content-Type"] + assert_equal "text/csv", response[1][CONTENT_TYPE_HEADER] end it "applies :disposition" do @@ -645,17 +645,17 @@ def endpoint(*args, **options) @shrine.plugin :derivation_endpoint, disposition: "attachment" response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "attachment; ", response[1]["Content-Disposition"] + assert_match "attachment; ", response[1][CONTENT_DISPOSITION_HEADER] @shrine.plugin :derivation_endpoint, disposition: -> { "attachment" } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "attachment; ", response[1]["Content-Disposition"] + assert_match "attachment; ", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, disposition: "inline") - assert_match "inline; ", response[1]["Content-Disposition"] + assert_match "inline; ", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, disposition: -> { "inline" }) - assert_match "inline; ", response[1]["Content-Disposition"] + assert_match "inline; ", response[1][CONTENT_DISPOSITION_HEADER] end it "applies :filename" do @@ -663,33 +663,33 @@ def endpoint(*args, **options) @shrine.plugin :derivation_endpoint, filename: "one" response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "inline; filename=\"one\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"one\"", response[1][CONTENT_DISPOSITION_HEADER] @shrine.plugin :derivation_endpoint, filename: -> { "one" } response = @uploaded_file.derivation_response(:gray, env: {}) - assert_match "inline; filename=\"one\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"one\"", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, filename: "two") - assert_match "inline; filename=\"two\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"two\"", response[1][CONTENT_DISPOSITION_HEADER] response = @uploaded_file.derivation_response(:gray, env: {}, filename: -> { "two" }) - assert_match "inline; filename=\"two\"", response[1]["Content-Disposition"] + assert_match "inline; filename=\"two\"", response[1][CONTENT_DISPOSITION_HEADER] end it "handles Range requests" do @uploaded_file.derivation_response(:gray, env: {}) response = @uploaded_file.derivation_response(:gray, env: { "HTTP_RANGE" => "bytes=0-3" }) assert_equal 206, response[0] - assert_equal "bytes 0-3/12", response[1]["Content-Range"] - assert_equal "bytes", response[1]["Accept-Ranges"] + assert_equal "bytes 0-3/12", response[1][CONTENT_RANGE_HEADER] + assert_equal "bytes", response[1][ACCEPT_RANGES_HEADER] assert_equal "gray", response[2].enum_for(:each).to_a.join end it "returns ETag" do @uploaded_file.derivation_response(:gray, env: {}) response = @uploaded_file.derivation_response(:gray, env: {}) - assert_instance_of String, response[1]["ETag"] - assert_match /^W\/"\w{32}"$/, response[1]["ETag"] + assert_instance_of String, response[1][ETAG_HEADER] + assert_match /^W\/"\w{32}"$/, response[1][ETAG_HEADER] end it "applies :upload_open_options" do @@ -739,7 +739,7 @@ def upload(io, id, **options) response = @uploaded_file.derivation_response(:gray, env: {}) assert_equal 200, response[0] - assert_equal "12", response[1]["Content-Length"] + assert_equal "12", response[1][CONTENT_LENGTH_HEADER] assert_equal "gray content", response[2].enum_for(:each).to_a.join end diff --git a/test/plugin/download_endpoint_test.rb b/test/plugin/download_endpoint_test.rb index ab660ceec..05cecc969 100644 --- a/test/plugin/download_endpoint_test.rb +++ b/test/plugin/download_endpoint_test.rb @@ -4,7 +4,7 @@ describe Shrine::Plugins::DownloadEndpoint do def app - Rack::TestApp.wrap(Rack::Lint.new(endpoint)) + Rack::TestApp.wrap(Rack::Lint.new(endpoint), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) end def endpoint @@ -23,9 +23,9 @@ def endpoint response = app.get(@uploaded_file.download_url) assert_equal 200, response.status assert_equal @uploaded_file.read, response.body_binary - assert_equal @uploaded_file.size.to_s, response.headers["Content-Length"] - assert_equal @uploaded_file.mime_type, response.headers["Content-Type"] - assert_equal ContentDisposition.inline(@uploaded_file.original_filename), response.headers["Content-Disposition"] + assert_equal @uploaded_file.size.to_s, response.headers[CONTENT_LENGTH_HEADER] + assert_equal @uploaded_file.mime_type, response.headers[CONTENT_TYPE_HEADER] + assert_equal ContentDisposition.inline(@uploaded_file.original_filename), response.headers[CONTENT_DISPOSITION_HEADER] end it "applies :download_options hash" do @@ -45,46 +45,46 @@ def endpoint it "applies :disposition to response" do @shrine.plugin :download_endpoint, disposition: "attachment" response = app.get(@uploaded_file.download_url) - assert_equal ContentDisposition.attachment(@uploaded_file.id), response.headers["Content-Disposition"] + assert_equal ContentDisposition.attachment(@uploaded_file.id), response.headers[CONTENT_DISPOSITION_HEADER] end it "returns Cache-Control" do response = app.get(@uploaded_file.download_url) - assert_equal "max-age=31536000", response.headers["Cache-Control"] + assert_equal "max-age=31536000", response.headers[CACHE_CONTROL_HEADER] end it "accepts :redirect with true" do @shrine.plugin :download_endpoint, redirect: true response = app.get(@uploaded_file.download_url) assert_equal 302, response.status - assert_match %r{^memory://\w+$}, response.headers["Location"] + assert_match %r{^memory://\w+$}, response.headers[LOCATION_HEADER] end it "accepts :redirect with proc" do @shrine.plugin :download_endpoint, redirect: -> (uploaded_file, request) { "/foo" } response = app.get(@uploaded_file.download_url) assert_equal 302, response.status - assert_equal "/foo", response.headers["Location"] + assert_equal "/foo", response.headers[LOCATION_HEADER] end it "returns Accept-Ranges" do response = app.get(@uploaded_file.download_url) - assert_equal "bytes", response.headers["Accept-Ranges"] + assert_equal "bytes", response.headers[ACCEPT_RANGES_HEADER] end it "supports ranged requests" do @uploaded_file = @uploader.upload(fakeio("content")) response = app.get(@uploaded_file.download_url, headers: { "Range" => "bytes=2-4" }) assert_equal 206, response.status - assert_equal "bytes 2-4/7", response.headers["Content-Range"] - assert_equal "3", response.headers["Content-Length"] + assert_equal "bytes 2-4/7", response.headers[CONTENT_RANGE_HEADER] + assert_equal "3", response.headers[CONTENT_LENGTH_HEADER] assert_equal "nte", response.body_binary end it "returns ETag" do response = app.get(@uploaded_file.download_url) - assert_instance_of String, response.headers["ETag"] - assert_match /^W\/"\w{32}"$/, response.headers["ETag"] + assert_instance_of String, response.headers[ETAG_HEADER] + assert_match /^W\/"\w{32}"$/, response.headers[ETAG_HEADER] end it "returns 404 for nonexisting file" do @@ -125,9 +125,9 @@ def endpoint end it "accepts ad-hoc options" do - app = Rack::TestApp.wrap(@shrine.download_endpoint(disposition: "attachment")) + app = Rack::TestApp.wrap(@shrine.download_endpoint(disposition: "attachment"), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) response = app.get(@uploaded_file.download_url) - assert_match /^attachment; /, response.headers["Content-Disposition"] + assert_match /^attachment; /, response.headers[CONTENT_DISPOSITION_HEADER] end it "returns same URL regardless of metadata order" do @@ -165,7 +165,7 @@ def endpoint status, headers, body = @shrine.download_response(env) assert_equal 200, status - assert_equal @uploaded_file.size.to_s, headers["Content-Length"] + assert_equal @uploaded_file.size.to_s, headers[CONTENT_LENGTH_HEADER] assert_equal @uploaded_file.read, body.enum_for(:each).to_a.join assert_equal "", env["SCRIPT_NAME"] @@ -207,7 +207,7 @@ def endpoint status, headers, body = @shrine.download_response(env, disposition: "attachment") assert_equal 200, status - assert_match /^attachment; /, headers["Content-Disposition"] + assert_match /^attachment; /, headers[CONTENT_DISPOSITION_HEADER] end it "fails when request path doesn't start with prefix" do diff --git a/test/plugin/presign_endpoint_test.rb b/test/plugin/presign_endpoint_test.rb index 34cf53bff..248c3ce1a 100644 --- a/test/plugin/presign_endpoint_test.rb +++ b/test/plugin/presign_endpoint_test.rb @@ -5,7 +5,7 @@ describe Shrine::Plugins::PresignEndpoint do def app - Rack::TestApp.wrap(Rack::Lint.new(endpoint)) + Rack::TestApp.wrap(Rack::Lint.new(endpoint), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) end def endpoint @@ -23,9 +23,9 @@ def endpoint assert_equal 200, response.status - assert_equal "application/json; charset=utf-8", response.headers["Content-Type"] - assert_equal response.body_binary.bytesize.to_s, response.headers["Content-Length"] - assert_equal "no-store", response.headers["Cache-Control"] + assert_equal "application/json; charset=utf-8", response.headers[CONTENT_TYPE_HEADER] + assert_equal response.body_binary.bytesize.to_s, response.headers[CONTENT_LENGTH_HEADER] + assert_equal "no-store", response.headers[CACHE_CONTROL_HEADER] assert_instance_of String, response.body_json["method"] assert_instance_of String, response.body_json["url"] @@ -86,23 +86,23 @@ def endpoint it "accepts response proc" do @shrine.plugin :presign_endpoint, rack_response: -> (o, r) do - [200, {"Content-Type" => "application/vnd.api+json"}, [{data: o}.to_json]] + [200, {CONTENT_TYPE_HEADER => "application/vnd.api+json"}, [{data: o}.to_json]] end response = app.get "/" assert_equal ["fields", "headers", "method", "url"], JSON.parse(response.body_binary)["data"].keys.sort - assert_equal "application/vnd.api+json", response.headers["Content-Type"] + assert_equal "application/vnd.api+json", response.headers[CONTENT_TYPE_HEADER] end it "allows overriding Cache-Control" do @shrine.plugin :presign_endpoint, rack_response: -> (o, r) do - [200, {"Content-Type" => "application/json", "Cache-Control" => "no-cache"}, [o.to_json]] + [200, {CONTENT_TYPE_HEADER => "application/json", "Cache-Control" => "no-cache"}, [o.to_json]] end response = app.get "/" - assert_equal "no-cache", response.headers["Cache-Control"] + assert_equal "no-cache", response.headers[CACHE_CONTROL_HEADER] end it "allows overriding options when instantiating the endpoint" do - app = Rack::TestApp.wrap(@shrine.presign_endpoint(:cache, presign_options: { content_type: "image/jpeg" })) + app = Rack::TestApp.wrap(@shrine.presign_endpoint(:cache, presign_options: { content_type: "image/jpeg" }), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) response = app.get "/" assert_equal "image/jpeg", response.body_json["fields"]["Content-Type"] end @@ -111,7 +111,7 @@ def endpoint response = app.options "/" assert_equal 200, response.status - assert_equal Hash["Content-Length" => "0"], response.headers + assert_equal Hash[CONTENT_LENGTH_HEADER => "0"], response.headers assert_equal "", response.body_binary end @@ -123,7 +123,7 @@ def endpoint it "doesn't accept verbs other than GET or OPTIONS" do response = app.put "/" assert_equal 405, response.status - assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "text/plain", response.headers[CONTENT_TYPE_HEADER] assert_equal "Method Not Allowed", response.body_binary end @@ -140,7 +140,7 @@ def endpoint response = @shrine.presign_response(:cache, env) assert_equal 200, response[0] - assert_equal "application/json; charset=utf-8", response[1]["Content-Type"] + assert_equal "application/json; charset=utf-8", response[1][CONTENT_TYPE_HEADER] assert_match /\.txt$/, JSON.parse(response[2].first)["fields"]["key"] end @@ -156,7 +156,7 @@ def endpoint response = @shrine.presign_response(:cache, env, presign_options: { content_type: "foo/bar" }) assert_equal 200, response[0] - assert_equal "application/json; charset=utf-8", response[1]["Content-Type"] + assert_equal "application/json; charset=utf-8", response[1][CONTENT_TYPE_HEADER] assert_equal "foo/bar", JSON.parse(response[2].first)["fields"]["Content-Type"] end end diff --git a/test/plugin/rack_response_test.rb b/test/plugin/rack_response_test.rb index a0cdb4492..f89e672a4 100644 --- a/test/plugin/rack_response_test.rb +++ b/test/plugin/rack_response_test.rb @@ -18,71 +18,71 @@ uploaded_file = @uploader.upload(fakeio("content")) uploaded_file.metadata.delete("size") response = uploaded_file.to_rack_response - assert_equal "7", response[1]["Content-Length"] + assert_equal "7", response[1][CONTENT_LENGTH_HEADER] end it "returns Content-Type header with mime_type metadata" do uploaded_file = @uploader.upload(fakeio(content_type: "text/plain")) response = uploaded_file.to_rack_response - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] end it "returns Content-Type header from extension if mime_type metadata is missing" do uploaded_file = @uploader.upload(fakeio(filename: "document.txt")) response = uploaded_file.to_rack_response - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] end it "doesn't return Content-Type header if MIME type is unknown" do uploaded_file = @uploader.upload(fakeio(filename: "foo.foo")) response = uploaded_file.to_rack_response - refute response[1].key?("Content-Type") + refute response[1].key?(CONTENT_TYPE_HEADER) end it "doesn't Content-Type header if MIME type is missing" do uploaded_file = @uploader.upload(fakeio) response = uploaded_file.to_rack_response - refute response[1].key?("Content-Type") + refute response[1].key?(CONTENT_TYPE_HEADER) end it "returns Content-Type header from :type" do uploaded_file = @uploader.upload(fakeio(content_type: "text/plain")) response = uploaded_file.to_rack_response(type: "text/plain; charset=utf-8") - assert_equal "text/plain; charset=utf-8", response[1]["Content-Type"] + assert_equal "text/plain; charset=utf-8", response[1][CONTENT_TYPE_HEADER] end it "it allows setting Content-Type to application/octet-stream" do uploaded_file = @uploader.upload(fakeio(content_type: "application/octet-stream")) response = uploaded_file.to_rack_response - assert_equal "application/octet-stream", response[1]["Content-Type"] + assert_equal "application/octet-stream", response[1][CONTENT_TYPE_HEADER] uploaded_file = @uploader.upload(fakeio) response = uploaded_file.to_rack_response(type: "application/octet-stream") - assert_equal "application/octet-stream", response[1]["Content-Type"] + assert_equal "application/octet-stream", response[1][CONTENT_TYPE_HEADER] end it "returns Content-Disposition filename from metadata" do uploaded_file = @uploader.upload(fakeio(filename: "plain.txt")) response = uploaded_file.to_rack_response - assert_equal ContentDisposition.inline("plain.txt"), response[1]["Content-Disposition"] + assert_equal ContentDisposition.inline("plain.txt"), response[1][CONTENT_DISPOSITION_HEADER] end it "returns Content-Disposition filename from :filename" do uploaded_file = @uploader.upload(fakeio(filename: "plain.txt")) response = uploaded_file.to_rack_response(filename: "custom.txt") - assert_equal ContentDisposition.inline("custom.txt"), response[1]["Content-Disposition"] + assert_equal ContentDisposition.inline("custom.txt"), response[1][CONTENT_DISPOSITION_HEADER] end it "returns Content-Disposition filename with id if metadata is missing" do uploaded_file = @uploader.upload(fakeio, location: "foo/bar/baz") response = uploaded_file.to_rack_response - assert_equal ContentDisposition.inline("baz"), response[1]["Content-Disposition"] + assert_equal ContentDisposition.inline("baz"), response[1][CONTENT_DISPOSITION_HEADER] end it "returns Content-Disposition disposition from :disposition" do uploaded_file = @uploader.upload(fakeio) response = uploaded_file.to_rack_response(disposition: "attachment") - assert_equal ContentDisposition.attachment(uploaded_file.id), response[1]["Content-Disposition"] + assert_equal ContentDisposition.attachment(uploaded_file.id), response[1][CONTENT_DISPOSITION_HEADER] end it "returns body which yields contents of the file" do @@ -114,69 +114,69 @@ uploaded_file = @uploader.upload(fakeio("content")) response = uploaded_file.to_rack_response(range: "bytes=0-6") assert_equal 206, response[0] - assert_equal "bytes 0-6/7", response[1]["Content-Range"] - assert_equal "7", response[1]["Content-Length"] + assert_equal "bytes 0-6/7", response[1][CONTENT_RANGE_HEADER] + assert_equal "7", response[1][CONTENT_LENGTH_HEADER] assert_equal "content", response[2].each { |chunk| break chunk } uploaded_file = @uploader.upload(fakeio("content")) response = uploaded_file.to_rack_response(range: "bytes=0-2") assert_equal 206, response[0] - assert_equal "bytes 0-2/7", response[1]["Content-Range"] - assert_equal "3", response[1]["Content-Length"] + assert_equal "bytes 0-2/7", response[1][CONTENT_RANGE_HEADER] + assert_equal "3", response[1][CONTENT_LENGTH_HEADER] assert_equal "con", response[2].each { |chunk| break chunk } uploaded_file = @uploader.upload(fakeio("content")) response = uploaded_file.to_rack_response(range: "bytes=2-4") assert_equal 206, response[0] - assert_equal "bytes 2-4/7", response[1]["Content-Range"] - assert_equal "3", response[1]["Content-Length"] + assert_equal "bytes 2-4/7", response[1][CONTENT_RANGE_HEADER] + assert_equal "3", response[1][CONTENT_LENGTH_HEADER] assert_equal "nte", response[2].each { |chunk| break chunk } uploaded_file = @uploader.upload(fakeio("content")) response = uploaded_file.to_rack_response(range: "bytes=4-6") assert_equal 206, response[0] - assert_equal "bytes 4-6/7", response[1]["Content-Range"] - assert_equal "3", response[1]["Content-Length"] + assert_equal "bytes 4-6/7", response[1][CONTENT_RANGE_HEADER] + assert_equal "3", response[1][CONTENT_LENGTH_HEADER] assert_equal "ent", response[2].each { |chunk| break chunk } end it "returns ranged responses across multiple chunks" do uploaded_file = @uploader.upload(fakeio("a" * 16*1024 + "b" * 16*1024 + "c" * 4*1024)) response = uploaded_file.to_rack_response(range: "bytes=0-36863") - assert_equal "bytes 0-36863/36864", response[1]["Content-Range"] - assert_equal "36864", response[1]["Content-Length"] + assert_equal "bytes 0-36863/36864", response[1][CONTENT_RANGE_HEADER] + assert_equal "36864", response[1][CONTENT_LENGTH_HEADER] yielded_content = "" response[2].each { |chunk| yielded_content << chunk } assert_equal "a" * 16*1024 + "b" * 16*1024 + "c" * 4*1024, yielded_content uploaded_file = @uploader.upload(fakeio("a" * 16*1024 + "b" * 16*1024 + "c" * 4*1024)) response = uploaded_file.to_rack_response(range: "bytes=0-20479") - assert_equal "bytes 0-20479/36864", response[1]["Content-Range"] - assert_equal "20480", response[1]["Content-Length"] + assert_equal "bytes 0-20479/36864", response[1][CONTENT_RANGE_HEADER] + assert_equal "20480", response[1][CONTENT_LENGTH_HEADER] yielded_content = "" response[2].each { |chunk| yielded_content << chunk } assert_equal "a" * 16*1024 + "b" * 4*1024, yielded_content uploaded_file = @uploader.upload(fakeio("a" * 16*1024 + "b" * 16*1024 + "c" * 4*1024)) response = uploaded_file.to_rack_response(range: "bytes=12288-20479") - assert_equal "bytes 12288-20479/36864", response[1]["Content-Range"] - assert_equal "8192", response[1]["Content-Length"] + assert_equal "bytes 12288-20479/36864", response[1][CONTENT_RANGE_HEADER] + assert_equal "8192", response[1][CONTENT_LENGTH_HEADER] yielded_content = "" response[2].each { |chunk| yielded_content << chunk } assert_equal "a" * 4*1024 + "b" * 4*1024, yielded_content uploaded_file = @uploader.upload(fakeio("a" * 16*1024 + "b" * 16*1024 + "c" * 4*1024)) response = uploaded_file.to_rack_response(range: "bytes=12288-33791") - assert_equal "bytes 12288-33791/36864", response[1]["Content-Range"] - assert_equal "21504", response[1]["Content-Length"] + assert_equal "bytes 12288-33791/36864", response[1][CONTENT_RANGE_HEADER] + assert_equal "21504", response[1][CONTENT_LENGTH_HEADER] yielded_content = "" response[2].each { |chunk| yielded_content << chunk } assert_equal "a" * 4*1024 + "b" * 16*1024 + "c" * 1*1024, yielded_content uploaded_file = @uploader.upload(fakeio("a" * 16*1024 + "b" * 16*1024 + "c" * 4*1024)) response = uploaded_file.to_rack_response(range: "bytes=35840-36863") - assert_equal "bytes 35840-36863/36864", response[1]["Content-Range"] - assert_equal "1024", response[1]["Content-Length"] + assert_equal "bytes 35840-36863/36864", response[1][CONTENT_RANGE_HEADER] + assert_equal "1024", response[1][CONTENT_LENGTH_HEADER] yielded_content = "" response[2].each { |chunk| yielded_content << chunk } assert_equal "c" * 1*1024, yielded_content @@ -187,24 +187,24 @@ uploaded_file.metadata.delete("size") response = uploaded_file.to_rack_response(range: "bytes=0-6") assert_equal 206, response[0] - assert_equal "bytes 0-6/7", response[1]["Content-Range"] - assert_equal "7", response[1]["Content-Length"] + assert_equal "bytes 0-6/7", response[1][CONTENT_RANGE_HEADER] + assert_equal "7", response[1][CONTENT_LENGTH_HEADER] assert_equal "content", response[2].each { |chunk| break chunk } end it "returns Accept-Ranges when :range is given" do uploaded_file = @uploader.upload(fakeio) response = uploaded_file.to_rack_response - refute response[1].key?("Accept-Ranges") + refute response[1].key?(ACCEPT_RANGES_HEADER) response = uploaded_file.to_rack_response(range: nil) - assert_equal "bytes", response[1]["Accept-Ranges"] + assert_equal "bytes", response[1][ACCEPT_RANGES_HEADER] end it "returns ETag" do uploaded_file = @uploader.upload(fakeio) response = uploaded_file.to_rack_response - assert_instance_of String, response[1]["ETag"] - assert_match /^W\/"\w{32}"$/, response[1]["ETag"] + assert_instance_of String, response[1][ETAG_HEADER] + assert_match /^W\/"\w{32}"$/, response[1][ETAG_HEADER] end it "makes ETag as unique as possible" do @@ -212,19 +212,19 @@ etags << @uploader.class.new(:cache) .upload(fakeio, location: "foo") - .to_rack_response[1]["ETag"] + .to_rack_response[1][ETAG_HEADER] etags << @uploader.class.new(:cache) .upload(fakeio, location: "bar") - .to_rack_response[1]["ETag"] + .to_rack_response[1][ETAG_HEADER] etags << @uploader.class.new(:store) .upload(fakeio, location: "foo") - .to_rack_response[1]["ETag"] + .to_rack_response[1][ETAG_HEADER] etags << uploader { plugin :rack_response } .upload(fakeio, location: "foo") - .to_rack_response[1]["ETag"] + .to_rack_response[1][ETAG_HEADER] assert_equal etags, etags.uniq end diff --git a/test/plugin/upload_endpoint_test.rb b/test/plugin/upload_endpoint_test.rb index f23698c9c..91750987f 100644 --- a/test/plugin/upload_endpoint_test.rb +++ b/test/plugin/upload_endpoint_test.rb @@ -5,7 +5,7 @@ describe Shrine::Plugins::UploadEndpoint do def app - Rack::TestApp.wrap(Rack::Lint.new(endpoint)) + Rack::TestApp.wrap(Rack::Lint.new(endpoint), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) end def endpoint @@ -21,7 +21,7 @@ def endpoint response = app.post "/", multipart: { file: image } assert_equal 200, response.status - assert_equal "application/json; charset=utf-8", response.headers["Content-Type"] + assert_equal "application/json; charset=utf-8", response.headers[CONTENT_TYPE_HEADER] assert_match /^\w+\.jpg$/, response.body_json["id"] assert_equal "cache", response.body_json["storage"] @@ -64,28 +64,28 @@ def endpoint @shrine.plugin :upload_endpoint, max_size: 10 response = app.post "/", multipart: { file: image } assert_equal 413, response.status - assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "text/plain", response.headers[CONTENT_TYPE_HEADER] assert_equal "Upload Too Large", response.body_binary end it "validates that param is a file" do response = app.post "/", multipart: { file: "image" } assert_equal 400, response.status - assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "text/plain", response.headers[CONTENT_TYPE_HEADER] assert_equal "Upload Not Valid", response.body_binary end it "validates that param is present" do response = app.post "/", multipart: { image: "image" } assert_equal 400, response.status - assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "text/plain", response.headers[CONTENT_TYPE_HEADER] assert_equal "Upload Not Found", response.body_binary end it "handles filenames with UTF-8 characters" do filename = "über_pdf_with_1337%_leetness.pdf" form = HTTP::FormData.create({ file: HTTP::FormData::Part.new("", filename: filename) }) - response = app.post "/", multipart: { input: form.to_s }, headers: {"Content-Type" => form.content_type} + response = app.post "/", multipart: { input: form.to_s }, headers: {CONTENT_TYPE_HEADER => form.content_type} assert_equal 200, response.status uploaded_file = @shrine.uploaded_file(response.body_json) assert_equal filename, uploaded_file.original_filename @@ -144,27 +144,27 @@ def endpoint it "accepts response proc" do @shrine.plugin :upload_endpoint, rack_response: -> (o, r) do - [200, {"Content-Type" => "application/vnd.api+json"}, [{data: o}.to_json]] + [200, {CONTENT_TYPE_HEADER => "application/vnd.api+json"}, [{data: o}.to_json]] end response = app.post "/", multipart: { file: image } assert_equal ["id", "storage", "metadata"], JSON.parse(response.body_binary)["data"].keys - assert_equal "application/vnd.api+json", response.headers["Content-Type"] + assert_equal "application/vnd.api+json", response.headers[CONTENT_TYPE_HEADER] end it "allows overriding options when instantiating the endpoint" do - app = Rack::TestApp.wrap(@shrine.upload_endpoint(:cache, max_size: 10)) + app = Rack::TestApp.wrap(@shrine.upload_endpoint(:cache, max_size: 10), { Rack::SERVER_PROTOCOL => "HTTP/1.1" }) response = app.post "/", multipart: { file: image } assert_equal 413, response.status end it "doesn't react to parseable Content-Type" do - response = app.post "/", headers: { "Content-Type" => "application/x-www-form-urlencoded" } + response = app.post "/", headers: { CONTENT_TYPE_HEADER => "application/x-www-form-urlencoded" } assert_equal 400, response.status assert_equal "Upload Not Found", response.body_binary end it "doesn't react to blank Content-Type" do - response = app.post "/", headers: { "Content-Type" => "" } + response = app.post "/", headers: { CONTENT_TYPE_HEADER => "" } assert_equal 400, response.status assert_equal "Upload Not Found", response.body_binary end @@ -172,7 +172,7 @@ def endpoint it "accepts only POST requests" do response = app.put "/", multipart: { file: image } assert_equal 405, response.status - assert_equal "text/plain", response.headers["Content-Type"] + assert_equal "text/plain", response.headers[CONTENT_TYPE_HEADER] assert_equal "Method Not Allowed", response.body_binary end @@ -199,7 +199,7 @@ def endpoint response = @shrine.upload_response(:cache, env) assert_equal 200, response[0] - assert_equal "application/json; charset=utf-8", response[1]["Content-Type"] + assert_equal "application/json; charset=utf-8", response[1][CONTENT_TYPE_HEADER] assert_equal "content", @shrine.uploaded_file(response[2].first).read end @@ -220,7 +220,7 @@ def endpoint response = @shrine.upload_response(:cache, env, max_size: 1) assert_equal 413, response[0] - assert_equal "text/plain", response[1]["Content-Type"] + assert_equal "text/plain", response[1][CONTENT_TYPE_HEADER] assert_equal "Upload Too Large", response[2].first end end diff --git a/test/support/ext.rb b/test/support/ext.rb index 307bba938..af1dbe286 100644 --- a/test/support/ext.rb +++ b/test/support/ext.rb @@ -9,3 +9,19 @@ class Rack::TestApp::Wrapper alias_method verb, verb.upcase end end + +module RackTestAppResultPatch + def body_binary + @body_binary ||= super + end + + def body_text + @body_text ||= super + end + + def body_json + @body_json ||= super + end +end + +Rack::TestApp::Result.send(:prepend, RackTestAppResultPatch) diff --git a/test/test_helper.rb b/test/test_helper.rb index 66426eef4..5d3789cf1 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -39,3 +39,12 @@ def self.load(data) eval(data) end end + +ACCEPT_RANGES_HEADER = Rack.release >= "3" ? "accept-ranges" : "Accept-Ranges" +CACHE_CONTROL_HEADER = Rack.release >= "3" ? "cache-control" : "Cache-Control" +CONTENT_DISPOSITION_HEADER = Rack.release >= "3" ? "content-disposition" : "Content-Disposition" +CONTENT_LENGTH_HEADER = Rack.release >= "3" ? "content-length" : "Content-Length" +CONTENT_RANGE_HEADER = Rack.release >= "3" ? "content-range" : "Content-Range" +CONTENT_TYPE_HEADER = Rack.release >= "3" ? "content-type" : "Content-Type" +ETAG_HEADER = Rack.release >= "3" ? "etag" : "ETag" +LOCATION_HEADER = Rack.release >= "3" ? "location" : "Location"