fluentd 1.2.0.pre1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of fluentd might be problematic. Click here for more details.

@@ -0,0 +1,44 @@
1
+ require_relative '../helper'
2
+ require 'fluent/counter/error'
3
+
4
+ class CounterErrorTest < ::Test::Unit::TestCase
5
+ setup do
6
+ @message = 'error message'
7
+ end
8
+
9
+ test 'invalid_params' do
10
+ error = Fluent::Counter::InvalidParams.new(@message)
11
+ expected = { 'code' => 'invalid_params', 'message' => @message }
12
+ assert_equal expected, error.to_hash
13
+ end
14
+
15
+ test 'unknown_key' do
16
+ error = Fluent::Counter::UnknownKey.new(@message)
17
+ expected = { 'code' => 'unknown_key', 'message' => @message }
18
+ assert_equal expected, error.to_hash
19
+ end
20
+
21
+ test 'parse_error' do
22
+ error = Fluent::Counter::ParseError.new(@message)
23
+ expected = { 'code' => 'parse_error', 'message' => @message }
24
+ assert_equal expected, error.to_hash
25
+ end
26
+
27
+ test 'method_not_found' do
28
+ error = Fluent::Counter::MethodNotFound.new(@message)
29
+ expected = { 'code' => 'method_not_found', 'message' => @message }
30
+ assert_equal expected, error.to_hash
31
+ end
32
+
33
+ test 'invalid_request' do
34
+ error = Fluent::Counter::InvalidRequest.new(@message)
35
+ expected = { 'code' => 'invalid_request', 'message' => @message }
36
+ assert_equal expected, error.to_hash
37
+ end
38
+
39
+ test 'internal_server_error' do
40
+ error = Fluent::Counter::InternalServerError.new(@message)
41
+ expected = { 'code' => 'internal_server_error', 'message' => @message }
42
+ assert_equal expected, error.to_hash
43
+ end
44
+ end
@@ -0,0 +1,179 @@
1
+ require_relative '../helper'
2
+ require 'fluent/counter/mutex_hash'
3
+ require 'fluent/counter/store'
4
+ require 'flexmock/test_unit'
5
+ require 'timecop'
6
+
7
+ class MutexHashTest < ::Test::Unit::TestCase
8
+ setup do
9
+ @store = {}
10
+ @value = 'sample value'
11
+ @counter_store_mutex = Fluent::Counter::MutexHash.new(@store)
12
+ end
13
+
14
+ sub_test_case 'synchronize' do
15
+ test "create new mutex values if keys don't exist" do
16
+ keys = ['key', 'key1']
17
+
18
+ @counter_store_mutex.synchronize(*keys) do |store, k|
19
+ store[k] = @value
20
+ end
21
+
22
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
23
+ keys.each do |key|
24
+ assert_true mhash[key].is_a?(Mutex)
25
+ assert_equal @value, @store[key]
26
+ end
27
+ end
28
+
29
+ test 'nothing to do when an empty array passed' do
30
+ @counter_store_mutex.synchronize(*[]) do |store, k|
31
+ store[k] = @value
32
+ end
33
+
34
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
35
+ assert_true mhash.empty?
36
+ assert_true @store.empty?
37
+ end
38
+
39
+ test 'use a one mutex value when the same key specified' do
40
+ key = 'key'
41
+ @counter_store_mutex.synchronize(key) do |store, k|
42
+ store[k] = @value
43
+ end
44
+
45
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
46
+ m = mhash[key]
47
+ assert_true m.is_a?(Mutex)
48
+ assert_equal @value, @store[key]
49
+
50
+ # access the same key once again
51
+ value2 = 'test value2'
52
+ @counter_store_mutex.synchronize(key) do |store, k|
53
+ store[k] = value2
54
+ end
55
+
56
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
57
+ m2 = mhash[key]
58
+ assert_true m2.is_a?(Mutex)
59
+ assert_equal value2, @store[key]
60
+
61
+ assert_equal m, m2
62
+ end
63
+ end
64
+
65
+ sub_test_case 'synchronize_key' do
66
+ test "create new mutex values if keys don't exist" do
67
+ keys = ['key', 'key1']
68
+
69
+ @counter_store_mutex.synchronize_keys(*keys) do |store, k|
70
+ store[k] = @value
71
+ end
72
+
73
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
74
+ keys.each do |key|
75
+ assert_true mhash[key].is_a?(Mutex)
76
+ assert_equal @value, @store[key]
77
+ end
78
+ end
79
+
80
+ test 'nothing to do when an empty array passed' do
81
+ @counter_store_mutex.synchronize_keys(*[]) do |store, k|
82
+ store[k] = @value
83
+ end
84
+
85
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
86
+ assert_true mhash.empty?
87
+ assert_true @store.empty?
88
+ end
89
+
90
+ test 'use a one mutex value when the same key specified' do
91
+ key = 'key'
92
+ @counter_store_mutex.synchronize_keys(key) do |store, k|
93
+ store[k] = @value
94
+ end
95
+
96
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
97
+ m = mhash[key]
98
+ assert_true m.is_a?(Mutex)
99
+ assert_equal @value, @store[key]
100
+
101
+ # access the same key once again
102
+ value2 = 'test value2'
103
+ @counter_store_mutex.synchronize_keys(key) do |store, k|
104
+ store[k] = value2
105
+ end
106
+
107
+ mhash = @counter_store_mutex.instance_variable_get(:@mutex_hash)
108
+ m2 = mhash[key]
109
+ assert_true m2.is_a?(Mutex)
110
+ assert_equal value2, @store[key]
111
+
112
+ assert_equal m, m2
113
+ end
114
+ end
115
+ end
116
+
117
+ class CleanupThreadTest < ::Test::Unit::TestCase
118
+ StoreValue = Struct.new(:last_modified_at)
119
+
120
+ setup do
121
+ # timecop isn't compatible with EventTime
122
+ t = Time.parse('2016-09-22 16:59:59 +0900')
123
+ Timecop.freeze(t)
124
+
125
+ @store = Fluent::Counter::Store.new
126
+ @mhash = Fluent::Counter::MutexHash.new(@store)
127
+
128
+ # stub sleep method to avoid waiting CLEANUP_INTERVAL
129
+ ct = @mhash.instance_variable_get(:@cleanup_thread)
130
+ flexstub(ct).should_receive(:sleep)
131
+ end
132
+
133
+ teardown do
134
+ @mhash.stop
135
+ Timecop.return
136
+ end
137
+
138
+ test 'clean up unused mutex' do
139
+ name = 'key1'
140
+ init_obj = { 'name' => name, 'reset_interval' => 2 }
141
+
142
+ @mhash.synchronize(init_obj['name']) do
143
+ @store.init(name, init_obj)
144
+ end
145
+
146
+ ct = @mhash.instance_variable_get(:@mutex_hash)
147
+ assert ct[name]
148
+
149
+ Timecop.travel(15 * 60 + 1) # 15 min
150
+
151
+ @mhash.start # start cleanup
152
+ sleep 1
153
+
154
+ ct = @mhash.instance_variable_get(:@mutex_hash)
155
+ assert_empty ct
156
+
157
+ @mhash.stop
158
+ end
159
+
160
+ test "don't remove when `last_modified_at` is greater than (Time.now - CLEANUP_INTERVAL)" do
161
+ name = 'key1'
162
+ init_obj = { 'name' => name, 'reset_interval' => 2 }
163
+
164
+ @mhash.synchronize(init_obj['name']) do
165
+ @store.init(name, init_obj)
166
+ end
167
+
168
+ ct = @mhash.instance_variable_get(:@mutex_hash)
169
+ assert ct[name]
170
+
171
+ @mhash.start # start cleanup
172
+ sleep 1
173
+
174
+ ct = @mhash.instance_variable_get(:@mutex_hash)
175
+ assert ct[name]
176
+
177
+ @mhash.stop
178
+ end
179
+ end
@@ -0,0 +1,583 @@
1
+ require_relative '../helper'
2
+ require 'fluent/counter/server'
3
+ require 'fluent/counter/store'
4
+ require 'fluent/time'
5
+ require 'flexmock/test_unit'
6
+ require 'timecop'
7
+
8
+ class CounterServerTest < ::Test::Unit::TestCase
9
+ setup do
10
+ # timecop isn't compatible with EventTime
11
+ t = Time.parse('2016-09-22 16:59:59 +0900')
12
+ Timecop.freeze(t)
13
+ @now = Fluent::EventTime.now
14
+
15
+ @scope = "server\tworker\tplugin"
16
+ @server_name = 'server1'
17
+ @server = Fluent::Counter::Server.new(@server_name, opt: { log: $log })
18
+ @server.instance_eval { @server.close }
19
+ end
20
+
21
+ teardown do
22
+ Timecop.return
23
+ end
24
+
25
+ def extract_value_from_counter(counter, scope, name)
26
+ store = counter.instance_variable_get(:@store).instance_variable_get(:@storage).instance_variable_get(:@store)
27
+ key = Fluent::Counter::Store.gen_key(scope, name)
28
+ store[key]
29
+ end
30
+
31
+ test 'raise an error when server name is invalid' do
32
+ assert_raise do
33
+ Fluent::Counter::Server.new("\tinvalid_name")
34
+ end
35
+ end
36
+
37
+ sub_test_case 'on_message' do
38
+ data(
39
+ establish: 'establish',
40
+ init: 'init',
41
+ delete: 'delete',
42
+ inc: 'inc',
43
+ get: 'get',
44
+ reset: 'reset',
45
+ )
46
+ test 'call valid methods' do |method|
47
+ stub(@server).send do |_m, params, scope, options|
48
+ { 'data' => [params, scope, options] }
49
+ end
50
+
51
+ request = { 'id' => 0, 'method' => method }
52
+ expected = { 'id' => 0, 'data' => [nil, nil, nil] }
53
+ assert_equal expected, @server.on_message(request)
54
+ end
55
+
56
+ data(
57
+ missing_id: [
58
+ { 'method' => 'init' },
59
+ { 'code' => 'invalid_request', 'message' => 'Request should include `id`' }
60
+ ],
61
+ missing_method: [
62
+ { 'id' => 0 },
63
+ { 'code' => 'invalid_request', 'message' => 'Request should include `method`' }
64
+ ],
65
+ invalid_method: [
66
+ { 'id' => 0, 'method' => 'invalid_method' },
67
+ { 'code' => 'method_not_found', 'message' => 'Unknown method name passed: invalid_method' }
68
+ ]
69
+ )
70
+ test 'invalid request' do |(request, error)|
71
+ expected = {
72
+ 'id' => request['id'],
73
+ 'data' => [],
74
+ 'errors' => [error],
75
+ }
76
+
77
+ assert_equal expected, @server.on_message(request)
78
+ end
79
+
80
+ test 'return an `internal_server_error` error object when an error raises in safe_run' do
81
+ stub(@server).send do |_m, _params, _scope, _options|
82
+ raise 'Error in safe_run'
83
+ end
84
+
85
+ request = { 'id' => 0, 'method' => 'init' }
86
+ expected = {
87
+ 'id' => request['id'],
88
+ 'data' => [],
89
+ 'errors' => [
90
+ { 'code' => 'internal_server_error', 'message' => 'Error in safe_run' }
91
+ ]
92
+ }
93
+ assert_equal expected, @server.on_message(request)
94
+ end
95
+
96
+ test 'output an error log when passed data is not Hash' do
97
+ data = 'this is not a hash'
98
+ mock($log).error("Received data is not Hash: #{data}")
99
+ @server.on_message(data)
100
+ end
101
+ end
102
+
103
+ sub_test_case 'establish' do
104
+ test 'establish a scope in a counter' do
105
+ result = @server.send('establish', ['key'], nil, nil)
106
+ expected = { 'data' => ["#{@server_name}\tkey"] }
107
+ assert_equal expected, result
108
+ end
109
+
110
+ data(
111
+ empty: [[], 'One or more `params` are required'],
112
+ empty_key: [[''], '`scope` is the invalid format'],
113
+ invalid_key: [['_key'], '`scope` is the invalid format'],
114
+ )
115
+ test 'raise an error: invalid_params' do |(params, msg)|
116
+ result = @server.send('establish', params, nil, nil)
117
+ expected = {
118
+ 'data' => [],
119
+ 'errors' => [{ 'code' => 'invalid_params', 'message' => msg }]
120
+ }
121
+ assert_equal expected, result
122
+ end
123
+ end
124
+
125
+ sub_test_case 'init' do
126
+ setup do
127
+ @name = 'key1'
128
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
129
+ end
130
+
131
+ test 'create new value in a counter' do
132
+ assert_nil extract_value_from_counter(@server, @scope, @name)
133
+ result = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
134
+ assert_nil result['errors']
135
+
136
+ counter = result['data'].first
137
+ assert_equal 'numeric', counter['type']
138
+ assert_equal @name, counter['name']
139
+ assert_equal 0, counter['current']
140
+ assert_equal 0, counter['total']
141
+ assert_equal @now, counter['last_reset_at']
142
+ assert extract_value_from_counter(@server, @scope, @name)
143
+ end
144
+
145
+ data(
146
+ numeric: 'numeric',
147
+ integer: 'integer',
148
+ float: 'float'
149
+ )
150
+ test 'set the type of a counter value' do |type|
151
+ result = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1, 'type' => type }], @scope, {})
152
+ counter = result['data'].first
153
+ assert_equal type, counter['type']
154
+
155
+ v = extract_value_from_counter(@server, @scope, @name)
156
+ assert_equal type, v['type']
157
+ end
158
+
159
+ data(
160
+ empty: [[], 'One or more `params` are required'],
161
+ missing_name: [
162
+ [{ 'rest_interval' => 20 }],
163
+ '`name` is required'
164
+ ],
165
+ invalid_name: [
166
+ [{ 'name' => '_test', 'reset_interval' => 20 }],
167
+ '`name` is the invalid format'
168
+ ],
169
+ missing_interval: [
170
+ [{ 'name' => 'test' }],
171
+ '`reset_interval` is required'
172
+ ],
173
+ minus_interval: [
174
+ [{ 'name' => 'test', 'reset_interval' => -1 }],
175
+ '`reset_interval` should be a positive number'
176
+ ],
177
+ invalid_type: [
178
+ [{ 'name' => 'test', 'reset_interval' => 1, 'type' => 'invalid_type' }],
179
+ '`type` should be integer, float, or numeric'
180
+ ]
181
+ )
182
+ test 'return an error object: invalid_params' do |(params, msg)|
183
+ result = @server.send('init', params, @scope, {})
184
+ assert_empty result['data']
185
+ error = result['errors'].first
186
+ assert_equal 'invalid_params', error['code']
187
+ assert_equal msg, error['message']
188
+ end
189
+
190
+ test 'return error objects when passed key already exists' do
191
+ @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
192
+
193
+ # call `init` to same key twice
194
+ result = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
195
+ assert_empty result['data']
196
+ error = result['errors'].first
197
+
198
+ expected = { 'code' => 'invalid_params', 'message' => "#{@key} already exists in counter" }
199
+ assert_equal expected, error
200
+ end
201
+
202
+ test 'return existing value when passed key already exists and ignore option is true' do
203
+ v1 = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
204
+
205
+ # call `init` to same key twice
206
+ v2 = @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, { 'ignore' => true })
207
+ assert_nil v2['errors']
208
+ assert_equal v1['data'], v2['data']
209
+ end
210
+
211
+ test 'call `synchronize_keys` when random option is true' do
212
+ mhash = @server.instance_variable_get(:@mutex_hash)
213
+ mock(mhash).synchronize(@key).once
214
+ @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, {})
215
+
216
+ mhash = @server.instance_variable_get(:@mutex_hash)
217
+ mock(mhash).synchronize_keys(@key).once
218
+ @server.send('init', [{ 'name' => @name, 'reset_interval' => 1 }], @scope, { 'random' => true })
219
+ end
220
+ end
221
+
222
+ sub_test_case 'delete' do
223
+ setup do
224
+ @name = 'key1'
225
+ @key = Fluent::Counter::Store.gen_key(@scope, @name)
226
+ @server.send('init', [{ 'name' => @name, 'reset_interval' => 20 }], @scope, {})
227
+ end
228
+
229
+ test 'delete a value from a counter' do
230
+ assert extract_value_from_counter(@server, @scope, @name)
231
+
232
+ result = @server.send('delete', [@name], @scope, {})
233
+ assert_nil result['errors']
234
+
235
+ counter = result['data'].first
236
+ assert_equal 0, counter['current']
237
+ assert_equal 0, counter['total']
238
+ assert_equal 'numeric', counter['type']
239
+ assert_equal @name, counter['name']
240
+ assert_equal @now, counter['last_reset_at']
241
+
242
+ assert_nil extract_value_from_counter(@server, @scope, @name)
243
+ end
244
+
245
+ data(
246
+ empty: [[], 'One or more `params` are required'],
247
+ empty_key: [[''], '`key` is the invalid format'],
248
+ invalid_key: [['_key'], '`key` is the invalid format'],
249
+ )
250
+ test 'return an error object: invalid_params' do |(params, msg)|
251
+ result = @server.send('delete', params, @scope, {})
252
+
253
+ assert_empty result['data']
254
+ error = result['errors'].first
255
+ assert_equal 'invalid_params', error['code']
256
+ assert_equal msg, error['message']
257
+ end
258
+
259
+ test 'return an error object: unknown_key' do
260
+ unknown_key = 'unknown_key'
261
+ result = @server.send('delete', [unknown_key], @scope, {})
262
+
263
+ assert_empty result['data']
264
+ error = result['errors'].first
265
+ assert_equal unknown_key, error['code']
266
+ assert_equal "`#{@scope}\t#{unknown_key}` doesn't exist in counter", error['message']
267
+ end
268
+
269
+ test 'call `synchronize_keys` when random option is true' do
270
+ mhash = @server.instance_variable_get(:@mutex_hash)
271
+ mock(mhash).synchronize(@key).once
272
+ @server.send('delete', [@name], @scope, {})
273
+
274
+ mhash = @server.instance_variable_get(:@mutex_hash)
275
+ mock(mhash).synchronize_keys(@key).once
276
+ @server.send('delete', [@name], @scope, { 'random' => true })
277
+ end
278
+ end
279
+
280
+ sub_test_case 'inc' do
281
+ setup do
282
+ @name1 = 'key1'
283
+ @name2 = 'key2'
284
+ @key1 = Fluent::Counter::Store.gen_key(@scope, @name1)
285
+ inc_objects = [
286
+ { 'name' => @name1, 'reset_interval' => 20 },
287
+ { 'name' => @name2, 'type' => 'integer', 'reset_interval' => 20 }
288
+ ]
289
+ @server.send('init', inc_objects, @scope, {})
290
+ end
291
+
292
+ test 'increment or decrement a value in counter' do
293
+ result = @server.send('inc', [{ 'name' => @name1, 'value' => 10 }], @scope, {})
294
+ assert_nil result['errors']
295
+
296
+ counter = result['data'].first
297
+ assert_equal 10, counter['current']
298
+ assert_equal 10, counter['total']
299
+ assert_equal 'numeric', counter['type']
300
+ assert_equal @name1, counter['name']
301
+ assert_equal @now, counter['last_reset_at']
302
+
303
+ c = extract_value_from_counter(@server, @scope, @name1)
304
+ assert_equal 10, c['current']
305
+ assert_equal 10, c['total']
306
+ assert_equal @now, Fluent::EventTime.new(*c['last_reset_at'])
307
+ assert_equal @now, Fluent::EventTime.new(*c['last_modified_at'])
308
+ end
309
+
310
+ test 'create new value and increment/decrement its value when `force` option is true' do
311
+ new_name = 'new_key'
312
+ assert_nil extract_value_from_counter(@server, @scope, new_name)
313
+
314
+ v1 = @server.send('inc', [{ 'name' => new_name, 'value' => 10 }], @scope, {})
315
+ assert_empty v1['data']
316
+ error = v1['errors'].first
317
+ assert_equal 'unknown_key', error['code']
318
+
319
+ assert_nil extract_value_from_counter(@server, @scope, new_name)
320
+
321
+ v2 = @server.send(
322
+ 'inc',
323
+ [{ 'name' => new_name, 'value' => 10, 'reset_interval' => 20 }],
324
+ @scope,
325
+ { 'force' => true }
326
+ )
327
+
328
+ assert_nil v2['errors']
329
+
330
+ counter = v2['data'].first
331
+ assert_equal 10, counter['current']
332
+ assert_equal 10, counter['total']
333
+ assert_equal 'numeric', counter['type']
334
+ assert_equal new_name, counter['name']
335
+ assert_equal @now, counter['last_reset_at']
336
+
337
+ assert extract_value_from_counter(@server, @scope, new_name)
338
+ end
339
+
340
+ data(
341
+ empty: [[], 'One or more `params` are required', {}],
342
+ missing_name: [
343
+ [{ 'value' => 10 }],
344
+ '`name` is required', {}
345
+ ],
346
+ missing_value: [
347
+ [{ 'name' => 'key1' }],
348
+ '`value` is required', {}
349
+ ],
350
+ invalid_type: [
351
+ [{ 'name' => 'key2', 'value' => 10.0 }],
352
+ '`type` is integer. You should pass integer value as a `value`', {}
353
+ ],
354
+ missing_reset_interval: [
355
+ [{ 'name' => 'key1', 'value' => 1 }],
356
+ '`reset_interval` is required',
357
+ { 'force' => true }
358
+ ]
359
+ )
360
+ test 'return an error object: invalid_params' do |(params, msg, opt)|
361
+ result = @server.send('inc', params, @scope, opt)
362
+ assert_empty result['data']
363
+
364
+ error = result['errors'].first
365
+ assert_equal 'invalid_params', error['code']
366
+ assert_equal msg, error['message']
367
+ end
368
+
369
+ test 'call `synchronize_keys` when random option is true' do
370
+ mhash = @server.instance_variable_get(:@mutex_hash)
371
+ mock(mhash).synchronize(@key1).once
372
+ params = [{ 'name' => @name1, 'value' => 1 }]
373
+ @server.send('inc', params, @scope, {})
374
+
375
+ mhash = @server.instance_variable_get(:@mutex_hash)
376
+ mock(mhash).synchronize_keys(@key1).once
377
+ @server.send('inc', params, @scope, { 'random' => true })
378
+ end
379
+ end
380
+
381
+ sub_test_case 'reset' do
382
+ setup do
383
+ @name = 'key'
384
+ @travel_sec = 10
385
+ @server.send('init', [{ 'name' => @name, 'reset_interval' => 10 }], @scope, {})
386
+ @server.send('inc', [{ 'name' => @name, 'value' => 10 }], @scope, {})
387
+ end
388
+
389
+ test 'reset a value in the counter' do
390
+ Timecop.travel(@travel_sec)
391
+
392
+ result = @server.send('reset', [@name], @scope, {})
393
+ assert_nil result['errors']
394
+
395
+ data = result['data'].first
396
+ assert_true data['success']
397
+ assert_equal @travel_sec, data['elapsed_time']
398
+
399
+ counter = data['counter_data']
400
+ assert_equal 10, counter['current']
401
+ assert_equal 10, counter['total']
402
+ assert_equal 'numeric', counter['type']
403
+ assert_equal @name, counter['name']
404
+ assert_equal @now, counter['last_reset_at']
405
+
406
+ v = extract_value_from_counter(@server, @scope, @name)
407
+ assert_equal 0, v['current']
408
+ assert_equal 10, v['total']
409
+ assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v['last_reset_at'])
410
+ assert_equal (@now + @travel_sec), Fluent::EventTime.new(*v['last_modified_at'])
411
+ end
412
+
413
+ test 'reset a value after `reset_interval` passed' do
414
+ first_travel_sec = 5
415
+ Timecop.travel(first_travel_sec) # jump time less than reset_interval
416
+ result = @server.send('reset', [@name], @scope, {})
417
+ v = result['data'].first
418
+
419
+ assert_equal false, v['success']
420
+ assert_equal first_travel_sec, v['elapsed_time']
421
+
422
+ store = extract_value_from_counter(@server, @scope, @name)
423
+ assert_equal 10, store['current']
424
+ assert_equal @now, Fluent::EventTime.new(*store['last_reset_at'])
425
+
426
+ # time is passed greater than reset_interval
427
+ Timecop.travel(@travel_sec)
428
+ result = @server.send('reset', [@name], @scope, {})
429
+ v = result['data'].first
430
+
431
+ assert_true v['success']
432
+ assert_equal @travel_sec + first_travel_sec, v['elapsed_time']
433
+
434
+ v1 = extract_value_from_counter(@server, @scope, @name)
435
+ assert_equal 0, v1['current']
436
+ assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_reset_at'])
437
+ assert_equal (@now + @travel_sec + first_travel_sec), Fluent::EventTime.new(*v1['last_modified_at'])
438
+ end
439
+
440
+ data(
441
+ empty: [[], 'One or more `params` are required'],
442
+ empty_key: [[''], '`key` is the invalid format'],
443
+ invalid_key: [['_key'], '`key` is the invalid format'],
444
+ )
445
+ test 'return an error object: invalid_params' do |(params, msg)|
446
+ result = @server.send('reset', params, @scope, {})
447
+ assert_empty result['data']
448
+ assert_equal 'invalid_params', result['errors'].first['code']
449
+ assert_equal msg, result['errors'].first['message']
450
+ end
451
+
452
+ test 'return an error object: unknown_key' do
453
+ unknown_key = 'unknown_key'
454
+ result = @server.send('reset', [unknown_key], @scope, {})
455
+
456
+ assert_empty result['data']
457
+ error = result['errors'].first
458
+ assert_equal unknown_key, error['code']
459
+ assert_equal "`#{@scope}\t#{unknown_key}` doesn't exist in counter", error['message']
460
+ end
461
+ end
462
+
463
+ sub_test_case 'get' do
464
+ setup do
465
+ @name1 = 'key1'
466
+ @name2 = 'key2'
467
+ init_objects = [
468
+ { 'name' => @name1, 'reset_interval' => 0 },
469
+ { 'name' => @name2, 'reset_interval' => 0 },
470
+ ]
471
+ @server.send('init', init_objects, @scope, {})
472
+ end
473
+
474
+ test 'get a counter value' do
475
+ key = @name1
476
+ result = @server.send('get', [key], @scope, {})
477
+ assert_nil result['errors']
478
+
479
+ counter = result['data'].first
480
+ assert_equal 0, counter['current']
481
+ assert_equal 0, counter['total']
482
+ assert_equal 'numeric', counter['type']
483
+ assert_equal key, counter['name']
484
+ end
485
+
486
+ test 'get counter values' do
487
+ result = @server.send('get', [@name1, @name2], @scope, {})
488
+ assert_nil result['errors']
489
+
490
+ counter1 = result['data'][0]
491
+ assert_equal 0, counter1['current']
492
+ assert_equal 0, counter1['total']
493
+ assert_equal 'numeric', counter1['type']
494
+ assert_equal @name1, counter1['name']
495
+
496
+ counter2 = result['data'][1]
497
+ assert_equal 0, counter2['current']
498
+ assert_equal 0, counter2['total']
499
+ assert_equal 'numeric', counter2['type']
500
+ assert_equal @name2, counter2['name']
501
+ end
502
+
503
+ data(
504
+ empty: [[], 'One or more `params` are required'],
505
+ empty_key: [[''], '`key` is the invalid format'],
506
+ invalid_key: [['_key'], '`key` is the invalid format'],
507
+ )
508
+ test 'return an error object: invalid_params' do |(params, msg)|
509
+ result = @server.send('get', params, @scope, {})
510
+ assert_empty result['data']
511
+ assert_equal 'invalid_params', result['errors'].first['code']
512
+ assert_equal msg, result['errors'].first['message']
513
+ end
514
+
515
+ test 'return an error object: unknown_key' do
516
+ unknown_key = 'unknown_key'
517
+ result = @server.send('get', [unknown_key], @scope, {})
518
+
519
+ assert_empty result['data']
520
+ error = result['errors'].first
521
+ assert_equal unknown_key, error['code']
522
+ assert_equal "`#{@scope}\t#{unknown_key}` doesn't exist in counter", error['message']
523
+ end
524
+ end
525
+ end
526
+
527
+ class CounterCounterResponseTest < ::Test::Unit::TestCase
528
+ setup do
529
+ @response = Fluent::Counter::Server::Response.new
530
+ @errors = [
531
+ StandardError.new('standard error'),
532
+ Fluent::Counter::InternalServerError.new('internal server error')
533
+ ]
534
+ @now = Fluent::EventTime.now
535
+ value = {
536
+ 'name' => 'name',
537
+ 'total' => 100,
538
+ 'current' => 11,
539
+ 'type' => 'numeric',
540
+ 'reset_interval' => 10,
541
+ 'last_reset_at' => @now,
542
+ }
543
+ @values = [value, 'test']
544
+ end
545
+
546
+ test 'push_error' do
547
+ @errors.each do |e|
548
+ @response.push_error(e)
549
+ end
550
+
551
+ v = @response.instance_variable_get(:@errors)
552
+ assert_equal @errors, v
553
+ end
554
+
555
+ test 'push_data' do
556
+ @values.each do |v|
557
+ @response.push_data v
558
+ end
559
+
560
+ data = @response.instance_variable_get(:@data)
561
+ assert_equal @values, data
562
+ end
563
+
564
+ test 'to_hash' do
565
+ @errors.each do |e|
566
+ @response.push_error(e)
567
+ end
568
+
569
+ @values.each do |v|
570
+ @response.push_data v
571
+ end
572
+
573
+ expected_errors = [
574
+ { 'code' => 'internal_server_error', 'message' => 'standard error' },
575
+ { 'code' => 'internal_server_error', 'message' => 'internal server error' }
576
+ ]
577
+ expected_data = @values
578
+
579
+ hash = @response.to_hash
580
+ assert_equal expected_errors, hash['errors']
581
+ assert_equal expected_data, hash['data']
582
+ end
583
+ end