fluent-plugin-gcloud-pubsub-universe-domain 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,424 @@
1
+ require 'net/http'
2
+ require 'json'
3
+
4
+ require_relative "../helper"
5
+ require 'fluent/plugin/compressable'
6
+ require "fluent/test/driver/input"
7
+
8
+ class GcloudPubSubInputTest < Test::Unit::TestCase
9
+ CONFIG = %[
10
+ tag test
11
+ project project-test
12
+ topic topic-test
13
+ subscription subscription-test
14
+ key key-test
15
+ ]
16
+
17
+ DEFAULT_HOST = '127.0.0.1'
18
+ DEFAULT_PORT = 24680
19
+
20
+ class DummyInvalidMsgData
21
+ def data
22
+ 'foo:bar'
23
+ end
24
+ end
25
+ class DummyInvalidMessage
26
+ def message
27
+ DummyInvalidMsgData.new
28
+ end
29
+ def attributes
30
+ {"attr_1" => "a", "attr_2" => "b"}
31
+ end
32
+ def ack_id
33
+ "ack-id"
34
+ end
35
+ end
36
+
37
+ def create_driver(conf=CONFIG)
38
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::GcloudPubSubInput).configure(conf)
39
+ end
40
+
41
+ def http_get(path)
42
+ http = Net::HTTP.new(DEFAULT_HOST, DEFAULT_PORT)
43
+ req = Net::HTTP::Get.new(path, {'Content-Type' => 'application/x-www-form-urlencoded'})
44
+ http.request(req)
45
+ end
46
+
47
+ setup do
48
+ Fluent::Test.setup
49
+ end
50
+
51
+ sub_test_case 'configure' do
52
+ test 'all params are configured' do
53
+ d = create_driver(%[
54
+ tag test
55
+ project project-test
56
+ topic topic-test
57
+ subscription subscription-test
58
+ key key-test
59
+ max_messages 1000
60
+ return_immediately true
61
+ pull_interval 2
62
+ pull_threads 3
63
+ attribute_keys attr-test
64
+ enable_rpc true
65
+ rpc_bind 127.0.0.1
66
+ rpc_port 24681
67
+ universe_domain universe-domain-test
68
+ ])
69
+
70
+ assert_equal('test', d.instance.tag)
71
+ assert_equal('project-test', d.instance.project)
72
+ assert_equal('topic-test', d.instance.topic)
73
+ assert_equal('subscription-test', d.instance.subscription)
74
+ assert_equal('key-test', d.instance.key)
75
+ assert_equal(2.0, d.instance.pull_interval)
76
+ assert_equal(1000, d.instance.max_messages)
77
+ assert_equal(true, d.instance.return_immediately)
78
+ assert_equal(3, d.instance.pull_threads)
79
+ assert_equal(['attr-test'], d.instance.attribute_keys)
80
+ assert_equal(true, d.instance.enable_rpc)
81
+ assert_equal('127.0.0.1', d.instance.rpc_bind)
82
+ assert_equal(24681, d.instance.rpc_port)
83
+ assert_equal('universe-domain-test', d.instance.universe_domain)
84
+ end
85
+
86
+ test 'default values are configured' do
87
+ d = create_driver
88
+ assert_equal(5.0, d.instance.pull_interval)
89
+ assert_equal(100, d.instance.max_messages)
90
+ assert_equal(true, d.instance.return_immediately)
91
+ assert_equal(1, d.instance.pull_threads)
92
+ assert_equal([], d.instance.attribute_keys)
93
+ assert_equal(false, d.instance.enable_rpc)
94
+ assert_equal('0.0.0.0', d.instance.rpc_bind)
95
+ assert_equal(24680, d.instance.rpc_port)
96
+ assert_equal(nil, d.instance.universe_domain)
97
+ end
98
+ end
99
+
100
+ sub_test_case 'start' do
101
+ setup do
102
+ @pubsub_mock = mock!
103
+ stub(Google::Cloud::Pubsub).new { @pubsub_mock }
104
+ end
105
+
106
+ test '40x error occurred on connecting to Pub/Sub' do
107
+ @pubsub_mock.subscriber('subscription-test').once do
108
+ raise Google::Cloud::NotFoundError.new('TEST')
109
+ end
110
+
111
+ d = create_driver
112
+ assert_raise Google::Cloud::NotFoundError do
113
+ d.run {}
114
+ end
115
+ end
116
+
117
+ test '50x error occurred on connecting to Pub/Sub' do
118
+ @pubsub_mock.subscriber('subscription-test').once do
119
+ raise Google::Cloud::UnavailableError.new('TEST')
120
+ end
121
+
122
+ d = create_driver
123
+ assert_raise Google::Cloud::UnavailableError do
124
+ d.run {}
125
+ end
126
+ end
127
+ end
128
+
129
+ sub_test_case 'emit' do
130
+ class DummyMsgData
131
+ def data
132
+ '{"foo": "bar"}'
133
+ end
134
+ end
135
+ class DummyMessage
136
+ def message
137
+ DummyMsgData.new
138
+ end
139
+ def attributes
140
+ {"attr_1" => "a", "attr_2" => "b"}
141
+ end
142
+ def ack_id
143
+ "ack-id"
144
+ end
145
+ end
146
+
147
+ class DummyMsgDataWithTagKey
148
+ def initialize(tag)
149
+ @tag = tag
150
+ end
151
+ def data
152
+ '{"foo": "bar", "test_tag_key": "' + @tag + '"}'
153
+ end
154
+ end
155
+ class DummyMessageWithTagKey
156
+ def initialize(tag)
157
+ @tag = tag
158
+ end
159
+ def message
160
+ DummyMsgDataWithTagKey.new @tag
161
+ end
162
+ def attributes
163
+ {"attr_1" => "a", "attr_2" => "b"}
164
+ end
165
+ def ack_id
166
+ "ack-id"
167
+ end
168
+ end
169
+
170
+ class DummyMsgDataGzipped
171
+ include Fluent::Plugin::Compressable
172
+
173
+ def data
174
+ compress '{"foo": "bar"}'
175
+ end
176
+ end
177
+ class DummyMessageGzipped
178
+ def message
179
+ DummyMsgDataGzipped.new
180
+ end
181
+ def attributes
182
+ {"attr_1" => "a", "attr_2" => "b"}
183
+ end
184
+ def ack_id
185
+ "ack-id"
186
+ end
187
+ end
188
+
189
+ setup do
190
+ @subscriber = mock!
191
+ @pubsub_mock = mock!.subscriber('subscription-test') { @subscriber }
192
+ stub(Google::Cloud::Pubsub).new { @pubsub_mock }
193
+ end
194
+
195
+ test 'empty' do
196
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { [] }
197
+ @subscriber.acknowledge.times(0)
198
+
199
+ d = create_driver
200
+ d.run(expect_emits: 1, timeout: 3)
201
+
202
+ assert_true d.events.empty?
203
+ end
204
+
205
+ test 'simple' do
206
+ messages = Array.new(1, DummyMessage.new)
207
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
208
+ @subscriber.acknowledge(messages).at_least(1)
209
+
210
+ d = create_driver
211
+ d.run(expect_emits: 1, timeout: 3)
212
+ emits = d.events
213
+
214
+ assert(1 <= emits.length)
215
+ emits.each do |tag, time, record|
216
+ assert_equal("test", tag)
217
+ assert_equal({"foo" => "bar"}, record)
218
+ end
219
+ end
220
+
221
+ test 'multithread' do
222
+ messages = Array.new(1, DummyMessage.new)
223
+ @subscriber.pull(immediate: true, max: 100).at_least(2) { messages }
224
+ @subscriber.acknowledge(messages).at_least(2)
225
+
226
+ d = create_driver("#{CONFIG}\npull_threads 2")
227
+ d.run(expect_emits: 2, timeout: 1)
228
+ emits = d.events
229
+
230
+ assert(2 <= emits.length)
231
+ emits.each do |tag, time, record|
232
+ assert_equal("test", tag)
233
+ assert_equal({"foo" => "bar"}, record)
234
+ end
235
+ end
236
+
237
+ test 'with tag_key' do
238
+ messages = [
239
+ DummyMessageWithTagKey.new('tag1'),
240
+ DummyMessageWithTagKey.new('tag2'),
241
+ DummyMessage.new
242
+ ]
243
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
244
+ @subscriber.acknowledge(messages).at_least(1)
245
+
246
+ d = create_driver("#{CONFIG}\ntag_key test_tag_key")
247
+ d.run(expect_emits: 1, timeout: 3)
248
+ emits = d.events
249
+
250
+ assert(3 <= emits.length)
251
+ # test tag
252
+ assert_equal("tag1", emits[0][0])
253
+ assert_equal("tag2", emits[1][0])
254
+ assert_equal("test", emits[2][0])
255
+ # test record
256
+ emits.each do |tag, time, record|
257
+ assert_equal({"foo" => "bar"}, record)
258
+ end
259
+ end
260
+
261
+ test 'invalid messages with parse_error_action exception ' do
262
+ messages = Array.new(1, DummyInvalidMessage.new)
263
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
264
+ @subscriber.acknowledge.times(0)
265
+
266
+ d = create_driver
267
+ d.run(expect_emits: 1, timeout: 3)
268
+ assert_true d.events.empty?
269
+ end
270
+
271
+ test 'with attributes' do
272
+ messages = Array.new(1, DummyMessage.new)
273
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
274
+ @subscriber.acknowledge(messages).at_least(1)
275
+
276
+ d = create_driver("#{CONFIG}\nattribute_keys attr_1")
277
+ d.run(expect_emits: 1, timeout: 3)
278
+ emits = d.events
279
+
280
+ assert(1 <= emits.length)
281
+ emits.each do |tag, time, record|
282
+ assert_equal("test", tag)
283
+ assert_equal({"foo" => "bar", "attr_1" => "a"}, record)
284
+ end
285
+ end
286
+
287
+ test 'with gzip decompression' do
288
+ messages = Array.new(1, DummyMessageGzipped.new)
289
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
290
+ @subscriber.acknowledge(messages).at_least(1)
291
+
292
+ d = create_driver("#{CONFIG}\ndecompression gzip")
293
+ d.run(expect_emits: 1, timeout: 3)
294
+ emits = d.events
295
+
296
+ assert_equal(1, emits.length)
297
+ emits.each do |tag, time, record|
298
+ assert_equal("test", tag)
299
+ assert_equal({"foo" => "bar"}, record)
300
+ end
301
+ end
302
+
303
+ test 'invalid messages with parse_error_action warning' do
304
+ messages = Array.new(1, DummyInvalidMessage.new)
305
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
306
+ @subscriber.acknowledge(messages).at_least(1)
307
+
308
+ d = create_driver("#{CONFIG}\nparse_error_action warning")
309
+ d.run(expect_emits: 1, timeout: 3)
310
+ assert_true d.events.empty?
311
+ end
312
+
313
+ test 'retry if raised error' do
314
+ class UnknownError < StandardError
315
+ end
316
+ @subscriber.pull(immediate: true, max: 100).at_least(2) { raise UnknownError.new('test') }
317
+ @subscriber.acknowledge.times(0)
318
+
319
+ d = create_driver(CONFIG + 'pull_interval 0.5')
320
+ d.run(expect_emits: 1, timeout: 0.8)
321
+
322
+ assert_equal(0.5, d.instance.pull_interval)
323
+ assert_true d.events.empty?
324
+ end
325
+
326
+ test 'retry if raised RetryableError on pull' do
327
+ @subscriber.pull(immediate: true, max: 100).at_least(2) { raise Google::Cloud::UnavailableError.new('TEST') }
328
+ @subscriber.acknowledge.times(0)
329
+
330
+ d = create_driver("#{CONFIG}\npull_interval 0.5")
331
+ d.run(expect_emits: 1, timeout: 0.8)
332
+
333
+ assert_equal(0.5, d.instance.pull_interval)
334
+ assert_true d.events.empty?
335
+ end
336
+
337
+ test 'retry if raised RetryableError on acknowledge' do
338
+ messages = Array.new(1, DummyMessage.new)
339
+ @subscriber.pull(immediate: true, max: 100).at_least(2) { messages }
340
+ @subscriber.acknowledge(messages).at_least(2) { raise Google::Cloud::UnavailableError.new('TEST') }
341
+
342
+ d = create_driver("#{CONFIG}\npull_interval 0.5")
343
+ d.run(expect_emits: 2, timeout: 3)
344
+ emits = d.events
345
+
346
+ # not acknowledged, but already emitted to engine.
347
+ assert(2 <= emits.length)
348
+ emits.each do |tag, time, record|
349
+ assert_equal("test", tag)
350
+ assert_equal({"foo" => "bar"}, record)
351
+ end
352
+ end
353
+
354
+ test 'stop by http rpc' do
355
+ messages = Array.new(1, DummyMessage.new)
356
+ @subscriber.pull(immediate: true, max: 100).once { messages }
357
+ @subscriber.acknowledge(messages).once
358
+
359
+ d = create_driver("#{CONFIG}\npull_interval 1.0\nenable_rpc true")
360
+ assert_equal(false, d.instance.instance_variable_get(:@stop_pull))
361
+
362
+ d.run {
363
+ http_get('/api/in_gcloud_pubsub/pull/stop')
364
+ sleep 0.75
365
+ # d.run sleeps 0.5 sec
366
+ }
367
+ emits = d.events
368
+
369
+ assert_equal(1, emits.length)
370
+ assert_true d.instance.instance_variable_get(:@stop_pull)
371
+
372
+ emits.each do |tag, time, record|
373
+ assert_equal("test", tag)
374
+ assert_equal({"foo" => "bar"}, record)
375
+ end
376
+ end
377
+
378
+ test 'start by http rpc' do
379
+ messages = Array.new(1, DummyMessage.new)
380
+ @subscriber.pull(immediate: true, max: 100).at_least(1) { messages }
381
+ @subscriber.acknowledge(messages).at_least(1)
382
+
383
+ d = create_driver("#{CONFIG}\npull_interval 1.0\nenable_rpc true")
384
+ d.instance.stop_pull
385
+ assert_equal(true, d.instance.instance_variable_get(:@stop_pull))
386
+
387
+ d.run(expect_emits: 1, timeout: 3) {
388
+ http_get('/api/in_gcloud_pubsub/pull/start')
389
+ sleep 0.75
390
+ # d.run sleeps 0.5 sec
391
+ }
392
+ emits = d.events
393
+
394
+ assert_equal(true, emits.length > 0)
395
+ assert_false d.instance.instance_variable_get(:@stop_pull)
396
+
397
+ emits.each do |tag, time, record|
398
+ assert_equal("test", tag)
399
+ assert_equal({"foo" => "bar"}, record)
400
+ end
401
+ end
402
+
403
+ test 'get status by http rpc when started' do
404
+ d = create_driver("#{CONFIG}\npull_interval 1.0\nenable_rpc true")
405
+ assert_false d.instance.instance_variable_get(:@stop_pull)
406
+
407
+ d.run {
408
+ res = http_get('/api/in_gcloud_pubsub/pull/status')
409
+ assert_equal({"ok" => true, "status" => "started"}, JSON.parse(res.body))
410
+ }
411
+ end
412
+
413
+ test 'get status by http rpc when stopped' do
414
+ d = create_driver("#{CONFIG}\npull_interval 1.0\nenable_rpc true")
415
+ d.instance.stop_pull
416
+ assert_true d.instance.instance_variable_get(:@stop_pull)
417
+
418
+ d.run {
419
+ res = http_get('/api/in_gcloud_pubsub/pull/status')
420
+ assert_equal({"ok" => true, "status" => "stopped"}, JSON.parse(res.body))
421
+ }
422
+ end
423
+ end
424
+ end