scheduler_spec.rb 4.9 KB

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