require_relative '../helper' require 'fluent/counter/store' require 'fluent/time' require 'timecop' class CounterStoreTest < ::Test::Unit::TestCase setup do @name = 'key_name' @scope = "server\tworker\tplugin" # timecop isn't compatible with EventTime t = Time.parse('2016-09-22 16:59:59 +0900') Timecop.freeze(t) @now = Fluent::EventTime.now end teardown do Timecop.return end def extract_value_from_counter(counter, key) store = counter.instance_variable_get(:@storage).instance_variable_get(:@store) store[key] end def travel(sec) # Since Timecop.travel() causes test failures on Windows/AppVeyor by inducing # rounding errors to Time.now, we need to use Timecop.freeze() instead. Timecop.freeze(Time.now + sec) end sub_test_case 'init' do setup do @reset_interval = 10 @store = Fluent::Counter::Store.new @data = { 'name' => @name, 'reset_interval' => @reset_interval } @key = Fluent::Counter::Store.gen_key(@scope, @name) end test 'create new value in the counter' do v = @store.init(@key, @data) assert_equal @name, v['name'] assert_equal @reset_interval, v['reset_interval'] v2 = extract_value_from_counter(@store, @key) v2 = @store.send(:build_response, v2) assert_equal v, v2 end test 'raise an error when a passed key already exists' do @store.init(@key, @data) assert_raise Fluent::Counter::InvalidParams do @store.init(@key, @data) end end test 'return a value when passed key already exists and a ignore option is true' do v = @store.init(@key, @data) v1 = extract_value_from_counter(@store, @key) v1 = @store.send(:build_response, v1) v2 = @store.init(@key, @data, ignore: true) assert_equal v, v2 assert_equal v1, v2 end end sub_test_case 'get' do setup do @store = Fluent::Counter::Store.new data = { 'name' => @name, 'reset_interval' => 10 } @key = Fluent::Counter::Store.gen_key(@scope, @name) @store.init(@key, data) end test 'return a value from the counter' do v = extract_value_from_counter(@store, @key) expected = @store.send(:build_response, v) assert_equal expected, @store.get(@key) end test 'return a raw value from the counter when raw option is true' do v = extract_value_from_counter(@store, @key) assert_equal v, @store.get(@key, raw: true) end test "return nil when a passed key doesn't exist" do assert_equal nil, @store.get('unknown_key') end test "raise a error when a passed key doesn't exist and raise_error option is true" do assert_raise Fluent::Counter::UnknownKey do @store.get('unknown_key', raise_error: true) end end end sub_test_case 'key?' do setup do @store = Fluent::Counter::Store.new data = { 'name' => @name, 'reset_interval' => 10 } @key = Fluent::Counter::Store.gen_key(@scope, @name) @store.init(@key, data) end test 'return true when passed key exists' do assert_true @store.key?(@key) end test "return false when passed key doesn't exist" do assert_true !@store.key?('unknown_key') end end sub_test_case 'delete' do setup do @store = Fluent::Counter::Store.new data = { 'name' => @name, 'reset_interval' => 10 } @key = Fluent::Counter::Store.gen_key(@scope, @name) @init_value = @store.init(@key, data) end test 'delete a value from the counter' do v = @store.delete(@key) assert_equal @init_value, v assert_nil extract_value_from_counter(@store, @key) end test "raise an error when passed key doesn't exist" do assert_raise Fluent::Counter::UnknownKey do @store.delete('unknown_key') end end end sub_test_case 'inc' do setup do @store = Fluent::Counter::Store.new @init_data = { 'name' => @name, 'reset_interval' => 10 } @travel_sec = 10 end data( positive: 10, negative: -10 ) test 'increment or decrement a value in the counter' do |value| key = Fluent::Counter::Store.gen_key(@scope, @name) @store.init(key, @init_data) travel(@travel_sec) v = @store.inc(key, { 'value' => value }) assert_equal value, v['total'] assert_equal value, v['current'] assert_equal @now, v['last_reset_at'] # last_reset_at doesn't change v1 = extract_value_from_counter(@store, key) v1 = @store.send(:build_response, v1) assert_equal v, v1 end test "raise an error when passed key doesn't exist" do assert_raise Fluent::Counter::UnknownKey do @store.inc('unknown_key', { 'value' => 1 }) end end test 'raise an error when a type of passed value is incompatible with a stored value' do key1 = Fluent::Counter::Store.gen_key(@scope, @name) key2 = Fluent::Counter::Store.gen_key(@scope, 'name2') key3 = Fluent::Counter::Store.gen_key(@scope, 'name3') v1 = @store.init(key1, @init_data.merge('type' => 'integer')) v2 = @store.init(key2, @init_data.merge('type' => 'float')) v3 = @store.init(key3, @init_data.merge('type' => 'numeric')) assert_equal 'integer', v1['type'] assert_equal 'float', v2['type'] assert_equal 'numeric', v3['type'] assert_raise Fluent::Counter::InvalidParams do @store.inc(key1, { 'value' => 1.1 }) end assert_raise Fluent::Counter::InvalidParams do @store.inc(key2, { 'value' => 1 }) end assert_nothing_raised do @store.inc(key3, { 'value' => 1 }) @store.inc(key3, { 'value' => 1.0 }) end end end sub_test_case 'reset' do setup do @store = Fluent::Counter::Store.new @travel_sec = 10 @inc_value = 10 @key = Fluent::Counter::Store.gen_key(@scope, @name) @store.init(@key, { 'name' => @name, 'reset_interval' => 10 }) @store.inc(@key, { 'value' => 10 }) end test 'reset a value in the counter' do travel(@travel_sec) v = @store.reset(@key) assert_equal @travel_sec, v['elapsed_time'] assert_true v['success'] counter = v['counter_data'] assert_equal @name, counter['name'] assert_equal @inc_value, counter['total'] assert_equal @inc_value, counter['current'] assert_equal 'numeric', counter['type'] assert_equal @now, counter['last_reset_at'] assert_equal 10, counter['reset_interval'] v1 = extract_value_from_counter(@store, @key) assert_equal 0, v1['current'] assert_true v1['current'].is_a?(Integer) assert_equal @inc_value, v1['total'] assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v1['last_reset_at']) assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v1['last_modified_at']) end test 'reset a value after `reset_interval` passed' do first_travel_sec = 5 travel(first_travel_sec) # jump time less than reset_interval v = @store.reset(@key) assert_equal false, v['success'] assert_equal first_travel_sec, v['elapsed_time'] store = extract_value_from_counter(@store, @key) assert_equal 10, store['current'] assert_equal @now, Fluent::EventTime.new(*store['last_reset_at']) # time is passed greater than reset_interval travel(@travel_sec) v = @store.reset(@key) assert_true v['success'] assert_equal @travel_sec + first_travel_sec, v['elapsed_time'] v1 = extract_value_from_counter(@store, @key) assert_equal 0, v1['current'] assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_reset_at']) assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_modified_at']) end test "raise an error when passed key doesn't exist" do assert_raise Fluent::Counter::UnknownKey do @store.reset('unknown_key') end end end end