Skip to content

Commit

Permalink
Implement RSpec connection testing
Browse files Browse the repository at this point in the history
  • Loading branch information
palkan committed Mar 2, 2018
1 parent 83697f1 commit 075b1c5
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 8 deletions.
77 changes: 77 additions & 0 deletions features/channel_specs/channel_spec.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ Feature: channel spec
of the behavior and assertions that it provides, in addition to RSpec's own
behavior and expectations.

It also includes helpers from ActionCable::Connection::TestCase to make it possible to
test connection behavior.

Background:
Given action cable is available

Expand Down Expand Up @@ -112,3 +115,77 @@ Feature: channel spec
"""
When I run `rspec spec/channels/chat_channel_spec.rb`
Then the example should pass

Scenario: successful connection with url params
Given a file named "spec/channels/connection_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe ApplicationCable::Connection, :type => :channel do
it "successfully connects" do
connect "/cable?user_id=323"
expect(connection.user_id).to eq "323"
end
end
"""
When I run `rspec spec/channels/connection_spec.rb`
Then the example should pass

Scenario: successful connection with cookies
Given a file named "spec/channels/connection_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe ApplicationCable::Connection, :type => :channel do
it "successfully connects" do
connect "/cable", cookies: { user_id: "324" }
expect(connection.user_id).to eq "324"
end
end
"""
When I run `rspec spec/channels/connection_spec.rb`
Then the example should pass

Scenario: successful connection with headers
Given a file named "spec/channels/connection_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe ApplicationCable::Connection, :type => :channel do
it "successfully connects" do
connect "/cable", headers: { "X-USER-ID" => "325" }
expect(connection.user_id).to eq "325"
end
end
"""
When I run `rspec spec/channels/connection_spec.rb`
Then the example should pass

Scenario: rejected connection
Given a file named "spec/channels/connection_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe ApplicationCable::Connection, :type => :channel do
it "rejects connection" do
expect { connect "/cable" }.to have_rejected_connection
end
end
"""
When I run `rspec spec/channels/connection_spec.rb`
Then the example should pass

Scenario: disconnect connection
Given a file named "spec/channels/connection_spec.rb" with:
"""ruby
require "rails_helper"
RSpec.describe ApplicationCable::Connection, :type => :channel do
it "disconnects" do
connect "/cable?user_id=42"
expect { disconnect }.to output(/User 42 disconnected/).to_stdout
end
end
"""
When I run `rspec spec/channels/connection_spec.rb`
Then the example should pass
6 changes: 2 additions & 4 deletions lib/action_cable/channel/test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ module Behavior

include ActiveSupport::Testing::ConstantLookup
include ActionCable::TestHelper
include ActionCable::Connection::TestCase::Behavior

CHANNEL_IDENTIFIER = "test_stub"

Expand Down Expand Up @@ -191,6 +192,7 @@ def stub_connection(identifiers = {})

# Subsribe to the channel under test. Optionally pass subscription parameters as a Hash.
def subscribe(params = {})
@connection ||= stub_connection
# NOTE: Rails < 5.0.1 calls subscribe_to_channel during #initialize.
# We have to stub before it
@subscription = self.class.channel_class.allocate
Expand All @@ -209,10 +211,6 @@ def perform(action, data = {})
subscription.perform_action(data.stringify_keys.merge("action" => action.to_s))
end

def connection # :nodoc:
@connection ||= stub_connection
end

# Returns messages transmitted into channel
def transmissions
# Return only directly sent message (via #transmit)
Expand Down
5 changes: 3 additions & 2 deletions lib/action_cable/connection/test_case.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "active_support"
require "active_support/test_case"
require "active_support/core_ext/hash/indifferent_access"
require "action_dispatch"
require "action_dispatch/testing/test_request"

module ActionCable
Expand Down Expand Up @@ -50,7 +51,7 @@ def cookie_jar=(val)
end
end

module ConnectionStub
module TestConnection
attr_reader :logger, :request

def initialize(path, cookies, headers)
Expand Down Expand Up @@ -168,7 +169,7 @@ def determine_default_connection(name)
# Accepts request path as the first argument and cookies and headers as options.
def connect(path = "/cable", cookies: {}, headers: {})
connection = self.class.connection_class.allocate
connection.singleton_class.include(ConnectionStub)
connection.singleton_class.include(TestConnection)
connection.send(:initialize, path, cookies, headers)
connection.connect if connection.respond_to?(:connect)

Expand Down
11 changes: 11 additions & 0 deletions lib/rspec/rails/example/channel_example_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ module ClassMethods
def channel_class
described_class
end

# @private
def connection_class
raise "Described class is not a Connection class" unless
described_class <= ::ActionCable::Connection::Base
described_class
end
end

def have_rejected_connection
raise_error(::ActionCable::Connection::Authorization::UnauthorizedError)
end
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/rspec/rails/matchers/action_cable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module Matchers
#
# @api private
module ActionCable
# rubocop: disable Style/ClassLength
# rubocop: disable Metrics/ClassLength
# @private
class HaveBroadcastedTo < RSpec::Matchers::BuiltIn::BaseMatcher
def initialize(target, channel:)
Expand Down Expand Up @@ -167,7 +167,7 @@ def check_channel_presence
raise ArgumentError, error_msg
end
end
# rubocop: enable Style/ClassLength
# rubocop: enable Metrics/ClassLength
end

# @api public
Expand Down
18 changes: 18 additions & 0 deletions spec/dummy/app/channels/application_cable/connection.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :user_id

def connect
self.user_id = verify_user
end

def disconnect
$stdout.puts "User #{user_id} disconnected"
end

private

def verify_user
user_id = request.params[:user_id] ||
request.headers["x-user-id"] ||
cookies.signed[:user_id]
reject_unauthorized_connection unless user_id.present?
user_id
end
end
end

0 comments on commit 075b1c5

Please sign in to comment.