timeplan_calculation_spec.rb 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. # Copyright (C) 2012-2021 Zammad Foundation, http://zammad-foundation.org/
  2. require 'rails_helper'
  3. RSpec.describe Job::TimeplanCalculation do
  4. subject(:instance) { described_class.new(timeplan) }
  5. describe '#contains?' do
  6. context 'without a valid timeplan' do
  7. let(:timeplan) { {} }
  8. it { is_expected.not_to be_contains(Time.zone.now) }
  9. end
  10. context 'with monday 09:20' do
  11. let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '9' => true }, 'minutes' => { '20' => true } } }
  12. it { is_expected.to be_contains(Time.zone.parse('2020-12-28 09:20')) }
  13. it { is_expected.to be_contains(Time.zone.parse('2020-12-21 09:20')) }
  14. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-21 10:20')) }
  15. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-21 9:10')) }
  16. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-22 9:20')) }
  17. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-20 9:20')) }
  18. end
  19. context 'with monday and tuesday 09:20' do
  20. let(:timeplan) { { 'days' => { 'Mon' => true, 'Tue' => true }, 'hours' => { '9' => true }, 'minutes' => { '20' => true } } }
  21. it { is_expected.to be_contains(Time.zone.parse('2020-12-28 09:20')) }
  22. it { is_expected.to be_contains(Time.zone.parse('2020-12-21 09:20')) }
  23. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-21 10:20')) }
  24. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-21 9:10')) }
  25. it { is_expected.to be_contains(Time.zone.parse('2020-12-22 9:20')) }
  26. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-20 9:20')) }
  27. end
  28. context 'with monday 09:20 and 10:20' do
  29. let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '9' => true, '10' => true }, 'minutes' => { '20' => true } } }
  30. it { is_expected.to be_contains(Time.zone.parse('2020-12-28 09:20')) }
  31. it { is_expected.to be_contains(Time.zone.parse('2020-12-21 09:20')) }
  32. it { is_expected.to be_contains(Time.zone.parse('2020-12-21 10:20')) }
  33. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-21 9:10')) }
  34. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-22 9:20')) }
  35. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-20 9:20')) }
  36. end
  37. context 'with monday 09:20 and 9:10' do
  38. let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '9' => true }, 'minutes' => { '20' => true, '10' => true } } }
  39. it { is_expected.to be_contains(Time.zone.parse('2020-12-28 09:20')) }
  40. it { is_expected.to be_contains(Time.zone.parse('2020-12-21 09:20')) }
  41. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-21 10:20')) }
  42. it { is_expected.to be_contains(Time.zone.parse('2020-12-21 9:10')) }
  43. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-22 9:20')) }
  44. it { is_expected.not_to be_contains(Time.zone.parse('2020-12-20 9:20')) }
  45. end
  46. end
  47. describe 'next_at?' do
  48. context 'without a valid timeplan' do
  49. let(:timeplan) { {} }
  50. it { expect(instance.next_at(Time.zone.now)).to be_nil }
  51. end
  52. context 'with monday 09:20' do
  53. let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '9' => true }, 'minutes' => { '20' => true } } }
  54. it { expect(instance.next_at(Time.zone.parse('2020-12-28 09:31'))).to eq(Time.zone.parse('2021-01-04 09:20')) }
  55. it { expect(instance.next_at(Time.zone.parse('2020-12-28 09:30'))).to eq(Time.zone.parse('2021-01-04 09:20')) }
  56. it { expect(instance.next_at(Time.zone.parse('2020-12-28 09:20'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  57. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:21'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  58. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:35'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  59. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:20'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  60. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:21'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  61. it { expect(instance.next_at(Time.zone.parse('2020-12-21 9:10'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  62. it { expect(instance.next_at(Time.zone.parse('2020-12-22 9:20'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  63. it { expect(instance.next_at(Time.zone.parse('2020-12-20 9:20'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  64. end
  65. context 'with monday and tuesday 09:20' do
  66. let(:timeplan) { { 'days' => { 'Mon' => true, 'Tue' => true }, 'hours' => { '9' => true }, 'minutes' => { '20' => true } } }
  67. it { expect(instance.next_at(Time.zone.parse('2020-12-28 09:30'))).to eq(Time.zone.parse('2020-12-29 09:20')) }
  68. it { expect(instance.next_at(Time.zone.parse('2020-12-29 09:30'))).to eq(Time.zone.parse('2021-01-04 09:20')) }
  69. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:25'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  70. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:35'))).to eq(Time.zone.parse('2020-12-22 09:20')) }
  71. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:20'))).to eq(Time.zone.parse('2020-12-22 09:20')) }
  72. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:21'))).to eq(Time.zone.parse('2020-12-22 09:20')) }
  73. it { expect(instance.next_at(Time.zone.parse('2020-12-21 9:10'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  74. it { expect(instance.next_at(Time.zone.parse('2020-12-22 9:30'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  75. it { expect(instance.next_at(Time.zone.parse('2020-12-20 9:20'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  76. end
  77. context 'with monday 09:20 and 10:20' do
  78. let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '9' => true, '10' => true }, 'minutes' => { '20' => true } } }
  79. it { expect(instance.next_at(Time.zone.parse('2020-12-28 09:30'))).to eq(Time.zone.parse('2020-12-28 10:20')) }
  80. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:30'))).to eq(Time.zone.parse('2020-12-21 10:20')) }
  81. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:35'))).to eq(Time.zone.parse('2020-12-21 10:20')) }
  82. it { expect(instance.next_at(Time.zone.parse('2020-12-21 08:07'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  83. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:30'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  84. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:31'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  85. it { expect(instance.next_at(Time.zone.parse('2020-12-21 9:10'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  86. it { expect(instance.next_at(Time.zone.parse('2020-12-21 9:47'))).to eq(Time.zone.parse('2020-12-21 10:20')) }
  87. it { expect(instance.next_at(Time.zone.parse('2020-12-22 9:20'))).to eq(Time.zone.parse('2020-12-28 09:20')) }
  88. it { expect(instance.next_at(Time.zone.parse('2020-12-20 9:30'))).to eq(Time.zone.parse('2020-12-21 09:20')) }
  89. end
  90. context 'with monday 09:30 and 9:10' do
  91. let(:timeplan) { { 'days' => { 'Mon' => true }, 'hours' => { '9' => true }, 'minutes' => { '30' => true, '10' => true } } }
  92. it { expect(instance.next_at(Time.zone.parse('2020-12-28 09:40'))).to eq(Time.zone.parse('2021-01-04 09:10')) }
  93. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:40'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
  94. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:30'))).to eq(Time.zone.parse('2020-12-21 09:30')) }
  95. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:25'))).to eq(Time.zone.parse('2020-12-21 09:30')) }
  96. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:35'))).to eq(Time.zone.parse('2020-12-21 09:30')) }
  97. it { expect(instance.next_at(Time.zone.parse('2020-12-21 09:45'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
  98. it { expect(instance.next_at(Time.zone.parse('2020-12-21 08:07'))).to eq(Time.zone.parse('2020-12-21 09:10')) }
  99. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:20'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
  100. it { expect(instance.next_at(Time.zone.parse('2020-12-21 10:21'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
  101. it { expect(instance.next_at(Time.zone.parse('2020-12-21 9:10'))).to eq(Time.zone.parse('2020-12-21 09:10')) }
  102. it { expect(instance.next_at(Time.zone.parse('2020-12-21 9:20'))).to eq(Time.zone.parse('2020-12-21 09:30')) }
  103. it { expect(instance.next_at(Time.zone.parse('2020-12-22 9:20'))).to eq(Time.zone.parse('2020-12-28 09:10')) }
  104. it { expect(instance.next_at(Time.zone.parse('2020-12-20 9:20'))).to eq(Time.zone.parse('2020-12-21 09:10')) }
  105. end
  106. end
  107. describe 'legacy tests moved from Job model' do
  108. let(:job) { create(:job, :never_on) }
  109. let(:timeplan) { job.timeplan }
  110. let(:time) { Time.current }
  111. context 'when the current day, hour, and minute all match true values in #timeplan' do
  112. it 'for Symbol/Integer keys returns true' do
  113. timeplan[:days].transform_keys!(&:to_sym)[time.strftime('%a').to_sym] = true
  114. timeplan[:hours].transform_keys!(&:to_i)[time.hour] = true
  115. timeplan[:minutes].transform_keys!(&:to_i)[time.min.floor(-1)] = true
  116. expect(instance.contains?(time)).to be(true)
  117. end
  118. it 'for String keys returns true' do
  119. timeplan[:days].transform_keys!(&:to_s)[time.strftime('%a')] = true
  120. timeplan[:hours].transform_keys!(&:to_s)[time.hour.to_s] = true
  121. timeplan[:minutes].transform_keys!(&:to_s)[time.min.floor(-1).to_s] = true
  122. expect(instance.contains?(time)).to be(true)
  123. end
  124. end
  125. context 'when the current day does not match a true value in #timeplan' do
  126. it 'for Symbol/Integer keys returns false' do
  127. timeplan[:days].transform_keys!(&:to_sym).transform_values! { true }[time.strftime('%a').to_sym] = false
  128. timeplan[:hours].transform_keys!(&:to_i)[time.hour] = true
  129. timeplan[:minutes].transform_keys!(&:to_i)[time.min.floor(-1)] = true
  130. expect(instance.contains?(time)).to be(false)
  131. end
  132. it 'for String keys returns false' do
  133. timeplan[:days].transform_keys!(&:to_s).transform_values! { true }[time.strftime('%a')] = false
  134. timeplan[:hours].transform_keys!(&:to_s)[time.hour.to_s] = true
  135. timeplan[:minutes].transform_keys!(&:to_s)[time.min.floor(-1).to_s] = true
  136. expect(instance.contains?(time)).to be(false)
  137. end
  138. end
  139. context 'when the current hour does not match a true value in #timeplan' do
  140. it 'for Symbol/Integer keys returns false' do
  141. timeplan[:days].transform_keys!(&:to_sym)[time.strftime('%a').to_sym] = true
  142. timeplan[:hours].transform_keys!(&:to_i).transform_values! { true }[time.hour] = false
  143. timeplan[:minutes].transform_keys!(&:to_i)[time.min.floor(-1)] = true
  144. expect(instance.contains?(time)).to be(false)
  145. end
  146. it 'for String keys returns false' do
  147. timeplan[:days].transform_keys!(&:to_s)[time.strftime('%a')] = true
  148. timeplan[:hours].transform_keys!(&:to_s).transform_values! { true }[time.hour.to_s] = false
  149. timeplan[:minutes].transform_keys!(&:to_s)[time.min.floor(-1).to_s] = true
  150. expect(instance.contains?(time)).to be(false)
  151. end
  152. end
  153. context 'when the current minute does not match a true value in #timeplan' do
  154. it 'for Symbol/Integer keys returns false' do
  155. timeplan[:days].transform_keys!(&:to_sym)[time.strftime('%a').to_sym] = true
  156. timeplan[:hours].transform_keys!(&:to_i)[time.hour] = true
  157. timeplan[:minutes].transform_keys!(&:to_i).transform_values! { true }[time.min.floor(-1)] = false
  158. expect(instance.contains?(time)).to be(false)
  159. end
  160. it 'for String keys returns false' do
  161. timeplan[:days].transform_keys!(&:to_s)[time.strftime('%a')] = true
  162. timeplan[:hours].transform_keys!(&:to_s)[time.hour.to_s] = true
  163. timeplan[:minutes].transform_keys!(&:to_s).transform_values! { true }[time.min.floor(-1).to_s] = false
  164. expect(instance.contains?(time)).to be(false)
  165. end
  166. end
  167. end
  168. end