Files

339 lines
13 KiB
Ruby

require "spec_helper"
describe WebSocket::Extensions do
ExtensionError = WebSocket::Extensions::ExtensionError
Message = Struct.new(:frames)
before do
@extensions = WebSocket::Extensions.new
@ext = double(:extension, :name => "deflate", :type => "permessage", :rsv1 => true, :rsv2 => false, :rsv3 => false)
@session = double(:session)
end
describe :add do
it "does not raise on valid extensions" do
expect { @extensions.add(@ext) }.not_to raise_error
end
it "raises if ext.name is not a string" do
allow(@ext).to receive(:name).and_return(42)
expect { @extensions.add(@ext) }.to raise_error(TypeError)
end
it "raises if ext.rsv1 is not a boolean" do
allow(@ext).to receive(:rsv1).and_return(42)
expect { @extensions.add(@ext) }.to raise_error(TypeError)
end
it "raises if ext.rsv2 is not a boolean" do
allow(@ext).to receive(:rsv2).and_return(42)
expect { @extensions.add(@ext) }.to raise_error(TypeError)
end
it "raises if ext.rsv3 is not a boolean" do
allow(@ext).to receive(:rsv3).and_return(42)
expect { @extensions.add(@ext) }.to raise_error(TypeError)
end
end
describe "client sessions" do
before do
@offer = { "mode" => "compress" }
allow(@ext).to receive(:create_client_session).and_return(@session)
allow(@session).to receive(:generate_offer).and_return(@offer)
@extensions.add(@ext)
@conflict = double(:extension, :name => "tar", :type => "permessage", :rsv1 => true, :rsv2 => false, :rsv3 => false)
@conflict_session = double(:session)
allow(@conflict).to receive(:create_client_session).and_return(@conflict_session)
allow(@conflict_session).to receive(:generate_offer).and_return("gzip" => true)
@nonconflict = double(:extension, :name => "reverse", :type => "permessage", :rsv1 => false, :rsv2 => true, :rsv3 => false)
@nonconflict_session = double(:session)
allow(@nonconflict).to receive(:create_client_session).and_return(@nonconflict_session)
allow(@nonconflict_session).to receive(:generate_offer).and_return("utf8" => true)
allow(@session).to receive(:activate).and_return(true)
allow(@conflict_session).to receive(:activate).and_return(true)
allow(@nonconflict_session).to receive(:activate).and_return(true)
end
describe :generate_offer do
it "asks the extension to create a client session" do
expect(@ext).to receive(:create_client_session).exactly(1).and_return(@session)
@extensions.generate_offer
end
it "asks the session to generate an offer" do
expect(@session).to receive(:generate_offer).exactly(1).and_return(@offer)
@extensions.generate_offer
end
it "does not ask the session to generate an offer if the extension doesn't build a session" do
allow(@ext).to receive(:create_client_session).and_return(nil)
expect(@session).not_to receive(:generate_offer)
@extensions.generate_offer
end
it "returns the serialized offer from the session" do
expect(@extensions.generate_offer).to eq "deflate; mode=compress"
end
it "returns a null offer from the session" do
allow(@session).to receive(:generate_offer).and_return(nil)
expect(@extensions.generate_offer).to be_nil
end
it "returns multiple serialized offers from the session" do
allow(@session).to receive(:generate_offer).and_return([@offer, {}])
expect(@extensions.generate_offer).to eq "deflate; mode=compress, deflate"
end
it "returns serialized offers from multiple sessions" do
@extensions.add(@nonconflict)
expect(@extensions.generate_offer).to eq "deflate; mode=compress, reverse; utf8"
end
it "generates offers for potentially conflicting extensions" do
@extensions.add(@conflict)
expect(@extensions.generate_offer).to eq "deflate; mode=compress, tar; gzip"
end
end
describe :activate do
before do
@extensions.add(@conflict)
@extensions.add(@nonconflict)
@extensions.generate_offer
end
it "raises if given unregistered extensions" do
expect { @extensions.activate("xml") }.to raise_error(ExtensionError)
end
it "does not raise if given registered extensions" do
expect { @extensions.activate("deflate") }.not_to raise_error
end
it "does not raise if given only one potentially conflicting extension" do
expect { @extensions.activate("tar") }.not_to raise_error
end
it "raises if two extensions conflict on RSV bits" do
expect { @extensions.activate("deflate, tar") }.to raise_error(ExtensionError)
end
it "does not raise if given two non-conflicting extensions" do
expect { @extensions.activate("deflate, reverse") }.not_to raise_error
end
it "activates one session with no params" do
expect(@session).to receive(:activate).with({}).exactly(1).and_return(true)
@extensions.activate("deflate")
end
it "activates one session with a boolean param" do
expect(@session).to receive(:activate).with({ "gzip" => true }).exactly(1).and_return(true)
@extensions.activate("deflate; gzip")
end
it "activates one session with a string param" do
expect(@session).to receive(:activate).with({ "mode" => "compress" }).exactly(1).and_return(true)
@extensions.activate("deflate; mode=compress")
end
it "activate multiple sessions" do
expect(@session).to receive(:activate).with({ "a" => true }).exactly(1).and_return(true)
expect(@nonconflict_session).to receive(:activate).with({ "b" => true }).exactly(1).and_return(true)
@extensions.activate("deflate; a, reverse; b")
end
it "does not activate extensions not named in the header" do
expect(@session).not_to receive(:activate)
expect(@nonconflict_session).to receive(:activate).exactly(1).and_return(true)
@extensions.activate("reverse")
end
it "raises if session.activate does not return true" do
allow(@session).to receive(:activate).and_return("yes")
expect { @extensions.activate("deflate") }.to raise_error(ExtensionError)
end
end
describe :process_incoming_message do
before do
@extensions.add(@conflict)
@extensions.add(@nonconflict)
@extensions.generate_offer
allow(@session).to receive(:process_incoming_message) do |message|
message.frames << "deflate"
message
end
allow(@nonconflict_session).to receive(:process_incoming_message) do |message|
message.frames << "reverse"
message
end
end
it "processes messages in the reverse order given in the server's response" do
@extensions.activate("deflate, reverse")
message = @extensions.process_incoming_message(Message.new [])
expect(message.frames).to eq ["reverse", "deflate"]
end
it "raises if a session yields an error" do
@extensions.activate("deflate")
allow(@session).to receive(:process_incoming_message).and_raise(TypeError)
expect { @extensions.process_incoming_message(Message.new []) }.to raise_error(ExtensionError)
end
it "does not call sessions after one has yield an error" do
@extensions.activate("deflate, reverse")
allow(@nonconflict_session).to receive(:process_incoming_message).and_raise(TypeError)
expect(@session).not_to receive(:process_incoming_message)
@extensions.process_incoming_message(Message.new []) rescue nil
end
end
describe :process_outgoing_message do
before do
@extensions.add(@conflict)
@extensions.add(@nonconflict)
@extensions.generate_offer
allow(@session).to receive(:process_outgoing_message) do |message|
message.frames << "deflate"
message
end
allow(@nonconflict_session).to receive(:process_outgoing_message) do |message|
message.frames << "reverse"
message
end
end
it "processes messages in the order given in the server's response" do
@extensions.activate("deflate, reverse")
message = @extensions.process_outgoing_message(Message.new [])
expect(message.frames).to eq ["deflate", "reverse"]
end
it "processes messages in the server's order, not the client's order" do
@extensions.activate("reverse, deflate")
message = @extensions.process_outgoing_message(Message.new [])
expect(message.frames).to eq ["reverse" ,"deflate"]
end
it "raises if a session yields an error" do
@extensions.activate("deflate")
allow(@session).to receive(:process_outgoing_message).and_raise(TypeError)
expect { @extensions.process_outgoing_message(Message.new []) }.to raise_error(ExtensionError)
end
it "does not call sessions after one has yield an error" do
@extensions.activate("deflate, reverse")
allow(@session).to receive(:process_outgoing_message).and_raise(TypeError)
expect(@nonconflict_session).not_to receive(:process_outgoing_message)
@extensions.process_outgoing_message(Message.new []) rescue nil
end
end
end
describe "server sessions" do
before do
@response = { "mode" => "compress" }
allow(@ext).to receive(:create_server_session).and_return(@session)
allow(@session).to receive(:generate_response).and_return(@response)
@conflict = double(:extension, :name => "tar", :type => "permessage", :rsv1 => true, :rsv2 => false, :rsv3 => false)
@conflict_session = double(:session)
allow(@conflict).to receive(:create_server_session).and_return(@conflict_session)
allow(@conflict_session).to receive(:generate_response).and_return("gzip" => true)
@nonconflict = double(:extension, :name => "reverse", :type => "permessage", :rsv1 => false, :rsv2 => true, :rsv3 => false)
@nonconflict_session = double(:session)
allow(@nonconflict).to receive(:create_server_session).and_return(@nonconflict_session)
allow(@nonconflict_session).to receive(:generate_response).and_return("utf8" => true)
@extensions.add(@ext)
@extensions.add(@conflict)
@extensions.add(@nonconflict)
end
describe :generate_response do
it "asks the extension for a server session with the offer" do
expect(@ext).to receive(:create_server_session).with([{ "flag" => true }]).exactly(1).and_return(@session)
@extensions.generate_response("deflate; flag")
end
it "asks the extension for a server session with multiple offers" do
expect(@ext).to receive(:create_server_session).with([{ "a" => true }, { "b" => true }]).exactly(1).and_return(@session)
@extensions.generate_response("deflate; a, deflate; b")
end
it "asks the session to generate a response" do
expect(@session).to receive(:generate_response).exactly(1).and_return(@response)
@extensions.generate_response("deflate")
end
it "asks multiple sessions to generate a response" do
expect(@session).to receive(:generate_response).exactly(1).and_return(@response)
expect(@nonconflict_session).to receive(:generate_response).exactly(1).and_return(@response)
@extensions.generate_response("deflate, reverse")
end
it "does not ask the session to generate a response if the extension doesn't build a session" do
allow(@ext).to receive(:create_server_session).and_return(nil)
expect(@session).not_to receive(:generate_response)
@extensions.generate_response("deflate")
end
it "does not ask the extension to build a session for unoffered extensions" do
expect(@nonconflict).not_to receive(:create_server_session)
@extensions.generate_response("deflate")
end
it "does not ask the extension to build a session for conflicting extensions" do
expect(@conflict).not_to receive(:create_server_session)
@extensions.generate_response("deflate, tar")
end
it "returns the serialized response from the session" do
expect(@extensions.generate_response("deflate")).to eq "deflate; mode=compress"
end
it "returns serialized responses from multiple sessions" do
expect(@extensions.generate_response("deflate, reverse")).to eq "deflate; mode=compress, reverse; utf8"
end
it "returns responses in registration orde" do
expect(@extensions.generate_response("reverse, deflate")).to eq "deflate; mode=compress, reverse; utf8"
end
it "does not return responses for unoffered extensions" do
expect(@extensions.generate_response("reverse")).to eq "reverse; utf8"
end
it "does not return responses for conflicting extensions" do
expect(@extensions.generate_response("deflate, tar")).to eq "deflate; mode=compress"
end
it "raises an error if the header is invalid" do
expect { @extensions.generate_response("x-webkit- -frame") }.to raise_error(WebSocket::Extensions::Parser::ParseError)
end
it "returns a response for potentially conflicting extensions if their preceding extensions don't build a session" do
allow(@ext).to receive(:create_server_session).and_return(nil)
expect(@extensions.generate_response("deflate, tar")).to eq "tar; gzip"
end
end
end
end