require_relative '../test_helper'
require 'aws-xray-sdk'
require 'aws-sdk-dynamodb'
require 'aws-sdk-s3'
require 'aws-sdk-xray'

# Test subsegments recording on AWS Ruby SDK
class TestAwsSdk < Minitest::Test
  @@recorder = XRay::Recorder.new
  config = {
    sampling: false,
    emitter: XRay::TestHelper::StubbedEmitter.new,
    patch: %I[aws_sdk]
  }
  @@recorder.configure(config)
  Aws.config.update xray_recorder: @@recorder

  def setup
    @@recorder.context.clear!
    @@recorder.emitter.clear
  end

  def teardown
    @@recorder.context.clear!
    @@recorder.emitter.clear
  end

  # By default the X-Ray SDK doesn't have parameter whitelisting for S3 APIs.
  def test_simple_s3_call
    @@recorder.begin_segment name
    s3 = Aws::S3::Client.new(stub_responses: true)
    bucket_data = s3.stub_data(:list_buckets, buckets: [{ name: '1' }, { name: '2' }])
    s3.stub_responses(:list_buckets, bucket_data)
    s3.list_buckets
    @@recorder.end_segment
    subsegment = @@recorder.emitter.entities[0].subsegments[0]

    assert_equal 'aws', subsegment.namespace
    aws_meta = subsegment.aws
    assert_equal 'ListBuckets', aws_meta[:operation]
  end

  def test_return_list_count
    @@recorder.begin_segment name
    dynamodb = Aws::DynamoDB::Client.new(stub_responses: true)
    table_list = dynamodb.stub_data(:list_tables, table_names: %w[t1 t2])
    dynamodb.stub_responses(:list_tables, table_list)
    dynamodb.list_tables
    @@recorder.end_segment
    subsegment = @@recorder.emitter.entities[0].subsegments[0]

    assert_equal 'aws', subsegment.namespace
    aws_meta = subsegment.aws
    assert_equal 'ListTables', aws_meta[:operation]
    assert_equal 2, aws_meta[:table_count]
    refute aws_meta.key?(:table_names)
  end

  def test_capture_map_keys
    @@recorder.begin_segment name
    dynamodb = Aws::DynamoDB::Client.new(stub_responses: true)

    mocked_req = {
      request_items: {
        table1: {
          keys: [{ id: 1 }]
        },
        table2: {
          keys: [{ id: 2 }]
        }
      }
    }

    mocked_resp = {
      consumed_capacity: [
        {
          table_name: 'table1',
          capacity_units: 1.0
        },
        {
          table_name: 'table2',
          capacity_units: 2.0
        }
      ]
    }

    resp = dynamodb.stub_data(:batch_get_item, mocked_resp)
    dynamodb.stub_responses(:batch_get_item, resp)
    dynamodb.batch_get_item mocked_req
    @@recorder.end_segment
    subsegment = @@recorder.emitter.entities[0].subsegments[0]

    assert_equal 'aws', subsegment.namespace
    aws_meta = subsegment.aws
    assert_equal 'BatchGetItem', aws_meta[:operation]
    assert_equal %w[table1 table2], aws_meta[:table_names].sort
    assert_equal mocked_resp[:consumed_capacity], aws_meta[:consumed_capacity]
  end

  def test_not_capture_sampling_calls
    xray = Aws::XRay::Client.new(stub_responses: true)

    rules_resp = xray.stub_data(:get_sampling_rules, {})
    xray.stub_responses(:get_sampling_rules, rules_resp)
    xray.get_sampling_rules

    targets_resp = xray.stub_data(:get_sampling_targets, {})
    xray.stub_responses(:get_sampling_targets, targets_resp)
    xray.get_sampling_targets sampling_statistics_documents: {}
  end

  def test_capture_client_error
    @@recorder.begin_segment name
    s3 = Aws::S3::Client.new(stub_responses: true)
    s3.stub_responses(:head_bucket, Timeout::Error)
    # makes sure the capture code doesn't swallow the error
    assert_raises Timeout::Error do
      s3.head_bucket bucket: 'my_bucket'
    end
    @@recorder.end_segment
    subsegment = @@recorder.emitter.entities[0].subsegments[0]
    ex_h = subsegment.to_h[:cause][:exceptions][0]
    assert_equal 'Timeout::Error', ex_h[:message]
    assert_equal 'Timeout::Error', ex_h[:type]
  end

  def test_log_error_pass_through
    recorder = XRay::Recorder.new
    config = {
      sampling: false,
      emitter: XRay::TestHelper::StubbedEmitter.new,
      patch: %I[aws_sdk],
      context_missing: 'LOG_ERROR'
    }
    recorder.configure(config)
    Aws.config.update xray_recorder: recorder

    s3 = Aws::S3::Client.new(stub_responses: true)
    bucket_data = s3.stub_data(:list_buckets, buckets: [{ name: '1' }])
    s3.stub_responses(:list_buckets, bucket_data)
    resp = s3.list_buckets

    # nothing should be sent
    assert_nil recorder.emitter.entities
    # s3 call is executed
    assert_equal '1', resp.buckets[0].name
    recorder.context.clear!
  end
end
