scheduler_spec.rb 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. require 'rails_helper'
  2. RSpec.describe Scheduler do
  3. before do
  4. module SpecSpace
  5. class DelayedJobBackend
  6. def self.start
  7. # noop
  8. end
  9. # rubocop:disable Style/TrivialAccessors
  10. def self.reschedule=(reschedule)
  11. @reschedule = reschedule
  12. end
  13. # rubocop:enable Style/TrivialAccessors
  14. def self.reschedule?(_delayed_job)
  15. @reschedule || false
  16. end
  17. end
  18. end
  19. end
  20. after do
  21. SpecSpace.send(:remove_const, :DelayedJobBackend)
  22. end
  23. describe '.failed_jobs' do
  24. it 'does list failed jobs' do
  25. job = create(:scheduler, status: 'error', active: false)
  26. failed_list = described_class.failed_jobs
  27. expect(failed_list).to be_present
  28. expect(failed_list).to include(job)
  29. end
  30. end
  31. describe '.restart_failed_jobs' do
  32. it 'does restart failed jobs' do
  33. job = create(:scheduler, status: 'error', active: false)
  34. described_class.restart_failed_jobs
  35. job.reload
  36. expect(job.active).to be true
  37. end
  38. end
  39. describe '._start_job' do
  40. it 'sets error status/message for failed jobs' do
  41. job = create(:scheduler)
  42. described_class._start_job(job)
  43. expect(job.status).to eq 'error'
  44. expect(job.active).to be false
  45. expect(job.error_message).to be_present
  46. end
  47. it 'executes job that is expected to succeed' do
  48. expect(Setting).to receive(:reload)
  49. job = create(:scheduler, method: 'Setting.reload')
  50. described_class._start_job(job)
  51. expect(job.status).to eq 'ok'
  52. end
  53. end
  54. describe '.cleanup' do
  55. it 'gets called by .threads' do
  56. expect(described_class).to receive(:cleanup).and_throw(:called)
  57. expect do
  58. described_class.threads
  59. end.to throw_symbol(:called)
  60. end
  61. context 'not called from .threads method' do
  62. it 'throws an exception' do
  63. expect do
  64. described_class.cleanup
  65. end.to raise_error(RuntimeError)
  66. end
  67. it 'throws no exception with force parameter' do
  68. expect do
  69. described_class.cleanup(force: true)
  70. end.not_to raise_error
  71. end
  72. end
  73. # helpers to avoid the throwing behaviour "describe"d above
  74. def simulate_threads_call
  75. threads
  76. end
  77. def threads
  78. described_class.cleanup
  79. end
  80. context 'Delayed::Job' do
  81. it 'keeps unlocked' do
  82. # meta :)
  83. described_class.delay.cleanup
  84. expect do
  85. simulate_threads_call
  86. end.not_to change {
  87. Delayed::Job.count
  88. }
  89. end
  90. context 'locked' do
  91. it 'gets destroyed' do
  92. # meta :)
  93. described_class.delay.cleanup
  94. # lock job (simluates interrupted scheduler task)
  95. locked_job = Delayed::Job.last
  96. locked_job.update!(locked_at: Time.zone.now)
  97. expect do
  98. simulate_threads_call
  99. end.to change {
  100. Delayed::Job.count
  101. }.by(-1)
  102. end
  103. context 'respond to reschedule?' do
  104. it 'gets rescheduled for positive responses' do
  105. SpecSpace::DelayedJobBackend.reschedule = true
  106. SpecSpace::DelayedJobBackend.delay.start
  107. # lock job (simluates interrupted scheduler task)
  108. locked_job = Delayed::Job.last
  109. locked_job.update!(locked_at: Time.zone.now)
  110. expect do
  111. simulate_threads_call
  112. end.to not_change {
  113. Delayed::Job.count
  114. }.and change {
  115. Delayed::Job.last.locked_at
  116. }
  117. end
  118. it 'gets destroyed for negative responses' do
  119. SpecSpace::DelayedJobBackend.reschedule = false
  120. SpecSpace::DelayedJobBackend.delay.start
  121. # lock job (simluates interrupted scheduler task)
  122. locked_job = Delayed::Job.last
  123. locked_job.update!(locked_at: Time.zone.now)
  124. expect do
  125. simulate_threads_call
  126. end.to change {
  127. Delayed::Job.count
  128. }.by(-1)
  129. end
  130. end
  131. end
  132. end
  133. context 'ImportJob' do
  134. context 'affected job' do
  135. let(:job) { create(:import_job, started_at: 5.minutes.ago) }
  136. it 'finishes stuck jobs' do
  137. expect do
  138. simulate_threads_call
  139. end.to change {
  140. job.reload.finished_at
  141. }
  142. end
  143. it 'adds an error message to the result' do
  144. expect do
  145. simulate_threads_call
  146. end.to change {
  147. job.reload.result[:error]
  148. }
  149. end
  150. end
  151. it "doesn't change jobs added after stop" do
  152. job = create(:import_job)
  153. expect do
  154. simulate_threads_call
  155. end.not_to change {
  156. job.reload
  157. }
  158. end
  159. end
  160. end
  161. end