PageRenderTime 32ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/test/functional/ft_5_on_error.rb

http://github.com/jmettraux/ruote
Ruby | 739 lines | 521 code | 168 blank | 50 comment | 7 complexity | 3998a6cfd20fd9a9ce4bb572bf93b030 MD5 | raw file
  1. #
  2. # testing ruote
  3. #
  4. # Tue Jun 2 18:48:02 JST 2009
  5. #
  6. require File.expand_path('../base', __FILE__)
  7. require 'ruote/participant'
  8. class FtOnErrorTest < Test::Unit::TestCase
  9. include FunctionalBase
  10. class TroubleMaker
  11. include Ruote::LocalParticipant
  12. def consume(workitem)
  13. hits = (workitem.fields['hits'] || 0) + 1
  14. workitem.fields['hits'] = hits
  15. workitem.trace << "#{hits.to_s}\n"
  16. raise 'Houston, we have a problem !' if hits == 1
  17. workitem.trace << 'done.'
  18. reply(workitem)
  19. end
  20. def cancel(fei, flavour)
  21. # nothing to do
  22. end
  23. end
  24. def test_on_error
  25. pdef = Ruote.process_definition do
  26. sequence :on_error => 'catcher' do
  27. nada
  28. end
  29. end
  30. @dashboard.register_participant :catcher do
  31. tracer << "caught\n"
  32. end
  33. assert_trace('caught', pdef)
  34. assert_equal 1, logger.log.select { |e| e['action'] == 'fail' }.size
  35. end
  36. def test_on_error_unknown_participant_name
  37. pdef = Ruote.process_definition :name => 'test' do
  38. participant :mark_started
  39. sequence :on_error => :mark_failed do
  40. participant :bogus
  41. end
  42. participant :mark_finished
  43. end
  44. @dashboard.context.stash[:marks] = []
  45. @dashboard.register_participant 'mark\_.+' do |workitem|
  46. stash[:marks] << workitem.participant_name
  47. end
  48. wfid = @dashboard.launch(pdef)
  49. wait_for(wfid)
  50. assert_equal(
  51. %w[ mark_started mark_failed mark_finished ],
  52. @dashboard.context.stash[:marks])
  53. end
  54. def test_on_error_unknown_participant_name_2
  55. pdef = Ruote.process_definition :name => 'test' do
  56. participant :mark_started
  57. participant :bogus, :on_error => :mark_failed
  58. participant :mark_finished
  59. end
  60. @dashboard.context.stash[:marks] = []
  61. @dashboard.register_participant 'mark\_.+' do |workitem|
  62. stash[:marks] << workitem.participant_name
  63. end
  64. wfid = @dashboard.launch(pdef)
  65. wait_for(wfid)
  66. assert_equal(
  67. %w[ mark_started mark_failed mark_finished ],
  68. @dashboard.context.stash[:marks])
  69. end
  70. def test_on_error_neutralization
  71. pdef = Ruote.process_definition do
  72. sequence :on_error => 'catcher' do
  73. sequence :on_error => '' do
  74. nada
  75. end
  76. end
  77. end
  78. @dashboard.register_participant :catcher do
  79. tracer << "caught\n"
  80. end
  81. wfid = @dashboard.launch(pdef)
  82. wait_for(wfid)
  83. ps = @dashboard.process(wfid)
  84. assert_equal(1, ps.errors.size)
  85. end
  86. # redo and retry are aliases, the faulty segment is retried
  87. #
  88. def test_on_error_redo
  89. pdef = Ruote.process_definition do
  90. sequence :on_error => :redo do
  91. troublemaker
  92. end
  93. end
  94. @dashboard.register_participant :troublemaker, TroubleMaker
  95. assert_trace(%w[ 1 2 done. ], pdef)
  96. end
  97. # redo and retry are aliases, the faulty segment is retried
  98. #
  99. def test_on_error_retry
  100. pdef = Ruote.process_definition do
  101. sequence :on_error => :retry do
  102. troublemaker
  103. end
  104. end
  105. @dashboard.register_participant :troublemaker, TroubleMaker
  106. assert_trace(%w[ 1 2 done. ], pdef)
  107. end
  108. def test_on_error_raise
  109. pdef = Ruote.define do
  110. sequence :on_error => :raise do
  111. error 'nada'
  112. end
  113. end
  114. wfid = @dashboard.launch(pdef)
  115. r = @dashboard.wait_for(wfid)
  116. assert_equal 'error_intercepted', r['action']
  117. assert_equal 'nada', r['error']['message']
  118. end
  119. def test_on_error_undo
  120. pdef = Ruote.process_definition do
  121. sequence do
  122. echo 'a'
  123. sequence :on_error => :undo do
  124. echo 'b'
  125. nemo
  126. echo 'c'
  127. end
  128. echo 'd'
  129. end
  130. end
  131. wfid = assert_trace(%w[ a b d ], pdef)
  132. assert_nil @dashboard.process(wfid)
  133. end
  134. def test_on_error_undo_single_expression
  135. @dashboard.register_participant :nemo do |wi|
  136. wi.fields['fail_count'] = 1
  137. raise 'nemo'
  138. end
  139. pdef = Ruote.process_definition do
  140. sequence do
  141. echo 'in'
  142. nemo :on_error => 'undo'
  143. echo '${f:error}|${f:fail_count}'
  144. end
  145. end
  146. wfid = assert_trace(%w[ in |1 ], pdef)
  147. assert_nil @dashboard.process(wfid)
  148. end
  149. def test_on_error_pass
  150. pdef = Ruote.process_definition do
  151. sequence do
  152. echo 'a'
  153. sequence :on_error => :pass do
  154. echo 'b'
  155. nemo
  156. echo 'c'
  157. end
  158. echo 'd'
  159. end
  160. end
  161. wfid = assert_trace(%w[ a b d ], pdef)
  162. assert_nil @dashboard.process(wfid)
  163. end
  164. def test_missing_handler_triggers_regular_error
  165. pdef = Ruote.process_definition :on_error => 'failpath' do
  166. nemo
  167. end
  168. wfid = @dashboard.launch(pdef)
  169. wait_for(wfid)
  170. ps = @dashboard.process(wfid)
  171. assert_equal 1, ps.errors.size
  172. assert_equal 1, logger.log.select { |e| e['action'] == 'error_intercepted' }.size
  173. end
  174. def test_on_error_at_process_level
  175. pdef = Ruote.process_definition :on_error => 'failpath' do
  176. nemo
  177. define :failpath do
  178. echo 'failed.'
  179. end
  180. end
  181. assert_trace('failed.', pdef)
  182. end
  183. def test_with_concurrence
  184. pdef = Ruote.process_definition do
  185. sequence do
  186. concurrence :on_error => 'emil' do
  187. alpha
  188. error 'nada0'
  189. error 'nada1'
  190. end
  191. echo 'done.'
  192. end
  193. end
  194. @dashboard.context.stash[:a_count] = 0
  195. @dashboard.context.stash[:e_count] = 0
  196. @dashboard.register_participant(:alpha) { |wi| stash[:a_count] += 1 }
  197. @dashboard.register_participant(:emil) { |wi| stash[:e_count] += 1 }
  198. assert_trace 'done.', pdef
  199. assert_equal 1, @dashboard.context.stash[:a_count]
  200. assert_equal 1, @dashboard.context.stash[:e_count]
  201. end
  202. def test_participant_on_error
  203. pdef = Ruote.process_definition do
  204. troublemaker :on_error => 'handle_error'
  205. define 'handle_error' do
  206. troublespotter
  207. end
  208. end
  209. @dashboard.register_participant :troublemaker do |wi|
  210. wi.fields['seen'] = true
  211. raise 'Beijing, we have a problem !'
  212. end
  213. @dashboard.register_participant :troublespotter do |wi|
  214. stash[:workitem] = wi
  215. tracer << 'err...'
  216. end
  217. wfid = @dashboard.launch(pdef)
  218. wait_for(wfid)
  219. #er = @dashboard.process(wfid).errors.first
  220. #puts er.message
  221. #puts er.trace
  222. wi = @dashboard.context.stash[:workitem]
  223. assert_equal 'err...', @tracer.to_s
  224. assert_equal 'RuntimeError', wi.error['class']
  225. assert_equal 'Beijing, we have a problem !', wi.error['message']
  226. assert_equal Array, wi.error['trace'].class
  227. assert_equal true, wi.fields['seen']
  228. assert_equal(
  229. %w[ at class details deviations fei message trace tree ],
  230. wi.error.keys.sort)
  231. end
  232. class Murphy
  233. include Ruote::LocalParticipant
  234. def cancel(fei, flavour)
  235. # nothing to do
  236. end
  237. def consume(workitem)
  238. raise "something got wrong"
  239. end
  240. end
  241. def test_subprocess_on_error
  242. pdef = Ruote.process_definition do
  243. sequence :on_error => 'error_path' do
  244. murphy
  245. end
  246. define 'error_path' do
  247. catcher
  248. end
  249. end
  250. @dashboard.register do
  251. murphy FtOnErrorTest::Murphy
  252. catchall
  253. end
  254. @dashboard.launch(pdef)
  255. @dashboard.wait_for(:catcher)
  256. end
  257. class RescuerOne
  258. include Ruote::LocalParticipant
  259. def consume(workitem)
  260. @context.tracer << 'one'
  261. reply_to_engine(workitem)
  262. end
  263. def accept?(workitem)
  264. false
  265. end
  266. end
  267. class RescuerTwo
  268. include Ruote::LocalParticipant
  269. def consume(workitem)
  270. @context.tracer << 'two'
  271. reply_to_engine(workitem)
  272. end
  273. #def accept?(workitem)
  274. # true
  275. #end
  276. end
  277. def test_participants_and_accept
  278. pdef = Ruote.process_definition do
  279. sequence :on_error => 'rescuer' do
  280. nada
  281. end
  282. end
  283. @dashboard.register do
  284. rescuer RescuerOne
  285. rescuer RescuerTwo
  286. end
  287. assert_trace('two', pdef)
  288. assert_equal 1, logger.log.select { |e| e['action'] == 'fail' }.size
  289. end
  290. # Only to show what's behind :on_error
  291. #
  292. def test_on_error_multi
  293. pdef = Ruote.define do
  294. sequence :on_error => [
  295. [ /unknown participant/, 'alpha' ],
  296. [ nil, 'bravo' ]
  297. ] do
  298. nada
  299. end
  300. end
  301. @dashboard.register_participant /alpha|bravo/ do |workitem|
  302. tracer << workitem.participant_name
  303. end
  304. wfid = @dashboard.launch(pdef)
  305. @dashboard.wait_for(wfid)
  306. assert_equal 'alpha', @tracer.to_s
  307. end
  308. # Let's be open
  309. #
  310. def test_on_error_multi_nice
  311. pdef = Ruote.process_definition do
  312. sequence :on_error => [
  313. { /unknown participant/ => 'alpha' },
  314. { // => 'bravo' }
  315. ] do
  316. nada
  317. end
  318. end
  319. @dashboard.register_participant /alpha|bravo/ do |workitem|
  320. tracer << workitem.participant_name
  321. end
  322. wfid = @dashboard.launch(pdef)
  323. @dashboard.wait_for(wfid)
  324. assert_equal 'alpha', @tracer.to_s
  325. end
  326. def test_on_error_multi_pass
  327. @dashboard.register_participant /alpha|bravo/ do |workitem|
  328. tracer << workitem.participant_name
  329. end
  330. pdef = Ruote.define do
  331. sequence :on_error => [
  332. { /unknown participant/ => :pass },
  333. { // => 'bravo' }
  334. ] do
  335. nada
  336. end
  337. echo 'done.'
  338. end
  339. wfid = @dashboard.launch(pdef)
  340. @dashboard.wait_for(wfid)
  341. assert_equal 'done.', @tracer.to_s
  342. end
  343. def test_on_error_rewind
  344. pdef = Ruote.define do
  345. cursor :on_error => 'rewind' do
  346. echo 'in'
  347. inc 'v:counter'
  348. error 'fail', :if => '${v:counter} == 1'
  349. echo 'over.'
  350. end
  351. end
  352. wfid = @dashboard.launch(pdef)
  353. @dashboard.wait_for(wfid)
  354. assert_equal %w[ in in over. ], @tracer.to_a
  355. end
  356. def test_on_error_jump_to
  357. pdef = Ruote.define do
  358. cursor :on_error => 'jump to shark' do
  359. alpha
  360. error 'fail'
  361. bravo
  362. shark
  363. delta
  364. end
  365. end
  366. @dashboard.register '.+' do |workitem|
  367. tracer << workitem.participant_name + "\n"
  368. end
  369. wfid = @dashboard.launch(pdef)
  370. @dashboard.wait_for(wfid)
  371. assert_equal %w[ alpha shark delta ], @tracer.to_a
  372. end
  373. def test_on_error_var
  374. pdef = Ruote.define do
  375. define 'sub0' do
  376. set 'v:/a' => '$f:__error__'
  377. end
  378. sequence :on_error => 'sub0' do
  379. error 'nada'
  380. end
  381. end
  382. wfid = @dashboard.launch(pdef)
  383. r = @dashboard.wait_for(wfid)
  384. assert_equal(
  385. 'terminated',
  386. r['action'])
  387. assert_equal(
  388. %w[ at class details deviations fei message trace tree ],
  389. r['variables']['a'].keys.sort)
  390. assert_equal(
  391. [ 'error', { 'nada' => nil }, [] ],
  392. r['variables']['a']['tree'])
  393. end
  394. def test_on_error_kill_process
  395. pdef = Ruote.define do
  396. sequence do
  397. sequence :on_error => 'cancel_process' do
  398. nemo0
  399. end
  400. nemo1
  401. end
  402. end
  403. wfid = @dashboard.launch(pdef)
  404. r = @dashboard.wait_for(wfid)
  405. assert_equal 'terminated', r['action']
  406. end
  407. #
  408. # the "second take" feature
  409. def test_second_take
  410. @dashboard.register_participant :troublemaker, TroubleMaker
  411. pdef = Ruote.define do
  412. define 'sub0' do
  413. set '__on_error__' => 'redo'
  414. end
  415. sequence :on_error => 'sub0' do
  416. troublemaker
  417. end
  418. end
  419. wfid = @dashboard.launch(pdef)
  420. r = @dashboard.wait_for(wfid)
  421. assert_equal 'terminated', r['action']
  422. assert_equal 3, r['workitem']['fields']['_trace'].size
  423. end
  424. def test_blank_second_take
  425. pdef = Ruote.define do
  426. define 'sub0' do
  427. set '__on_error__' => ''
  428. end
  429. sequence :on_error => 'sub0' do
  430. nada
  431. end
  432. end
  433. wfid = @dashboard.launch(pdef)
  434. r = @dashboard.wait_for(wfid)
  435. assert_equal 'terminated', r['action']
  436. end
  437. def test_second_take_raise
  438. pdef = Ruote.define do
  439. define 'sub0' do
  440. set '__on_error__' => 'raise'
  441. end
  442. sequence :on_error => 'sub0' do
  443. error 'nada'
  444. end
  445. end
  446. wfid = @dashboard.launch(pdef)
  447. r = @dashboard.wait_for(wfid)
  448. assert_equal 'error_intercepted', r['action']
  449. assert_equal 'nada', r['error']['message']
  450. end
  451. # "stashing that for now"
  452. #
  453. # def test_second_take_skip
  454. #
  455. # pdef = Ruote.define do
  456. # define 'sub0' do
  457. # set '__on_error__' => 'cancel'
  458. # end
  459. # define 'sub1' do
  460. # set 'sub1' => true
  461. # end
  462. # sequence :on_error => 'sub0', :on_cancel => 'sub1' do
  463. # error 'nada'
  464. # end
  465. # end
  466. #
  467. # wfid = @dashboard.launch(pdef)
  468. # r = @dashboard.wait_for(wfid)
  469. #
  470. # #assert_equal 'error_intercepted', r['action']
  471. # #assert_equal 'nada', r['error']['message']
  472. # end
  473. # behaves like 'undo' when there is no on_cancel present
  474. #
  475. def test_on_error_cancel
  476. pdef = Ruote.define do
  477. sequence :on_error => 'cancel' do
  478. echo 'n'
  479. error 'nada'
  480. end
  481. end
  482. wfid = @dashboard.launch(pdef)
  483. r = @dashboard.wait_for(wfid)
  484. assert_equal 'terminated', r['action']
  485. assert_equal %w[ n ], @tracer.to_a
  486. end
  487. def test_on_error_and_on_cancel
  488. pdef = Ruote.define do
  489. define 'rollback' do
  490. echo 'rollback'
  491. end
  492. sequence :on_cancel => 'rollback', :on_error => 'cancel_process' do
  493. echo 'in'
  494. error 'nada'
  495. end
  496. end
  497. wfid = @dashboard.launch(pdef)
  498. r = @dashboard.wait_for(wfid)
  499. assert_equal 'terminated', r['action']
  500. assert_equal %w[ in rollback ], @tracer.to_a
  501. assert_equal(
  502. 1,
  503. @dashboard.logger.log.select { |m| m['action'] == 'cancel_process' }.size)
  504. end
  505. def test_on_error_immediate
  506. # the rollback isn't performed,
  507. # the !kill_process short-circuits cancelling the errored segment...
  508. pdef = Ruote.define do
  509. define 'rollback' do
  510. echo 'rollback'
  511. end
  512. sequence :on_error => '!kill_process' do
  513. sequence :on_cancel => 'rollback' do
  514. echo 'in'
  515. error 'nada'
  516. end
  517. end
  518. end
  519. wfid = @dashboard.launch(pdef)
  520. r = @dashboard.wait_for(wfid)
  521. assert_equal 'terminated', r['action']
  522. assert_equal %w[ in ], @tracer.to_a
  523. assert_nil @dashboard.ps(wfid)
  524. end
  525. def test_on_error_immediate_with_tree
  526. pdef = Ruote.define do
  527. define 'rollback' do
  528. echo 'rollback'
  529. end
  530. sequence :on_error => [ '!kill_process', {}, [] ] do
  531. sequence :on_cancel => 'rollback' do
  532. echo 'in'
  533. error 'nada'
  534. end
  535. end
  536. end
  537. wfid = @dashboard.launch(pdef)
  538. r = @dashboard.wait_for(wfid)
  539. assert_equal 'terminated', r['action']
  540. assert_equal %w[ in ], @tracer.to_a
  541. assert_nil @dashboard.ps(wfid)
  542. end
  543. def test_on_error_store_to_field
  544. pdef =
  545. Ruote.define do
  546. sequence :on_error => 'store: x' do
  547. error 'nada'
  548. end
  549. echo 'out'
  550. end
  551. wfid = @dashboard.launch(pdef)
  552. r = @dashboard.wait_for(wfid)
  553. assert_equal %w[ out ], @tracer.to_a
  554. assert_equal 'Ruote::ForcedError', r['workitem']['fields']['x']['class']
  555. end
  556. def test_on_error_store_to_variable
  557. pdef =
  558. Ruote.define do
  559. #sequence :on_error => 'store:v:y' do
  560. sequence :on_error => 'bury:v:y' do
  561. error 'nada'
  562. end
  563. echo 'out'
  564. end
  565. wfid = @dashboard.launch(pdef)
  566. r = @dashboard.wait_for(wfid)
  567. assert_equal 'terminated', r['action']
  568. assert_equal %w[ out ], @tracer.to_a
  569. assert_equal 'Ruote::ForcedError', r['variables']['y']['class']
  570. end
  571. end