From 79563dce7fecb42c578f5149d8ae1b453593a36e Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 11 Sep 2026 15:39:20 +0200 Subject: [PATCH] test: cover MiddlewareCompressionTest and gunzip --- test/test_middleware_compression.rb | 164 ++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 test/test_middleware_compression.rb diff --git a/test/test_middleware_compression.rb b/test/test_middleware_compression.rb new file mode 100644 index 0000000..1f95c36 --- /dev/null +++ b/test/test_middleware_compression.rb @@ -0,0 +1,164 @@ +require 'minitest/autorun' +require 'prism' +require 'support' +require 'zlib' +require 'stringio' + +class MiddlewareCompressionTest < Minitest::Test + include TestSupport + + LARGE_BODY = 'x' * 1000 + + def gunzip(bytes) + Zlib::GzipReader.new(StringIO.new(bytes)).read + end + + def app_returning(status, headers, body) + Prism::Middleware::Compression.new(lambda { |_env| [status, headers, body] }) + end + + def test_compresses_a_large_buffered_body_when_client_accepts_gzip + app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY]) + env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'}) + status, headers, body = app.call(env) + + assert_equal 200, status + assert_equal 'gzip', headers['Content-Encoding'] + assert_equal LARGE_BODY, gunzip(body.join) + assert_equal body.join.bytesize.to_s, headers['Content-Length'] + end + + def test_leaves_the_body_alone_when_the_client_sends_no_accept_encoding + app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY]) + status, headers, body = app.call(env_for('GET', '/posts')) + + assert_equal 200, status + assert_nil headers['Content-Encoding'] + assert_equal LARGE_BODY, body.join + end + + def test_respects_an_explicit_gzip_q0_declining_the_encoding + app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY]) + env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip;q=0, *;q=1'}) + _, headers, body = app.call(env) + + assert_nil headers['Content-Encoding'] + assert_equal LARGE_BODY, body.join + end + + def test_a_wildcard_accept_encoding_is_honored + app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY]) + env = env_for('GET', '/posts', headers: {'Accept-Encoding' => '*'}) + _, headers, body = app.call(env) + + assert_equal 'gzip', headers['Content-Encoding'] + assert_equal LARGE_BODY, gunzip(body.join) + end + + def test_skips_bodies_smaller_than_the_threshold + app = app_returning(200, {'Content-Type' => 'text/html'}, ['tiny']) + env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'}) + _, headers, body = app.call(env) + + assert_nil headers['Content-Encoding'] + assert_equal 'tiny', body.join + end + + def test_skips_a_response_that_already_carries_a_content_encoding + headers = {'Content-Type' => 'text/html', 'Content-Encoding' => 'br'} + app = app_returning(200, headers, [LARGE_BODY]) + env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'}) + _, out_headers, body = app.call(env) + + assert_equal 'br', out_headers['Content-Encoding'] + assert_equal LARGE_BODY, body.join + end + + def test_skips_media_content_types + app = app_returning(200, {'Content-Type' => 'image/png'}, [LARGE_BODY]) + env = env_for('GET', '/logo.png', headers: {'Accept-Encoding' => 'gzip'}) + _, headers, body = app.call(env) + + assert_nil headers['Content-Encoding'] + assert_equal LARGE_BODY, body.join + end + + def test_skips_a_partial_content_range_response + headers = {'Content-Type' => 'text/plain', 'Content-Range' => 'bytes 0-99/1000'} + app = app_returning(206, headers, [LARGE_BODY[0, 100]]) + env = env_for('GET', '/file', headers: {'Accept-Encoding' => 'gzip'}) + status, out_headers, = app.call(env) + + assert_equal 206, status + assert_nil out_headers['Content-Encoding'] + end + + def test_skips_a_304_not_modified_response + app = app_returning(304, {}, []) + env = env_for('GET', '/file', headers: {'Accept-Encoding' => 'gzip'}) + status, headers, = app.call(env) + + assert_equal 304, status + assert_nil headers['Content-Encoding'] + end + + def test_skips_head_requests + app = app_returning(200, {'Content-Type' => 'text/html'}, [LARGE_BODY]) + env = env_for('HEAD', '/posts', headers: {'Accept-Encoding' => 'gzip'}) + _, headers, body = app.call(env) + + assert_nil headers['Content-Encoding'] + assert_equal LARGE_BODY, body.join + end + + def test_appends_to_an_existing_vary_header_instead_of_replacing_it + app = app_returning(200, {'Content-Type' => 'text/html', 'Vary' => 'Accept'}, [LARGE_BODY]) + env = env_for('GET', '/posts', headers: {'Accept-Encoding' => 'gzip'}) + _, headers, = app.call(env) + + assert_equal 'Accept, Accept-Encoding', headers['Vary'] + end + + def test_streams_a_streaming_body_as_a_single_gzip_stream_chunk_by_chunk + written_before_producer_returns = nil + downstream = lambda do |_env| + body = Prism::StreamingBody.new do |writer| + writer.write("id,title\n") + writer.write("1,one\n") + writer.write("2,two\n") + end + [200, {'Content-Type' => 'text/csv'}, body] + end + app = Prism::Middleware::Compression.new(downstream) + env = env_for('GET', '/posts/export', headers: {'Accept-Encoding' => 'gzip'}) + status, headers, body = app.call(env) + + assert_equal 200, status + assert_equal 'gzip', headers['Content-Encoding'] + assert_nil headers['Content-Length'] + assert_kind_of Prism::StreamingBody, body + + chunks = [] + body.each { |chunk| chunks << chunk } + assert chunks.size > 1, 'expected more than one gzip chunk (one per producer write)' + assert_equal "id,title\n1,one\n2,two\n", gunzip(chunks.join) + end + + def test_a_second_each_on_the_wrapped_streaming_body_reruns_the_producer_with_a_fresh_gzip_stream + downstream = lambda do |_env| + body = Prism::StreamingBody.new { |writer| writer.write('hello world') } + [200, {'Content-Type' => 'text/plain'}, body] + end + app = Prism::Middleware::Compression.new(downstream) + env = env_for('GET', '/x', headers: {'Accept-Encoding' => 'gzip'}) + _, _, body = app.call(env) + + first = +'' + body.each { |chunk| first << chunk } + second = +'' + body.each { |chunk| second << chunk } + + assert_equal 'hello world', gunzip(first) + assert_equal 'hello world', gunzip(second) + end +end