scheduler_spec.rb 5.0 KB

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