|
5 | 5 | from itertools import count
|
6 | 6 | from time import monotonic
|
7 | 7 |
|
| 8 | +try: |
| 9 | + from zoneinfo import ZoneInfo # Python 3.9+ |
| 10 | +except ImportError: |
| 11 | + from backports.zoneinfo import ZoneInfo # Python 3.8 |
| 12 | + |
8 | 13 | import pytest
|
9 | 14 | from celery.schedules import crontab, schedule, solar
|
10 | 15 | from django.contrib.admin.sites import AdminSite
|
@@ -776,6 +781,123 @@ def test_starttime_trigger(self, monkeypatch):
|
776 | 781 | assert s._heap[0]
|
777 | 782 | assert s._heap[0][2].name == m1.name
|
778 | 783 |
|
| 784 | + def test_crontab_with_start_time_between_now_and_crontab(self, app): |
| 785 | + now = app.now() |
| 786 | + delay_minutes = 2 |
| 787 | + |
| 788 | + test_start_time = now + timedelta(minutes=delay_minutes) |
| 789 | + |
| 790 | + crontab_time = test_start_time + timedelta(minutes=delay_minutes) |
| 791 | + |
| 792 | + task = self.create_model_crontab( |
| 793 | + crontab(minute=f'{crontab_time.minute}'), |
| 794 | + start_time=test_start_time) |
| 795 | + |
| 796 | + entry = EntryTrackSave(task, app=app) |
| 797 | + |
| 798 | + is_due, next_check = entry.is_due() |
| 799 | + |
| 800 | + expected_delay = 2 * delay_minutes * 60 |
| 801 | + |
| 802 | + assert not is_due |
| 803 | + assert next_check == pytest.approx(expected_delay, abs=60) |
| 804 | + |
| 805 | + def test_crontab_with_start_time_after_crontab(self, app): |
| 806 | + now = app.now() |
| 807 | + |
| 808 | + delay_minutes = 2 |
| 809 | + |
| 810 | + crontab_time = now + timedelta(minutes=delay_minutes) |
| 811 | + |
| 812 | + test_start_time = crontab_time + timedelta(minutes=delay_minutes) |
| 813 | + |
| 814 | + task = self.create_model_crontab( |
| 815 | + crontab(minute=f'{crontab_time.minute}'), |
| 816 | + start_time=test_start_time) |
| 817 | + |
| 818 | + entry = EntryTrackSave(task, app=app) |
| 819 | + |
| 820 | + is_due, next_check = entry.is_due() |
| 821 | + |
| 822 | + expected_delay = delay_minutes * 60 + 3600 |
| 823 | + |
| 824 | + assert not is_due |
| 825 | + assert next_check == pytest.approx(expected_delay, abs=60) |
| 826 | + |
| 827 | + def test_crontab_with_start_time_different_time_zone(self, app): |
| 828 | + now = app.now() |
| 829 | + |
| 830 | + delay_minutes = 2 |
| 831 | + |
| 832 | + test_start_time = now + timedelta(minutes=delay_minutes) |
| 833 | + |
| 834 | + crontab_time = test_start_time + timedelta(minutes=delay_minutes) |
| 835 | + |
| 836 | + tz = ZoneInfo('Asia/Shanghai') |
| 837 | + test_start_time = test_start_time.astimezone(tz) |
| 838 | + |
| 839 | + task = self.create_model_crontab( |
| 840 | + crontab(minute=f'{crontab_time.minute}'), |
| 841 | + start_time=test_start_time) |
| 842 | + |
| 843 | + entry = EntryTrackSave(task, app=app) |
| 844 | + |
| 845 | + is_due, next_check = entry.is_due() |
| 846 | + |
| 847 | + expected_delay = 2 * delay_minutes * 60 |
| 848 | + |
| 849 | + assert not is_due |
| 850 | + assert next_check == pytest.approx(expected_delay, abs=60) |
| 851 | + |
| 852 | + now = app.now() |
| 853 | + |
| 854 | + crontab_time = now + timedelta(minutes=delay_minutes) |
| 855 | + |
| 856 | + test_start_time = crontab_time + timedelta(minutes=delay_minutes) |
| 857 | + |
| 858 | + tz = ZoneInfo('Asia/Shanghai') |
| 859 | + test_start_time = test_start_time.astimezone(tz) |
| 860 | + |
| 861 | + task = self.create_model_crontab( |
| 862 | + crontab(minute=f'{crontab_time.minute}'), |
| 863 | + start_time=test_start_time) |
| 864 | + |
| 865 | + entry = EntryTrackSave(task, app=app) |
| 866 | + |
| 867 | + is_due, next_check = entry.is_due() |
| 868 | + |
| 869 | + expected_delay = delay_minutes * 60 + 3600 |
| 870 | + |
| 871 | + assert not is_due |
| 872 | + assert next_check == pytest.approx(expected_delay, abs=60) |
| 873 | + |
| 874 | + def test_crontab_with_start_time_tick(self, app): |
| 875 | + PeriodicTask.objects.all().delete() |
| 876 | + s = self.Scheduler(app=self.app) |
| 877 | + assert not s._heap |
| 878 | + |
| 879 | + m1 = self.create_model_interval(schedule(timedelta(seconds=3))) |
| 880 | + m1.save() |
| 881 | + |
| 882 | + now = timezone.now() |
| 883 | + start_time = now + timedelta(minutes=1) |
| 884 | + crontab_trigger_time = now + timedelta(minutes=2) |
| 885 | + |
| 886 | + m2 = self.create_model_crontab( |
| 887 | + crontab(minute=f'{crontab_trigger_time.minute}'), |
| 888 | + start_time=start_time) |
| 889 | + m2.save() |
| 890 | + |
| 891 | + e2 = EntryTrackSave(m2, app=self.app) |
| 892 | + is_due, _ = e2.is_due() |
| 893 | + |
| 894 | + max_iterations = 1000 |
| 895 | + iterations = 0 |
| 896 | + while (not is_due and iterations < max_iterations): |
| 897 | + s.tick() |
| 898 | + assert s._heap[0][2].name != m2.name |
| 899 | + is_due, _ = e2.is_due() |
| 900 | + |
779 | 901 |
|
780 | 902 | @pytest.mark.django_db
|
781 | 903 | class test_models(SchedulerCase):
|
|
0 commit comments