Skip to content

Commit

Permalink
Refactor due date tests and datetimes_are_equal helper
Browse files Browse the repository at this point in the history
  • Loading branch information
MattyMay committed Sep 16, 2024
1 parent ec5499d commit 25e2030
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 146 deletions.
22 changes: 6 additions & 16 deletions autograder/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,25 +31,15 @@ def datetimes_are_equal(datetime1: datetime.datetime | str | None,
:raises: ValueError when either argument is an invalid datetime string.
"""
# need to do these checks because parse_datetime returns None when passed
# an invalid string.
if datetime1 is None and datetime2 is not None:
return False
elif datetime1 is not None and datetime2 is None:
return False
elif datetime1 is None and datetime2 is None:
return True

if isinstance(datetime1, str):
time1_parsed = parse_datetime(datetime1)
if time1_parsed is None:
datetime1 = parse_datetime(datetime1)
if datetime1 is None:
raise ValueError(f"{datetime1} is not a valid datetime")
datetime1 = time1_parsed

if isinstance(datetime2, str):
time2_parsed = parse_datetime(datetime2)
if time2_parsed is None:
raise ValueError(f"{datetime2} is not a valid datetime")
datetime2 = time2_parsed
datetime2 = parse_datetime(datetime2)
if datetime2 is None:
raise ValueError(f"{datetime1} is not a valid datetime")

return datetime1 == datetime2

Expand Down
246 changes: 117 additions & 129 deletions autograder/rest_api/tests/test_views/test_group_views/test_group_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -431,155 +431,143 @@ def group_url(self, group: ag_models.Group) -> str:
class UpdateGroupExtendedDueDatesTestCase(AGViewTestBase):
def setUp(self):
super().setUp()
self.new_due_date = timezone.now()
now = timezone.now()

# old soft < new soft < old hard < new hard
self.old_soft_due_date = now
self.new_soft_due_date = now + datetime.timedelta(days=1)
self.old_hard_due_date = now + datetime.timedelta(days=2)
self.new_hard_due_date = now + datetime.timedelta(days=3)

self.client = APIClient()
self.project = obj_build.make_project()
self.course = self.project.course
self.admin = obj_build.make_admin_user(self.course)

def test_admin_update_group_deprecated_extended_due_date(self):
def do_test(self, req_body: dict, expected_resp: dict | None = None):
# test updating group with no extended due dates
group = obj_build.make_group(project=self.project)

invalid_extended_due_date = "not a date"
response = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{'extended_due_date': invalid_extended_due_date})
self.assertIn('soft_extended_due_date', response.data)

self.do_patch_object_test(
group, self.client, self.admin, self.group_url(group),
{
'extended_due_date': self.new_due_date,
'soft_extended_due_date': group.soft_extended_due_date,
'hard_extended_due_date': group.hard_extended_due_date
},
expected_response_overrides={
'extended_due_date': self.new_due_date.replace(second=0, microsecond=0),
'soft_extended_due_date': self.new_due_date.replace(second=0, microsecond=0),
'hard_extended_due_date': self.new_due_date.replace(second=0, microsecond=0)
})
self.do_patch_object_test(
group, self.client, self.admin, self.group_url(group),
{'extended_due_date': None})

def test_admin_update_deprecated_extended_due_date_mixed_use(self):
self.do_patch_object_test(group, self.client, self.admin, self.group_url(group),
req_body, expected_response_overrides=expected_resp)

# test updating group with existing soft extended due date
group = obj_build.make_group(project=self.project,
soft_extended_due_date=self.old_soft_due_date)
self.do_patch_object_test(group, self.client, self.admin, self.group_url(group),
req_body, expected_response_overrides=expected_resp)

# test updating group with existing hard extended due date
group = obj_build.make_group(project=self.project,
soft_extended_due_date=self.old_soft_due_date,
hard_extended_due_date=self.old_hard_due_date)
self.do_patch_object_test(group, self.client, self.admin, self.group_url(group),
req_body, expected_response_overrides=expected_resp)

def do_invalid_mixed_use_test(self, req_body: dict):
# test updating group with no extended due dates
group = obj_build.make_group(project=self.project)
resp = self.do_patch_object_invalid_args_test(group, self.client, self.admin,
self.group_url(group), req_body)
self.assertIn('extended_due_date', resp.data)

# test updating group with existing soft extended due date
group = obj_build.make_group(project=self.project,
soft_extended_due_date=self.old_soft_due_date)
resp = self.do_patch_object_invalid_args_test(group, self.client, self.admin,
self.group_url(group), req_body)
self.assertIn('extended_due_date', resp.data)

# test updating group with existing hard extended due date
group = obj_build.make_group(project=self.project,
soft_extended_due_date=self.old_soft_due_date,
hard_extended_due_date=self.old_hard_due_date)
resp = self.do_patch_object_invalid_args_test(group, self.client, self.admin,
self.group_url(group), req_body)
self.assertIn('extended_due_date', resp.data)

def test_no_new_due_dates(self):
body = {'bonus_submissions_remaining': 1}
self.do_test(body)

def test_new_soft(self):
body = {'soft_extended_due_date': self.new_soft_due_date}
expected_resp = {
'soft_extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0),
'extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0)
}
self.do_test(body, expected_resp)

self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{
'extended_due_date': self.new_due_date,
'soft_extended_due_date': self.new_due_date
})

self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{
'extended_due_date': self.new_due_date,
'hard_extended_due_date': self.new_due_date
})
def test_new_hard(self):
body = {'hard_extended_due_date': self.new_hard_due_date}
expected_resp = {
'hard_extended_due_date': self.new_hard_due_date.replace(second=0, microsecond=0),
}
self.do_test(body, expected_resp)

def test_new_legacy(self):
body = {'extended_due_date': self.new_soft_due_date}
expected_resp = {
'hard_extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0),
'soft_extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0),
'extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0)
}
self.do_test(body, expected_resp)

self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{
'extended_due_date': self.new_due_date,
'soft_extended_due_date': self.new_due_date,
'hard_extended_due_date': self.new_due_date
})
def test_new_legacy_new_soft(self):
body = {
'extended_due_date': self.new_soft_due_date,
'soft_extended_due_date': self.new_soft_due_date
}
self.do_invalid_mixed_use_test(body)

def test_admin_update_soft_extended_due_date(self):
group = obj_build.make_group(
project=self.project, hard_extended_due_date=self.new_due_date)
def test_new_legacy_new_hard(self):
body = {
'extended_due_date': self.new_soft_due_date,
'hard_extended_due_date': self.new_hard_due_date
}
self.do_invalid_mixed_use_test(body)

invalid_soft_extended_due_date = "not a date"
response = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{'soft_extended_due_date': invalid_soft_extended_due_date})
self.assertIn('soft_extended_due_date', response.data)
def test_invalid_dates(self):
group = obj_build.make_group(project=self.project)
date = 'not a date'

# soft_extended_due_date can't be later than hard_extended_due_date
invalid_soft_extended_due_date = self.new_due_date + datetime.timedelta(days=1)
response = self.do_patch_object_invalid_args_test(
resp = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{'soft_extended_due_date': invalid_soft_extended_due_date})
self.assertIn('hard_extended_due_date', response.data)
{'extended_due_date': date}
)
self.assertIn('extended_due_date', resp.data)

valid_soft_extended_due_date = self.new_due_date - datetime.timedelta(days=1)
self.do_patch_object_test(
resp = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{
'hard_extended_due_date': group.hard_extended_due_date,
'soft_extended_due_date': valid_soft_extended_due_date,
'extended_due_date': group.extended_due_date
},
expected_response_overrides={
'soft_extended_due_date': valid_soft_extended_due_date.replace(
second=0, microsecond=0),
# setting soft_extended_due_date will also set extended_due_date
# for backwards compatibility
'extended_due_date': valid_soft_extended_due_date.replace(
second=0, microsecond=0)
})

def test_admin_update_hard_extended_due_date(self):
group = obj_build.make_group(
project=self.project, soft_extended_due_date=self.new_due_date)
{'soft_extended_due_date': date}
)
self.assertIn('soft_extended_due_date', resp.data)

invalid_hard_extended_due_date = "not a date"
response = self.do_patch_object_invalid_args_test(
resp = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{'hard_extended_due_date': invalid_hard_extended_due_date})
self.assertIn('hard_extended_due_date', response.data)
{'hard_extended_due_date': date}
)
self.assertIn('hard_extended_due_date', resp.data)

# hard_extended_due_date can't be before soft_extended_due_date
invalid_hard_extended_due_date = self.new_due_date - datetime.timedelta(days=1)
response = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{'hard_extended_due_date': invalid_hard_extended_due_date})
self.assertIn('hard_extended_due_date', response.data)
def test_new_soft_new_hard(self):
body = {
'soft_extended_due_date': self.new_soft_due_date,
'hard_extended_due_date': self.new_hard_due_date
}
expected_resp = {
'hard_extended_due_date': self.new_hard_due_date.replace(second=0, microsecond=0),
'soft_extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0),
'extended_due_date': self.new_soft_due_date.replace(second=0, microsecond=0)
}
self.do_test(body, expected_resp)

valid_hard_extended_due_date = self.new_due_date + datetime.timedelta(days=1)
self.do_patch_object_test(
group, self.client, self.admin, self.group_url(group),
{
'hard_extended_due_date': valid_hard_extended_due_date,
'soft_extended_due_date': group.soft_extended_due_date,
'extended_due_date': group.extended_due_date
},
expected_response_overrides={
'hard_extended_due_date': valid_hard_extended_due_date.replace(
second=0, microsecond=0),
# extended_due_date is deprecated but should reflect soft_extended_deadline
'extended_due_date': self.new_due_date.replace(
second=0, microsecond=0)
})

def test_admin_update_soft_and_hard_extended_deadlines(self):
group = obj_build.make_group(project=self.project)
later = self.new_due_date + datetime.timedelta(days=1)
response = self.do_patch_object_invalid_args_test(
group, self.client, self.admin, self.group_url(group),
{
'hard_extended_due_date': self.new_due_date,
'soft_extended_due_date': later,
'extended_due_date': group.extended_due_date
})
self.assertIn('hard_extended_due_date', response.data)

self.do_patch_object_test(
group, self.client, self.admin, self.group_url(group),
{
'soft_extended_due_date': self.new_due_date,
'hard_extended_due_date': later,
'extended_due_date': group.extended_due_date
},
expected_response_overrides={
'soft_extended_due_date': self.new_due_date.replace(second=0, microsecond=0),
'hard_extended_due_date': later.replace(second=0, microsecond=0),
'extended_due_date': self.new_due_date.replace(second=0, microsecond=0)

})
def test_new_legacy_new_soft_new_hard(self):
body = {
'extended_due_date': self.new_soft_due_date,
'soft_extended_due_date': self.new_soft_due_date,
'hard_extended_due_date': self.new_hard_due_date
}
self.do_invalid_mixed_use_test(body)

def group_url(self, group: ag_models.Group) -> str:
return reverse('group-detail', kwargs={'pk': group.pk})
Expand Down
1 change: 0 additions & 1 deletion autograder/rest_api/views/group_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,6 @@ def clean_extended_due_dates(update_data: Mapping, old_group: ag_models.Group) -
'hard_extended_due_date', or if any datetime strings in `update_data` are invalid.
"""
update_data = dict(copy.deepcopy(update_data))

try:
legacy_changed = 'extended_due_date' in update_data \
and not core_ut.datetimes_are_equal(update_data['extended_due_date'],
Expand Down

0 comments on commit 25e2030

Please sign in to comment.