Testing Stdout Filter Implementation in Fluent/Fluentd
This test suite validates the functionality of Fluentd’s stdout filter plugin, focusing on output formatting and time handling capabilities. The tests cover various output types including JSON, hash, and LTSV formats, along with configuration options for time key inclusion and timezone handling.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
fluent/fluentd
test/plugin/test_filter_stdout.rb
require_relative '../helper'
require 'fluent/test/driver/filter'
require 'fluent/plugin/filter_stdout'
require 'timecop'
require 'flexmock/test_unit'
class StdoutFilterTest < Test::Unit::TestCase
include FlexMock::TestCase
def setup
Fluent::Test.setup
@old_tz = ENV["TZ"]
ENV["TZ"] = "UTC"
Timecop.freeze
@default_newline = if Fluent.windows?
"\r\n"
else
"\n"
end
end
def teardown
super # FlexMock::TestCase requires this
# http://flexmock.rubyforge.org/FlexMock/TestCase.html
Timecop.return
ENV["TZ"] = @old_tz
end
CONFIG = config_element('ROOT')
def create_driver(conf = CONFIG)
Fluent::Test::Driver::Filter.new(Fluent::Plugin::StdoutFilter).configure(conf)
end
def filter(d, time, record)
d.run {
d.feed("filter.test", time, record)
}
d.filtered_records
end
def test_through_record
d = create_driver
filtered = filter(d, event_time, {'test' => 'test'})
assert_equal([{'test' => 'test'}], filtered)
end
sub_test_case "flat style parameters" do
sub_test_case "configure" do
def test_configure_default
d = create_driver
d.run {}
assert_equal 'json', d.instance.formatter.output_type
end
data(json: "json",
hash: "hash",
ltsv: "ltsv")
def test_output_type(data)
d = create_driver(CONFIG + config_element("", "", { "output_type" => data }))
d.run {}
assert_equal data, d.instance.formatter.output_type
end
def test_invalid_output_type
assert_raise(Fluent::NotFoundPluginError) do
d = create_driver(CONFIG + config_element("", "", { "output_type" => "foo" }))
d.run {}
end
end
end
def test_output_type_json
d = create_driver(CONFIG + config_element("", "", { "output_type" => "json" }))
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => 'test'}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\":\"test\"}\n", out
# NOTE: Float::NAN is not jsonable
d = create_driver(CONFIG + config_element("", "", { "output_type" => "json" }))
flexmock(d.instance.router).should_receive(:emit_error_event)
filter(d, etime, {'test' => Float::NAN})
end
def test_output_type_hash
d = create_driver(CONFIG + config_element("", "", { "output_type" => "hash" }))
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => 'test'}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\"=>\"test\"}\n", out.gsub(' => ', '=>')
# NOTE: Float::NAN is not jsonable, but hash string can output it.
d = create_driver(CONFIG + config_element("", "", { "output_type" => "hash" }))
out = capture_log(d) { filter(d, etime, {'test' => Float::NAN}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\"=>NaN}\n", out.gsub(' => ', '=>')
end
# Use include_time_key to output the message's time
def test_include_time_key
config = config_element("", "", {
"output_type" => "json",
"include_time_key" => true,
"localtime" => false
})
d = create_driver(config)
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => 'test'}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\":\"test\",\"time\":\"2016-10-07T21:09:31Z\"}\n", out
end
# out_stdout formatter itself can also be replaced
def test_format_json
d = create_driver(CONFIG + config_element("", "", { "format" => "json" }))
out = capture_log(d) { filter(d, event_time, {'test' => 'test'}) }
assert_equal "{\"test\":\"test\"}#{@default_newline}", out
end
end
sub_test_case "with <format> sub section" do
sub_test_case "configure" do
def test_default
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout"})
d = create_driver(conf)
d.run {}
assert_equal("json", d.instance.formatter.output_type)
end
data(json: "json",
hash: "hash",
ltsv: "ltsv")
def test_output_type(data)
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => data })
d = create_driver(conf)
d.run {}
assert_equal(data, d.instance.formatter.output_type)
end
def test_invalid_output_type
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => "foo" })
assert_raise(Fluent::NotFoundPluginError) do
d = create_driver(conf)
d.run {}
end
end
end
sub_test_case "output_type" do
def test_json
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => "json" })
d = create_driver(conf)
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => 'test'}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\":\"test\"}\n", out
end
def test_json_nan
# NOTE: Float::NAN is not jsonable
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => "json" })
d = create_driver(conf)
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
flexmock(d.instance.router).should_receive(:emit_error_event)
filter(d, etime, {'test' => Float::NAN})
end
def test_hash
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => "hash" })
d = create_driver(conf)
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => 'test'}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\"=>\"test\"}\n", out.gsub(' => ', '=>')
end
def test_hash_nan
# NOTE: Float::NAN is not jsonable, but hash string can output it.
conf = config_element
conf.elements << config_element("format", "", { "@type" => "stdout", "output_type" => "hash" })
d = create_driver(conf)
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => Float::NAN}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\"=>NaN}\n", out.gsub(' => ', '=>')
end
# Use include_time_key to output the message's time
def test_include_time_key
conf = config_element
conf.elements << config_element("format", "", {
"@type" => "stdout",
"output_type" => "json"
})
conf.elements << config_element("inject", "", {
"time_key" => "time",
"time_type" => "string",
"localtime" => false
})
d = create_driver(conf)
etime = event_time("2016-10-07 21:09:31.012345678 UTC")
out = capture_log(d) { filter(d, etime, {'test' => 'test'}) }
assert_equal "2016-10-07 21:09:31.012345678 +0000 filter.test: {\"test\":\"test\",\"time\":\"2016-10-07T21:09:31Z\"}\n", out
end
end
end
end