tengine_core 0.5.28

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.
Files changed (156) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +40 -0
  4. data/Gemfile.lock +95 -0
  5. data/README.md +54 -0
  6. data/Rakefile +44 -0
  7. data/VERSION +1 -0
  8. data/bin/tengine_atd +8 -0
  9. data/bin/tengine_heartbeat_watchd +8 -0
  10. data/bin/tengined +182 -0
  11. data/examples/VERSION +1 -0
  12. data/examples/uc01_execute_processing_for_event.rb +11 -0
  13. data/examples/uc02_fire_another_event.rb +16 -0
  14. data/examples/uc03_2handlers_for_1event.rb +16 -0
  15. data/examples/uc08_if_both_a_and_b_occurs.rb +11 -0
  16. data/examples/uc10_if_the_event_occurs_at_the_server.rb +15 -0
  17. data/examples/uc50_commit_event_at_first.rb +17 -0
  18. data/examples/uc51_commit_event_at_first_submit.rb +29 -0
  19. data/examples/uc52_commit_event_after_all_handler_submit.rb +31 -0
  20. data/examples/uc52_never_commit_event_unless_all_handler_submit.rb +31 -0
  21. data/examples/uc60_event_in_handler.rb +18 -0
  22. data/examples/uc62_session_in_driver.rb +16 -0
  23. data/examples/uc64_safety_countup.rb +14 -0
  24. data/examples/uc70_driver_enabled_on_activation.rb +13 -0
  25. data/examples/uc71_driver_disabled_on_activation.rb +14 -0
  26. data/examples/uc72_setup_eventmachine.rb +17 -0
  27. data/examples/uc80_raise_io_error.rb +10 -0
  28. data/examples/uc81_raise_runtime_error.rb +10 -0
  29. data/examples2/driver01.rb +18 -0
  30. data/examples2/driver02.rb +19 -0
  31. data/examples2/uc08_if_both_a_and_b_occurs.rb +13 -0
  32. data/examples2/uc10_if_the_event_occurs_at_the_server.rb +18 -0
  33. data/examples2/uc51_commit_event_at_first_submit_1.rb +16 -0
  34. data/examples2/uc51_commit_event_at_first_submit_2.rb +17 -0
  35. data/examples2/uc51_commit_event_at_first_submit_3.rb +17 -0
  36. data/examples2/uc62_session_in_driver.rb +16 -0
  37. data/examples2/uc71_driver_disabled_on_activation.rb +16 -0
  38. data/failure_examples/VERSION +1 -0
  39. data/failure_examples/uc53_submit_outside_of_handler.rb +15 -0
  40. data/failure_examples/uc61_event_outside_of_handler.rb +12 -0
  41. data/failure_examples/uc63_session_outside_of_driver.rb +13 -0
  42. data/lib/tengine/core.rb +74 -0
  43. data/lib/tengine/core/bootstrap.rb +123 -0
  44. data/lib/tengine/core/collection_accessible.rb +34 -0
  45. data/lib/tengine/core/config.rb +10 -0
  46. data/lib/tengine/core/config/atd.rb +225 -0
  47. data/lib/tengine/core/config/core.rb +319 -0
  48. data/lib/tengine/core/config/heartbeat_watcher.rb +229 -0
  49. data/lib/tengine/core/connection_test/.gitignore +1 -0
  50. data/lib/tengine/core/connection_test/fire_bar_on_foo.rb +16 -0
  51. data/lib/tengine/core/driveable.rb +213 -0
  52. data/lib/tengine/core/driver.rb +69 -0
  53. data/lib/tengine/core/driver/finder.rb +42 -0
  54. data/lib/tengine/core/dsl_evaluator.rb +110 -0
  55. data/lib/tengine/core/dsl_filter_def.rb +11 -0
  56. data/lib/tengine/core/dsl_loader.rb +108 -0
  57. data/lib/tengine/core/event.rb +145 -0
  58. data/lib/tengine/core/event/finder.rb +82 -0
  59. data/lib/tengine/core/event_exception_reportable.rb +88 -0
  60. data/lib/tengine/core/event_wrapper.rb +21 -0
  61. data/lib/tengine/core/find_by_name.rb +31 -0
  62. data/lib/tengine/core/handler.rb +152 -0
  63. data/lib/tengine/core/handler_path.rb +33 -0
  64. data/lib/tengine/core/heartbeat_watcher.rb +161 -0
  65. data/lib/tengine/core/io_to_logger.rb +22 -0
  66. data/lib/tengine/core/kernel.rb +510 -0
  67. data/lib/tengine/core/kernel_runtime.rb +91 -0
  68. data/lib/tengine/core/method_traceable.rb +38 -0
  69. data/lib/tengine/core/mongoid_fix.rb +19 -0
  70. data/lib/tengine/core/mutex.rb +177 -0
  71. data/lib/tengine/core/optimistic_lock.rb +69 -0
  72. data/lib/tengine/core/plugins.rb +54 -0
  73. data/lib/tengine/core/schedule.rb +21 -0
  74. data/lib/tengine/core/scheduler.rb +156 -0
  75. data/lib/tengine/core/selectable_attr.rb +29 -0
  76. data/lib/tengine/core/session.rb +21 -0
  77. data/lib/tengine/core/session_wrapper.rb +68 -0
  78. data/lib/tengine/core/setting.rb +21 -0
  79. data/lib/tengine/core/validation.rb +36 -0
  80. data/lib/tengine/errors.rb +18 -0
  81. data/lib/tengine/rspec.rb +8 -0
  82. data/lib/tengine/rspec/context_wrapper.rb +51 -0
  83. data/lib/tengine/rspec/extension.rb +53 -0
  84. data/lib/tengine_core.rb +23 -0
  85. data/spec/factories/tengine_core_drivers.rb +10 -0
  86. data/spec/factories/tengine_core_events.rb +14 -0
  87. data/spec/factories/tengine_core_handler_paths.rb +9 -0
  88. data/spec/factories/tengine_core_handlers.rb +9 -0
  89. data/spec/factories/tengine_core_sessions.rb +9 -0
  90. data/spec/mongoid.yml +35 -0
  91. data/spec/spec_helper.rb +48 -0
  92. data/spec/support/mongo_index_key_log.rb +91 -0
  93. data/spec/tengine/core/bootstrap_spec.rb +278 -0
  94. data/spec/tengine/core/bugfix/bind_dsl_file_in_multi_byte_dir_spec.rb +21 -0
  95. data/spec/tengine/core/bugfix/enabled_on_activation_spec.rb +112 -0
  96. data/spec/tengine/core/bugfix/receive_event_spec.rb +133 -0
  97. data/spec/tengine/core/bugfix/use_dsl_version_method.rb +12 -0
  98. data/spec/tengine/core/bugfix/use_dsl_version_method_spec.rb +28 -0
  99. data/spec/tengine/core/bugfix/use_event_in_handler_dsl.rb +11 -0
  100. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
  101. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/206/343/202/231/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
  102. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215/source_location_encoding.rb +35 -0
  103. data/spec/tengine/core/bugfix//351/235/236ACSII/343/201/256/343/203/207/343/202/243/343/203/254/343/202/257/343/203/210/343/203/252/345/220/215//351/235/236ASCII/343/201/256/343/203/225/343/202/241/343/202/244/343/203/253/345/220/215_dsl.rb +38 -0
  104. data/spec/tengine/core/config/atd_spec.rb +62 -0
  105. data/spec/tengine/core/config/core_spec.rb +479 -0
  106. data/spec/tengine/core/config/heartbeat_watcher_spec.rb +62 -0
  107. data/spec/tengine/core/config/syntax_error_in_erb.yml.erb +13 -0
  108. data/spec/tengine/core/config/wrong_category_name.yml.erb +13 -0
  109. data/spec/tengine/core/config/wrong_field_name.yml.erb +12 -0
  110. data/spec/tengine/core/config/wrong_yaml.yml.erb +13 -0
  111. data/spec/tengine/core/config_spec/another_port.yml +54 -0
  112. data/spec/tengine/core/config_spec/config_with_dir_absolute_load_path.yml +16 -0
  113. data/spec/tengine/core/config_spec/config_with_dir_relative_load_path.yml +16 -0
  114. data/spec/tengine/core/config_spec/config_with_file_absolute_load_path.yml +16 -0
  115. data/spec/tengine/core/config_spec/config_with_file_relative_load_path.yml +16 -0
  116. data/spec/tengine/core/config_spec/log_config_spec.rb +235 -0
  117. data/spec/tengine/core/driveable_spec.rb +240 -0
  118. data/spec/tengine/core/driver_spec.rb +159 -0
  119. data/spec/tengine/core/dsl_loader_spec.rb +172 -0
  120. data/spec/tengine/core/dsls/uc08_if_both_a_and_b_occurs_spec.rb +35 -0
  121. data/spec/tengine/core/dsls/uc10_if_the_event_occurs_at_the_server_spec.rb +58 -0
  122. data/spec/tengine/core/dsls/uc50_commit_event_at_first_spec.rb +29 -0
  123. data/spec/tengine/core/dsls/uc52_commit_event_after_all_handler_submit_spec.rb +33 -0
  124. data/spec/tengine/core/dsls/uc52_never_commit_event_unless_all_handler_submit_spec.rb +37 -0
  125. data/spec/tengine/core/dsls/uc53_submit_outside_of_handler_spec.rb +37 -0
  126. data/spec/tengine/core/dsls/uc60_event_in_handler_spec.rb +31 -0
  127. data/spec/tengine/core/dsls/uc61_event_outside_of_handler_spec.rb +37 -0
  128. data/spec/tengine/core/dsls/uc62_session_in_driver_spec.rb +36 -0
  129. data/spec/tengine/core/dsls/uc63_session_outside_of_driver_spec.rb +35 -0
  130. data/spec/tengine/core/dsls/uc64_safety_countup_spec.rb +134 -0
  131. data/spec/tengine/core/dsls/uc70_driver_enabled_on_activation_spec.rb +39 -0
  132. data/spec/tengine/core/dsls/uc71_driver_disabled_on_activation_spec.rb +36 -0
  133. data/spec/tengine/core/dsls/uc72_setup_eventmachine_spec.rb +39 -0
  134. data/spec/tengine/core/dsls/uc80_raise_io_error_spec.rb +53 -0
  135. data/spec/tengine/core/dsls/uc81_raise_runtime_error_spec.rb +49 -0
  136. data/spec/tengine/core/event/finder_spec.rb +136 -0
  137. data/spec/tengine/core/event_exception_reportable_spec.rb +33 -0
  138. data/spec/tengine/core/event_spec.rb +161 -0
  139. data/spec/tengine/core/event_wrapper_spec.rb +35 -0
  140. data/spec/tengine/core/handler_path_spec.rb +87 -0
  141. data/spec/tengine/core/handler_spec.rb +190 -0
  142. data/spec/tengine/core/heartbeat_watcher_spec.rb +131 -0
  143. data/spec/tengine/core/io_to_logger_spec.rb +30 -0
  144. data/spec/tengine/core/kernel_spec.rb +885 -0
  145. data/spec/tengine/core/mutex_spec.rb +184 -0
  146. data/spec/tengine/core/optimistic_lock_spec.rb +55 -0
  147. data/spec/tengine/core/scheculer_spec.rb +121 -0
  148. data/spec/tengine/core/selectable_attr_spec.rb +30 -0
  149. data/spec/tengine/core/session_spec.rb +104 -0
  150. data/spec/tengine/core/setting_spec.rb +79 -0
  151. data/spec/tengine/core_spec.rb +13 -0
  152. data/spec/tengine_spec.rb +14 -0
  153. data/tengine_core.gemspec +248 -0
  154. data/tmp/log/.gitignore +1 -0
  155. data/tmp/tengined_status/.gitignore +1 -0
  156. metadata +421 -0
@@ -0,0 +1,885 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'spec_helper'
3
+ require 'amqp'
4
+ require 'eventmachine'
5
+
6
+ # ログを黙らせたり喋らせたりする
7
+ require 'amq/client'
8
+ require 'mongoid'
9
+ if $DEBUG
10
+ require 'logger'
11
+ AMQP::Session.logger = Tengine.logger = Mongoid.logger = Logger.new(STDERR)
12
+ else
13
+ AMQP::Session.logger = Tengine.logger = Mongoid.logger = Tengine::NullLogger.new
14
+ end
15
+
16
+ describe Tengine::Core::Kernel do
17
+ before do
18
+ Tengine::Core::Driver.delete_all
19
+ Tengine::Core::HandlerPath.delete_all
20
+ Tengine::Core::Event.delete_all
21
+ end
22
+
23
+ describe :start do
24
+ # describe :bind, "handlerのblockをメモリ上で保持" do
25
+ # before do
26
+ # config = Tengine::Core::Config::Core.new({
27
+ # :tengined => {
28
+ # :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
29
+ # },
30
+ # })
31
+ # @kernel = Tengine::Core::Kernel.new(config)
32
+ # @driver = Tengine::Core::Driver.new(:name => "driver01", :version => config.dsl_version, :enabled => true)
33
+ # @handler1 = @driver.handlers.new(:filepath => "uc01_execute_processing_for_event.rb", :lineno => 7, :event_type_names => ["event01"])
34
+ # @driver.save!
35
+ # end
36
+
37
+ # it "event_type_nameからblockを検索することができる" do
38
+ # @kernel.bind
39
+ # @kernel.context.__block_for__(@handler1).should_not be_nil
40
+ # end
41
+
42
+ # context "拡張モジュールあり" do
43
+ # before(:all) do
44
+ # @ext_mod1 = Module.new{}
45
+ # @ext_mod1.instance_eval do
46
+ # def dsl_binder; self; end
47
+ # end
48
+ # Tengine.plugins.add(@ext_mod1)
49
+ # end
50
+
51
+ # it "Kernel#contextに拡張モジュールがextendされる" do
52
+ # @kernel.bind
53
+ # @kernel.context.__block_for__(@handler1).should_not be_nil
54
+ # @kernel.context.should be_a(Tengine::Core::DslBinder)
55
+ # @kernel.context.should be_a(@ext_mod1)
56
+ # end
57
+ # end
58
+
59
+ # end
60
+
61
+ describe :wait_for_activation, "activate待ち" do
62
+ before do
63
+ config = Tengine::Core::Config::Core.new({
64
+ :tengined => {
65
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
66
+ :wait_activation => true,
67
+ :activation_timeout => 3,
68
+ :activation_dir => File.expand_path('.', File.dirname(__FILE__)),
69
+ },
70
+ })
71
+ @kernel = Tengine::Core::Kernel.new(config)
72
+ @driver = Tengine::Core::Driver.new(:name => "driver01", :version => config.dsl_version, :enabled => true)
73
+ @handler1 = @driver.handlers.new(:filepath => "uc01_execute_processing_for_event.rb", :lineno => 7, :event_type_names => ["event01"])
74
+ @driver.save!
75
+ @activation_file_path = "#{@kernel.config[:tengined][:activation_dir]}\/tengined_#{Process.pid}.activation"
76
+ end
77
+
78
+ after do
79
+ FileUtils.rm_f(@activation_file_path)
80
+ end
81
+
82
+ it "activationファイルが生成されたらactivateされる" do
83
+ @kernel.should_receive(:activate)
84
+ t1 = Thread.new {
85
+ @kernel.start
86
+ }
87
+ t2 = Thread.new {
88
+ FileUtils.touch(@activation_file_path)
89
+ }
90
+ t1.join
91
+ t2.join
92
+ end
93
+
94
+ it "activationファイルが生成されないままならタイムアウトになる" do
95
+ lambda {
96
+ @kernel.should_not_receive(:activate)
97
+ @kernel.start
98
+ }.should raise_error(Tengine::Core::ActivationTimeoutError, "activation file found timeout error.")
99
+ end
100
+ end
101
+
102
+ describe :activate, "メッセージの受信を開始" do
103
+ before do
104
+ @header = AMQP::Header.new(@mock_channel, nil, {
105
+ :routing_key => "",
106
+ :content_type => "application/octet-stream",
107
+ :priority => 0,
108
+ :headers => { },
109
+ :timestamp => Time.now,
110
+ :type => "",
111
+ :delivery_tag => 1,
112
+ :redelivered => false,
113
+ :exchange => "tengine_event_exchange",
114
+ })
115
+
116
+ config = Tengine::Core::Config::Core.new({
117
+ :tengined => {
118
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
119
+ :wait_activation => false,
120
+ :confirmation_threshold => 'info'
121
+ },
122
+ :heartbeat => {
123
+ :core => {
124
+ :interval => 1024,
125
+ :expire => 32768,
126
+ },
127
+ },
128
+ })
129
+ @kernel = Tengine::Core::Kernel.new(config)
130
+ @driver = Tengine::Core::Driver.new(
131
+ :name => "driver01", :version => config.dsl_version, :enabled => true)
132
+ @handler1 = @driver.handlers.new(
133
+ :filepath => "uc01_execute_processing_for_event.rb", :lineno => 7, :event_type_names => ["event01"])
134
+ @driver.save!
135
+ @event1 = Tengine::Core::Event.new(:event_type_name => :event01, :key => "uuid1", :sender_name => "localhost")
136
+ @event1.save!
137
+ end
138
+
139
+ context "イベントの受信待ち状態になる" do
140
+ before do
141
+ # eventmachine と mq の mock を生成
142
+ mock_mq = mock("mq")
143
+ mock_sender = mock("sender")
144
+ @kernel.stub(:mq).and_return(mock_mq)
145
+ @kernel.stub(:sender).and_return(mock_sender)
146
+ mock_sender.stub(:fire)
147
+ mock_mq.stub(:initiate_termination).and_yield
148
+ mock_mq.stub(:unsubscribe).and_yield
149
+ mock_mq.stub(:stop).and_yield
150
+ mock_mq.stub(:add_hook)
151
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
152
+ h[:confirm].yield(mock("confirm-ok"))
153
+ #b.yield(@header, :message)
154
+ end
155
+ # subscribe されていることを検証
156
+ mock_mq.should_receive(:subscribe).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc))
157
+ end
158
+
159
+ it "heartbeatは有効にならない" do
160
+ @kernel.config[:heartbeat][:core][:interval] = -1
161
+ @kernel.should_receive(:setup_mq_connection)
162
+ sender = mock(:sender)
163
+ @kernel.stub(:sender).and_return(sender)
164
+ sender.stub(:fire).with("finished.process.core.tengine", an_instance_of(Hash)).and_yield
165
+ @kernel.start
166
+ end
167
+
168
+ it "heartbeatは有効になる" do
169
+ @kernel.config[:heartbeat][:core][:interval] = 65535
170
+ EM.should_receive(:add_periodic_timer).with(65535).and_yield
171
+ sender = mock(:sender)
172
+ @kernel.stub(:sender).and_return(sender)
173
+ sender.stub(:fire).with("finished.process.core.tengine", an_instance_of(Hash)).and_yield
174
+ sender.stub(:fire).with("core.heartbeat.tengine", an_instance_of(Hash))
175
+ @kernel.start do
176
+ @kernel.stop
177
+ end
178
+ end
179
+
180
+ it "heartbeatが送られる" do
181
+ @kernel.should_receive(:setup_mq_connection)
182
+ EM.should_receive(:add_periodic_timer).with(1024).and_yield
183
+ mock_sender = mock(:sender)
184
+ @kernel.stub(:sender).and_return(mock_sender)
185
+ mock_sender.stub(:fire).with("finished.process.core.tengine", an_instance_of(Hash)).and_yield
186
+ mock_sender.stub(:fire).with("core.heartbeat.tengine", an_instance_of(Hash))
187
+ @kernel.start
188
+ end
189
+ end
190
+
191
+ context "発火されたイベントを登録できる" do
192
+ before do
193
+ # eventmachine と mq の mock を生成
194
+ mock_mq = mock("mq")
195
+ mock_sender = mock("sender")
196
+ @kernel.stub(:mq).and_return(mock_mq)
197
+ @kernel.stub(:sender).and_return(mock_sender)
198
+ mock_sender.stub(:fire)
199
+ mock_mq.stub(:initiate_termination).and_yield
200
+ mock_mq.stub(:unsubscribe).and_yield
201
+ mock_mq.stub(:stop).and_yield
202
+ mock_mq.stub(:add_hook)
203
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
204
+ h[:confirm].yield(mock("confirm-ok"))
205
+ b.yield(@header, :message)
206
+ end
207
+
208
+ # subscribe してみる
209
+ @mock_raw_event = mock(:row_event)
210
+ Tengine::Event.should_receive(:parse).with(:message).and_return(@mock_raw_event)
211
+
212
+ @header.should_receive(:ack)
213
+ end
214
+
215
+ it "confirmation_threshold以下なら登録されたイベントはconfirmedがtrue" do
216
+ @mock_raw_event.stub!(:key).and_return("uniq_key")
217
+ @mock_raw_event.stub!(:sender_name).and_return("localhost")
218
+ @mock_raw_event.stub!(:attributes).and_return(:event_type_name => :foo, :key => "uniq_key", :level => Tengine::Event::LEVELS_INV[:info])
219
+ @mock_raw_event.stub!(:level).and_return(Tengine::Event::LEVELS_INV[:info])
220
+ @mock_raw_event.stub!(:event_type_name).and_return("foo")
221
+ count = lambda{ Tengine::Core::Event.where(:event_type_name => :foo, :confirmed => true).count }
222
+ expect{ @kernel.start { @kernel.stop } }.should change(count, :call).by(1) # イベントが登録されていることを検証
223
+ end
224
+
225
+ it "confirmation_threshold以下なら登録されたイベントはconfirmedがfalse" do
226
+ @mock_raw_event.stub!(:key).and_return("uniq_key")
227
+ @mock_raw_event.stub!(:sender_name).and_return("localhost")
228
+ @mock_raw_event.stub!(:attributes).and_return(:event_type_name => :foo, :key => "uniq_key", :level => Tengine::Event::LEVELS_INV[:warn])
229
+ @mock_raw_event.stub!(:level).and_return(Tengine::Event::LEVELS_INV[:warn])
230
+ @mock_raw_event.stub!(:event_type_name).and_return("foo")
231
+ count = lambda{ Tengine::Core::Event.where(:event_type_name => :foo, :confirmed => false).count }
232
+ @kernel.should_receive(:setup_mq_connection)
233
+ expect{ @kernel.start }.should change(count, :call).by(1) # イベントが登録されていることを検証
234
+ end
235
+ end
236
+
237
+ context "イベントストアへの登録有無" do
238
+ it "不正なフォーマットのメッセージの場合、イベントストアへ登録を行わずACKを返却" do
239
+ @header.should_receive(:ack)
240
+ @kernel.process_message(@header, "invalid format message").should_not be_true
241
+ end
242
+
243
+ it "https://www.pivotaltracker.com/story/show/22698533" do
244
+ ev = Tengine::Event.new :key => "2498e870-11cd-012f-f8c0-48bcc89f84e1", :source_name => "localhost/8110", :sender_name => "localhost/8110", :level => 2, :occurred_at => Time.now, :properties => {}, :event_type_name => ""
245
+ @header.should_receive(:ack)
246
+ @kernel.process_message(@header, ev.to_json).should_not be_true
247
+ end
248
+
249
+ it "keyがnilのイベント場合、イベントストアへ登録を行わずACKを返却" do
250
+ raw_event = Tengine::Event.new(:key => "", :sender_name => "another_host", :event_type_name => "event1")
251
+ @header.should_receive(:ack)
252
+ @kernel.process_message(@header, raw_event.to_json).should_not be_true
253
+ end
254
+
255
+ it "keyが同じ、sender_nameが異なる場合は、イベントストアへ登録を行わずACKを返却" do
256
+ @header.should_receive(:ack)
257
+ raw_event = Tengine::Event.new(:key => "uuid1", :sender_name => "another_host", :event_type_name => "event1")
258
+ lambda {
259
+ Tengine::Core::Event.create!(raw_event.attributes.update(:confirmed => (raw_event.level <= @kernel.config.confirmation_threshold)))
260
+ }.should raise_error(Mongo::OperationFailure)
261
+ @kernel.process_message(@header, raw_event.to_json).should_not be_true
262
+ end
263
+
264
+ it "keyが異なる場合は、イベントストアへ登録を行い、ACKを返却" do
265
+ @header.should_receive(:ack)
266
+ raw_event = Tengine::Event.new(:key => "uuid99", :sender_name => "another_host", :event_type_name => "event1")
267
+ Tengine::Core::Event.should_receive(:create!).and_return(Tengine::Core::Event.new(raw_event.attributes))
268
+ @kernel.process_message(@header, raw_event.to_json).should be_true
269
+ end
270
+ end
271
+
272
+ context "イベント処理失敗イベントの発火" do
273
+ before do
274
+ # eventmachine と mq の mock を生成
275
+ mock_mq = mock("mq")
276
+ @kernel.stub(:mq).and_return(mock_mq)
277
+ mock_mq.stub(:initiate_termination).and_yield
278
+ mock_mq.stub(:unsubscribe).and_yield
279
+ mock_mq.stub(:stop).and_yield
280
+ mock_mq.stub(:add_hook)
281
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
282
+ h[:confirm].yield(mock("confirm-ok"))
283
+ b.yield(@header, :message)
284
+ end
285
+
286
+ # subscribe してみる
287
+ @raw_event = Tengine::Event.new(:key => "uuid1", :sender_name => "localhost", :event_type_name => "event1")
288
+ Tengine::Event.should_receive(:parse).with(:message).and_return(@raw_event)
289
+ @header.should_receive(:ack)
290
+ end
291
+
292
+ it "既に登録されているイベントとkey, sender_nameが一致するイベントを受信した場合、発火" do
293
+ mock_sender = mock(:sender)
294
+ Tengine::Event::Sender.should_receive(:new).with(@kernel.mq).and_return(mock_sender)
295
+ mock_sender.should_receive(:default_keep_connection=).with(true)
296
+ mock_sender.should_receive(:fire).with("#{@raw_event.event_type_name}.failed.tengined",
297
+ {
298
+ :level => Tengine::Event::LEVELS_INV[:error],
299
+ :properties => { :original_event => @raw_event }
300
+ })
301
+ mock_sender.stub(:fire).with("finished.process.core.tengine", an_instance_of(Hash)).and_yield
302
+ @kernel.start { @kernel.stop }
303
+ events = Tengine::Core::Event.where(:key => @raw_event.key, :sender_name => @raw_event.sender_name)
304
+ events.count.should == 1
305
+ end
306
+ end
307
+
308
+ it "イベント種別に対応したハンドラの処理を実行することができる" do
309
+ # eventmachine と mq の mock を生成
310
+ mock_mq = mock("mq")
311
+ mock_sender = mock("sender")
312
+ @kernel.stub(:mq).and_return(mock_mq)
313
+ @kernel.stub(:sender).and_return(mock_sender)
314
+ mock_sender.stub(:fire)
315
+ mock_mq.stub(:initiate_termination).and_yield
316
+ mock_mq.stub(:unsubscribe).and_yield
317
+ mock_mq.stub(:stop).and_yield
318
+ mock_mq.stub(:add_hook)
319
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
320
+ h[:confirm].yield(mock("confirm-ok"))
321
+ b.yield(@header, :message)
322
+ end
323
+
324
+ # subscribe してみる
325
+ mock_raw_event = mock(:row_event)
326
+ mock_raw_event.stub!(:key).and_return("uuid")
327
+ mock_raw_event.stub!(:sender_name).and_return("localhost")
328
+ mock_raw_event.should_receive(:attributes).and_return(:event_type_name => :event01, :key => "uuid")
329
+ mock_raw_event.stub!(:level).and_return(Tengine::Event::LEVELS_INV[:info])
330
+ mock_raw_event.stub!(:event_type_name).and_return("event01")
331
+ Tengine::Event.should_receive(:parse).with(:message).and_return(mock_raw_event)
332
+ # イベントの登録
333
+ Tengine::Core::Event.should_receive(:create!).with(:event_type_name => :event01, :key => "uuid", :confirmed => true).and_return(@event1)
334
+
335
+ # ハンドラの実行を検証
336
+ Tengine::Core::HandlerPath.should_receive(:find_handlers).with("event01").and_return([@handler1])
337
+ @handler1.should_receive(:match?).with(@event1).and_return(true)
338
+
339
+ # 仕様変更のためイベントハンドラの処理を確認するのは一旦コメントアウトしました
340
+ # @kernel.context.should_receive(:puts).with("handler01")
341
+
342
+ @header.should_receive(:ack)
343
+
344
+ # 実行
345
+ @kernel.start
346
+ end
347
+
348
+ context "*.failed.tengine" do
349
+ before do
350
+ @uuid = ::UUID.new
351
+ @header.stub(:ack)
352
+ @sender = mock(:sender)
353
+ @kernel.stub(:sender).and_return(@sender)
354
+ @sender.should_not_receive(:fire)
355
+ end
356
+
357
+ context "正常系" do
358
+ it "メッセージストアに保存" do
359
+ e = Tengine::Event.new key: @uuid.generate, event_type_name: "something.failed.tengine"
360
+
361
+ @kernel.process_message @header, e.to_json
362
+
363
+ Tengine::Core::Event.where(key: e.key).count.should == 1
364
+ Tengine::Core::Event.where(key: e.key).first.event_type_name.should =~ /failed\.tengine$/
365
+ end
366
+ end
367
+
368
+ context "異常系" do
369
+ it "無限地獄の回避" do
370
+ Tengine::Core::Event.stub(:create!).and_raise(Mongo::OperationFailure.new)
371
+ e = Tengine::Event.new key: @uuid.generate, event_type_name: "something.failed.tengine"
372
+
373
+ @kernel.process_message @header, e.to_json
374
+ end
375
+ end
376
+ end
377
+
378
+ context "heartbeat" do
379
+ before do
380
+ @uuid = ::UUID.new
381
+ @header.stub(:ack)
382
+ @sender = mock(:sender)
383
+ @kernel.stub(:sender).and_return(@sender)
384
+ @sender.should_not_receive(:fire)
385
+ end
386
+
387
+ shared_examples "generic heartbeats" do
388
+ context "正常系" do
389
+ context "初回" do
390
+ before do
391
+ @u = @uuid.generate
392
+ Tengine::Core::Event.where(key: @u).delete_all
393
+ end
394
+ it "beatは保存される" do
395
+ @kernel.process_message(@header, Tengine::Event.new(key: @u, event_type_name: "#{kind}.heartbeat.tengine").to_json).should be_true
396
+ Tengine::Core::Event.where(key: @u).count.should == 1
397
+ Tengine::Core::Event.where(key: @u).first.event_type_name.should =~ /^#{kind}/
398
+ end
399
+
400
+ it "finishedは保存される" do
401
+ @kernel.process_message(@header, Tengine::Event.new(key: @u, event_type_name: "finished.process.#{kind}.tengine").to_json).should be_true
402
+ Tengine::Core::Event.where(key: @u).count.should == 1
403
+ Tengine::Core::Event.where(key: @u).first.event_type_name.should =~ /^finished/
404
+ end
405
+
406
+ it "expiredは保存される" do
407
+ @kernel.process_message(@header, Tengine::Event.new(key: @u, event_type_name: "expired.#{kind}.heartbeat.tengine").to_json).should be_true
408
+ Tengine::Core::Event.where(key: @u).count.should == 1
409
+ Tengine::Core::Event.where(key: @u).first.event_type_name.should =~ /^expired/
410
+ end
411
+ end
412
+
413
+ it "beat -> beat -> beat" do
414
+ e = Tengine::Event.new key: @uuid.generate, event_type_name: "#{kind}.heartbeat.tengine"
415
+
416
+ @kernel.process_message @header, e.to_json
417
+ @kernel.process_message @header, e.to_json
418
+ @kernel.process_message @header, e.to_json
419
+
420
+ Tengine::Core::Event.where(key: e.key).count.should == 1
421
+ Tengine::Core::Event.where(key: e.key).first.event_type_name.should =~ /^#{kind}/
422
+ end
423
+
424
+ it "beat -> finished (finishedが勝つ)" do
425
+ u = @uuid.generate
426
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "#{kind}.heartbeat.tengine").to_json
427
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "finished.process.#{kind}.tengine").to_json
428
+
429
+ Tengine::Core::Event.where(key: u).count.should == 1
430
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /finished/
431
+ end
432
+
433
+ it "beat -> expired (expiredが勝つ)" do
434
+ u = @uuid.generate
435
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "#{kind}.heartbeat.tengine").to_json
436
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "expired.#{kind}.heartbeat.tengine").to_json
437
+
438
+ Tengine::Core::Event.where(key: u).count.should == 1
439
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /expired/
440
+ end
441
+
442
+ it "beat -> finish -> expired (expiredが勝つ)" do
443
+ u = @uuid.generate
444
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "#{kind}.heartbeat.tengine").to_json
445
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "finished.process.#{kind}.tengine").to_json
446
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "expired.#{kind}.heartbeat.tengine").to_json
447
+
448
+ Tengine::Core::Event.where(key: u).count.should == 1
449
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /expired/
450
+ end
451
+
452
+ it "finished -> beat (finishedが勝つ)" do
453
+ u = @uuid.generate
454
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "finished.process.#{kind}.tengine").to_json
455
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "#{kind}.heartbeat.tengine").to_json
456
+
457
+ Tengine::Core::Event.where(key: u).count.should == 1
458
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /finished/
459
+ end
460
+
461
+ it "finished -> finished (上書き)" do
462
+ e = Tengine::Event.new key: @uuid.generate, event_type_name: "finished.process.#{kind}.tengine"
463
+
464
+ @kernel.process_message @header, e.to_json
465
+ @kernel.process_message @header, e.to_json
466
+
467
+ Tengine::Core::Event.where(key: e.key).count.should == 1
468
+ Tengine::Core::Event.where(key: e.key).first.event_type_name.should =~ /finished/
469
+ end
470
+
471
+ it "finished -> expired (expiredが勝つ)" do
472
+ u = @uuid.generate
473
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "finished.process.#{kind}.tengine").to_json
474
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "expired.#{kind}.heartbeat.tengine").to_json
475
+
476
+ Tengine::Core::Event.where(key: u).count.should == 1
477
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /expired/
478
+ end
479
+
480
+ it "expired -> beat (expiredが勝つ)" do
481
+ u = @uuid.generate
482
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "expired.#{kind}.heartbeat.tengine").to_json
483
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "#{kind}.heartbeat.tengine").to_json
484
+
485
+ Tengine::Core::Event.where(key: u).count.should == 1
486
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /expired/
487
+ end
488
+
489
+ it "expired -> finished (expiredが勝つ)" do
490
+ u = @uuid.generate
491
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "expired.#{kind}.heartbeat.tengine").to_json
492
+ @kernel.process_message @header, Tengine::Event.new(key: u, event_type_name: "finished.process.#{kind}.tengine").to_json
493
+
494
+ Tengine::Core::Event.where(key: u).count.should == 1
495
+ Tengine::Core::Event.where(key: u).first.event_type_name.should =~ /expired/
496
+ end
497
+
498
+ it "expired -> expired (上書き)" do
499
+ e = Tengine::Event.new key: @uuid.generate, event_type_name: "expired.#{kind}.heartbeat.tengine"
500
+
501
+ @kernel.process_message @header, e.to_json
502
+ @kernel.process_message @header, e.to_json
503
+
504
+ Tengine::Core::Event.where(key: e.key).count.should == 1
505
+ Tengine::Core::Event.where(key: e.key).first.event_type_name.should =~ /expired/
506
+ end
507
+ end
508
+
509
+ context "異常系" do
510
+ ['"#{kind}.heartbeat.tengine"',
511
+ '"finished.process.#{kind}.tengine"',
512
+ '"expired.#{kind}.heartbeat.tengine"'
513
+ ].each do |name|
514
+ context name do
515
+ it "Mongo::OperationFailureの場合、failed eventを連鎖" do
516
+ @sender.stub(:fire).with("#{kind}.heartbeat.tengine.failed.tengine", an_instance_of(Hash))
517
+ Tengine::Core::Event.stub(:create!).and_raise(Mongo::OperationFailure.new)
518
+ @kernel.stub(:upsert).and_raise Mongo::OperationFailure
519
+
520
+ @kernel.process_message @header, Tengine::Event.new(key: @uuid.generate, event_type_name: eval(name)).to_json
521
+ end
522
+
523
+ it "Mongoid::Errors::Validationの場合、failed eventを連鎖" do
524
+ x = Tengine::Core::Event.new
525
+ x.valid? # false
526
+ @sender.stub(:fire).with("#{kind}.heartbeat.tengine.failed.tengine", an_instance_of(Hash))
527
+ Tengine::Core::Event.stub(:create!).and_raise(Mongoid::Errors::Validations.new(x))
528
+ @kernel.stub(:upsert).and_raise Mongoid::Errors::Validations.new(x)
529
+
530
+ @kernel.process_message @header, Tengine::Event.new(key: @uuid.generate, event_type_name: eval(name)).to_json
531
+ end
532
+
533
+ it "その他の場合、例外を外に伝播しない" do
534
+ Tengine::Core::Event.stub(:find_or_create_by_key_then_update_with_block).and_raise StandardError
535
+ expect do
536
+ @kernel.process_message @header, Tengine::Event.new(key: @uuid.generate, event_type_name: eval(name)).to_json
537
+ end.to_not raise_exception(StandardError)
538
+ @kernel.process_message @header, Tengine::Event.new(key: @uuid.generate, event_type_name: eval(name)).to_json
539
+ end
540
+ end
541
+ end
542
+ end
543
+ end
544
+
545
+ describe "job heartbeat" do
546
+ let(:kind) {"job"}
547
+ it_behaves_like "generic heartbeats"
548
+ end
549
+
550
+ describe "core heartbeat" do
551
+ let(:kind) {"core"}
552
+ it_behaves_like "generic heartbeats"
553
+ end
554
+
555
+ describe "heartbeat watcher's heartbeat" do
556
+ let(:kind) {"hbw"}
557
+ it_behaves_like "generic heartbeats"
558
+ end
559
+
560
+ describe "atd heartbeat" do
561
+ let(:kind) {"atd"}
562
+ it_behaves_like "generic heartbeats"
563
+ end
564
+ end
565
+
566
+ describe "schedule" do
567
+ it "tenginedが調停する" do
568
+ @header.stub(:ack)
569
+ n = "alert.execution.job.tengine"
570
+ s = "test test"
571
+ e = Tengine::Event.new event_type_name: n, source_name: s
572
+
573
+ @kernel.process_message @header, e.to_json
574
+ @kernel.process_message @header, e.to_json
575
+ @kernel.process_message @header, e.to_json
576
+
577
+ Tengine::Core::Event.where(event_type_name: n, source_name: s).count.should == 1
578
+ end
579
+
580
+ it "tenginedが調停する #2" do
581
+ @header.stub(:ack)
582
+ n = "alert.execution.job.tengine"
583
+ s = "test test"
584
+ e = Tengine::Event.new event_type_name: n, source_name: s, key: "k1"
585
+ f = Tengine::Event.new event_type_name: n, source_name: s, key: "k2"
586
+ g = Tengine::Event.new event_type_name: n, source_name: s, key: "k3"
587
+
588
+ @kernel.process_message @header, e.to_json
589
+ @kernel.process_message @header, f.to_json
590
+ @kernel.process_message @header, g.to_json
591
+
592
+ Tengine::Core::Event.where(event_type_name: n, source_name: s).count.should == 1
593
+ end
594
+ end
595
+ end
596
+ end
597
+
598
+ describe :setup_mq_connection do
599
+ if RUBY_VERSION >= "1.9.2"
600
+ before do
601
+ EM.instance_eval do
602
+ @timers.each {|i| EM.cancel_timer i } if @timers
603
+ @next_tick_queue = nil
604
+ end
605
+ trigger
606
+ config = Tengine::Core::Config::Core.new({
607
+ :tengined => {
608
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
609
+ },
610
+ :event_queue => { :connection => { :port => @port } }
611
+ })
612
+ @kernel = Tengine::Core::Kernel.new(config)
613
+ end
614
+
615
+ let(:rabbitmq) do
616
+ ret = nil
617
+ ENV["PATH"].split(/:/).find do |dir|
618
+ Dir.glob("#{dir}/rabbitmq-server") do |path|
619
+ if File.executable?(path)
620
+ ret = path
621
+ break
622
+ end
623
+ end
624
+ end
625
+
626
+ pending "rabbitmq が見つかりません" unless ret
627
+ ret
628
+ end
629
+
630
+ def trigger port = rand(32768)
631
+ require 'tmpdir'
632
+ @dir = Dir.mktmpdir
633
+ # 指定したポートはもう使われているかもしれないので、その際は
634
+ # rabbitmqが起動に失敗するので、何回かポートを変えて試す。
635
+ n = 0
636
+ begin
637
+ envp = {
638
+ "RABBITMQ_NODENAME" => "rspec",
639
+ "RABBITMQ_NODE_PORT" => port.to_s,
640
+ "RABBITMQ_NODE_IP_ADDRESS" => "auto",
641
+ "RABBITMQ_MNESIA_BASE" => @dir.to_s,
642
+ "RABBITMQ_LOG_BASE" => @dir.to_s,
643
+ }
644
+ @pid = Process.spawn(envp, rabbitmq, :chdir => @dir, :in => :close, :out => '/dev/null', :err => '/dev/null')
645
+ x = Time.now
646
+ while Time.now < x + 64 do # まあこんくらい待てばいいでしょ
647
+ sleep 0.1
648
+ Process.waitpid2(@pid, Process::WNOHANG)
649
+ Process.kill 0, @pid
650
+ # netstat -an は Linux / BSD ともに有効
651
+ # どちらかに限ればもう少し効率的な探し方はある。たとえば Linux 限定でよければ netstat -lnt ...
652
+ y = `netstat -an | fgrep LISTEN | fgrep #{port}`
653
+ if y.lines.to_a.size > 1
654
+ @port = port
655
+ return
656
+ end
657
+ end
658
+ pending "failed to invoke rabbitmq in 16 secs."
659
+ rescue Errno::ECHILD, Errno::ESRCH
660
+ pending "10 attempts to invoke rabbitmq failed." if (n += 1) > 10
661
+ port = rand(32768)
662
+ retry
663
+ end
664
+ end
665
+
666
+ def finish
667
+ if @pid
668
+ begin
669
+ Process.kill "INT", @pid
670
+ Process.waitpid @pid
671
+ rescue Errno::ECHILD, Errno::ESRCH
672
+ ensure
673
+ require 'fileutils'
674
+ FileUtils.remove_entry_secure @dir, :force
675
+ end
676
+ end
677
+ end
678
+
679
+ after do
680
+ finish
681
+ end
682
+
683
+ it "MQ接続時にエラーなどのイベントハンドリングを行います" do
684
+ EM.run do
685
+ mq = @kernel.mq
686
+
687
+ @kernel.setup_mq_connection
688
+
689
+ # ここではイベント発生時の振る舞いもチェックします
690
+ @kernel.subscribe_queue do
691
+ Tengine::Core.stderr_logger.should_receive(:warn).with('mq.connection.on_tcp_connection_loss.').at_least(1).times
692
+ finish
693
+ EM.add_timer(1) do
694
+ Tengine::Core.stderr_logger.should_receive(:info).with('mq.connection.after_recovery: recovered successfully.')
695
+ EM.defer(
696
+ lambda { trigger @port; sleep 2; true },
697
+ lambda do |a|
698
+ Tengine::Core.stderr_logger.should_receive(:error).with('mq.channel.on_error channel_close: "channel close reason object"')
699
+ mq.channel.exec_callback_once_yielding_self(:error, "channel close reason object")
700
+
701
+ Tengine::Core.stderr_logger.should_receive(:error).with('mq.connection.on_error connection_close: "connection close reason object"')
702
+ mq.connection.exec_callback_yielding_self(:error, "connection close reason object")
703
+
704
+ EM.next_tick do
705
+ mq.stop
706
+ end
707
+ end
708
+ )
709
+ end
710
+ end
711
+ end
712
+ end
713
+ end
714
+ end
715
+
716
+ describe :status do
717
+ describe :starting do
718
+ before do
719
+ config = Tengine::Core::Config::Core.new({
720
+ :tengined => {
721
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
722
+ },
723
+ })
724
+ @kernel = Tengine::Core::Kernel.new(config)
725
+ end
726
+
727
+ it "カーネルのインスタンス生成直後は「初期化済み」の状態を返す" do
728
+ @kernel.status.should == :initialized
729
+ end
730
+
731
+ it "起動要求を受け取った直後は「起動中」の状態を返す", :start => true do
732
+ @kernel.should_receive(:bind)
733
+ @kernel.should_receive(:activate)
734
+
735
+ @kernel.start
736
+ @kernel.status.should == :starting
737
+ end
738
+
739
+ it "内部で使用されるupdate_statusにおかしな値を入れるとArgumentError" do
740
+ expect {
741
+ @kernel.send(:update_status, :invalid_status)
742
+ }.to raise_error(ArgumentError)
743
+ end
744
+ end
745
+
746
+ describe :waiting_for_activation do
747
+ before do
748
+ config = Tengine::Core::Config::Core.new({
749
+ :tengined => {
750
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
751
+ :wait_activation => true,
752
+ :activation_timeout => 3,
753
+ :activation_dir => File.expand_path('.', File.dirname(__FILE__)),
754
+ },
755
+ })
756
+ @kernel = Tengine::Core::Kernel.new(config)
757
+ end
758
+
759
+ it "起動処理が終了した直後に「稼働要求待ち」の状態を返す" do
760
+ @kernel.should_receive(:bind)
761
+ @kernel.should_receive(:wait_for_activation)
762
+
763
+ @kernel.start
764
+ @kernel.status.should == :waiting_activation
765
+ end
766
+ end
767
+
768
+ describe :running do
769
+ before do
770
+ config = Tengine::Core::Config::Core.new({
771
+ :tengined => {
772
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
773
+ },
774
+ })
775
+ @kernel = Tengine::Core::Kernel.new(config)
776
+ @kernel.should_receive(:bind)
777
+ end
778
+
779
+ it "稼働要求を受け取った直後では「稼働中」の状態を返す" do
780
+ mock_mq = mock("mq")
781
+ mock_sender = mock("sender")
782
+ @kernel.stub(:mq).and_return(mock_mq)
783
+ @kernel.stub(:sender).and_return(mock_sender)
784
+ mock_sender.stub(:fire).and_yield
785
+ mock_mq.stub(:initiate_termination).and_yield
786
+ mock_mq.stub(:unsubscribe).and_yield
787
+ mock_mq.stub(:stop).and_yield
788
+ mock_mq.stub(:add_hook)
789
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
790
+ h[:confirm].yield(mock("confirm-ok"))
791
+ end
792
+
793
+ @kernel.should_receive(:setup_mq_connection)
794
+ @kernel.start do
795
+ @kernel.status.should == :running
796
+ @kernel.stop
797
+ end
798
+ end
799
+ end
800
+
801
+ describe :stop do
802
+ it "停止要求を受け取った直後では「停止中」および「停止済」の状態を返す(稼働中)" do
803
+ config = Tengine::Core::Config::Core.new({
804
+ :tengined => {
805
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
806
+ },
807
+ })
808
+ kernel = Tengine::Core::Kernel.new(config)
809
+ kernel.should_receive(:bind)
810
+
811
+ mock_mq = mock("mq")
812
+ mock_sender = mock("sender")
813
+ kernel.stub(:mq).and_return(mock_mq)
814
+ kernel.stub(:sender).and_return(mock_sender)
815
+ mock_sender.stub(:fire).and_yield
816
+ mock_mq.stub(:initiate_termination).and_yield
817
+ mock_mq.stub(:unsubscribe).and_yield
818
+ mock_mq.stub(:stop).and_yield
819
+ mock_mq.stub(:add_hook)
820
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
821
+ h[:confirm].yield(mock("confirm-ok"))
822
+ end
823
+
824
+ kernel.start do
825
+ kernel.status.should == :running
826
+
827
+ kernel.stop do
828
+ kernel.status.should == :terminated
829
+ end
830
+ end
831
+ end
832
+
833
+ it "停止要求を受け取った直後では「停止中」および「停止済」の状態を返す(稼働要求待ち)" do
834
+ config = Tengine::Core::Config::Core.new({
835
+ :tengined => {
836
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
837
+ :wait_activation => true,
838
+ :activation_timeout => 3,
839
+ :activation_dir => File.expand_path('.', File.dirname(__FILE__)),
840
+ },
841
+ })
842
+ kernel = Tengine::Core::Kernel.new(config)
843
+ kernel.should_receive(:bind)
844
+
845
+ lambda {
846
+ kernel.start
847
+ # kernel.stop
848
+ }.should raise_error(Tengine::Core::ActivationTimeoutError, "activation file found timeout error.")
849
+ kernel.status.should == :shutting_down
850
+ end
851
+
852
+ it "heartbeatの停止" do
853
+ kernel = Tengine::Core::Kernel.new(Tengine::Core::Config::Core.new({
854
+ :tengined => {
855
+ :load_path => File.expand_path('../../../examples/uc01_execute_processing_for_event.rb', File.dirname(__FILE__)),
856
+ :wait_activation => true,
857
+ :activation_timeout => 3,
858
+ :activation_dir => File.expand_path('.', File.dirname(__FILE__)),
859
+ },
860
+ }))
861
+ kernel.instance_eval do
862
+ @status = :running
863
+ @heartbeat_timer = true
864
+ end
865
+ mock_mq = mock("mq")
866
+ kernel.stub(:mq).and_return(mock_mq)
867
+ mock_mq.stub(:initiate_termination).and_yield
868
+ mock_mq.stub(:unsubscribe).and_yield
869
+ mock_mq.stub(:stop).and_yield
870
+ mock_mq.stub(:add_hook)
871
+ mock_mq.stub(:subscribe).with(nil).with(:ack => true, :nowait => false, :confirm => an_instance_of(Proc)) do |h, b|
872
+ h[:confirm].yield(mock("confirm-ok"))
873
+ end
874
+ sender = mock(:sender)
875
+ kernel.stub(:sender).and_return(sender)
876
+
877
+ EM.should_receive(:cancel_timer)
878
+ sender.should_receive(:fire).with("finished.process.core.tengine", an_instance_of(Hash))
879
+
880
+ kernel.stop
881
+ end
882
+ end
883
+ end
884
+
885
+ end