Skip to content

Commit

Permalink
Add workaround for ConnectionStub's missing interface (#1686)
Browse files Browse the repository at this point in the history
* Add specs for ActionCable's connection handling

* Add workaround for ActionCable's ConnectionStub

ActionCable's ConnectionStub (for testing) doesn't implement the exact same interfaces as Connection::Base.
One thing that's missing is `env`. So calling `connection.env` direclty will fail in test environments when `stub_connection` is used.
See #1684 for more information.

* Update changelog
  • Loading branch information
st0012 committed Jan 14, 2022
1 parent 6d312f8 commit 7697af6
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 14 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 4.9.1

### Bug Fixes

- Add workaround for ConnectionStub's missing interface [#1686](https://github.com/getsentry/sentry-ruby/pull/1686)
- Fixes [#1685](https://github.com/getsentry/sentry-ruby/issues/1685)

## 4.9.0

### Features
Expand Down
15 changes: 10 additions & 5 deletions sentry-rails/lib/sentry/rails/action_cable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ module Rails
module ActionCableExtensions
class ErrorHandler
class << self
def capture(env, transaction_name:, extra_context: nil, &block)
def capture(connection, transaction_name:, extra_context: nil, &block)
# ActionCable's ConnectionStub (for testing) doesn't implement the exact same interfaces as Connection::Base.
# One thing that's missing is `env`. So calling `connection.env` direclty will fail in test environments when `stub_connection` is used.
# See https://github.com/getsentry/sentry-ruby/pull/1684 for more information.
env = connection.respond_to?(:env) ? connection.env : {}

Sentry.with_scope do |scope|
scope.set_rack_env(env)
scope.set_context("action_cable", extra_context) if extra_context
Expand Down Expand Up @@ -43,13 +48,13 @@ module Connection
private

def handle_open
ErrorHandler.capture(env, transaction_name: "#{self.class.name}#connect") do
ErrorHandler.capture(self, transaction_name: "#{self.class.name}#connect") do
super
end
end

def handle_close
ErrorHandler.capture(env, transaction_name: "#{self.class.name}#disconnect") do
ErrorHandler.capture(self, transaction_name: "#{self.class.name}#disconnect") do
super
end
end
Expand All @@ -69,7 +74,7 @@ def self.included(base)
def sentry_capture(hook, &block)
extra_context = { params: params }

ErrorHandler.capture(connection.env, transaction_name: "#{self.class.name}##{hook}", extra_context: extra_context, &block)
ErrorHandler.capture(connection, transaction_name: "#{self.class.name}##{hook}", extra_context: extra_context, &block)
end
end

Expand All @@ -79,7 +84,7 @@ module Actions
def dispatch_action(action, data)
extra_context = { params: params, data: data }

ErrorHandler.capture(connection.env, transaction_name: "#{self.class.name}##{action}", extra_context: extra_context) do
ErrorHandler.capture(connection, transaction_name: "#{self.class.name}##{action}", extra_context: extra_context) do
super
end
end
Expand Down
62 changes: 53 additions & 9 deletions sentry-rails/spec/sentry/rails/action_cable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,6 @@

::ActionCable.server.config.cable = { "adapter" => "test" }

# ensure we can access `connection.env` in tests like we can in production
ActiveSupport.on_load :action_cable_channel_test_case do
class ::ActionCable::Channel::ConnectionStub
def env
@_env ||= ::ActionCable::Connection::TestRequest.create.env
end
end
end

class ChatChannel < ::ActionCable::Channel::Base
def subscribed
raise "foo"
Expand All @@ -29,13 +20,66 @@ def unsubscribed
end
end

class FailToOpenConnection < ActionCable::Connection::Base
def connect
raise "foo"
end
end

class FailToCloseConnection < ActionCable::Connection::Base
def disconnect
raise "bar"
end
end


RSpec.describe "Sentry::Rails::ActionCableExtensions", type: :channel do
let(:transport) { Sentry.get_current_client.transport }

after do
transport.events = []
end

describe "Connection" do
before do
make_basic_app
end

let(:connection) do
env = Rack::MockRequest.env_for "/test", "HTTP_CONNECTION" => "upgrade", "HTTP_UPGRADE" => "websocket",
"HTTP_HOST" => "localhost", "HTTP_ORIGIN" => "http://rubyonrails.com"
described_class.new(spy, env)
end

before do
connection.process
end

describe FailToOpenConnection do
it "captures errors happen when establishing connection" do
expect { connection.send(:handle_open) }.to raise_error(RuntimeError, "foo")

expect(transport.events.count).to eq(1)

event = transport.events.last.to_json_compatible
expect(event["transaction"]).to eq("FailToOpenConnection#connect")
end
end

describe FailToCloseConnection do
it "captures errors happen when establishing connection" do
connection.send(:handle_open)

expect { connection.send(:handle_close) }.to raise_error(RuntimeError, "bar")

expect(transport.events.count).to eq(1)

event = transport.events.last.to_json_compatible
expect(event["transaction"]).to eq("FailToCloseConnection#disconnect")
end
end
end

context "without tracing" do
before do
make_basic_app
Expand Down

0 comments on commit 7697af6

Please sign in to comment.