from unittest.mock import MagicMock, patch import pytest from django.contrib.auth import get_user_model User = get_user_model() @pytest.fixture def user_profile(db): user = User.objects.create_user(username="testuser", password="testpass") return user.profile @pytest.mark.django_db class TestGenerateTodoistOauthUrl: def test_generates_url_with_state(self, user_profile): from tasks.todoist import generate_todoist_oauth_url url = generate_todoist_oauth_url(user_profile.user_id) user_profile.refresh_from_db() assert user_profile.todoist_state is not None assert len(user_profile.todoist_state) == 32 assert url.startswith("https://todoist.com/oauth/authorize") assert user_profile.todoist_state in url def test_updates_existing_state(self, user_profile): from tasks.todoist import generate_todoist_oauth_url old_state = "oldstate12345678901234567890123" user_profile.todoist_state = old_state user_profile.save() url = generate_todoist_oauth_url(user_profile.user_id) user_profile.refresh_from_db() assert user_profile.todoist_state != old_state @pytest.mark.django_db class TestGetTodoistAccessToken: def test_raises_when_profile_not_found(self): from tasks.todoist import get_todoist_access_token with pytest.raises(Exception, match="Could not find profile"): get_todoist_access_token(user_id=999, state="anystate", code="anycode") def test_raises_when_state_mismatch(self, user_profile): from tasks.todoist import get_todoist_access_token user_profile.todoist_state = "correctstate1234567890123" user_profile.save() with pytest.raises(Exception, match="state mismatch"): get_todoist_access_token( user_id=user_profile.user_id, state="wrongstate", code="anycode" ) @patch("tasks.todoist.requests.post") def test_exchanges_code_for_token(self, mock_post, user_profile): from tasks.todoist import get_todoist_access_token user_profile.todoist_state = "correctstate1234567890123" user_profile.save() mock_token_response = MagicMock() mock_token_response.status_code = 200 mock_token_response.json.return_value = {"access_token": "test_access_token"} mock_post.return_value = mock_token_response get_todoist_access_token( user_id=user_profile.user_id, state="correctstate1234567890123", code="testcode", ) user_profile.refresh_from_db() assert user_profile.todoist_auth_key == "test_access_token" assert user_profile.todoist_state is None @patch("tasks.todoist.requests.post") def test_fetches_todoist_user_id(self, mock_post, user_profile): from tasks.todoist import get_todoist_access_token user_profile.todoist_state = "correctstate1234567890123" user_profile.save() mock_token_response = MagicMock() mock_token_response.status_code = 200 mock_token_response.json.return_value = {"access_token": "test_access_token"} mock_sync_response = MagicMock() mock_sync_response.status_code = 200 mock_sync_response.json.return_value = {"user": {"id": "12345"}} mock_post.side_effect = [mock_token_response, mock_sync_response] get_todoist_access_token( user_id=user_profile.user_id, state="correctstate1234567890123", code="testcode", ) user_profile.refresh_from_db() assert user_profile.todoist_user_id == "12345" @patch("tasks.todoist.requests.post") def test_handles_token_exchange_failure(self, mock_post, user_profile): from tasks.todoist import get_todoist_access_token user_profile.todoist_state = "correctstate1234567890123" user_profile.save() mock_response = MagicMock() mock_response.status_code = 400 mock_post.return_value = mock_response get_todoist_access_token( user_id=user_profile.user_id, state="correctstate1234567890123", code="badcode", ) user_profile.refresh_from_db() assert user_profile.todoist_auth_key is None assert user_profile.todoist_state == "correctstate1234567890123"