84 lines
2.2 KiB
Ruby
84 lines
2.2 KiB
Ruby
require 'minitest/autorun'
|
|
require 'prism'
|
|
require 'support'
|
|
|
|
class StreamingExportController < Prism::Controller
|
|
after_action :log_after
|
|
|
|
def self.after_log
|
|
@after_log ||= []
|
|
end
|
|
|
|
def export
|
|
headers('Content-Type' => 'text/csv')
|
|
stream do |out|
|
|
out.write("id,title\n")
|
|
%w[one two three].each_with_index do |title, index|
|
|
out.write("#{index + 1},#{title}\n")
|
|
end
|
|
end
|
|
end
|
|
|
|
def plain
|
|
json(ok: true)
|
|
end
|
|
|
|
private
|
|
|
|
def log_after
|
|
self.class.after_log << action_name
|
|
end
|
|
end
|
|
|
|
class StreamingApp < Prism::Application
|
|
get '/export', to: 'streaming_export#export'
|
|
get '/plain', to: 'streaming_export#plain'
|
|
end
|
|
|
|
class ControllerStreamingTest < Minitest::Test
|
|
include TestSupport
|
|
|
|
def setup
|
|
StreamingExportController.after_log.clear
|
|
end
|
|
|
|
def call(env)
|
|
StreamingApp.call(env)
|
|
end
|
|
|
|
def test_streaming_action_returns_a_streaming_body
|
|
_status, _headers, body = call(env_for(:get, '/export'))
|
|
assert_instance_of Prism::StreamingBody, body
|
|
end
|
|
|
|
def test_streaming_action_produces_chunks_in_order_when_drained
|
|
_status, _headers, body = call(env_for(:get, '/export'))
|
|
collected = []
|
|
body.each { |chunk| collected << chunk }
|
|
assert_equal ["id,title\n", "1,one\n", "2,two\n", "3,three\n"], collected
|
|
end
|
|
|
|
def test_streaming_action_keeps_status_and_explicit_headers
|
|
status, headers, _body = call(env_for(:get, '/export'))
|
|
assert_equal 200, status
|
|
assert_equal 'text/csv', headers['Content-Type']
|
|
end
|
|
|
|
# Regression guard: finalize_body used to call `@response.body.empty?`
|
|
# unconditionally after every action, which raises NoMethodError against
|
|
# a StreamingBody (it deliberately has no #empty?, since a producer
|
|
# block's total chunk count is not known up front).
|
|
def test_after_actions_still_run_for_a_streaming_action
|
|
call(env_for(:get, '/export'))
|
|
assert_equal ['export'], StreamingExportController.after_log
|
|
end
|
|
|
|
# Non-streaming actions on the same controller are unaffected by the
|
|
# streaming? guard added to finalize_body.
|
|
def test_ordinary_action_on_the_same_controller_is_unaffected
|
|
status, _headers, body = call(env_for(:get, '/plain'))
|
|
assert_equal 200, status
|
|
assert_equal ['{"ok":true}'], body
|
|
end
|
|
end
|