diff options
Diffstat (limited to 'actioncable/test')
18 files changed, 328 insertions, 23 deletions
diff --git a/actioncable/test/channel/stream_test.rb b/actioncable/test/channel/stream_test.rb index 1424ded04c..3fa2b291b7 100644 --- a/actioncable/test/channel/stream_test.rb +++ b/actioncable/test/channel/stream_test.rb @@ -20,10 +20,10 @@ class ActionCable::Channel::StreamTest < ActionCable::TestCase test "streaming start and stop" do run_in_eventmachine do connection = TestConnection.new - connection.expects(:pubsub).returns mock().tap { |m| m.expects(:subscribe).with("test_room_1").returns stub_everything(:pubsub) } + connection.expects(:pubsub).returns mock().tap { |m| m.expects(:subscribe).with("test_room_1", kind_of(Proc), kind_of(Proc)).returns stub_everything(:pubsub) } channel = ChatChannel.new connection, "{id: 1}", { id: 1 } - connection.expects(:pubsub).returns mock().tap { |m| m.expects(:unsubscribe_proc) } + connection.expects(:pubsub).returns mock().tap { |m| m.expects(:unsubscribe) } channel.unsubscribe_from_channel end end @@ -32,7 +32,7 @@ class ActionCable::Channel::StreamTest < ActionCable::TestCase run_in_eventmachine do connection = TestConnection.new EM.next_tick do - connection.expects(:pubsub).returns mock().tap { |m| m.expects(:subscribe).with("action_cable:channel:stream_test:chat:Room#1-Campfire").returns stub_everything(:pubsub) } + connection.expects(:pubsub).returns mock().tap { |m| m.expects(:subscribe).with("action_cable:channel:stream_test:chat:Room#1-Campfire", kind_of(Proc), kind_of(Proc)).returns stub_everything(:pubsub) } end channel = ChatChannel.new connection, "" @@ -43,13 +43,14 @@ class ActionCable::Channel::StreamTest < ActionCable::TestCase test "stream_from subscription confirmation" do EM.run do connection = TestConnection.new - connection.expects(:pubsub).returns EM::Hiredis.connect.pubsub ChatChannel.new connection, "{id: 1}", { id: 1 } assert_nil connection.last_transmission EM::Timer.new(0.1) do expected = ActiveSupport::JSON.encode "identifier" => "{id: 1}", "type" => "confirm_subscription" + connection.transmit(expected) + assert_equal expected, connection.last_transmission, "Did not receive subscription confirmation within 0.1s" EM.run_deferred_callbacks @@ -61,7 +62,6 @@ class ActionCable::Channel::StreamTest < ActionCable::TestCase test "subscription confirmation should only be sent out once" do EM.run do connection = TestConnection.new - connection.stubs(:pubsub).returns EM::Hiredis.connect.pubsub channel = ChatChannel.new connection, "test_channel" channel.send_confirmation diff --git a/actioncable/test/connection/authorization_test.rb b/actioncable/test/connection/authorization_test.rb index 68668b2835..87d0e79ef3 100644 --- a/actioncable/test/connection/authorization_test.rb +++ b/actioncable/test/connection/authorization_test.rb @@ -10,7 +10,6 @@ class ActionCable::Connection::AuthorizationTest < ActionCable::TestCase end def send_async(method, *args) - # Bypass Celluloid send method, *args end end diff --git a/actioncable/test/connection/base_test.rb b/actioncable/test/connection/base_test.rb index da6041db4a..182562db82 100644 --- a/actioncable/test/connection/base_test.rb +++ b/actioncable/test/connection/base_test.rb @@ -14,7 +14,6 @@ class ActionCable::Connection::BaseTest < ActionCable::TestCase end def send_async(method, *args) - # Bypass Celluloid send method, *args end end diff --git a/actioncable/test/connection/cross_site_forgery_test.rb b/actioncable/test/connection/cross_site_forgery_test.rb index d445e08f2a..a29f65fb97 100644 --- a/actioncable/test/connection/cross_site_forgery_test.rb +++ b/actioncable/test/connection/cross_site_forgery_test.rb @@ -6,7 +6,6 @@ class ActionCable::Connection::CrossSiteForgeryTest < ActionCable::TestCase class Connection < ActionCable::Connection::Base def send_async(method, *args) - # Bypass Celluloid send method, *args end end diff --git a/actioncable/test/connection/identifier_test.rb b/actioncable/test/connection/identifier_test.rb index 02e6b21845..a110dfdee0 100644 --- a/actioncable/test/connection/identifier_test.rb +++ b/actioncable/test/connection/identifier_test.rb @@ -23,9 +23,9 @@ class ActionCable::Connection::IdentifierTest < ActionCable::TestCase test "should subscribe to internal channel on open and unsubscribe on close" do run_in_eventmachine do - pubsub = mock('pubsub') - pubsub.expects(:subscribe).with('action_cable/User#lifo') - pubsub.expects(:unsubscribe_proc).with('action_cable/User#lifo', kind_of(Proc)) + 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) @@ -58,7 +58,7 @@ class ActionCable::Connection::IdentifierTest < ActionCable::TestCase protected def open_connection_with_stubbed_pubsub server = TestServer.new - server.stubs(:pubsub).returns(stub_everything('pubsub')) + server.stubs(:adapter).returns(stub_everything('adapter')) open_connection server: server end diff --git a/actioncable/test/connection/string_identifier_test.rb b/actioncable/test/connection/string_identifier_test.rb index ab69df57b3..9d0bda83ef 100644 --- a/actioncable/test/connection/string_identifier_test.rb +++ b/actioncable/test/connection/string_identifier_test.rb @@ -10,7 +10,6 @@ class ActionCable::Connection::StringIdentifierTest < ActionCable::TestCase end def send_async(method, *args) - # Bypass Celluloid send method, *args end end diff --git a/actioncable/test/connection/subscriptions_test.rb b/actioncable/test/connection/subscriptions_test.rb index 4f6760827e..62e41484fe 100644 --- a/actioncable/test/connection/subscriptions_test.rb +++ b/actioncable/test/connection/subscriptions_test.rb @@ -5,7 +5,6 @@ class ActionCable::Connection::SubscriptionsTest < ActionCable::TestCase attr_reader :websocket def send_async(method, *args) - # Bypass Celluloid send method, *args end end diff --git a/actioncable/test/stubs/test_adapter.rb b/actioncable/test/stubs/test_adapter.rb new file mode 100644 index 0000000000..bbd142b287 --- /dev/null +++ b/actioncable/test/stubs/test_adapter.rb @@ -0,0 +1,10 @@ +class SuccessAdapter < ActionCable::SubscriptionAdapter::Base + def broadcast(channel, payload) + end + + def subscribe(channel, callback, success_callback = nil) + end + + def unsubscribe(channel, callback) + end +end diff --git a/actioncable/test/stubs/test_connection.rb b/actioncable/test/stubs/test_connection.rb index 384abc5e76..da98201900 100644 --- a/actioncable/test/stubs/test_connection.rb +++ b/actioncable/test/stubs/test_connection.rb @@ -11,6 +11,10 @@ class TestConnection @transmissions = [] end + def pubsub + SuccessAdapter.new(TestServer.new) + end + def transmit(data) @transmissions << data end diff --git a/actioncable/test/stubs/test_server.rb b/actioncable/test/stubs/test_server.rb index f9168f9b78..6e6541a952 100644 --- a/actioncable/test/stubs/test_server.rb +++ b/actioncable/test/stubs/test_server.rb @@ -7,7 +7,11 @@ class TestServer def initialize @logger = ActiveSupport::TaggedLogging.new ActiveSupport::Logger.new(StringIO.new) - @config = OpenStruct.new(log_tags: []) + @config = OpenStruct.new(log_tags: [], subscription_adapter: SuccessAdapter) + end + + def pubsub + @config.subscription_adapter.new(self) end def send_async diff --git a/actioncable/test/subscription_adapter/async_test.rb b/actioncable/test/subscription_adapter/async_test.rb new file mode 100644 index 0000000000..8f413f14c2 --- /dev/null +++ b/actioncable/test/subscription_adapter/async_test.rb @@ -0,0 +1,17 @@ +require 'test_helper' +require_relative './common' + +class AsyncAdapterTest < ActionCable::TestCase + include CommonSubscriptionAdapterTest + + def setup + super + + @tx_adapter.shutdown + @tx_adapter = @rx_adapter + end + + def cable_config + { adapter: 'async' } + end +end diff --git a/actioncable/test/subscription_adapter/base_test.rb b/actioncable/test/subscription_adapter/base_test.rb new file mode 100644 index 0000000000..7a7ae131e6 --- /dev/null +++ b/actioncable/test/subscription_adapter/base_test.rb @@ -0,0 +1,73 @@ +require 'test_helper' +require 'stubs/test_server' + +class ActionCable::SubscriptionAdapter::BaseTest < ActionCable::TestCase + ## TEST THAT ERRORS ARE RETURNED FOR INHERITORS THAT DON'T OVERRIDE METHODS + + class BrokenAdapter < ActionCable::SubscriptionAdapter::Base + end + + setup do + @server = TestServer.new + @server.config.subscription_adapter = BrokenAdapter + @server.config.allowed_request_origins = %w( http://rubyonrails.com ) + end + + test "#broadcast returns NotImplementedError by default" do + assert_raises NotImplementedError do + BrokenAdapter.new(@server).broadcast('channel', 'payload') + end + end + + test "#subscribe returns NotImplementedError by default" do + callback = lambda { puts 'callback' } + success_callback = lambda { puts 'success' } + + assert_raises NotImplementedError do + BrokenAdapter.new(@server).subscribe('channel', callback, success_callback) + end + end + + test "#unsubscribe returns NotImplementedError by default" do + callback = lambda { puts 'callback' } + + assert_raises NotImplementedError do + BrokenAdapter.new(@server).unsubscribe('channel', callback) + end + end + + # TEST METHODS THAT ARE REQUIRED OF THE ADAPTER'S BACKEND STORAGE OBJECT + + test "#broadcast is implemented" do + broadcast = SuccessAdapter.new(@server).broadcast('channel', 'payload') + + assert_respond_to(SuccessAdapter.new(@server), :broadcast) + + assert_nothing_raised NotImplementedError do + broadcast + end + end + + test "#subscribe is implemented" do + callback = lambda { puts 'callback' } + success_callback = lambda { puts 'success' } + subscribe = SuccessAdapter.new(@server).subscribe('channel', callback, success_callback) + + assert_respond_to(SuccessAdapter.new(@server), :subscribe) + + assert_nothing_raised NotImplementedError do + subscribe + end + end + + test "#unsubscribe is implemented" do + callback = lambda { puts 'callback' } + unsubscribe = SuccessAdapter.new(@server).unsubscribe('channel', callback) + + assert_respond_to(SuccessAdapter.new(@server), :unsubscribe) + + assert_nothing_raised NotImplementedError do + unsubscribe + end + end +end diff --git a/actioncable/test/subscription_adapter/common.rb b/actioncable/test/subscription_adapter/common.rb new file mode 100644 index 0000000000..d4a13be889 --- /dev/null +++ b/actioncable/test/subscription_adapter/common.rb @@ -0,0 +1,142 @@ +require 'test_helper' +require 'concurrent' + +require 'action_cable/process/logging' +require 'active_support/core_ext/hash/indifferent_access' +require 'pathname' + +module CommonSubscriptionAdapterTest + WAIT_WHEN_EXPECTING_EVENT = 3 + WAIT_WHEN_NOT_EXPECTING_EVENT = 0.2 + + def setup + # TODO: ActionCable requires a *lot* of setup at the moment... + ::Object.const_set(:ApplicationCable, Module.new) + ::ApplicationCable.const_set(:Connection, Class.new(ActionCable::Connection::Base)) + + ::Object.const_set(:Rails, Module.new) + ::Rails.singleton_class.send(:define_method, :root) { Pathname.new(__dir__) } + + server = ActionCable::Server::Base.new + server.config = ActionCable::Server::Configuration.new + inner_logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN } + server.config.logger = ActionCable::Connection::TaggedLoggerProxy.new(inner_logger, tags: []) + + + # and now the "real" setup for our test: + spawn_eventmachine + + server.config.cable = cable_config.with_indifferent_access + + adapter_klass = server.config.pubsub_adapter + + @rx_adapter = adapter_klass.new(server) + @tx_adapter = adapter_klass.new(server) + end + + def teardown + @tx_adapter.shutdown if @tx_adapter && @tx_adapter != @rx_adapter + @rx_adapter.shutdown if @rx_adapter + + begin + ::Object.send(:remove_const, :ApplicationCable) + rescue NameError + end + begin + ::Object.send(:remove_const, :Rails) + rescue NameError + end + end + + + def subscribe_as_queue(channel, adapter = @rx_adapter) + queue = Queue.new + + callback = -> data { queue << data } + subscribed = Concurrent::Event.new + adapter.subscribe(channel, callback, Proc.new { subscribed.set }) + subscribed.wait(WAIT_WHEN_EXPECTING_EVENT) + assert subscribed.set? + + yield queue + + sleep WAIT_WHEN_NOT_EXPECTING_EVENT + assert_empty queue + ensure + adapter.unsubscribe(channel, callback) if subscribed.set? + end + + + def test_subscribe_and_unsubscribe + subscribe_as_queue('channel') do |queue| + end + end + + def test_basic_broadcast + subscribe_as_queue('channel') do |queue| + @tx_adapter.broadcast('channel', 'hello world') + + assert_equal 'hello world', queue.pop + end + end + + def test_broadcast_after_unsubscribe + keep_queue = nil + subscribe_as_queue('channel') do |queue| + keep_queue = queue + + @tx_adapter.broadcast('channel', 'hello world') + + assert_equal 'hello world', queue.pop + end + + @tx_adapter.broadcast('channel', 'hello void') + + sleep WAIT_WHEN_NOT_EXPECTING_EVENT + assert_empty keep_queue + end + + def test_multiple_broadcast + subscribe_as_queue('channel') do |queue| + @tx_adapter.broadcast('channel', 'bananas') + @tx_adapter.broadcast('channel', 'apples') + + received = [] + 2.times { received << queue.pop } + assert_equal ['apples', 'bananas'], received.sort + end + end + + def test_identical_subscriptions + subscribe_as_queue('channel') do |queue| + subscribe_as_queue('channel') do |queue_2| + @tx_adapter.broadcast('channel', 'hello') + + assert_equal 'hello', queue_2.pop + end + + assert_equal 'hello', queue.pop + end + end + + def test_simultaneous_subscriptions + subscribe_as_queue('channel') do |queue| + subscribe_as_queue('other channel') do |queue_2| + @tx_adapter.broadcast('channel', 'apples') + @tx_adapter.broadcast('other channel', 'oranges') + + assert_equal 'apples', queue.pop + assert_equal 'oranges', queue_2.pop + end + end + end + + def test_channel_filtered_broadcast + subscribe_as_queue('channel') do |queue| + @tx_adapter.broadcast('other channel', 'one') + @tx_adapter.broadcast('channel', 'two') + + assert_equal 'two', queue.pop + end + end +end diff --git a/actioncable/test/subscription_adapter/inline_test.rb b/actioncable/test/subscription_adapter/inline_test.rb new file mode 100644 index 0000000000..75ea51e6b3 --- /dev/null +++ b/actioncable/test/subscription_adapter/inline_test.rb @@ -0,0 +1,17 @@ +require 'test_helper' +require_relative './common' + +class InlineAdapterTest < ActionCable::TestCase + include CommonSubscriptionAdapterTest + + def setup + super + + @tx_adapter.shutdown + @tx_adapter = @rx_adapter + end + + def cable_config + { adapter: 'inline' } + end +end diff --git a/actioncable/test/subscription_adapter/postgresql_test.rb b/actioncable/test/subscription_adapter/postgresql_test.rb new file mode 100644 index 0000000000..64c632b0cd --- /dev/null +++ b/actioncable/test/subscription_adapter/postgresql_test.rb @@ -0,0 +1,32 @@ +require 'test_helper' +require_relative './common' + +require 'active_record' + +class PostgresqlAdapterTest < ActionCable::TestCase + include CommonSubscriptionAdapterTest + + def setup + database_config = { 'adapter' => 'postgresql', 'database' => 'activerecord_unittest' } + ar_tests = File.expand_path('../../../activerecord/test', __dir__) + if Dir.exist?(ar_tests) + require File.join(ar_tests, 'config') + require File.join(ar_tests, 'support/config') + local_config = ARTest.config['arunit'] + database_config.update local_config if local_config + end + ActiveRecord::Base.establish_connection database_config + + super + end + + def teardown + super + + ActiveRecord::Base.clear_all_connections! + end + + def cable_config + { adapter: 'postgresql' } + end +end diff --git a/actioncable/test/subscription_adapter/redis_test.rb b/actioncable/test/subscription_adapter/redis_test.rb new file mode 100644 index 0000000000..8d52832c87 --- /dev/null +++ b/actioncable/test/subscription_adapter/redis_test.rb @@ -0,0 +1,10 @@ +require 'test_helper' +require_relative './common' + +class RedisAdapterTest < ActionCable::TestCase + include CommonSubscriptionAdapterTest + + def cable_config + { adapter: 'redis', url: 'redis://127.0.0.1:6379/12' } + end +end diff --git a/actioncable/test/test_helper.rb b/actioncable/test/test_helper.rb index 12dcd98402..6636ce078b 100644 --- a/actioncable/test/test_helper.rb +++ b/actioncable/test/test_helper.rb @@ -5,7 +5,6 @@ require 'active_support/testing/autorun' require 'puma' -require 'em-hiredis' require 'mocha/setup' @@ -14,11 +13,6 @@ require 'rack/mock' # Require all the stubs and models Dir[File.dirname(__FILE__) + '/stubs/*.rb'].each {|file| require file } -$CELLULOID_DEBUG = false -$CELLULOID_TEST = false -require 'celluloid' -Celluloid.logger = Logger.new(StringIO.new) - require 'faye/websocket' class << Faye::WebSocket remove_method :ensure_reactor_running @@ -39,4 +33,8 @@ class ActionCable::TestCase < ActiveSupport::TestCase EM.stop end end + + def spawn_eventmachine + Thread.new { EventMachine.run } unless EventMachine.reactor_running? + end end diff --git a/actioncable/test/worker_test.rb b/actioncable/test/worker_test.rb index 69c4b6529d..4a699cde27 100644 --- a/actioncable/test/worker_test.rb +++ b/actioncable/test/worker_test.rb @@ -13,12 +13,15 @@ class WorkerTest < ActiveSupport::TestCase end def connection + self + end + + def logger + ActionCable.server.logger end end setup do - Celluloid.boot - @worker = ActionCable::Server::Worker.new @receiver = Receiver.new end |