scheduler_spec.rb 4.8 KB

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