diff options
Diffstat (limited to 'actioncable/test/connection')
-rw-r--r-- | actioncable/test/connection/authorization_test.rb | 31 | ||||
-rw-r--r-- | actioncable/test/connection/base_test.rb | 138 | ||||
-rw-r--r-- | actioncable/test/connection/cross_site_forgery_test.rb | 81 | ||||
-rw-r--r-- | actioncable/test/connection/identifier_test.rb | 77 | ||||
-rw-r--r-- | actioncable/test/connection/multiple_identifiers_test.rb | 41 | ||||
-rw-r--r-- | actioncable/test/connection/string_identifier_test.rb | 43 | ||||
-rw-r--r-- | actioncable/test/connection/subscriptions_test.rb | 115 |
7 files changed, 526 insertions, 0 deletions
diff --git a/actioncable/test/connection/authorization_test.rb b/actioncable/test/connection/authorization_test.rb new file mode 100644 index 0000000000..87d0e79ef3 --- /dev/null +++ b/actioncable/test/connection/authorization_test.rb @@ -0,0 +1,31 @@ +require 'test_helper' +require 'stubs/test_server' + +class ActionCable::Connection::AuthorizationTest < ActionCable::TestCase + class Connection < ActionCable::Connection::Base + attr_reader :websocket + + def connect + reject_unauthorized_connection + end + + def send_async(method, *args) + send method, *args + end + end + + test "unauthorized connection" do + run_in_eventmachine do + server = TestServer.new + server.config.allowed_request_origins = %w( http://rubyonrails.com ) + + env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket', + 'HTTP_ORIGIN' => 'http://rubyonrails.com' + + connection = Connection.new(server, env) + connection.websocket.expects(:close) + + connection.process + end + end +end diff --git a/actioncable/test/connection/base_test.rb b/actioncable/test/connection/base_test.rb new file mode 100644 index 0000000000..fb11f9be64 --- /dev/null +++ b/actioncable/test/connection/base_test.rb @@ -0,0 +1,138 @@ +require 'test_helper' +require 'stubs/test_server' + +class ActionCable::Connection::BaseTest < ActionCable::TestCase + class Connection < ActionCable::Connection::Base + attr_reader :websocket, :subscriptions, :message_buffer, :connected + + def connect + @connected = true + end + + def disconnect + @connected = false + end + + def send_async(method, *args) + send method, *args + end + end + + setup do + @server = TestServer.new + @server.config.allowed_request_origins = %w( http://rubyonrails.com ) + end + + test "making a connection with invalid headers" do + run_in_eventmachine do + connection = ActionCable::Connection::Base.new(@server, Rack::MockRequest.env_for("/test")) + response = connection.process + assert_equal 404, response[0] + end + end + + test "websocket connection" do + run_in_eventmachine do + connection = open_connection + connection.process + + assert connection.websocket.possible? + + wait_for_async + assert connection.websocket.alive? + end + end + + test "rack response" do + run_in_eventmachine do + connection = open_connection + response = connection.process + + assert_equal [ -1, {}, [] ], response + end + end + + test "on connection open" do + run_in_eventmachine do + connection = open_connection + + connection.websocket.expects(:transmit).with({ identifier: "_ping", type: "confirm_subscription" }.to_json) + connection.message_buffer.expects(:process!) + + connection.process + wait_for_async + + assert_equal [ connection ], @server.connections + assert connection.connected + end + end + + test "on connection close" do + run_in_eventmachine do + connection = open_connection + connection.process + + # Setup the connection + Concurrent::TimerTask.stubs(:new).returns(true) + connection.send :handle_open + assert connection.connected + + connection.subscriptions.expects(:unsubscribe_from_all) + connection.send :handle_close + + assert ! connection.connected + assert_equal [], @server.connections + end + end + + test "connection statistics" do + run_in_eventmachine do + connection = open_connection + connection.process + + statistics = connection.statistics + + assert statistics[:identifier].blank? + assert_kind_of Time, statistics[:started_at] + assert_equal [], statistics[:subscriptions] + end + end + + test "explicitly closing a connection" do + run_in_eventmachine do + connection = open_connection + connection.process + + connection.websocket.expects(:close) + connection.close + end + end + + test "rejecting a connection causes a 404" do + run_in_eventmachine do + class CallMeMaybe + def call(*) + raise 'Do not call me!' + end + end + + env = Rack::MockRequest.env_for( + "/test", + { 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket', + 'HTTP_ORIGIN' => 'http://rubyonrails.org', 'rack.hijack' => CallMeMaybe.new } + ) + + connection = ActionCable::Connection::Base.new(@server, env) + response = connection.process + assert_equal 404, response[0] + end + end + + private + def open_connection + env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket', + 'HTTP_ORIGIN' => 'http://rubyonrails.com' + + Connection.new(@server, env) + end +end diff --git a/actioncable/test/connection/cross_site_forgery_test.rb b/actioncable/test/connection/cross_site_forgery_test.rb new file mode 100644 index 0000000000..a29f65fb97 --- /dev/null +++ b/actioncable/test/connection/cross_site_forgery_test.rb @@ -0,0 +1,81 @@ +require 'test_helper' +require 'stubs/test_server' + +class ActionCable::Connection::CrossSiteForgeryTest < ActionCable::TestCase + HOST = 'rubyonrails.com' + + class Connection < ActionCable::Connection::Base + def send_async(method, *args) + send method, *args + end + end + + setup do + @server = TestServer.new + @server.config.allowed_request_origins = %w( http://rubyonrails.com ) + end + + teardown do + @server.config.disable_request_forgery_protection = false + @server.config.allowed_request_origins = [] + end + + test "disable forgery protection" do + @server.config.disable_request_forgery_protection = true + assert_origin_allowed 'http://rubyonrails.com' + assert_origin_allowed 'http://hax.com' + end + + test "explicitly specified a single allowed origin" do + @server.config.allowed_request_origins = 'http://hax.com' + assert_origin_not_allowed 'http://rubyonrails.com' + assert_origin_allowed 'http://hax.com' + end + + test "explicitly specified multiple allowed origins" do + @server.config.allowed_request_origins = %w( http://rubyonrails.com http://www.rubyonrails.com ) + assert_origin_allowed 'http://rubyonrails.com' + assert_origin_allowed 'http://www.rubyonrails.com' + assert_origin_not_allowed 'http://hax.com' + end + + test "explicitly specified a single regexp allowed origin" do + @server.config.allowed_request_origins = /.*ha.*/ + assert_origin_not_allowed 'http://rubyonrails.com' + assert_origin_allowed 'http://hax.com' + end + + test "explicitly specified multiple regexp allowed origins" do + @server.config.allowed_request_origins = [/http:\/\/ruby.*/, /.*rai.s.*com/, 'string' ] + assert_origin_allowed 'http://rubyonrails.com' + assert_origin_allowed 'http://www.rubyonrails.com' + assert_origin_not_allowed 'http://hax.com' + assert_origin_not_allowed 'http://rails.co.uk' + end + + private + def assert_origin_allowed(origin) + response = connect_with_origin origin + assert_equal(-1, response[0]) + end + + def assert_origin_not_allowed(origin) + response = connect_with_origin origin + assert_equal 404, response[0] + end + + def connect_with_origin(origin) + response = nil + + run_in_eventmachine do + response = Connection.new(@server, env_for_origin(origin)).process + end + + response + end + + def env_for_origin(origin) + Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket', 'SERVER_NAME' => HOST, + 'HTTP_ORIGIN' => origin + end +end diff --git a/actioncable/test/connection/identifier_test.rb b/actioncable/test/connection/identifier_test.rb new file mode 100644 index 0000000000..1019ad541e --- /dev/null +++ b/actioncable/test/connection/identifier_test.rb @@ -0,0 +1,77 @@ +require 'test_helper' +require 'stubs/test_server' +require 'stubs/user' + +class ActionCable::Connection::IdentifierTest < ActionCable::TestCase + class Connection < ActionCable::Connection::Base + identified_by :current_user + attr_reader :websocket + + public :process_internal_message + + def connect + self.current_user = User.new "lifo" + end + end + + test "connection identifier" do + run_in_eventmachine do + open_connection_with_stubbed_pubsub + assert_equal "User#lifo", @connection.connection_identifier + end + end + + test "should subscribe to internal channel on open and unsubscribe on close" do + run_in_eventmachine do + pubsub = mock('pubsub_adapter') + pubsub.expects(:subscribe).with('action_cable/User#lifo', kind_of(Proc)) + pubsub.expects(:unsubscribe).with('action_cable/User#lifo', kind_of(Proc)) + + server = TestServer.new + server.stubs(:pubsub).returns(pubsub) + + open_connection server: server + close_connection + end + end + + test "processing disconnect message" do + run_in_eventmachine do + open_connection_with_stubbed_pubsub + + @connection.websocket.expects(:close) + message = ActiveSupport::JSON.encode('type' => 'disconnect') + @connection.process_internal_message message + end + end + + test "processing invalid message" do + run_in_eventmachine do + open_connection_with_stubbed_pubsub + + @connection.websocket.expects(:close).never + message = ActiveSupport::JSON.encode('type' => 'unknown') + @connection.process_internal_message message + end + end + + protected + def open_connection_with_stubbed_pubsub + server = TestServer.new + server.stubs(:adapter).returns(stub_everything('adapter')) + + open_connection server: server + end + + def open_connection(server:) + env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket' + @connection = Connection.new(server, env) + + @connection.process + @connection.send :handle_open + end + + def close_connection + @connection.send :handle_close + end +end diff --git a/actioncable/test/connection/multiple_identifiers_test.rb b/actioncable/test/connection/multiple_identifiers_test.rb new file mode 100644 index 0000000000..e9bb4e6d7f --- /dev/null +++ b/actioncable/test/connection/multiple_identifiers_test.rb @@ -0,0 +1,41 @@ +require 'test_helper' +require 'stubs/test_server' +require 'stubs/user' + +class ActionCable::Connection::MultipleIdentifiersTest < ActionCable::TestCase + class Connection < ActionCable::Connection::Base + identified_by :current_user, :current_room + + def connect + self.current_user = User.new "lifo" + self.current_room = Room.new "my", "room" + end + end + + test "multiple connection identifiers" do + run_in_eventmachine do + open_connection_with_stubbed_pubsub + assert_equal "Room#my-room:User#lifo", @connection.connection_identifier + end + end + + protected + def open_connection_with_stubbed_pubsub + server = TestServer.new + server.stubs(:pubsub).returns(stub_everything('pubsub')) + + open_connection server: server + end + + def open_connection(server:) + env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket' + @connection = Connection.new(server, env) + + @connection.process + @connection.send :handle_open + end + + def close_connection + @connection.send :handle_close + end +end diff --git a/actioncable/test/connection/string_identifier_test.rb b/actioncable/test/connection/string_identifier_test.rb new file mode 100644 index 0000000000..9d0bda83ef --- /dev/null +++ b/actioncable/test/connection/string_identifier_test.rb @@ -0,0 +1,43 @@ +require 'test_helper' +require 'stubs/test_server' + +class ActionCable::Connection::StringIdentifierTest < ActionCable::TestCase + class Connection < ActionCable::Connection::Base + identified_by :current_token + + def connect + self.current_token = "random-string" + end + + def send_async(method, *args) + send method, *args + end + end + + test "connection identifier" do + run_in_eventmachine do + open_connection_with_stubbed_pubsub + assert_equal "random-string", @connection.connection_identifier + end + end + + protected + def open_connection_with_stubbed_pubsub + @server = TestServer.new + @server.stubs(:pubsub).returns(stub_everything('pubsub')) + + open_connection + end + + def open_connection + env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket' + @connection = Connection.new(@server, env) + + @connection.process + @connection.send :on_open + end + + def close_connection + @connection.send :on_close + end +end diff --git a/actioncable/test/connection/subscriptions_test.rb b/actioncable/test/connection/subscriptions_test.rb new file mode 100644 index 0000000000..62e41484fe --- /dev/null +++ b/actioncable/test/connection/subscriptions_test.rb @@ -0,0 +1,115 @@ +require 'test_helper' + +class ActionCable::Connection::SubscriptionsTest < ActionCable::TestCase + class Connection < ActionCable::Connection::Base + attr_reader :websocket + + def send_async(method, *args) + send method, *args + end + end + + class ChatChannel < ActionCable::Channel::Base + attr_reader :room, :lines + + def subscribed + @room = Room.new params[:id] + @lines = [] + end + + def speak(data) + @lines << data + end + end + + setup do + @server = TestServer.new + @server.stubs(:channel_classes).returns(ChatChannel.name => ChatChannel) + + @chat_identifier = ActiveSupport::JSON.encode(id: 1, channel: 'ActionCable::Connection::SubscriptionsTest::ChatChannel') + end + + test "subscribe command" do + run_in_eventmachine do + setup_connection + channel = subscribe_to_chat_channel + + assert_kind_of ChatChannel, channel + assert_equal 1, channel.room.id + end + end + + test "subscribe command without an identifier" do + run_in_eventmachine do + setup_connection + + @subscriptions.execute_command 'command' => 'subscribe' + assert @subscriptions.identifiers.empty? + end + end + + test "unsubscribe command" do + run_in_eventmachine do + setup_connection + subscribe_to_chat_channel + + channel = subscribe_to_chat_channel + channel.expects(:unsubscribe_from_channel) + + @subscriptions.execute_command 'command' => 'unsubscribe', 'identifier' => @chat_identifier + assert @subscriptions.identifiers.empty? + end + end + + test "unsubscribe command without an identifier" do + run_in_eventmachine do + setup_connection + + @subscriptions.execute_command 'command' => 'unsubscribe' + assert @subscriptions.identifiers.empty? + end + end + + test "message command" do + run_in_eventmachine do + setup_connection + channel = subscribe_to_chat_channel + + data = { 'content' => 'Hello World!', 'action' => 'speak' } + @subscriptions.execute_command 'command' => 'message', 'identifier' => @chat_identifier, 'data' => ActiveSupport::JSON.encode(data) + + assert_equal [ data ], channel.lines + end + end + + test "unsubscrib from all" do + run_in_eventmachine do + setup_connection + + channel1 = subscribe_to_chat_channel + + channel2_id = ActiveSupport::JSON.encode(id: 2, channel: 'ActionCable::Connection::SubscriptionsTest::ChatChannel') + channel2 = subscribe_to_chat_channel(channel2_id) + + channel1.expects(:unsubscribe_from_channel) + channel2.expects(:unsubscribe_from_channel) + + @subscriptions.unsubscribe_from_all + end + end + + private + def subscribe_to_chat_channel(identifier = @chat_identifier) + @subscriptions.execute_command 'command' => 'subscribe', 'identifier' => identifier + assert_equal identifier, @subscriptions.identifiers.last + + @subscriptions.send :find, 'identifier' => identifier + end + + def setup_connection + env = Rack::MockRequest.env_for "/test", 'HTTP_CONNECTION' => 'upgrade', 'HTTP_UPGRADE' => 'websocket' + @connection = Connection.new(@server, env) + + @subscriptions = ActionCable::Connection::Subscriptions.new(@connection) + end +end |