tengine_event 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
data/lib/tengine/mq.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'tengine_event'
2
+
3
+ module Tengine::Mq
4
+ autoload :Suite, "tengine/mq/suite"
5
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine_event'
3
+
4
+ # NullLoggerはロガーのNull Objectです。
5
+ # Loggerと同じインタフェースを持ちますが何も動作しません。
6
+ class Tengine::NullLogger
7
+ %w[debug info warn error fatal log].each do |log_level|
8
+ class_eval("def #{log_level}(*args); end")
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ module Tengine
2
+ autoload :Event , 'tengine/event'
3
+ autoload :Mq , 'tengine/mq'
4
+ autoload :NullLogger, 'tengine/null_logger'
5
+
6
+ class << self
7
+ def logger
8
+ @logger ||= NullLogger.new # ::Logger.new(STDOUT)
9
+ end
10
+ attr_writer :logger
11
+ end
12
+
13
+ end
data/spec/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /mq_config.yml
@@ -0,0 +1,13 @@
1
+ connection:
2
+ host: 'localhost'
3
+ port: 5672
4
+ # vhost:
5
+ # user:
6
+ # pass:
7
+ exchange:
8
+ name: 'tengine_event_test_exchange'
9
+ type: 'direct'
10
+ durable: true
11
+ queue:
12
+ name: 'tengine_event_test_queue'
13
+ durable: true
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'tengine_event'
5
+
6
+ require 'simplecov'
7
+ SimpleCov.start if ENV["COVERAGE"]
8
+
9
+ # Requires supporting files with custom matchers and macros, etc,
10
+ # in ./support/ and its subdirectories.
11
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
12
+
13
+ RSpec.configure do |config|
14
+
15
+ end
@@ -0,0 +1,80 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper')
3
+
4
+ describe "Tengine::Event::ModelNotifiable" do
5
+
6
+ module TengineTest
7
+ class MockModel
8
+ attr_accessor :attributes, :changes
9
+ def initialize(attributes)
10
+ @attributes = attributes
11
+ end
12
+ end
13
+ end
14
+
15
+ class TestModelNotifier
16
+ include Tengine::Event::ModelNotifiable
17
+
18
+ def initialize(sender)
19
+ @sender= sender
20
+ end
21
+
22
+ def event_sender
23
+ @sender
24
+ end
25
+ end
26
+
27
+ context "ActiveModelなどのObserverでそれぞれメソッドが呼び出されます" do
28
+ before do
29
+ @mock_sender = mock(:sender)
30
+ @notifier = TestModelNotifier.new(@mock_sender)
31
+ end
32
+
33
+ it "after_create" do
34
+ @mock_sender.should_receive(:fire).with("TengineTest::MockModel.created.TestModelNotifier", {
35
+ :level_key => :info,
36
+ :properties => {
37
+ :class_name => "TengineTest::MockModel",
38
+ :attributes => {
39
+ :name => "GariGariKun",
40
+ :price => 50
41
+ }
42
+ }
43
+ })
44
+ @notifier.after_create(TengineTest::MockModel.new(:name => "GariGariKun", :price => 50))
45
+ end
46
+
47
+ it "after_update" do
48
+ @mock_sender.should_receive(:fire).with("TengineTest::MockModel.updated.TestModelNotifier", {
49
+ :level_key => :info,
50
+ :properties => {
51
+ :class_name => "TengineTest::MockModel",
52
+ :attributes => {
53
+ :name => "GariGariKun",
54
+ :price => 60
55
+ },
56
+ :changes => {"price" => [50, 60]},
57
+ }
58
+ })
59
+ model = TengineTest::MockModel.new(:name => "GariGariKun", :price => 60)
60
+ model.changes = {"price" => [50, 60]}
61
+ @notifier.after_update(model)
62
+ end
63
+
64
+ it "after_destroy" do
65
+ @mock_sender.should_receive(:fire).with("TengineTest::MockModel.destroyed.TestModelNotifier", {
66
+ :level_key => :info,
67
+ :properties => {
68
+ :class_name => "TengineTest::MockModel",
69
+ :attributes => {
70
+ :name => "GariGariKun",
71
+ :price => 60
72
+ }
73
+ }
74
+ })
75
+ @notifier.after_destroy(TengineTest::MockModel.new(:name => "GariGariKun", :price => 60))
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,124 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('../../../spec_helper', __FILE__)
3
+
4
+ # fire の機能面(何を送信するか等)に関してはmq/suiteに記載済み
5
+ describe "Tengine::Event::Sender" do
6
+ describe "#initialize" do
7
+ context "no args" do
8
+ subject{ Tengine::Event::Sender.new.mq_suite }
9
+ it { should_not be_nil }
10
+ it { should be_kind_of Tengine::Mq::Suite }
11
+ end
12
+
13
+ context "with options" do
14
+ subject{ Tengine::Event::Sender.new(:exchange => {:name => "another_exhange"}).mq_suite.config[:exchange][:name] }
15
+ it { should == "another_exhange" }
16
+ end
17
+
18
+ context "with mq_suite" do
19
+ before{ @mq_suite = Tengine::Mq::Suite.new }
20
+ subject{ Tengine::Event::Sender.new(@mq_suite).mq_suite }
21
+ it { should == @mq_suite }
22
+ end
23
+
24
+ context "with mq_suite and options" do
25
+ before do
26
+ @mq_suite = Tengine::Mq::Suite.new
27
+ @logger = mock(:logger)
28
+ end
29
+ subject { Tengine::Event::Sender.new(@mq_suite, :logger => @logger) }
30
+ its (:mq_suite) { should == @mq_suite }
31
+ its (:logger) { should == @logger }
32
+ end
33
+ end
34
+
35
+ describe "#stop" do
36
+ before { @mq_suite = Tengine::Mq::Suite.new }
37
+ subject { Tengine::Event::Sender.new(@mq_suite) }
38
+
39
+ it "stops suite" do
40
+ @mq_suite.should_receive :stop
41
+ subject.stop
42
+ end
43
+
44
+ it "calls the given block (if any) afterwards" do
45
+ @mq_suite.should_receive(:stop).and_yield
46
+ block_called = false
47
+ subject.stop { block_called = true }
48
+ block_called.should == true
49
+ end
50
+ end
51
+
52
+ describe "#pending_events" do
53
+ before do
54
+ @mq_suite = Tengine::Mq::Suite.new
55
+ @event = Tengine::Event.new
56
+ @mq_suite.stub(:pending_events_for).with(an_instance_of(Tengine::Event::Sender)).and_return([@event])
57
+ end
58
+ subject { Tengine::Event::Sender.new(@mq_suite).pending_events }
59
+
60
+ it { should be_kind_of Array }
61
+ it { should have(1).events }
62
+ it { should include(@event) }
63
+ end
64
+
65
+ describe "#wait_for_connection" do
66
+ it "does nothing" do
67
+ expect {
68
+ Tengine::Event::Sender.new.wait_for_connection { }
69
+ }.to_not raise_error
70
+ end
71
+ end
72
+
73
+ describe "#fire" do
74
+ before do
75
+ @event = Tengine::Event.new
76
+ @mq_suite = Tengine::Mq::Suite.new
77
+ @mq_suite.stub(:fire).
78
+ with(an_instance_of(Tengine::Event::Sender),
79
+ an_instance_of(Tengine::Event),
80
+ an_instance_of(Hash),
81
+ an_instance_of(Proc)) {
82
+ |q, w, e, r|
83
+ r.yield
84
+ }
85
+ @mq_suite.stub(:fire).
86
+ with(an_instance_of(Tengine::Event::Sender),
87
+ an_instance_of(Tengine::Event),
88
+ an_instance_of(Hash),
89
+ an_instance_of(NilClass))
90
+ end
91
+
92
+ context "mandatory one arg" do
93
+ it { expect { Tengine::Event::Sender.new(@mq_suite).fire }.to raise_exception(ArgumentError) }
94
+ end
95
+
96
+ context "one, string arg" do
97
+ subject { Tengine::Event::Sender.new(@mq_suite).fire("foo") }
98
+ it { should be_kind_of(Tengine::Event) }
99
+ its (:event_type_name) { should == "foo" }
100
+ end
101
+
102
+ context "one, event arg" do
103
+ subject { Tengine::Event::Sender.new(@mq_suite).fire(@event) }
104
+ it { should be_kind_of(Tengine::Event) }
105
+ it { should == @event }
106
+ end
107
+
108
+ context "event arg + option" do
109
+ subject { Tengine::Event::Sender.new(@mq_suite).fire(@event, :keep_connection => true) }
110
+ it { should == @event }
111
+ end
112
+
113
+ context "block argument" do
114
+ before { @event = Tengine::Event.new }
115
+ it "is called" do
116
+ block_called = false
117
+ Tengine::Event::Sender.new(@mq_suite).fire(@event) do
118
+ block_called = true
119
+ end
120
+ block_called.should == true
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,397 @@
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
+ {
226
+ 0 => :gr_heartbeat,
227
+ 1 => :debug,
228
+ 2 => :info,
229
+ 3 => :warn,
230
+ 4 => :error,
231
+ 5 => :fatal,
232
+ }.each do |level, level_key|
233
+ context "set by level" do
234
+ subject{ Tengine::Event.new(:level => level) }
235
+ its(:level_key){ should == level_key}
236
+ end
237
+ context "set Symbol by level_key" do
238
+ subject{ Tengine::Event.new(:level_key => level_key.to_sym) }
239
+ its(:level){ should == level}
240
+ its(:level_key){ should == level_key.to_sym}
241
+ end
242
+ context "set String by level_key" do
243
+ subject{ Tengine::Event.new(:level_key => level_key.to_s) }
244
+ its(:level){ should == level}
245
+ its(:level_key){ should == level_key.to_sym}
246
+ end
247
+ end
248
+ end
249
+
250
+ describe :properties do
251
+ subject{ Tengine::Event.new }
252
+ it "valid usage" do
253
+ subject.properties.should == {}
254
+ subject.properties = {:foo => 123}
255
+ subject.properties.should == {'foo' => 123}
256
+ subject.properties = {:bar => 456}
257
+ subject.properties.should == {'bar' => 456}
258
+ end
259
+ it "assign nil" do
260
+ subject.properties = nil
261
+ subject.properties.should == {}
262
+ end
263
+ end
264
+
265
+
266
+ describe :fire do
267
+ before do
268
+ Tengine::Event.config = {
269
+ :connection => {"foo" => "aaa"},
270
+ :exchange => {'name' => "exchange1", 'type' => 'direct', 'durable' => true, 'publish' => {'persistent' => true}},
271
+ :queue => {'name' => "queue1", 'durable' => true},
272
+ }
273
+ @mock_connection = mock(:connection)
274
+ @mock_channel = mock(:channel)
275
+ @mock_exchange = mock(:exchange)
276
+ AMQP.should_receive(:connect).with(an_instance_of(Hash)).and_yield(@mock_connection).and_return(@mock_connection)
277
+ @mock_connection.stub(:connected?).and_return(true)
278
+ @mock_connection.stub(:disconnect).and_yield
279
+ @mock_connection.stub(:server_capabilities).and_return(nil)
280
+ AMQP::Channel.should_receive(:new).with(@mock_connection, an_instance_of(Fixnum), an_instance_of(Hash)).and_yield(@mock_channel).and_return(@mock_channel)
281
+ @mock_channel.stub(:publisher_index).and_return(nil)
282
+ AMQP::Exchange.should_receive(:new).with(@mock_channel, :direct, "exchange1",
283
+ :passive=>false, :durable=>true, :auto_delete=>false, :internal=>false, :nowait=>false).and_yield(@mock_exchange).and_return(@mock_exchange)
284
+ @mock_channel.stub(:close)
285
+ end
286
+
287
+ it "JSON形式にserializeしてexchangeにpublishする" do
288
+ expected_event = Tengine::Event.new(:event_type_name => :foo, :key => "uniq_key")
289
+ @mock_exchange.should_receive(:publish).with(expected_event.to_json, :persistent => true, :content_type => "application/json")
290
+ EM.run do
291
+ Tengine::Event.fire(:foo, :key => "uniq_key")
292
+ EM.add_timer(0.1) do
293
+ EM.next_tick do
294
+ EM.stop
295
+ end
296
+ end
297
+ end
298
+ end
299
+ end
300
+
301
+
302
+ describe :parse do
303
+ context "can parse valid json object" do
304
+ subject do
305
+ source = Tengine::Event.new(
306
+ :event_type_name => :foo,
307
+ :key => "hoge",
308
+ 'source_name' => "server1",
309
+ :occurred_at => Time.utc(2011,8,11,12,0),
310
+ :level_key => 'error',
311
+ 'sender_name' => "server2",
312
+ :properties => {:bar => "ABC", :baz => 999}
313
+ )
314
+ Tengine::Event.parse(source.to_json)
315
+ end
316
+ its(:key){ should == "hoge" }
317
+ its(:event_type_name){ should == "foo" }
318
+ its(:source_name){ should == "server1" }
319
+ its(:occurred_at){ should == Time.utc(2011,8,11,12,0) }
320
+ its(:level){ should == 4}
321
+ its(:level_key){ should == :error}
322
+ its(:sender_name){ should == "server2" }
323
+ its(:properties){ should == {'bar' => "ABC", 'baz' => 999}}
324
+ end
325
+
326
+ context "can parse valid json array" do
327
+ before do
328
+ source1 = Tengine::Event.new(
329
+ :event_type_name => :foo,
330
+ :key => "hoge1",
331
+ 'source_name' => "server1",
332
+ :occurred_at => Time.utc(2011,8,11,12,0),
333
+ :level_key => 'error',
334
+ 'sender_name' => "server2",
335
+ :properties => {:bar => "ABC", :baz => 999}
336
+ )
337
+ source2 = Tengine::Event.new(
338
+ :event_type_name => :bar,
339
+ :key => "hoge2",
340
+ 'source_name' => "server3",
341
+ :occurred_at => Time.utc(2011,9,24,15,50),
342
+ :level_key => 'warn',
343
+ 'sender_name' => "server4",
344
+ :properties => {:bar => "DEF", :baz => 777}
345
+ )
346
+ @parsed = Tengine::Event.parse([source1, source2].to_json)
347
+ end
348
+
349
+ context "first" do
350
+ subject{ @parsed.first }
351
+ its(:key){ should == "hoge1" }
352
+ its(:event_type_name){ should == "foo" }
353
+ its(:source_name){ should == "server1" }
354
+ its(:occurred_at){ should == Time.utc(2011,8,11,12,0) }
355
+ its(:level){ should == 4}
356
+ its(:level_key){ should == :error}
357
+ its(:sender_name){ should == "server2" }
358
+ its(:properties){ should == {'bar' => "ABC", 'baz' => 999}}
359
+ end
360
+
361
+ context "last" do
362
+ subject{ @parsed.last }
363
+ its(:key){ should == "hoge2" }
364
+ its(:event_type_name){ should == "bar" }
365
+ its(:source_name){ should == "server3" }
366
+ its(:occurred_at){ should == Time.utc(2011,9,24,15,50) }
367
+ its(:level){ should == 3}
368
+ its(:level_key){ should == :warn}
369
+ its(:sender_name){ should == "server4" }
370
+ its(:properties){ should == {'bar' => "DEF", 'baz' => 777}}
371
+ end
372
+ end
373
+
374
+ it "raise ArgumentError for invalid attribute name" do
375
+ expect{
376
+ Tengine::Event.parse({'name' => :foo}.to_json)
377
+ }.to raise_error(NoMethodError)
378
+ end
379
+
380
+ end
381
+
382
+ describe "#transmitted?" do
383
+
384
+ subject { Tengine::Event.new }
385
+
386
+ it "returns false while not transmitted" do
387
+ Tengine::Mq::Suite.stub(:pending?).with(subject).and_return(true)
388
+ subject.transmitted?.should be_false
389
+ end
390
+
391
+ it "returns true once transmitted" do
392
+ Tengine::Mq::Suite.stub(:pending?).with(subject).and_return(false)
393
+ subject.transmitted?.should be_true
394
+ end
395
+ end
396
+
397
+ end