background_services_spec.rb 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'rails_helper'
  3. class SampleService
  4. def self.pre_run; end
  5. def initialize(manager:, fork_id: nil)
  6. # no-op
  7. end
  8. def run
  9. sleep 1
  10. end
  11. def self.service_name
  12. 'Sample'
  13. end
  14. def self.skip?(manager:)
  15. false
  16. end
  17. def self.max_workers
  18. 1
  19. end
  20. end
  21. class ProcessService < SampleService
  22. def run
  23. f = File.new self.class.path, 'w'
  24. f.write 'run'
  25. f.close
  26. sleep 1
  27. end
  28. def self.path
  29. Rails.root.join('tmp/process.service')
  30. end
  31. end
  32. RSpec.describe BackgroundServices do
  33. let(:instance) { described_class.new(config) }
  34. let(:config) { [] }
  35. describe '.available_services' do
  36. it 'matches existing classes' do
  37. expect(described_class.available_services).to contain_exactly(
  38. BackgroundServices::Service::ManageSessionsJobs,
  39. BackgroundServices::Service::ProcessScheduledJobs,
  40. BackgroundServices::Service::ProcessSessionsJobs,
  41. BackgroundServices::Service::ProcessDelayedJobs
  42. )
  43. end
  44. end
  45. describe '#run', ensure_threads_exited: true do
  46. let(:config) { described_class::ServiceConfig.new(service: SampleService, disabled: false, workers: 0) }
  47. it 'runs given services' do
  48. allow(instance).to receive(:run_service)
  49. instance.run
  50. expect(instance).to have_received(:run_service).with(config)
  51. end
  52. context 'when config has multiple services' do
  53. let(:forked) { described_class::ServiceConfig.new(service: SampleService, disabled: false, workers: 3) }
  54. let(:threaded) { described_class::ServiceConfig.new(service: SampleService, disabled: false, workers: 0) }
  55. let(:config) { [threaded, forked] }
  56. it 'runs forked services before threaded', aggregate_failures: true do
  57. allow(instance).to receive(:run_service)
  58. instance.run
  59. expect(instance).to have_received(:run_service).with(forked).ordered
  60. expect(instance).to have_received(:run_service).with(threaded).ordered
  61. end
  62. end
  63. end
  64. describe '#run_service' do
  65. let(:config) { described_class::ServiceConfig.new(service: SampleService, disabled: is_disabled, workers: workers_count) }
  66. let(:is_disabled) { false }
  67. before do
  68. allow(instance).to receive(:start_as_forks)
  69. allow(instance).to receive(:start_as_thread)
  70. end
  71. shared_examples 'stops early if disabled' do
  72. context 'when disabled' do
  73. let(:is_disabled) { true }
  74. it 'stops early if disabled', :aggregate_failures do
  75. allow(Rails.logger).to receive(:info)
  76. instance.send(:run_service, config)
  77. expect(Rails.logger).to have_received(:info).with(no_args) do |&block|
  78. expect(block.call).to match(%r{Skipping disabled service})
  79. end
  80. end
  81. end
  82. end
  83. shared_examples 'calls pre_run' do
  84. it 'calls pre_run' do
  85. allow(config.service).to receive(:pre_run)
  86. instance.send(:run_service, config)
  87. expect(config.service).to have_received(:pre_run)
  88. end
  89. end
  90. context 'when workers present' do
  91. let(:workers_count) { 1 }
  92. include_examples 'stops early if disabled'
  93. include_examples 'calls pre_run'
  94. it 'starts as fork' do
  95. instance.send(:run_service, config)
  96. expect(instance).to have_received(:start_as_forks).with(config.service, config.workers)
  97. end
  98. end
  99. context 'when workers not present' do
  100. let(:workers_count) { 0 }
  101. include_examples 'stops early if disabled'
  102. include_examples 'calls pre_run'
  103. it 'starts as thread' do
  104. instance.send(:run_service, config)
  105. expect(instance).to have_received(:start_as_thread).with(config.service)
  106. end
  107. end
  108. end
  109. describe '#start_as_forks' do
  110. context 'with a file check' do
  111. after do
  112. File.delete ProcessService.path
  113. end
  114. it 'runs Service#run' do
  115. instance.send(:start_as_forks, ProcessService, 1)
  116. sleep 0.1 until File.exist? ProcessService.path
  117. expect(File.read(ProcessService.path)).to eq('run')
  118. end
  119. end
  120. it 'forks a new process' do
  121. process_pids = instance.send(:start_as_forks, SampleService, 1)
  122. Process.wait process_pids.first
  123. expect($CHILD_STATUS).to be_success
  124. end
  125. end
  126. describe '#start_as_thread', ensure_threads_exited: true do
  127. let(:config) { described_class::ServiceConfig.new(service: SampleService, disabled: false, workers: 0) }
  128. context 'with logging' do
  129. let(:log) { [] }
  130. before do
  131. allow_any_instance_of(SampleService).to receive(:run) do
  132. log << :run_called
  133. end
  134. end
  135. it 'runs Service#run' do
  136. instance.send(:start_as_thread, SampleService)
  137. sleep 0.1 until log.any?
  138. expect(log).to be_present
  139. end
  140. end
  141. it 'starts a new thread' do
  142. thread = instance.send(:start_as_thread, SampleService)
  143. expect(thread).to be_alive
  144. end
  145. end
  146. end