# encoding: utf-8
# This file is distributed under New Relic's license terms.
# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details.

require 'redis'

if NewRelic::Agent::Datastores::Redis.is_supported_version?
class NewRelic::Agent::Instrumentation::RedisInstrumentationTest < Minitest::Test
  include MultiverseHelpers
  setup_and_teardown_agent

  def after_setup
    super
    @redis = Redis.new

    # Creating a new client doesn't actually establish a connection, so make
    # sure we do that by issuing a dummy get command, and then drop metrics
    # generated by the connect
    @redis.get('bogus')
    NewRelic::Agent.drop_buffered_data
  end

  def after_teardown
    @redis.flushall
  end

  def test_records_metrics_for_connect
    redis = Redis.new
    redis.get("foo")

    expected = {
      "Datastore/operation/Redis/connect" => { :call_count => 1 },
      "Datastore/operation/Redis/get" => { :call_count => 1 },
      "Datastore/Redis/allOther" => { :call_count => 2 },
      "Datastore/Redis/all" => { :call_count => 2 },
      "Datastore/allOther" => { :call_count => 2 },
      "Datastore/all" => { :call_count => 2 }
    }

    assert_metrics_recorded_exclusive(expected, :ignore_filter => /Supportability/)
  end

  def test_records_connect_tt_node_within_call_that_triggered_it
    in_transaction do
      redis = Redis.new
      redis.get("foo")
    end

    tt = last_transaction_trace

    get_node = tt.root_node.called_nodes[0].called_nodes[0]
    assert_equal('Datastore/operation/Redis/get', get_node.metric_name)

    connect_node = get_node.called_nodes[0]
    assert_equal('Datastore/operation/Redis/connect', connect_node.metric_name)
  end

  def test_records_metrics_for_set
    @redis.set 'time', 'walk'

    expected = {
      "Datastore/operation/Redis/set" => { :call_count => 1 },
      "Datastore/Redis/allOther" => { :call_count => 1 },
      "Datastore/Redis/all"=> { :call_count => 1 },
      "Datastore/allOther"=> { :call_count => 1 },
      "Datastore/all"=> { :call_count => 1 }
    }
    assert_metrics_recorded(expected)
  end

  def test_records_metrics_for_get_in_web_transaction
    in_web_transaction do
      @redis.set 'prodigal', 'sorcerer'
    end

    expected = {
      "Datastore/operation/Redis/set" => { :call_count => 1 },
      "Datastore/Redis/allWeb" => { :call_count => 1 },
      "Datastore/Redis/all"=> { :call_count => 1 },
      "Datastore/allWeb"=> { :call_count => 1 },
      "Datastore/all"=> { :call_count => 1 }
    }
    assert_metrics_recorded(expected)
  end

  def test_records_metrics_for_get
    @redis.get 'mox sapphire'

    expected = {
      "Datastore/operation/Redis/get" => { :call_count => 1 },
      "Datastore/Redis/allOther" => { :call_count => 1 },
      "Datastore/Redis/all"=> { :call_count => 1 },
      "Datastore/allOther"=> { :call_count => 1 },
      "Datastore/all"=> { :call_count => 1 }
    }
    assert_metrics_recorded(expected)
  end

  def test_records_tt_node_for_get
    in_transaction do
      @redis.get 'mox sapphire'
    end

    tt = last_transaction_trace
    get_node = tt.root_node.called_nodes[0].called_nodes[0]
    assert_equal('Datastore/operation/Redis/get', get_node.metric_name)
  end

  def test_does_not_record_statement_on_individual_command_node_by_default
    in_transaction do
      @redis.get 'mox sapphire'
    end

    tt = last_transaction_trace
    get_node = tt.root_node.called_nodes[0].called_nodes[0]

    assert_equal('Datastore/operation/Redis/get', get_node.metric_name)
    refute get_node[:statement]
  end

  def test_records_metrics_for_set_in_web_transaction
    in_web_transaction do
      @redis.get 'timetwister'
    end

    expected = {
      "Datastore/operation/Redis/get" => { :call_count => 1 },
      "Datastore/Redis/allWeb" => { :call_count => 1 },
      "Datastore/Redis/all"=> { :call_count => 1 },
      "Datastore/allWeb"=> { :call_count => 1 },
      "Datastore/all"=> { :call_count => 1 }
    }
    assert_metrics_recorded(expected)
  end

  def test_records_metrics_for_pipelined_commands
    @redis.pipelined do
      @redis.get 'great log'
      @redis.get 'late log'
    end

    expected = {
      "Datastore/operation/Redis/pipeline" => { :call_count => 1 },
      "Datastore/Redis/allOther" => { :call_count => 1 },
      "Datastore/Redis/all" => { :call_count => 1 },
      "Datastore/allOther" => { :call_count => 1 },
      "Datastore/all" => { :call_count => 1 }
    }
    assert_metrics_recorded_exclusive(expected, :ignore_filter => /Supportability/)
  end

  def test_records_commands_without_args_in_pipelined_block_by_default
    in_transaction do
      @redis.pipelined do
        @redis.set 'late log', 'goof'
        @redis.get 'great log'
      end
    end

    tt = last_transaction_trace
    pipeline_node = tt.root_node.called_nodes[0].called_nodes[0]

    assert_equal "set ?\nget ?", pipeline_node[:statement]
  end

  def test_records_metrics_for_multi_blocks
    @redis.multi do
      @redis.get 'darkpact'
      @redis.get 'chaos orb'
    end

    expected = {
      "Datastore/operation/Redis/multi" => { :call_count => 1 },
      "Datastore/Redis/allOther" => { :call_count => 1 },
      "Datastore/Redis/all" => { :call_count => 1 },
      "Datastore/allOther" => { :call_count => 1 },
      "Datastore/all" => { :call_count => 1 }
    }
    assert_metrics_recorded_exclusive(expected, :ignore_filter => /Supportability/)
  end

  def test_records_commands_without_args_in_tt_node_for_multi_blocks
    in_transaction do
      @redis.multi do
        @redis.set 'darkpact', 'sorcery'
        @redis.get 'chaos orb'
      end
    end

    tt = last_transaction_trace
    pipeline_node = tt.root_node.called_nodes[0].called_nodes[0]

    assert_equal("multi\nset ?\nget ?\nexec", pipeline_node[:statement])
  end

  def test_records_commands_with_args_in_tt_node_for_multi_blocks
    with_config(:'transaction_tracer.record_redis_arguments' => true) do
      in_transaction do
        @redis.multi do
          @redis.set 'darkpact', 'sorcery'
          @redis.get 'chaos orb'
        end
      end
    end

    tt = last_transaction_trace
    pipeline_node = tt.root_node.called_nodes[0].called_nodes[0]

    assert_equal("multi\nset \"darkpact\" \"sorcery\"\nget \"chaos orb\"\nexec", pipeline_node[:statement])
  end
end
end
