Internal
Public Access
Fix task edit crashing after due_date/due_time save (regression from reminders)
The web edit/create/quick-add views and the sync endpoint assigned due_date/due_time/reminder_at straight from request data as raw strings. Django tolerates this at save() (types get coerced deep in the SQL layer), but the in-memory instance keeps the raw strings afterward. Task.reschedule_reminders(), added for per-task reminders and run from save() on that same instance, was the first code to actually operate on those fields before a page reload and crashed with a TypeError - which the service worker's 5xx-treated-as-offline fallback then masked as a misleading "you're offline" screen instead of a real error. Fixed by parsing with Django's parse_date/parse_time/parse_datetime at the point these values are read from the request/payload in tasks/views.py and sync/views.py, rather than trusting raw strings. 5 new regression tests exercise the actual views/endpoint with raw date/time strings and assert proper types land on the model. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5359bf243a
commit
a27fcc4c69
@@ -171,3 +171,55 @@ class SyncEndpointTests(TestCase):
|
||||
self.assertEqual(entry.notes, 'local notes')
|
||||
conflict.refresh_from_db()
|
||||
self.assertEqual(conflict.status, 'resolved_local')
|
||||
|
||||
def test_creating_task_with_due_date_and_time_does_not_crash(self):
|
||||
"""
|
||||
Sync payloads carry due_date/due_time as JSON strings. Regression
|
||||
test: these must end up as real date/time objects on the created
|
||||
Task, not raw strings left for Task.reschedule_reminders() (called
|
||||
from save()) to choke on.
|
||||
"""
|
||||
new_sync_id = str(uuid.uuid4())
|
||||
response = self.client.post('/api/sync/', {
|
||||
'device_id': 'web-test-device',
|
||||
'last_sync_token': None,
|
||||
'changes': {
|
||||
'tasks': [{
|
||||
'sync_id': new_sync_id,
|
||||
'title': 'Due-dated task',
|
||||
'status': 'pending',
|
||||
'priority': 'medium',
|
||||
'due_date': '2026-09-10',
|
||||
'due_time': '17:00:00',
|
||||
}],
|
||||
'tags': [],
|
||||
'time_entries': [],
|
||||
},
|
||||
}, format='json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
task = Task.objects.get(sync_id=new_sync_id)
|
||||
self.assertEqual(task.due_date.isoformat(), '2026-09-10')
|
||||
self.assertEqual(task.due_time.isoformat(), '17:00:00')
|
||||
|
||||
def test_updating_task_due_date_and_time_does_not_crash(self):
|
||||
task = Task.objects.create(user=self.user, title='Task to update')
|
||||
|
||||
response = self.client.post('/api/sync/', {
|
||||
'device_id': 'web-test-device',
|
||||
'last_sync_token': None,
|
||||
'changes': {
|
||||
'tasks': [{
|
||||
'sync_id': str(task.sync_id),
|
||||
'due_date': '2026-09-10',
|
||||
'due_time': '17:00:00',
|
||||
}],
|
||||
'tags': [],
|
||||
'time_entries': [],
|
||||
},
|
||||
}, format='json')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
task.refresh_from_db()
|
||||
self.assertEqual(task.due_date.isoformat(), '2026-09-10')
|
||||
self.assertEqual(task.due_time.isoformat(), '17:00:00')
|
||||
|
||||
Reference in New Issue
Block a user