class ClientTest
Constants
- WAIT_WHEN_EXPECTING_EVENT
- WAIT_WHEN_NOT_EXPECTING_EVENT
Public Instance Methods
concurrently(enum) { |*x| ... }
click to toggle source
# File actioncable/test/client_test.rb, line 203 def concurrently(enum) enum.map { |*x| Concurrent::Future.execute { yield(*x) } }.map(&:value!) end
setup()
click to toggle source
# File actioncable/test/client_test.rb, line 56 def setup ActionCable.instance_variable_set(:@server, nil) server = ActionCable.server server.config.logger = Logger.new(StringIO.new).tap { |l| l.level = Logger::UNKNOWN } server.config.cable = ActiveSupport::HashWithIndifferentAccess.new(adapter: "async") # and now the "real" setup for our test: server.config.disable_request_forgery_protection = true end
test_disappearing_client()
click to toggle source
# File actioncable/test/client_test.rb, line 258 def test_disappearing_client with_puma_server do |port| c = websocket_client(port) assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel") assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message) c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "delay", message: "hello") c.close # disappear before write c = websocket_client(port) assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel") assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message) c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello") assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "message" => { "dong" => "hello" } }, c.read_message) c.close # disappear before read end end
test_interacting_clients()
click to toggle source
# File actioncable/test/client_test.rb, line 219 def test_interacting_clients with_puma_server do |port| clients = concurrently(10.times) { websocket_client(port) } barrier_1 = Concurrent::CyclicBarrier.new(clients.size) barrier_2 = Concurrent::CyclicBarrier.new(clients.size) concurrently(clients) do |c| assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel") assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "type" => "confirm_subscription" }, c.read_message) c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello") assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "message" => { "dong" => "hello" } }, c.read_message) barrier_1.wait WAIT_WHEN_EXPECTING_EVENT c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "bulk", message: "hello") barrier_2.wait WAIT_WHEN_EXPECTING_EVENT assert_equal clients.size, c.read_messages(clients.size).size end concurrently(clients, &:close) end end
test_many_clients()
click to toggle source
# File actioncable/test/client_test.rb, line 242 def test_many_clients with_puma_server do |port| clients = concurrently(100.times) { websocket_client(port) } concurrently(clients) do |c| assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel") assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "type" => "confirm_subscription" }, c.read_message) c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello") assert_equal({ "identifier" => '{"channel":"ClientTest::EchoChannel"}', "message" => { "dong" => "hello" } }, c.read_message) end concurrently(clients, &:close) end end
test_server_restart()
click to toggle source
# File actioncable/test/client_test.rb, line 301 def test_server_restart with_puma_server do |port| c = websocket_client(port) assert_equal({ "type" => "welcome" }, c.read_message) c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel") assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message) ActionCable.server.restart c.wait_for_close assert c.closed? end end
test_single_client()
click to toggle source
# File actioncable/test/client_test.rb, line 207 def test_single_client with_puma_server do |port| c = websocket_client(port) assert_equal({ "type" => "welcome" }, c.read_message) # pop the first welcome message off the stack c.send_message command: "subscribe", identifier: JSON.generate(channel: "ClientTest::EchoChannel") assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message) c.send_message command: "message", identifier: JSON.generate(channel: "ClientTest::EchoChannel"), data: JSON.generate(action: "ding", message: "hello") assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "message" => { "dong" => "hello" } }, c.read_message) c.close end end
test_unsubscribe_client()
click to toggle source
# File actioncable/test/client_test.rb, line 277 def test_unsubscribe_client with_puma_server do |port| app = ActionCable.server identifier = JSON.generate(channel: "ClientTest::EchoChannel") c = websocket_client(port) assert_equal({ "type" => "welcome" }, c.read_message) c.send_message command: "subscribe", identifier: identifier assert_equal({ "identifier" => "{\"channel\":\"ClientTest::EchoChannel\"}", "type" => "confirm_subscription" }, c.read_message) assert_equal(1, app.connections.count) assert(app.remote_connections.where(identifier: identifier)) subscriptions = app.connections.first.subscriptions.send(:subscriptions) assert_not_equal 0, subscriptions.size, "Missing EchoChannel subscription" channel = subscriptions.first[1] channel.expects(:unsubscribed) c.close sleep 0.1 # Data takes a moment to process # All data is removed: No more connection or subscription information! assert_equal(0, app.connections.count) end end
websocket_client(port)
click to toggle source
# File actioncable/test/client_test.rb, line 199 def websocket_client(port) SyncClient.new(port) end
with_puma_server(rack_app = ActionCable.server, port = 3099) { |port| ... }
click to toggle source
# File actioncable/test/client_test.rb, line 67 def with_puma_server(rack_app = ActionCable.server, port = 3099) server = ::Puma::Server.new(rack_app, ::Puma::Events.strings) server.add_tcp_listener "127.0.0.1", port server.min_threads = 1 server.max_threads = 4 thread = server.run begin yield port ensure server.stop begin thread.join rescue IOError # Work around https://bugs.ruby-lang.org/issues/13405 # # Puma's sometimes raising while shutting down, when it closes # its internal pipe. We can safely ignore that, but we do need # to do the step skipped by the exception: server.binder.close rescue RuntimeError => ex # Work around https://bugs.ruby-lang.org/issues/13239 raise unless ex.message =~ /can't modify frozen IOError/ # Handle this as if it were the IOError: do the same as above. server.binder.close end end end