Skip to content

Commit 3205411

Browse files
fix: fix failing tests and eliminate naive datetime warnings (ietf-tools#4402)
* test: fix timestamp construction in several doc tests * refactor: rename date2datetime to datetime_from_date and clarify code * chore: helper to get tzinfo for PRODUCTION_TIMEZONE * fix: fix timezone handling in make_last_call() * test: fix datetime generation in doc.tests_charter * refactor: remove PRODUCTION_TIMEZONE setting Replaces the PRODUCTION_TIMEZONE setting with a constant, DEADLINE_TZINFO, in ietf.utils.timezone. * test: be more careful about timezone in tests_charter.py * test: be more careful about timezone in doc/tests.py * fix: fix timezone handling affecting doc.tests_draft * fix: fix timezone handling affecting tests_irsg_ballot.py * fix: fix timezone handling affecting tests_review.py * fix: fix timezone handling affecting last ietf.doc tests * fix: fix timezone handling affecting last ietf.group tests * fix: fix timezone handling affecting ietf.iesg tests * fix: handle timezones in get_8989_eligibility_querysets * fix: handle timezones affecting ietfauth tests * fix: return tz-aware datetime from utc_from_string * fix: specify timezone for constants in ipr_rfc_number() * fix: specify tz for ipr deadlines * fix: handle timezones affecting liaisons tests * fix: treat leap day in get_8989_eligibility_querysets() Manual cherry-pick of 248d647 * test: treat leap day properly in nomcom tests * fix: fix timezone handling affecting nomcom tests * test: fix timezone handling in review tests * fix: fix timezone handling affecting secr.meetings tests * fix: handle both pytz and zoneinfo timezones in ietf.utils.timezone * fix: fix timezone handling affecting secr.proceedings tests * refactor: use make_aware() helper in secr.meetings tests * test: fix timezone handling in secr.telechat tests * fix: fix timezone handling affecting stats tests * fix: eliminate tz-naive helpers affecting sync email parsing * fix: include timezone data when serializing DeletedEvent data * fix: fix timezone handling affecting sync tests * style: remove unused import
1 parent a4ecccb commit 3205411

39 files changed

Lines changed: 346 additions & 175 deletions

ietf/doc/expire.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ietf.meeting.models import Meeting
1919
from ietf.doc.utils import add_state_change_event, update_action_holders
2020
from ietf.mailtrigger.utils import gather_address_lists
21+
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
2122

2223

2324
nonexpirable_states: Optional[List[State]] = None
@@ -53,13 +54,13 @@ def expirable_drafts(queryset=None):
5354

5455

5556
def get_soon_to_expire_drafts(days_of_warning):
56-
start_date = datetime.date.today() - datetime.timedelta(1)
57+
start_date = datetime_today(DEADLINE_TZINFO) - datetime.timedelta(1)
5758
end_date = start_date + datetime.timedelta(days_of_warning)
5859

5960
return expirable_drafts().filter(expires__gte=start_date, expires__lt=end_date)
6061

6162
def get_expired_drafts():
62-
return expirable_drafts().filter(expires__lt=datetime.date.today() + datetime.timedelta(1))
63+
return expirable_drafts().filter(expires__lt=datetime_today(DEADLINE_TZINFO) + datetime.timedelta(1))
6364

6465
def in_draft_expire_freeze(when=None):
6566
if when == None:
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Generated by Django 2.2.28 on 2022-08-31 20:26
2+
3+
import datetime
4+
import json
5+
6+
from zoneinfo import ZoneInfo
7+
8+
from django.db import migrations
9+
10+
11+
TZ_BEFORE = ZoneInfo('PST8PDT')
12+
13+
14+
def forward(apps, schema_editor):
15+
DeletedEvent = apps.get_model('doc', 'DeletedEvent')
16+
for deleted_event in DeletedEvent.objects.all():
17+
fields = json.loads(deleted_event.json)
18+
replacements = {}
19+
for k, v in fields.items():
20+
if isinstance(v, str):
21+
try:
22+
dt = datetime.datetime.strptime(v, '%Y-%m-%d %H:%M:%S')
23+
except:
24+
pass
25+
else:
26+
replacements[k] = dt.replace(tzinfo=TZ_BEFORE).astimezone(datetime.timezone.utc).isoformat()
27+
if len(replacements) > 0:
28+
fields.update(replacements)
29+
deleted_event.json = json.dumps(fields)
30+
deleted_event.save()
31+
32+
33+
def reverse(apps, schema_editor):
34+
DeletedEvent = apps.get_model('doc', 'DeletedEvent')
35+
for deleted_event in DeletedEvent.objects.all():
36+
fields = json.loads(deleted_event.json)
37+
replacements = {}
38+
for k, v in fields.items():
39+
if isinstance(v, str) and 'T' in v:
40+
try:
41+
dt = datetime.datetime.fromisoformat(v)
42+
except:
43+
pass
44+
else:
45+
replacements[k] = dt.astimezone(TZ_BEFORE).replace(tzinfo=None).strftime('%Y-%m-%d %H:%M:%S')
46+
if len(replacements) > 0:
47+
fields.update(replacements)
48+
deleted_event.json = json.dumps(fields)
49+
deleted_event.save()
50+
51+
52+
class Migration(migrations.Migration):
53+
54+
dependencies = [
55+
('doc', '0045_use_timezone_now_for_doc_models'),
56+
('utils', '0003_pause_to_change_use_tz'),
57+
]
58+
59+
operations = [
60+
migrations.RunPython(forward, reverse),
61+
]

ietf/doc/tests.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from urllib.parse import urlparse, parse_qs
1919
from tempfile import NamedTemporaryFile
2020
from collections import defaultdict
21+
from zoneinfo import ZoneInfo
2122

2223
from django.core.management import call_command
2324
from django.urls import reverse as urlreverse
@@ -57,6 +58,8 @@
5758
from ietf.utils.test_utils import login_testing_unauthorized, unicontent, reload_db_objects
5859
from ietf.utils.test_utils import TestCase
5960
from ietf.utils.text import normalize_text
61+
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
62+
6063

6164
class SearchTests(TestCase):
6265
def test_search(self):
@@ -1428,6 +1431,8 @@ def _run_test(username=None, expect_buttons=False):
14281431

14291432
def test_draft_group_link(self):
14301433
"""Link to group 'about' page should have correct format"""
1434+
event_datetime = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo('America/Los_Angeles'))
1435+
14311436
for group_type_id in ['wg', 'rg', 'ag']:
14321437
group = GroupFactory(type_id=group_type_id)
14331438
draft = WgDraftFactory(name='draft-document-%s' % group_type_id, group=group)
@@ -1436,7 +1441,7 @@ def test_draft_group_link(self):
14361441
self.assert_correct_wg_group_link(r, group)
14371442

14381443
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
1439-
DocEventFactory.create(doc=rfc, type='published_rfc', time = '2010-10-10')
1444+
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
14401445
# get the rfc name to avoid a redirect
14411446
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
14421447
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
@@ -1451,7 +1456,7 @@ def test_draft_group_link(self):
14511456
self.assert_correct_non_wg_group_link(r, group)
14521457

14531458
rfc = WgRfcFactory(name='draft-rfc-document-%s' % group_type_id, group=group)
1454-
DocEventFactory.create(doc=rfc, type='published_rfc', time = '2010-10-10')
1459+
DocEventFactory.create(doc=rfc, type='published_rfc', time=event_datetime)
14551460
# get the rfc name to avoid a redirect
14561461
rfc_name = rfc.docalias.filter(name__startswith='rfc').first().name
14571462
r = self.client.get(urlreverse("ietf.doc.views_doc.document_main", kwargs=dict(name=rfc_name)))
@@ -1837,7 +1842,7 @@ def test_last_call_feed(self):
18371842
desc="Last call\x0b", # include a control character to be sure it does not break anything
18381843
type="sent_last_call",
18391844
by=Person.objects.get(user__username="secretary"),
1840-
expires=datetime.date.today() + datetime.timedelta(days=7))
1845+
expires=datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=7))
18411846

18421847
r = self.client.get("/feed/last-call/")
18431848
self.assertEqual(r.status_code, 200)
@@ -1885,10 +1890,14 @@ def test_document_bibtex(self):
18851890
#other_aliases = ['rfc6020',],
18861891
states = [('draft','rfc'),('draft-iesg','pub')],
18871892
std_level_id = 'ps',
1888-
time = datetime.datetime(2010,10,10),
1893+
time = datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo('America/Los_Angeles')),
18891894
)
18901895
num = rfc.rfc_number()
1891-
DocEventFactory.create(doc=rfc, type='published_rfc', time = '2010-10-10')
1896+
DocEventFactory.create(
1897+
doc=rfc,
1898+
type='published_rfc',
1899+
time=datetime.datetime(2010, 10, 10, tzinfo=ZoneInfo('America/Los_Angeles')),
1900+
)
18921901
#
18931902
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=rfc.name))
18941903
r = self.client.get(url)
@@ -1906,10 +1915,14 @@ def test_document_bibtex(self):
19061915
stream_id = 'ise',
19071916
states = [('draft','rfc'),('draft-iesg','pub')],
19081917
std_level_id = 'inf',
1909-
time = datetime.datetime(1990,0o4,0o1),
1918+
time = datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo('America/Los_Angeles')),
19101919
)
19111920
num = april1.rfc_number()
1912-
DocEventFactory.create(doc=april1, type='published_rfc', time = '1990-04-01')
1921+
DocEventFactory.create(
1922+
doc=april1,
1923+
type='published_rfc',
1924+
time=datetime.datetime(1990, 4, 1, tzinfo=ZoneInfo('America/Los_Angeles')),
1925+
)
19131926
#
19141927
url = urlreverse('ietf.doc.views_doc.document_bibtex', kwargs=dict(name=april1.name))
19151928
r = self.client.get(url)
@@ -2044,7 +2057,9 @@ def tearDown(self):
20442057
super().tearDown()
20452058

20462059
def testManagementCommand(self):
2047-
a_month_ago = timezone.now() - datetime.timedelta(30)
2060+
tz = ZoneInfo('America/Los_Angeles')
2061+
a_month_ago = (timezone.now() - datetime.timedelta(30)).astimezone(tz)
2062+
a_month_ago = a_month_ago.replace(hour=0, minute=0, second=0, microsecond=0)
20482063
ad = RoleFactory(name_id='ad', group__type_id='area', group__state_id='active').person
20492064
shepherd = PersonFactory()
20502065
author1 = PersonFactory()
@@ -2059,9 +2074,9 @@ def testManagementCommand(self):
20592074
doc1 = IndividualDraftFactory(authors=[author1], shepherd=shepherd.email(), ad=ad)
20602075
doc2 = WgDraftFactory(name='draft-ietf-mars-test', group__acronym='mars', authors=[author2], ad=ad)
20612076
doc3 = WgRfcFactory.create(name='draft-ietf-mars-finished', group__acronym='mars', authors=[author3], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=a_month_ago)
2062-
DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago.strftime("%Y-%m-%d"))
2063-
doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10))
2064-
DocEventFactory.create(doc=doc4, type='published_rfc', time = '2010-10-10')
2077+
DocEventFactory.create(doc=doc3, type='published_rfc', time=a_month_ago)
2078+
doc4 = WgRfcFactory.create(authors=[author4,author5], ad=ad, std_level_id='ps', states=[('draft','rfc'),('draft-iesg','pub')], time=datetime.datetime(2010,10,10, tzinfo=tz))
2079+
DocEventFactory.create(doc=doc4, type='published_rfc', time=datetime.datetime(2010, 10, 10, tzinfo=tz))
20652080
doc5 = IndividualDraftFactory(authors=[author6])
20662081

20672082
args = [ ]

ietf/doc/tests_charter.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
from ietf.utils.test_utils import TestCase
2727
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
2828
from ietf.utils.test_utils import login_testing_unauthorized
29+
from ietf.utils.timezone import datetime_today, date_today, DEADLINE_TZINFO
30+
2931

3032
class ViewCharterTests(TestCase):
3133
def test_view_revisions(self):
@@ -402,7 +404,7 @@ def test_no_returning_item_for_different_ballot(self):
402404

403405
# Make it so that the charter has been through internal review, and passed its external review
404406
# ballot on a previous telechat
405-
last_week = datetime.date.today()-datetime.timedelta(days=7)
407+
last_week = datetime_today(DEADLINE_TZINFO) - datetime.timedelta(days=7)
406408
BallotDocEvent.objects.create(type='created_ballot',by=login,doc=charter, rev=charter.rev,
407409
ballot_type=BallotType.objects.get(doc_type=charter.type,slug='r-extrev'),
408410
time=last_week)
@@ -746,7 +748,7 @@ def test_approve(self):
746748

747749
charter.set_state(State.objects.get(used=True, type="charter", slug="iesgrev"))
748750

749-
due_date = datetime.date.today() + datetime.timedelta(days=180)
751+
due_date = date_today(DEADLINE_TZINFO) + datetime.timedelta(days=180)
750752
m1 = GroupMilestone.objects.create(group=group,
751753
state_id="active",
752754
desc="Has been copied",
@@ -826,7 +828,7 @@ def test_charter_with_milestones(self):
826828
m = GroupMilestone.objects.create(group=charter.group,
827829
state_id="active",
828830
desc="Test milestone",
829-
due=datetime.date.today(),
831+
due=date_today(DEADLINE_TZINFO),
830832
resolved="")
831833

832834
url = urlreverse('ietf.doc.views_charter.charter_with_milestones_txt', kwargs=dict(name=charter.name, rev=charter.rev))

ietf/doc/tests_draft.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from ietf.utils.test_utils import login_testing_unauthorized
3535
from ietf.utils.mail import outbox, empty_outbox, get_payload_text
3636
from ietf.utils.test_utils import TestCase
37+
from ietf.utils.timezone import date_today, datetime_from_date
3738

3839

3940
class ChangeStateTests(TestCase):
@@ -402,11 +403,11 @@ def test_edit_telechat_date(self):
402403

403404
# change to a telechat that should cause returning item to be auto-detected
404405
# First, make it appear that the previous telechat has already passed
405-
telechat_event.telechat_date = datetime.date.today()-datetime.timedelta(days=7)
406+
telechat_event.telechat_date = date_today() - datetime.timedelta(days=7)
406407
telechat_event.save()
407408
ad = Person.objects.get(user__username="ad")
408409
ballot = create_ballot_if_not_open(None, draft, ad, 'approve')
409-
ballot.time = telechat_event.telechat_date
410+
ballot.time = datetime_from_date(telechat_event.telechat_date)
410411
ballot.save()
411412

412413
r = self.client.post(url, data)
@@ -429,7 +430,7 @@ def test_edit_telechat_date(self):
429430
self.assertTrue("Telechat update" in outbox[-1]['Subject'])
430431

431432
# Put it on an agenda that's very soon from now
432-
next_week = datetime.date.today()+datetime.timedelta(days=7)
433+
next_week = date_today() + datetime.timedelta(days=7)
433434
td = TelechatDate.objects.active()[0]
434435
td.date = next_week
435436
td.save()

ietf/doc/tests_irsg_ballot.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from ietf.person.utils import get_active_irsg, get_active_ads
2020
from ietf.group.factories import RoleFactory
2121
from ietf.person.models import Person
22+
from ietf.utils.timezone import datetime_today, DEADLINE_TZINFO
2223

2324

2425
class IssueIRSGBallotTests(TestCase):
@@ -254,7 +255,7 @@ def test_edit_ballot_position_permissions(self):
254255
irsgmember = get_active_irsg()[0]
255256
secr = RoleFactory(group__acronym='secretariat',name_id='secr')
256257
wg_ballot = create_ballot_if_not_open(None, wg_draft, ad.person, 'approve')
257-
due = datetime.date.today()+datetime.timedelta(days=14)
258+
due = datetime_today(DEADLINE_TZINFO) + datetime.timedelta(days=14)
258259
rg_ballot = create_ballot_if_not_open(None, rg_draft, secr.person, 'irsg-approve', due)
259260

260261
url = urlreverse('ietf.doc.views_ballot.edit_position', kwargs=dict(name=wg_draft.name, ballot_id=wg_ballot.pk))

ietf/doc/tests_review.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from mock import patch
1111
from requests import Response
1212

13-
1413
from django.apps import apps
1514
from django.urls import reverse as urlreverse
1615
from django.conf import settings
@@ -39,6 +38,7 @@
3938
from ietf.utils.test_utils import login_testing_unauthorized, reload_db_objects
4039
from ietf.utils.test_utils import TestCase
4140
from ietf.utils.text import strip_prefix, xslugify
41+
from ietf.utils.timezone import DEADLINE_TZINFO
4242
from django.utils.html import escape
4343

4444
class ReviewTests(TestCase):
@@ -734,15 +734,15 @@ def test_complete_review_enter_content_by_secretary(self):
734734
# The secretary is allowed to set a custom completion date (#2590)
735735
assignment = reload_db_objects(assignment)
736736
self.assertEqual(assignment.state_id, "completed")
737-
self.assertEqual(assignment.completed_on, datetime.datetime(2012, 12, 24, 12, 13, 14))
737+
self.assertEqual(assignment.completed_on, datetime.datetime(2012, 12, 24, 12, 13, 14, tzinfo=DEADLINE_TZINFO))
738738

739739
# There should be two events:
740740
# - the event logging when the change when it was entered, i.e. very close to now.
741741
# - the completion of the review, set to the provided date/time
742742
events = ReviewAssignmentDocEvent.objects.filter(doc=assignment.review_request.doc).order_by('-time')
743743
event0_time_diff = timezone.now() - events[0].time
744744
self.assertLess(event0_time_diff, datetime.timedelta(seconds=10))
745-
self.assertEqual(events[1].time, datetime.datetime(2012, 12, 24, 12, 13, 14))
745+
self.assertEqual(events[1].time, datetime.datetime(2012, 12, 24, 12, 13, 14, tzinfo=DEADLINE_TZINFO))
746746

747747
with io.open(os.path.join(self.review_subdir, assignment.review.name + ".txt")) as f:
748748
self.assertEqual(f.read(), "This is a review\nwith two lines")

ietf/doc/tests_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ def test_update_action_holders_resets_age(self):
143143
doc = self.doc_in_iesg_state('pub-req')
144144
doc.action_holders.set([self.ad])
145145
dah = doc.documentactionholder_set.get(person=self.ad)
146-
dah.time_added = datetime.datetime(2020, 1, 1) # arbitrary date in the past
146+
dah.time_added = datetime.datetime(2020, 1, 1, tzinfo=datetime.timezone.utc) # arbitrary date in the past
147147
dah.save()
148148

149149
self.assertNotEqual(doc.documentactionholder_set.get(person=self.ad).time_added.date(), datetime.date.today())

ietf/doc/utils.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from ietf.utils import draft, log
4040
from ietf.utils.mail import send_mail
4141
from ietf.mailtrigger.utils import gather_address_lists
42+
from ietf.utils.timezone import date_today, datetime_from_date, datetime_today, DEADLINE_TZINFO
4243
from ietf.utils.xmldraft import XMLDraft
4344

4445

@@ -637,11 +638,22 @@ def has_same_ballot(doc, date1, date2=None):
637638
""" Test if the most recent ballot created before the end of date1
638639
is the same as the most recent ballot created before the
639640
end of date 2. """
641+
datetime1 = datetime_from_date(date1, DEADLINE_TZINFO)
640642
if date2 is None:
641-
date2 = datetime.date.today()
642-
ballot1 = doc.latest_event(BallotDocEvent,type='created_ballot',time__lt=date1+datetime.timedelta(days=1))
643-
ballot2 = doc.latest_event(BallotDocEvent,type='created_ballot',time__lt=date2+datetime.timedelta(days=1))
644-
return ballot1==ballot2
643+
datetime2 = datetime_today(DEADLINE_TZINFO)
644+
else:
645+
datetime2 = datetime_from_date(date2, DEADLINE_TZINFO)
646+
ballot1 = doc.latest_event(
647+
BallotDocEvent,
648+
type='created_ballot',
649+
time__lt=datetime1 + datetime.timedelta(days=1),
650+
)
651+
ballot2 = doc.latest_event(
652+
BallotDocEvent,
653+
type='created_ballot',
654+
time__lt=datetime2 + datetime.timedelta(days=1),
655+
)
656+
return ballot1 == ballot2
645657

646658
def make_notify_changed_event(request, doc, by, new_notify, time=None):
647659

@@ -687,7 +699,7 @@ def update_telechat(request, doc, by, new_telechat_date, new_returning_item=None
687699
and on_agenda
688700
and prev_agenda
689701
and new_telechat_date != prev_telechat
690-
and prev_telechat < datetime.date.today()
702+
and prev_telechat < date_today(DEADLINE_TZINFO)
691703
and has_same_ballot(doc,prev.telechat_date)
692704
):
693705
returning = True

0 commit comments

Comments
 (0)