# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details.
# frozen_string_literal: true

begin
  require 'action_cable'
rescue LoadError
end

if defined?(ActionCable::Channel)

  require 'stringio'
  require 'logger'
  require 'json'

  class ActionCableTest < Minitest::Test
    include MultiverseHelpers

    class TestConnection
      attr_reader :transmissions, :identifiers, :logger

      def initialize
        @transmissions = []
        @identifiers = []
        @logger = Logger.new(StringIO.new)
      end

      # In Rails itself, `#config` is delegated via a stub.
      # See https://github.com/rails/rails/commit/8fff6d609cec2d20972235d3c2cf7d004e2d6983
      # But seeing as that stub is not distributed in the ActionCable gem, we
      # use this workaround.
      def config
        Rails.application.config
      end

      def transmit(data)
        @transmissions << data
      end

      def last_transmission
        JSON.parse(@transmissions.last)
      end
    end

    class TestChannel < ActionCable::Channel::Base
      def test_action(data)
        transmit(data['content'])
      end

      def boom(data)
        raise StandardError.new('Boom!')
      end
    end

    setup_and_teardown_agent do
      @connection = TestConnection.new
      @channel = TestChannel.new(@connection, '{id: 1}')
    end

    def test_creates_trace
      @channel.perform_action({'action' => :test_action, 'content' => 'hello'})

      last_sample = last_transaction_trace

      assert_equal('Controller/ActionCable/ActionCableTest::TestChannel/test_action', last_sample.transaction_name)
    end

    def test_creates_web_transaction
      @channel.perform_action({'action' => :test_action, 'content' => 'hello'})

      expected_metrics = {
        'HttpDispatcher' => {:call_count => 1},
        'Controller/ActionCable/ActionCableTest::TestChannel/test_action' => {:call_count => 1}
      }

      assert_metrics_recorded expected_metrics
    end

    def test_action_with_error_is_noticed_by_agent
      @channel.perform_action({'action' => :boom}) rescue nil

      error_trace = last_traced_error

      assert_equal 'StandardError', error_trace.exception_class_name
      assert_equal 'Boom!', error_trace.message
      assert_equal 'Controller/ActionCable/ActionCableTest::TestChannel/boom', error_trace.path
    end
  end

end
