require 'aws-sdk-resources' require 'fluent/test' require 'fluent/test/helpers' require 'fluent/test/log' require 'fluent/test/driver/input' require 'fluent/plugin/in_s3' require 'test/unit/rr' require 'zlib' require 'fileutils' include Fluent::Test::Helpers class S3InputTest < Test::Unit::TestCase def setup Fluent::Test.setup @time = event_time("2015-09-30 13:14:15 UTC") Fluent::Engine.now = @time if Fluent.const_defined?(:EventTime) stub(Fluent::EventTime).now { @time } end end CONFIG = %[ aws_key_id test_key_id aws_sec_key test_sec_key s3_bucket test_bucket buffer_type memory queue_name test_queue ] def create_driver(conf = CONFIG) Fluent::Test::Driver::Input.new(Fluent::Plugin::S3Input).configure(conf) end class ConfigTest < self def test_default d = create_driver extractor = d.instance.instance_variable_get(:@extractor) actual = { aws_key_id: d.instance.aws_key_id, aws_sec_key: d.instance.aws_sec_key, s3_bucket: d.instance.s3_bucket, s3_region: d.instance.s3_region, sqs_queue_name: d.instance.sqs.queue_name, extractor_ext: extractor.ext, extractor_content_type: extractor.content_type } expected = { aws_key_id: "test_key_id", aws_sec_key: "test_sec_key", s3_bucket: "test_bucket", s3_region: "us-east-1", sqs_queue_name: "test_queue", extractor_ext: "gz", extractor_content_type: "application/x-gzip" } assert_equal(expected, actual) end def test_empty assert_raise(Fluent::ConfigError) do create_driver("") end end def test_without_sqs_section conf = %[ aws_key_id test_key_id aws_sec_key test_sec_key s3_bucket test_bucket ] assert_raise_message("'' sections are required") do create_driver(conf) end end def test_unknown_store_as config = CONFIG + "\nstore_as unknown" assert_raise(Fluent::ConfigError) do create_driver(config) end end data("json" => ["json", "json", "application/json"], "text" => ["text", "txt", "text/plain"], "gzip_command" => ["gzip_command", "gz", "application/x-gzip"], "lzo" => ["lzo", "lzo", "application/x-lzop"], "lzma2" => ["lzma2", "xz", "application/x-xz"]) def test_extractor(data) store_type, ext, content_type = data config = CONFIG + "\nstore_as #{store_type}\n" d = create_driver(config) extractor = d.instance.instance_variable_get(:@extractor) expected = { ext: ext, content_type: content_type } actual = { ext: extractor.ext, content_type: extractor.content_type } assert_equal(expected, actual) rescue Fluent::ConfigError => e pend(e.message) end end Struct.new("StubResponse", :queue_url) Struct.new("StubMessage", :message_id, :receipt_handle, :body) def setup_mocks @s3_client = stub(Aws::S3::Client.new(stub_responses: true)) mock(Aws::S3::Client).new(anything).at_least(0) { @s3_client } @s3_resource = mock(Aws::S3::Resource.new(client: @s3_client)) mock(Aws::S3::Resource).new(client: @s3_client) { @s3_resource } @s3_bucket = mock(Aws::S3::Bucket.new(name: "test", client: @s3_client)) @s3_resource.bucket(anything) { @s3_bucket } test_queue_url = "http://example.com/test_queue" @sqs_client = stub(Aws::SQS::Client.new(stub_responses: true)) @sqs_response = stub(Struct::StubResponse.new(test_queue_url)) @sqs_client.get_queue_url(queue_name: "test_queue"){ @sqs_response } mock(Aws::SQS::Client).new(anything).once { @sqs_client } @real_poller = Aws::SQS::QueuePoller.new(test_queue_url, client: @sqs_client) @sqs_poller = stub(@real_poller) mock(Aws::SQS::QueuePoller).new(anything, client: @sqs_client) { @sqs_poller } end def test_no_records setup_mocks d = create_driver(CONFIG + "\ncheck_apikey_on_start false\n") mock(d.instance).process(anything).never message = Struct::StubMessage.new(1, 1, "{}") @sqs_poller.get_messages(anything, anything) do |config, stats| config.before_request.call(stats) if config.before_request stats.request_count += 1 if stats.request_count > 1 d.instance.instance_variable_set(:@running, false) end [message] end assert_nothing_raised do d.run {} end end def test_one_record setup_mocks d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\n") s3_object = stub(Object.new) s3_response = stub(Object.new) s3_response.body { StringIO.new("aaa") } s3_object.get { s3_response } @s3_bucket.object(anything).at_least(1) { s3_object } body = { "Records" => [ { "s3" => { "object" => { "key" => "test_key" } } } ] } message = Struct::StubMessage.new(1, 1, Yajl.dump(body)) @sqs_poller.get_messages(anything, anything) do |config, stats| config.before_request.call(stats) if config.before_request stats.request_count += 1 if stats.request_count >= 1 d.instance.instance_variable_set(:@running, false) end [message] end d.run(expect_emits: 1) events = d.events assert_equal({ "message" => "aaa" }, events.first[2]) end def test_one_record_url_encoded setup_mocks d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\n") s3_object = stub(Object.new) s3_response = stub(Object.new) s3_response.body { StringIO.new("aaa") } s3_object.get { s3_response } @s3_bucket.object('test key').at_least(1) { s3_object } body = { "Records" => [ { "s3" => { "object" => { "key" => "test+key" } } } ] } message = Struct::StubMessage.new(1, 1, Yajl.dump(body)) @sqs_poller.get_messages(anything, anything) do |config, stats| config.before_request.call(stats) if config.before_request stats.request_count += 1 if stats.request_count >= 1 d.instance.instance_variable_set(:@running, false) end [message] end d.run(expect_emits: 1) events = d.events assert_equal({ "message" => "aaa" }, events.first[2]) end def test_one_record_multi_line setup_mocks d = create_driver(CONFIG + "\ncheck_apikey_on_start false\nstore_as text\nformat none\n") s3_object = stub(Object.new) s3_response = stub(Object.new) s3_response.body { StringIO.new("aaa\nbbb\nccc\n") } s3_object.get { s3_response } @s3_bucket.object(anything).at_least(1) { s3_object } body = { "Records" => [ { "s3" => { "object" => { "key" => "test_key" } } } ] } message = Struct::StubMessage.new(1, 1, Yajl.dump(body)) @sqs_poller.get_messages(anything, anything) do |config, stats| config.before_request.call(stats) if config.before_request stats.request_count += 1 if stats.request_count >= 1 d.instance.instance_variable_set(:@running, false) end [message] end d.run(expect_emits: 1) events = d.events expected_records = [ { "message" => "aaa\n" }, { "message" => "bbb\n" }, { "message" => "ccc\n" } ] assert_equal(expected_records, events.map {|_tag, _time, record| record }) end end