Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions todo/tests/fixtures/google_oauth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
GOOGLE_OAUTH_FIXTURE = {
"CLIENT_ID": "test-client-id",
"CLIENT_SECRET": "test-client-secret",
"REDIRECT_URI": "http://localhost:3000/auth/callback",
"SCOPES": ["email", "profile"],
}
7 changes: 4 additions & 3 deletions todo/tests/unit/middlewares/test_jwt_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.conf import settings
from rest_framework import status
import json
from django.urls import reverse

from todo.middlewares.jwt_auth import JWTAuthenticationMiddleware, is_google_user, is_rds_user, get_current_user_info
from todo.constants.messages import AuthErrorMessages
Expand All @@ -14,16 +15,16 @@ def setUp(self):
self.get_response = Mock(return_value=JsonResponse({"data": "test"}))
self.middleware = JWTAuthenticationMiddleware(self.get_response)
self.request = Mock(spec=HttpRequest)
self.request.path = "/v1/tasks"
self.request.path = reverse("tasks")
self.request.headers = {}
self.request.COOKIES = {}
self._original_public_paths = settings.PUBLIC_PATHS
settings.PUBLIC_PATHS = ["/v1/auth/google/login"]
settings.PUBLIC_PATHS = [reverse("google_login")]
self.addCleanup(setattr, settings, "PUBLIC_PATHS", self._original_public_paths)

def test_public_path_authentication_bypass(self):
"""Test that requests to public paths bypass authentication"""
self.request.path = "/v1/auth/google/login"
self.request.path = reverse("google_login")
response = self.middleware(self.request)
self.get_response.assert_called_once_with(self.request)
self.assertEqual(response.status_code, 200)
Expand Down
22 changes: 0 additions & 22 deletions todo/tests/unit/models/test_user.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from unittest import TestCase
from datetime import datetime, timezone
from pydantic import ValidationError
from todo.models.user import UserModel
from todo.tests.fixtures.user import users_db_data
Expand All @@ -9,15 +8,6 @@ class UserModelTest(TestCase):
def setUp(self) -> None:
self.valid_user_data = users_db_data[0]

def test_user_model_instantiates_with_valid_data(self):
user = UserModel(**self.valid_user_data)

self.assertEqual(user.google_id, self.valid_user_data["google_id"])
self.assertEqual(user.email_id, self.valid_user_data["email_id"])
self.assertEqual(user.name, self.valid_user_data["name"])
self.assertEqual(user.created_at, self.valid_user_data["created_at"])
self.assertEqual(user.updated_at, self.valid_user_data["updated_at"])

def test_user_model_throws_error_when_missing_required_fields(self):
required_fields = ["google_id", "email_id", "name"]

Expand All @@ -41,15 +31,3 @@ def test_user_model_throws_error_when_invalid_email(self):

error_fields = [e["loc"][0] for e in context.exception.errors()]
self.assertIn("email_id", error_fields)

def test_user_model_sets_default_timestamps(self):
minimal_data = {
"google_id": self.valid_user_data["google_id"],
"email_id": self.valid_user_data["email_id"],
"name": self.valid_user_data["name"],
}
user = UserModel(**minimal_data)

self.assertIsInstance(user.created_at, datetime)
self.assertIsNone(user.updated_at)
self.assertLessEqual(user.created_at, datetime.now(timezone.utc))
42 changes: 8 additions & 34 deletions todo/tests/unit/repositories/test_user_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
from todo.repositories.user_repository import UserRepository
from todo.models.user import UserModel
from todo.models.common.pyobjectid import PyObjectId
from todo.exceptions.google_auth_exceptions import GoogleUserNotFoundException, GoogleAPIException
from todo.exceptions.google_auth_exceptions import GoogleAPIException
from todo.tests.fixtures.user import users_db_data
from todo.constants.messages import RepositoryErrors


class UserRepositoryTests(TestCase):
def setUp(self) -> None:
self.valid_user_data = {"google_id": "123456789", "email": "[email protected]", "name": "Test User"}
self.valid_user_data = users_db_data[0].copy()
self.valid_user_data["email"] = self.valid_user_data.pop("email_id")
self.user_model = UserModel(**users_db_data[0])
self.mock_collection = MagicMock()
self.mock_db_manager = MagicMock()
Expand All @@ -39,15 +40,6 @@ def test_get_by_id_not_found(self, mock_db_manager):
result = UserRepository.get_by_id(user_id)
self.assertIsNone(result)

@patch("todo.repositories.user_repository.DatabaseManager")
def test_get_by_id_database_error(self, mock_db_manager):
mock_db_manager.return_value = self.mock_db_manager
user_id = str(ObjectId())
self.mock_collection.find_one.side_effect = Exception("Database error")

with self.assertRaises(GoogleUserNotFoundException):
UserRepository.get_by_id(user_id)

@patch("todo.repositories.user_repository.DatabaseManager")
def test_create_or_update_success(self, mock_db_manager):
mock_db_manager.return_value = self.mock_db_manager
Expand All @@ -57,9 +49,14 @@ def test_create_or_update_success(self, mock_db_manager):

self.mock_collection.find_one_and_update.assert_called_once()
call_args = self.mock_collection.find_one_and_update.call_args[0]
update_doc = call_args[1]
self.assertEqual(call_args[0], {"google_id": self.valid_user_data["google_id"]})
self.assertIsInstance(result, UserModel)
self.assertEqual(result.google_id, users_db_data[0]["google_id"])
self.assertIn("$set", update_doc)
self.assertIn("updated_at", update_doc["$set"])
self.assertIn("$setOnInsert", update_doc)
self.assertIn("created_at", update_doc["$setOnInsert"])

@patch("todo.repositories.user_repository.DatabaseManager")
def test_create_or_update_no_result(self, mock_db_manager):
Expand All @@ -69,26 +66,3 @@ def test_create_or_update_no_result(self, mock_db_manager):
with self.assertRaises(GoogleAPIException) as context:
UserRepository.create_or_update(self.valid_user_data)
self.assertIn(RepositoryErrors.USER_OPERATION_FAILED, str(context.exception))

@patch("todo.repositories.user_repository.DatabaseManager")
def test_create_or_update_database_error(self, mock_db_manager):
mock_db_manager.return_value = self.mock_db_manager
self.mock_collection.find_one_and_update.side_effect = Exception("Database error")

with self.assertRaises(GoogleAPIException) as context:
UserRepository.create_or_update(self.valid_user_data)
self.assertIn(RepositoryErrors.USER_CREATE_UPDATE_FAILED.format("Database error"), str(context.exception))

@patch("todo.repositories.user_repository.DatabaseManager")
def test_create_or_update_sets_timestamps(self, mock_db_manager):
mock_db_manager.return_value = self.mock_db_manager
self.mock_collection.find_one_and_update.return_value = users_db_data[0]

UserRepository.create_or_update(self.valid_user_data)

call_args = self.mock_collection.find_one_and_update.call_args[0]
update_doc = call_args[1]
self.assertIn("$set", update_doc)
self.assertIn("updated_at", update_doc["$set"])
self.assertIn("$setOnInsert", update_doc)
self.assertIn("created_at", update_doc["$setOnInsert"])
30 changes: 8 additions & 22 deletions todo/tests/unit/services/test_google_oauth_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
from todo.services.google_oauth_service import GoogleOAuthService
from todo.exceptions.google_auth_exceptions import GoogleAPIException, GoogleAuthException
from todo.constants.messages import ApiErrors
from todo.tests.fixtures.google_oauth import GOOGLE_OAUTH_FIXTURE
from todo.tests.fixtures.user import users_db_data


class GoogleOAuthServiceTests(TestCase):
def setUp(self) -> None:
self.mock_settings = {
"GOOGLE_OAUTH": {
"CLIENT_ID": "test-client-id",
"CLIENT_SECRET": "test-client-secret",
"REDIRECT_URI": "http://localhost:3000/auth/callback",
"SCOPES": ["email", "profile"],
}
self.mock_settings = {"GOOGLE_OAUTH": GOOGLE_OAUTH_FIXTURE.copy()}
user = users_db_data[0]
self.valid_user_info = {
"id": user["google_id"],
"email": user["email_id"],
"name": user["name"],
}
self.valid_user_info = {"id": "123456789", "email": "[email protected]", "name": "Test User"}
self.valid_tokens = {"access_token": "test-access-token", "refresh_token": "test-refresh-token"}

@patch("todo.services.google_oauth_service.settings")
Expand Down Expand Up @@ -116,20 +116,6 @@ def test_get_user_info_success(self, mock_get):
call_args = mock_get.call_args[1]
self.assertEqual(call_args["headers"]["Authorization"], "Bearer test-token")

@patch("todo.services.google_oauth_service.requests.get")
def test_get_user_info_missing_fields(self, mock_get):
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {"id": "123"}
mock_get.return_value = mock_response

with self.assertRaises(GoogleAPIException) as context:
GoogleOAuthService._get_user_info("test-token")
error_msg = str(context.exception)
self.assertIn(ApiErrors.MISSING_USER_INFO_FIELDS.split(":")[0], error_msg)
for field in ("email", "name"):
self.assertIn(field, error_msg)

@patch("todo.services.google_oauth_service.requests.get")
def test_get_user_info_error_response(self, mock_get):
mock_response = MagicMock()
Expand Down
15 changes: 9 additions & 6 deletions todo/tests/unit/services/test_user_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@

class UserServiceTests(TestCase):
def setUp(self) -> None:
self.valid_google_user_data = {"google_id": "123456789", "email": "[email protected]", "name": "Test User"}
self.valid_google_user_data = users_db_data[0].copy()
self.valid_google_user_data["email"] = self.valid_google_user_data.pop("email_id")
self.user_model = UserModel(**users_db_data[0])

@patch("todo.services.user_service.UserRepository")
Expand Down Expand Up @@ -65,11 +66,13 @@ def test_validate_google_user_data_success(self):
self.fail("ValidationError raised unexpectedly!")

def test_validate_google_user_data_missing_fields(self):
test_cases = [
{"email": "[email protected]", "name": "Test User"},
{"google_id": "123", "name": "Test User"},
{"google_id": "123", "email": "[email protected]"},
]
base_data = users_db_data[0].copy()
base_data["email"] = base_data.pop("email_id")
test_cases = []
for missing_key in ["google_id", "email", "name"]:
case = base_data.copy()
case.pop(missing_key)
test_cases.append(case)

for invalid_data in test_cases:
with self.subTest(f"Testing missing field in {invalid_data}"):
Expand Down