tengine_event 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,419 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
3
+
4
+ require 'amqp'
5
+ require 'time'
6
+
7
+ describe "Tengine::Event" do
8
+ expected_source_name = "this_server1/12345"
9
+
10
+ before do
11
+ Tengine::Event.stub!(:host_name).and_return("this_server1")
12
+ Process.stub!(:pid).and_return(12345)
13
+ end
14
+
15
+ # hack!
16
+ after :each do
17
+ EM.instance_eval do
18
+ @next_tick_mutex.synchronize do
19
+ @next_tick_queue = nil
20
+ end
21
+ end
22
+ end
23
+
24
+ describe :new_object do
25
+ context "without config" do
26
+ before do
27
+ Tengine::Event.config = {}
28
+ @now = Time.now
29
+ Time.stub!(:now).and_return(@now)
30
+ end
31
+
32
+ it{ Tengine::Event.default_source_name.should == expected_source_name }
33
+ it{ Tengine::Event.default_sender_name.should == expected_source_name }
34
+ it{ Tengine::Event.default_level.should == 2 }
35
+
36
+ subject{ Tengine::Event.new }
37
+ it{ subject.should be_a(Tengine::Event) }
38
+ its(:key){ should =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ }
39
+ its(:event_type_name){ should be_nil }
40
+ its(:source_name){ should == expected_source_name}
41
+ its(:occurred_at){ should == @now.utc }
42
+ its(:level){ should == 2}
43
+ its(:level_key){ should == :info}
44
+ its(:sender_name){ should == expected_source_name }
45
+ its(:properties){ should be_a(Hash) }
46
+ its(:properties){ should be_empty }
47
+ it {
48
+ attrs = subject.attributes
49
+ attrs.should be_a(Hash)
50
+ attrs.delete(:key).should_not be_nil
51
+ attrs.should == {
52
+ :occurred_at => @now.utc,
53
+ :level=>2,
54
+ :source_name => expected_source_name,
55
+ :sender_name => expected_source_name,
56
+ }
57
+ }
58
+ it {
59
+ hash = JSON.parse(subject.to_json)
60
+ hash.should be_a(Hash)
61
+ hash.delete('key').should =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
62
+ hash.should == {
63
+ 'occurred_at' => @now.utc.iso8601,
64
+ "level" => 2,
65
+ 'source_name' => expected_source_name,
66
+ 'sender_name' => expected_source_name,
67
+ }
68
+ }
69
+ end
70
+
71
+ context "with config" do
72
+ before do
73
+ Tengine::Event.config = {
74
+ :default_source_name => "event_source1",
75
+ :default_sender_name => "sender1",
76
+ :default_level_key => :warn
77
+ }
78
+ @now = Time.now
79
+ Time.stub!(:now).and_return(@now)
80
+ end
81
+ after do
82
+ Tengine::Event.config = {}
83
+ end
84
+
85
+ it{ Tengine::Event.default_source_name.should == "event_source1" }
86
+ it{ Tengine::Event.default_sender_name.should == "sender1" }
87
+ it{ Tengine::Event.default_level.should == 3 }
88
+
89
+ subject{ Tengine::Event.new }
90
+ it{ subject.should be_a(Tengine::Event) }
91
+ its(:key){ should =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/ }
92
+ its(:event_type_name){ should be_nil }
93
+ its(:source_name){ should == "event_source1"}
94
+ its(:occurred_at){ should == @now.utc }
95
+ its(:level){ should == 3}
96
+ its(:level_key){ should == :warn}
97
+ its(:sender_name){ should == "sender1" }
98
+ its(:properties){ should be_a(Hash) }
99
+ its(:properties){ should be_empty }
100
+ it {
101
+ attrs = subject.attributes
102
+ attrs.should be_a(Hash)
103
+ attrs.delete(:key).should_not be_nil
104
+ attrs.should == {
105
+ :occurred_at => @now.utc,
106
+ :level=>3,
107
+ :source_name => "event_source1",
108
+ :sender_name => "sender1",
109
+ }
110
+ }
111
+ it {
112
+ hash = JSON.parse(subject.to_json)
113
+ hash.should be_a(Hash)
114
+ hash.delete('key').should =~ /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/
115
+ hash.should == {
116
+ 'occurred_at' => @now.utc.iso8601,
117
+ 'level'=>3,
118
+ 'source_name' => "event_source1",
119
+ 'sender_name' => "sender1",
120
+ }
121
+ }
122
+ end
123
+
124
+ end
125
+
126
+ describe :new_object_with_attrs do
127
+ subject{ Tengine::Event.new(
128
+ :event_type_name => :foo,
129
+ :key => "hoge",
130
+ 'source_name' => "server1",
131
+ :occurred_at => Time.utc(2011,8,11,12,0),
132
+ :level_key => 'error',
133
+ 'sender_name' => "server2",
134
+ :properties => {:bar => "ABC", :baz => 999}
135
+ )}
136
+ it{ subject.should be_a(Tengine::Event) }
137
+ its(:key){ should == "hoge" }
138
+ its(:event_type_name){ should == "foo" }
139
+ its(:source_name){ should == "server1" }
140
+ its(:occurred_at){ should == Time.utc(2011,8,11,12,0) }
141
+ its(:level){ should == 4}
142
+ its(:level_key){ should == :error}
143
+ its(:sender_name){ should == "server2" }
144
+ its(:properties){ should == {'bar' => "ABC", 'baz' => 999}}
145
+ it {
146
+ attrs = subject.attributes
147
+ attrs.should == {
148
+ :event_type_name => 'foo',
149
+ :key => "hoge",
150
+ :source_name => "server1",
151
+ :occurred_at => Time.utc(2011,8,11,12,0),
152
+ :level => 4,
153
+ :sender_name => "server2",
154
+ :properties => {'bar' => "ABC", 'baz' => 999}
155
+ }
156
+ }
157
+ it {
158
+ hash = JSON.parse(subject.to_json)
159
+ hash.should be_a(Hash)
160
+ hash.should == {
161
+ "level"=>4,
162
+ 'event_type_name' => 'foo',
163
+ 'key' => "hoge",
164
+ 'source_name' => "server1",
165
+ 'occurred_at' => "2011-08-11T12:00:00Z", # Timeオブジェクトは文字列に変換されます
166
+ 'sender_name' => "server2",
167
+ 'properties' => {'bar' => "ABC", 'baz' => 999}
168
+ }
169
+ }
170
+ end
171
+
172
+ describe 'occurred_at with local_time should convert to UTC' do
173
+ subject{ Tengine::Event.new(
174
+ :occurred_at => Time.parse("2011-08-31 12:00:00 +0900"),
175
+ :key => 'hoge'
176
+ )}
177
+ it{ subject.should be_a(Tengine::Event) }
178
+ its(:occurred_at){ should == Time.utc(2011,8,31,3,0) }
179
+ its(:occurred_at){ should be_utc }
180
+ it {
181
+ hash = JSON.parse(subject.to_json)
182
+ hash.should be_a(Hash)
183
+ hash.should == {
184
+ "level"=>2,
185
+ 'key' => "hoge",
186
+ 'occurred_at' => "2011-08-31T03:00:00Z", # Timeオブジェクトは文字列に変換されます
187
+ 'source_name' => "this_server1/12345",
188
+ 'sender_name' => "this_server1/12345",
189
+ }
190
+ }
191
+ end
192
+
193
+ it 'attrs_for_new must be Hash' do
194
+ expect{
195
+ Tengine::Event.new("{foo: 1, bar: 2}")
196
+ }.to raise_error(ArgumentError, /attrs must be a Hash but was/)
197
+ end
198
+
199
+
200
+ describe :occurred_at do
201
+ context "valid nil" do
202
+ subject{ Tengine::Event.new(:occurred_at => nil) }
203
+ it do
204
+ subject.occurred_at = nil
205
+ subject.occurred_at.should == nil
206
+ end
207
+ end
208
+ context "valid String" do
209
+ subject{ Tengine::Event.new(:occurred_at => "2011-08-31 12:00:00 +0900") }
210
+ its(:occurred_at){ should be_a(Time); should == Time.utc(2011, 8, 31, 3)}
211
+ end
212
+ it "invalid Class: Range" do
213
+ expect{
214
+ Tengine::Event.new(:occurred_at => (1..3))
215
+ }.to raise_error(ArgumentError)
216
+ end
217
+ end
218
+ it 'occurred_at must be Time' do
219
+ expect{
220
+ Tengine::Event.new(:occurred_at => "invalid time string")
221
+ }.to raise_error(ArgumentError, /no time information/)
222
+ end
223
+
224
+ describe :level do
225
+ context :valid_levels do
226
+ {
227
+ 0 => :gr_heartbeat,
228
+ 1 => :debug,
229
+ 2 => :info,
230
+ 3 => :warn,
231
+ 4 => :error,
232
+ 5 => :fatal,
233
+ }.each do |level, level_key|
234
+ context "set by level" do
235
+ subject{ Tengine::Event.new(:level => level) }
236
+ its(:level_key){ should == level_key}
237
+ end
238
+ context "set Symbol by level_key" do
239
+ subject{ Tengine::Event.new(:level_key => level_key.to_sym) }
240
+ its(:level){ should == level}
241
+ its(:level_key){ should == level_key.to_sym}
242
+ end
243
+ context "set String by level_key" do
244
+ subject{ Tengine::Event.new(:level_key => level_key.to_s) }
245
+ its(:level){ should == level}
246
+ its(:level_key){ should == level_key.to_sym}
247
+ end
248
+ end
249
+ end
250
+
251
+ context 'invalid_level values' do
252
+ [-100, 10, -5 -1, 6, 10, 100].each do |value|
253
+ it value do
254
+ expect{
255
+ Tengine::Event.new(:level => value)
256
+ }.to raise_error(ArgumentError)
257
+ end
258
+ end
259
+ end
260
+
261
+ context 'invalid_level keys' do
262
+ [:unknown , :zero , :INFO].each do |key|
263
+ it key.inspect do
264
+ expect{
265
+ Tengine::Event.new(:level_key => key)
266
+ }.to raise_error(ArgumentError)
267
+ end
268
+ end
269
+ end
270
+ end
271
+
272
+ describe :properties do
273
+ subject{ Tengine::Event.new }
274
+ it "valid usage" do
275
+ subject.properties.should == {}
276
+ subject.properties = {:foo => 123}
277
+ subject.properties.should == {'foo' => 123}
278
+ subject.properties = {:bar => 456}
279
+ subject.properties.should == {'bar' => 456}
280
+ end
281
+ it "assign nil" do
282
+ subject.properties = nil
283
+ subject.properties.should == {}
284
+ end
285
+ end
286
+
287
+
288
+ describe :fire do
289
+ before do
290
+ Tengine::Event.config = {
291
+ :connection => {"foo" => "aaa"},
292
+ :exchange => {'name' => "exchange1", 'type' => 'direct', 'durable' => true, 'publish' => {'persistent' => true}},
293
+ :queue => {'name' => "queue1", 'durable' => true},
294
+ }
295
+ @mock_connection = mock(:connection)
296
+ @mock_channel = mock(:channel)
297
+ @mock_exchange = mock(:exchange)
298
+ AMQP.should_receive(:connect).with(an_instance_of(Hash)).and_yield(@mock_connection).and_return(@mock_connection)
299
+ @mock_connection.stub(:connected?).and_return(true)
300
+ @mock_connection.stub(:disconnect).and_yield
301
+ @mock_connection.stub(:server_capabilities).and_return(nil)
302
+ AMQP::Channel.should_receive(:new).with(@mock_connection, an_instance_of(Fixnum), an_instance_of(Hash)).and_yield(@mock_channel).and_return(@mock_channel)
303
+ @mock_channel.stub(:publisher_index).and_return(nil)
304
+ AMQP::Exchange.should_receive(:new).with(@mock_channel, :direct, "exchange1",
305
+ :passive=>false, :durable=>true, :auto_delete=>false, :internal=>false, :nowait=>false).and_yield(@mock_exchange).and_return(@mock_exchange)
306
+ @mock_channel.stub(:close)
307
+ end
308
+
309
+ it "JSON形式にserializeしてexchangeにpublishする" do
310
+ expected_event = Tengine::Event.new(:event_type_name => :foo, :key => "uniq_key")
311
+ @mock_exchange.should_receive(:publish).with(expected_event.to_json, :persistent => true, :content_type => "application/json")
312
+ EM.run_test do
313
+ Tengine::Event.fire(:foo, :key => "uniq_key")
314
+ EM.add_timer(0.1) do
315
+ EM.next_tick do
316
+ EM.stop
317
+ end
318
+ end
319
+ end
320
+ end
321
+ end
322
+
323
+
324
+ describe :parse do
325
+ context "can parse valid json object" do
326
+ subject do
327
+ source = Tengine::Event.new(
328
+ :event_type_name => :foo,
329
+ :key => "hoge",
330
+ 'source_name' => "server1",
331
+ :occurred_at => Time.utc(2011,8,11,12,0),
332
+ :level_key => 'error',
333
+ 'sender_name' => "server2",
334
+ :properties => {:bar => "ABC", :baz => 999}
335
+ )
336
+ Tengine::Event.parse(source.to_json)
337
+ end
338
+ its(:key){ should == "hoge" }
339
+ its(:event_type_name){ should == "foo" }
340
+ its(:source_name){ should == "server1" }
341
+ its(:occurred_at){ should == Time.utc(2011,8,11,12,0) }
342
+ its(:level){ should == 4}
343
+ its(:level_key){ should == :error}
344
+ its(:sender_name){ should == "server2" }
345
+ its(:properties){ should == {'bar' => "ABC", 'baz' => 999}}
346
+ end
347
+
348
+ context "can parse valid json array" do
349
+ before do
350
+ source1 = Tengine::Event.new(
351
+ :event_type_name => :foo,
352
+ :key => "hoge1",
353
+ 'source_name' => "server1",
354
+ :occurred_at => Time.utc(2011,8,11,12,0),
355
+ :level_key => 'error',
356
+ 'sender_name' => "server2",
357
+ :properties => {:bar => "ABC", :baz => 999}
358
+ )
359
+ source2 = Tengine::Event.new(
360
+ :event_type_name => :bar,
361
+ :key => "hoge2",
362
+ 'source_name' => "server3",
363
+ :occurred_at => Time.utc(2011,9,24,15,50),
364
+ :level_key => 'warn',
365
+ 'sender_name' => "server4",
366
+ :properties => {:bar => "DEF", :baz => 777}
367
+ )
368
+ @parsed = Tengine::Event.parse([source1, source2].to_json)
369
+ end
370
+
371
+ context "first" do
372
+ subject{ @parsed.first }
373
+ its(:key){ should == "hoge1" }
374
+ its(:event_type_name){ should == "foo" }
375
+ its(:source_name){ should == "server1" }
376
+ its(:occurred_at){ should == Time.utc(2011,8,11,12,0) }
377
+ its(:level){ should == 4}
378
+ its(:level_key){ should == :error}
379
+ its(:sender_name){ should == "server2" }
380
+ its(:properties){ should == {'bar' => "ABC", 'baz' => 999}}
381
+ end
382
+
383
+ context "last" do
384
+ subject{ @parsed.last }
385
+ its(:key){ should == "hoge2" }
386
+ its(:event_type_name){ should == "bar" }
387
+ its(:source_name){ should == "server3" }
388
+ its(:occurred_at){ should == Time.utc(2011,9,24,15,50) }
389
+ its(:level){ should == 3}
390
+ its(:level_key){ should == :warn}
391
+ its(:sender_name){ should == "server4" }
392
+ its(:properties){ should == {'bar' => "DEF", 'baz' => 777}}
393
+ end
394
+ end
395
+
396
+ it "raise ArgumentError for invalid attribute name" do
397
+ expect{
398
+ Tengine::Event.parse({'name' => :foo}.to_json)
399
+ }.to raise_error(NoMethodError)
400
+ end
401
+
402
+ end
403
+
404
+ describe "#transmitted?" do
405
+
406
+ subject { Tengine::Event.new }
407
+
408
+ it "returns false while not transmitted" do
409
+ Tengine::Mq::Suite.stub(:pending?).with(subject).and_return(true)
410
+ subject.transmitted?.should be_false
411
+ end
412
+
413
+ it "returns true once transmitted" do
414
+ Tengine::Mq::Suite.stub(:pending?).with(subject).and_return(false)
415
+ subject.transmitted?.should be_true
416
+ end
417
+ end
418
+
419
+ end
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ require 'amqp'
5
+
6
+ config_filepath = File.expand_path("../../mq_config.yml", File.dirname(__FILE__))
7
+
8
+ if File.exist?(config_filepath) && (ENV['TENGINE_EVENT_MQ_TEST'] =~ /true|on/i)
9
+ describe "Tengine::Mq::Suite" do
10
+ before do
11
+ # キューをsubscribeすることで、キューを作ります
12
+ @config = YAML.load_file(config_filepath)
13
+ EM.run_test do
14
+ @mq_suite = Tengine::Mq::Suite.new(@config)
15
+ @mq_suite.queue.subscribe do |metadata, msg|
16
+ # 何もしません
17
+ end
18
+ EM.add_timer(1) do
19
+ @mq_suite.connection.close{ EM.stop_event_loop }
20
+ end
21
+ end
22
+ end
23
+
24
+ it "EM.run{...} を複数回実行できる" do
25
+ EM.run_test{
26
+ @mq_suite.exchange.publish("foo"){
27
+ @mq_suite.connection.close{ EM.stop_event_loop }
28
+ }
29
+ }
30
+ EM.run_test{
31
+ @mq_suite.exchange.publish("foo"){
32
+ @mq_suite.connection.close{ EM.stop_event_loop }
33
+ }
34
+ }
35
+ end
36
+
37
+ end
38
+ end