sinatra/sinatra-contrib/spec/streaming_spec.rb

415 lines
9.2 KiB
Ruby

require 'spec_helper'
describe Sinatra::Streaming do
def stream(&block)
rack_middleware = @use
out = nil
mock_app do
rack_middleware.each { |args| use(*args) }
helpers Sinatra::Streaming
get('/') { out = stream(&block) }
end
get('/')
out
end
def use(*args)
@use << args
end
before do
@use = []
end
context 'stream test helper' do
it 'runs the given block' do
ran = false
stream { ran = true }
expect(ran).to be true
end
it 'returns the stream object' do
out = stream { }
expect(out).to be_a(Sinatra::Helpers::Stream)
end
it 'fires a request against that stream' do
stream { |out| out << "Hello World!" }
expect(last_response).to be_ok
expect(body).to eq("Hello World!")
end
it 'passes the stream object to the block' do
passed = nil
returned = stream { |out| passed = out }
expect(passed).to eq(returned)
end
end
context Sinatra::Streaming::Stream do
it 'should extend the stream object' do
out = stream { }
expect(out).to be_a(Sinatra::Streaming::Stream)
end
it 'should not extend stream objects of other apps' do
out = nil
mock_app { get('/') { out = stream { }}}
get('/')
expect(out).to be_a(Sinatra::Helpers::Stream)
expect(out).not_to be_a(Sinatra::Streaming::Stream)
end
end
context 'app' do
it 'is the app instance the stream was created from' do
out = stream { }
expect(out.app).to be_a(Sinatra::Base)
end
end
context 'lineno' do
it 'defaults to 0' do
expect(stream { }.lineno).to eq(0)
end
it 'does not increase on write' do
stream do |out|
out << "many\nlines\n"
expect(out.lineno).to eq(0)
end
end
it 'is writable' do
out = stream { }
out.lineno = 10
expect(out.lineno).to eq(10)
end
end
context 'pos' do
it 'defaults to 0' do
expect(stream { }.pos).to eq(0)
end
it 'increases when writing data' do
stream do |out|
expect(out.pos).to eq(0)
out << 'hi'
expect(out.pos).to eq(2)
end
end
it 'is writable' do
out = stream { }
out.pos = 10
expect(out.pos).to eq(10)
end
it 'aliased to #tell' do
out = stream { }
expect(out.tell).to eq(0)
out.pos = 10
expect(out.tell).to eq(10)
end
end
context 'closed' do
it 'returns false while streaming' do
stream { |out| expect(out).not_to be_closed }
end
it 'returns true after streaming' do
expect(stream {}).to be_closed
end
end
context 'map!' do
it 'applies transformations later' do
stream do |out|
out.map! { |s| s.upcase }
out << 'ok'
end
expect(body).to eq("OK")
end
it 'is chainable' do
stream do |out|
out.map! { |s| s.upcase }
out.map! { |s| s.reverse }
out << 'ok'
end
expect(body).to eq("KO")
end
it 'works with middleware' do
middleware = Class.new do
def initialize(app) @app = app end
def call(env)
status, headers, body = @app.call(env)
body.map! { |s| s.upcase }
[status, headers, body]
end
end
use middleware
stream { |out| out << "ok" }
expect(body).to eq("OK")
end
it 'modifies each value separately' do
stream do |out|
out.map! { |s| s.reverse }
out << "ab" << "cd"
end
expect(body).to eq("badc")
end
end
context 'map' do
it 'works with middleware' do
middleware = Class.new do
def initialize(app) @app = app end
def call(env)
status, headers, body = @app.call(env)
[status, headers, body.map(&:upcase)]
end
end
use middleware
stream { |out| out << "ok" }
expect(body).to eq("OK")
end
it 'is chainable' do
middleware = Class.new do
def initialize(app) @app = app end
def call(env)
status, headers, body = @app.call(env)
[status, headers, body.map(&:upcase).map(&:reverse)]
end
end
use middleware
stream { |out| out << "ok" }
expect(body).to eq("KO")
end
it 'can be written as each.map' do
middleware = Class.new do
def initialize(app) @app = app end
def call(env)
status, headers, body = @app.call(env)
[status, headers, body.each.map(&:upcase)]
end
end
use middleware
stream { |out| out << "ok" }
expect(body).to eq("OK")
end
it 'does not modify the original body' do
stream do |out|
out.map { |s| s.reverse }
out << 'ok'
end
expect(body).to eq('ok')
end
end
context 'write' do
it 'writes to the stream' do
stream { |out| out.write 'hi' }
expect(body).to eq('hi')
end
it 'returns the number of bytes' do
stream do |out|
expect(out.write('hi')).to eq(2)
expect(out.write('hello')).to eq(5)
end
end
it 'accepts non-string objects' do
stream do |out|
expect(out.write(12)).to eq(2)
end
end
it 'should be aliased to syswrite' do
stream { |out| expect(out.syswrite('hi')).to eq(2) }
expect(body).to eq('hi')
end
it 'should be aliased to write_nonblock' do
stream { |out| expect(out.write_nonblock('hi')).to eq(2) }
expect(body).to eq('hi')
end
end
context 'print' do
it 'writes to the stream' do
stream { |out| out.print('hi') }
expect(body).to eq('hi')
end
it 'accepts multiple arguments' do
stream { |out| out.print(1, 2, 3, 4) }
expect(body).to eq('1234')
end
it 'returns nil' do
stream { |out| expect(out.print('hi')).to be_nil }
end
end
context 'printf' do
it 'writes to the stream' do
stream { |out| out.printf('hi') }
expect(body).to eq('hi')
end
it 'interpolates the format string' do
stream { |out| out.printf("%s: %d", "answer", 42) }
expect(body).to eq('answer: 42')
end
it 'returns nil' do
stream { |out| expect(out.printf('hi')).to be_nil }
end
end
context 'putc' do
it 'writes the first character of a string' do
stream { |out| out.putc('hi') }
expect(body).to eq('h')
end
it 'writes the character corresponding to an integer' do
stream { |out| out.putc(42) }
expect(body).to eq('*')
end
it 'returns nil' do
stream { |out| expect(out.putc('hi')).to be_nil }
end
end
context 'puts' do
it 'writes to the stream' do
stream { |out| out.puts('hi') }
expect(body).to eq("hi\n")
end
it 'accepts multiple arguments' do
stream { |out| out.puts(1, 2, 3, 4) }
expect(body).to eq("1\n2\n3\n4\n")
end
it 'returns nil' do
stream { |out| expect(out.puts('hi')).to be_nil }
end
end
context 'close' do
it 'sets #closed? to true' do
stream do |out|
out.close
expect(out).to be_closed
end
end
it 'sets #closed_write? to true' do
stream do |out|
expect(out).not_to be_closed_write
out.close
expect(out).to be_closed_write
end
end
it 'fires callbacks' do
stream do |out|
fired = false
out.callback { fired = true }
out.close
expect(fired).to be true
end
end
it 'prevents from further writing' do
stream do |out|
out.close
expect { out << 'hi' }.to raise_error(IOError, 'not opened for writing')
end
end
end
context 'close_read' do
it 'raises the appropriate exception' do
expect { stream { |out| out.close_read }}.
to raise_error(IOError, "closing non-duplex IO for reading")
end
end
context 'closed_read?' do
it('returns true') { stream { |out| expect(out).to be_closed_read }}
end
context 'rewind' do
it 'resets pos' do
stream do |out|
out << 'hi'
out.rewind
expect(out.pos).to eq(0)
end
end
it 'resets lineno' do
stream do |out|
out.lineno = 10
out.rewind
expect(out.lineno).to eq(0)
end
end
end
raises = %w[
bytes eof? eof getbyte getc gets read read_nonblock readbyte readchar
readline readlines readpartial sysread ungetbyte ungetc
]
enum = %w[chars each_line each_byte each_char lines]
dummies = %w[flush fsync internal_encoding pid]
raises.each do |method|
context method do
it 'raises the appropriate exception' do
expect { stream { |out| out.public_send(method) }}.
to raise_error(IOError, "not opened for reading")
end
end
end
enum.each do |method|
context method do
it 'creates an Enumerator' do
stream { |out| expect(out.public_send(method)).to be_a(Enumerator) }
end
it 'calling each raises the appropriate exception' do
expect { stream { |out| out.public_send(method).each { }}}.
to raise_error(IOError, "not opened for reading")
end
end
end
dummies.each do |method|
context method do
it 'returns nil' do
stream { |out| expect(out.public_send(method)).to be_nil }
end
end
end
end