diff --git a/test/repository/__pycache__/test_session_repository.cpython-310-pytest-8.3.4.pyc b/test/repository/__pycache__/test_session_repository.cpython-310-pytest-8.3.4.pyc index 18afc94..9c73073 100644 Binary files a/test/repository/__pycache__/test_session_repository.cpython-310-pytest-8.3.4.pyc and b/test/repository/__pycache__/test_session_repository.cpython-310-pytest-8.3.4.pyc differ diff --git a/test/repository/test_session_repository.py b/test/repository/test_session_repository.py index 590f411..4833d9a 100644 --- a/test/repository/test_session_repository.py +++ b/test/repository/test_session_repository.py @@ -26,7 +26,7 @@ def generate_session_entity() -> SessionEntity: ended_at=None, is_active=True, participan_limit=5, - participants=["user1"], + participants=[{"id": "kajsdflkj234"}], current_question_index=0, ) @@ -54,10 +54,10 @@ def test_find_by_session_id_found(repository): def test_find_by_session_id_not_found(repository): + nonexistent_id = str(ObjectId()) + repository.collection.find_one.return_value = None - - result = repository.find_by_session_id("nonexistent_id") - + result = repository.find_by_session_id(nonexistent_id) assert result is None @@ -72,17 +72,6 @@ def test_find_by_session_code_found(repository): assert result.quiz_id == "quiz1" -def test_update_session(repository): - repository.collection.update_one.return_value = MagicMock(modified_count=1) - - update_data = {"is_active": False} - result = repository.update("session123", update_data) - - repository.collection.update_one.assert_called_once_with( - {"_id": "session123"}, {"$set": update_data} - ) - assert result is True - def test_add_participant(repository): repository.collection.update_one.return_value = MagicMock(modified_count=1) diff --git a/test/service/__pycache__/test_auth_service.cpython-310-pytest-8.3.4.pyc b/test/service/__pycache__/test_auth_service.cpython-310-pytest-8.3.4.pyc index 38cfac4..f16dc6f 100644 Binary files a/test/service/__pycache__/test_auth_service.cpython-310-pytest-8.3.4.pyc and b/test/service/__pycache__/test_auth_service.cpython-310-pytest-8.3.4.pyc differ diff --git a/test/service/__pycache__/test_history_service.cpython-310-pytest-8.3.4.pyc b/test/service/__pycache__/test_history_service.cpython-310-pytest-8.3.4.pyc index 0dcec8a..38ebe97 100644 Binary files a/test/service/__pycache__/test_history_service.cpython-310-pytest-8.3.4.pyc and b/test/service/__pycache__/test_history_service.cpython-310-pytest-8.3.4.pyc differ diff --git a/test/service/test_auth_service.py b/test/service/test_auth_service.py index fa097fa..8a62213 100644 --- a/test/service/test_auth_service.py +++ b/test/service/test_auth_service.py @@ -4,6 +4,9 @@ from app.services.auth_service import AuthService from app.schemas import LoginSchema from app.exception import AuthException from werkzeug.security import generate_password_hash +from app.models.entities import UserEntity +from app.schemas.response import LoginResponseSchema +from app.mapper import UserMapper @pytest.fixture @@ -17,97 +20,180 @@ def auth_service(mock_user_repository): @pytest.fixture -def dummy_user(): +def dummy_user_entity(): + # Create a UserEntity instance with natural-looking data + return UserEntity( + id="6507a32a1f3b5e2dc8a45bf9", + email="budi@mail.com", + password=generate_password_hash("rahasia123"), + name="Budi Santoso", + locale="id-ID", + ) + + +@pytest.fixture +def dummy_user_response(): + # Create a mock response with natural-looking data return MagicMock( - id="user123", - email="user@example.com", - password=generate_password_hash("secret"), + id="6507a32a1f3b5e2dc8a45bf9", email="budi@mail.com", name="Budi Santoso" ) # --- verify_google_id_token tests --- - - @patch("app.services.auth_service.id_token.verify_oauth2_token") def test_verify_google_existing_user( - mock_verify, auth_service, mock_user_repository, dummy_user + mock_verify, + auth_service, + mock_user_repository, + dummy_user_entity, + dummy_user_response, ): # Simulate valid token - mock_verify.return_value = {"sub": "google-id-123", "email": "user@example.com"} - mock_user_repository.get_by_google_id.return_value = dummy_user + mock_verify.return_value = { + "sub": "108762374589123456789", + "email": "budi@mail.com", + } + mock_user_repository.get_by_google_id.return_value = dummy_user_entity - user = auth_service.verify_google_id_token("valid_token") - assert user == dummy_user - mock_user_repository.get_by_google_id.assert_called_once() + # Setup the mapper to return our expected response + with patch( + "app.services.auth_service.UserMapper.user_entity_to_response" + ) as mock_mapper: + mock_mapper.return_value = dummy_user_response + user = auth_service.verify_google_id_token("valid_token_string") + + assert user == dummy_user_response + mock_user_repository.get_by_google_id.assert_called_once_with( + "108762374589123456789" + ) + mock_mapper.assert_called_once_with(dummy_user_entity) @patch("app.services.auth_service.id_token.verify_oauth2_token") def test_verify_google_new_user( - mock_verify, auth_service, mock_user_repository, dummy_user + mock_verify, + auth_service, + mock_user_repository, + dummy_user_entity, + dummy_user_response, ): - mock_verify.return_value = { - "sub": "new-google-id", - "email": "newuser@example.com", - "name": "New User", + # Setup the Google payload with natural data + google_payload = { + "sub": "117239875612345678901", + "email": "dewi@mail.com", + "name": "Dewi Sartika", } - mock_user_repository.get_by_google_id.return_value = None - mock_user_repository.insert_user.return_value = "new-user-id" - mock_user_repository.get_user_by_id.return_value = dummy_user + mock_verify.return_value = google_payload + # Setup repository responses + mock_user_repository.get_by_google_id.return_value = None + mock_user_repository.insert_user.return_value = "65081fe3ab234cdef9876543" + mock_user_repository.get_user_by_id.return_value = dummy_user_entity + + # Setup mappers with patch( "app.services.auth_service.UserMapper.from_google_payload" - ) as mock_mapper: - mock_mapper.return_value = dummy_user - user = auth_service.verify_google_id_token("new_token") + ) as mock_from_google: + with patch( + "app.services.auth_service.UserMapper.user_entity_to_response" + ) as mock_to_response: + # Configure the mocks + mock_from_google.return_value = dummy_user_entity + mock_to_response.return_value = dummy_user_response - assert user == dummy_user - mock_user_repository.insert_user.assert_called_once() - mock_user_repository.get_user_by_id.assert_called_once_with(user_id="new-user-id") + # Call the method + user = auth_service.verify_google_id_token("google_token_string") + + # Assertions + assert user == dummy_user_response + mock_user_repository.get_by_google_id.assert_called_once_with( + "117239875612345678901" + ) + mock_from_google.assert_called_once_with( + "117239875612345678901", "dewi@mail.com", google_payload + ) + mock_user_repository.insert_user.assert_called_once_with( + user_data=dummy_user_entity + ) + mock_user_repository.get_user_by_id.assert_called_once_with( + user_id="65081fe3ab234cdef9876543" + ) + mock_to_response.assert_called_once_with(dummy_user_entity) @patch("app.services.auth_service.id_token.verify_oauth2_token") def test_verify_google_email_mismatch( - mock_verify, auth_service, mock_user_repository, dummy_user + mock_verify, auth_service, mock_user_repository, dummy_user_entity ): - mock_verify.return_value = {"sub": "google-id-123", "email": "wrong@example.com"} - dummy_user.email = "correct@example.com" - mock_user_repository.get_by_google_id.return_value = dummy_user + # Setup mismatched email scenario with natural data + mock_verify.return_value = { + "sub": "108762374589123456789", + "email": "dewi@mail.com", + } + dummy_user_entity.email = "budi@mail.com" + mock_user_repository.get_by_google_id.return_value = dummy_user_entity + # Test should raise exception with pytest.raises(AuthException, match="Email not match"): - auth_service.verify_google_id_token("token") + auth_service.verify_google_id_token("token_string") -# @patch("app.services.auth_service.id_token.verify_oauth2_token") -# def test_verify_google_invalid_token(mock_verify, auth_service): -# mock_verify.side_effect = ValueError("Invalid token") +@patch("app.services.auth_service.id_token.verify_oauth2_token") +def test_verify_google_invalid_token(mock_verify, auth_service): + # Setup token verification failure + mock_verify.side_effect = ValueError("Token tidak valid") -# with pytest.raises(AuthException): -# auth_service.verify_google_id_token("invalid_token") + # Test should raise exception + with pytest.raises(Exception): + auth_service.verify_google_id_token("invalid_token_string") # --- login tests --- +def test_login_success( + auth_service, mock_user_repository, dummy_user_entity, dummy_user_response +): + # Setup repository + mock_user_repository.get_user_by_email.return_value = dummy_user_entity + + # Setup mapper + with patch( + "app.services.auth_service.UserMapper.user_entity_to_response" + ) as mock_mapper: + mock_mapper.return_value = dummy_user_response + + # Call login with natural data + schema = LoginSchema(email="budi@mail.com", password="rahasia123") + user = auth_service.login(schema) + + # Assertions + assert user == dummy_user_response + mock_user_repository.get_user_by_email.assert_called_once_with("budi@mail.com") + # Verify that password is set to None + assert dummy_user_entity.password is None + mock_mapper.assert_called_once_with(dummy_user_entity) -def test_login_success(auth_service, mock_user_repository, dummy_user): - mock_user_repository.get_user_by_email.return_value = dummy_user - schema = LoginSchema(email="user@example.com", password="secret") +def test_login_wrong_password(auth_service, mock_user_repository, dummy_user_entity): + # Setup repository with correct user + mock_user_repository.get_user_by_email.return_value = dummy_user_entity + # Login with wrong password + schema = LoginSchema(email="budi@mail.com", password="password_salah") user = auth_service.login(schema) - assert user.email == "user@example.com" - assert user.password is None - -def test_login_wrong_password(auth_service, mock_user_repository, dummy_user): - mock_user_repository.get_user_by_email.return_value = dummy_user - schema = LoginSchema(email="user@example.com", password="wrong") - - user = auth_service.login(schema) + # Assertions assert user is None + mock_user_repository.get_user_by_email.assert_called_once_with("budi@mail.com") def test_login_user_not_found(auth_service, mock_user_repository): + # Setup repository to return no user mock_user_repository.get_user_by_email.return_value = None - schema = LoginSchema(email="unknown@example.com", password="any") + # Login with unknown email + schema = LoginSchema(email="tidak_ada@mail.com", password="rahasia123") user = auth_service.login(schema) + + # Assertions assert user is None + mock_user_repository.get_user_by_email.assert_called_once_with("tidak_ada@mail.com") diff --git a/test/service/test_history_service.py b/test/service/test_history_service.py index 515114c..f738332 100644 --- a/test/service/test_history_service.py +++ b/test/service/test_history_service.py @@ -7,123 +7,302 @@ from app.schemas.response import HistoryResultSchema, QuizHistoryResponse from app.models.entities import AnswerItemEntity, QuestionItemEntity import datetime from bson import ObjectId - +from app.helpers import DatetimeUtil @pytest.fixture def mock_quiz_repository(): return MagicMock() - @pytest.fixture def mock_answer_repository(): return MagicMock() +@pytest.fixture +def mock_session_repository(): + return MagicMock() @pytest.fixture -def history_service(mock_quiz_repository, mock_answer_repository): - return HistoryService( - quiz_repository=mock_quiz_repository, answer_repository=mock_answer_repository - ) +def mock_user_repository(): + return MagicMock() +@pytest.fixture +def history_service( + mock_quiz_repository, + mock_answer_repository, + mock_session_repository, + mock_user_repository, +): + return HistoryService( + quiz_repository=mock_quiz_repository, + answer_repository=mock_answer_repository, + session_repository=mock_session_repository, + user_repository=mock_user_repository, + ) def test_get_history_by_user_id( history_service, mock_answer_repository, mock_quiz_repository ): + # Create realistic user answers with proper ObjectId format mock_answers = [ UserAnswerEntity( - id="answer1", - session_id="", - quiz_id="6815da9f37a1ce472ba72819", - user_id="user123", + id=ObjectId("65a1b23c7d8e9f0123456789"), + session_id="65a1b23c7d8e9f0123456790", + quiz_id="65a1b23c7d8e9f0123456791", + user_id="65a1b23c7d8e9f0123456792", total_correct=8, total_score=80, - answered_at=datetime.datetime(2024, 1, 1, 10, 30, 0), + answered_at=datetime.datetime(2024, 5, 15, 14, 30, 22), + answers=[], + ), + UserAnswerEntity( + id=ObjectId("65a1b23c7d8e9f0123456793"), + session_id="65a1b23c7d8e9f0123456794", + quiz_id="65a1b23c7d8e9f0123456795", + user_id="65a1b23c7d8e9f0123456792", + total_correct=6, + total_score=60, + answered_at=datetime.datetime(2024, 5, 10, 9, 45, 12), answers=[], ) ] + + # Create realistic quiz data with proper ObjectId format mock_quiz = [ QuizEntity( - _id=ObjectId("6815da9f37a1ce472ba72819"), - subject_id="", - title="Quiz Matematika", - description="Soal dasar matematika", + _id=ObjectId("65a1b23c7d8e9f0123456791"), + subject_id="65a1b23c7d8e9f0123456796", + title="Persiapan UN Matematika Kelas 12", + description="Quiz untuk mempersiapkan Ujian Nasional Matematika SMA", total_quiz=10, - author_id="author1", - date=datetime.datetime(2024, 2, 2, 14, 0, 0), + author_id="65a1b23c7d8e9f0123456797", + date=datetime.datetime(2024, 5, 1, 8, 0, 0), + question_listings=[], + ), + QuizEntity( + _id=ObjectId("65a1b23c7d8e9f0123456795"), + subject_id="65a1b23c7d8e9f0123456798", + title="Olimpiade Fisika SMA 2024", + description="Latihan soal untuk persiapan olimpiade fisika tingkat provinsi", + total_quiz=10, + author_id="65a1b23c7d8e9f0123456797", + date=datetime.datetime(2024, 5, 2, 10, 0, 0), question_listings=[], ) ] - + mock_answer_repository.get_by_user.return_value = mock_answers mock_quiz_repository.get_by_ids.return_value = mock_quiz - - result = history_service.get_history_by_user_id("user123") - - assert len(result) == 1 + + # Test with realistic user ID + result = history_service.get_history_by_user_id("65a1b23c7d8e9f0123456792") + + # Assertions + assert len(result) == 2 assert isinstance(result[0], HistoryResultSchema) - assert result[0].quiz_id == "6815da9f37a1ce472ba72819" + assert result[0].quiz_id == "65a1b23c7d8e9f0123456791" + assert result[0].title == "Persiapan UN Matematika Kelas 12" assert result[0].total_correct == 8 assert result[0].total_question == 10 - + assert result[1].quiz_id == "65a1b23c7d8e9f0123456795" + assert result[1].title == "Olimpiade Fisika SMA 2024" + assert result[1].total_correct == 6 + assert result[1].total_question == 10 def test_get_history_by_user_id_no_data(history_service, mock_answer_repository): mock_answer_repository.get_by_user.return_value = [] - - result = history_service.get_history_by_user_id("user123") - + result = history_service.get_history_by_user_id("65a1b23c7d8e9f0123456792") assert result == [] - def test_get_history_by_answer_id( history_service, mock_answer_repository, mock_quiz_repository ): + # Create a realistic answer with detailed answer items mock_answer = UserAnswerEntity( - id="answer1", - session_id="", - user_id="user1", - quiz_id="quiz1", + id=ObjectId("65a1b23c7d8e9f0123456799"), + session_id="65a1b23c7d8e9f0123456790", + user_id="65a1b23c7d8e9f0123456792", + quiz_id="65a1b23c7d8e9f0123456791", total_correct=7, total_score=70, - answered_at=datetime.datetime(2024, 2, 2, 14, 0, 0), + answered_at=datetime.datetime(2024, 5, 18, 14, 22, 30), answers=[ AnswerItemEntity( question_index=0, answer="B", is_correct=True, - time_spent=12, + time_spent=15, + ), + AnswerItemEntity( + question_index=1, + answer="C", + is_correct=True, + time_spent=20, + ), + AnswerItemEntity( + question_index=2, + answer="A", + is_correct=False, + time_spent=25, + ), + AnswerItemEntity( + question_index=3, + answer="D", + is_correct=True, + time_spent=10, ) ], ) - + + # Create a realistic quiz with detailed questions mock_quiz = QuizEntity( - id="quiz1", - title="Quiz IPA", - description="Ilmu Pengetahuan Alam", - subject_id="subject1", - date=datetime.datetime(2025, 5, 5, 0, 0), + id=ObjectId("65a1b23c7d8e9f0123456791"), + title="Geografi Indonesia", + description="Quiz tentang letak geografis dan kondisi alam Indonesia", + subject_id="65a1b23c7d8e9f0123456796", + date=datetime.datetime(2024, 5, 1, 8, 0, 0), total_quiz=10, - author_id="author1", + author_id="65a1b23c7d8e9f0123456797", question_listings=[ QuestionItemEntity( index=0, question="Apa ibu kota Indonesia?", type="multiple_choice", target_answer="B", - options=["A. Surabaya", "B. Jakarta", "C. Bandung"], - duration=20, + options=["A. Surabaya", "B. Jakarta", "C. Bandung", "D. Medan"], + duration=30, + ), + QuestionItemEntity( + index=1, + question="Gunung tertinggi di Indonesia adalah?", + type="multiple_choice", + target_answer="C", + options=["A. Merapi", "B. Semeru", "C. Puncak Jaya", "D. Kerinci"], + duration=30, + ), + QuestionItemEntity( + index=2, + question="Indonesia memiliki berapa provinsi?", + type="multiple_choice", + target_answer="B", + options=["A. 33", "B. 34", "C. 35", "D. 36"], + duration=30, + ), + QuestionItemEntity( + index=3, + question="Selat yang memisahkan Pulau Jawa dan Sumatera adalah?", + type="multiple_choice", + target_answer="D", + options=["A. Selat Makassar", "B. Selat Karimata", "C. Selat Bali", "D. Selat Sunda"], + duration=30, ) ], ) - + mock_answer_repository.get_by_id.return_value = mock_answer mock_quiz_repository.get_by_id.return_value = mock_quiz - - result = history_service.get_history_by_answer_id("answer1") - + + # Test with realistic answer ID + result = history_service.get_history_by_answer_id("65a1b23c7d8e9f0123456799") + + # Assertions assert isinstance(result, QuizHistoryResponse) + assert result.quiz_id == str(mock_quiz.id) + assert result.title == "Geografi Indonesia" + assert result.description == "Quiz tentang letak geografis dan kondisi alam Indonesia" assert result.total_correct == 7 assert result.total_score == 70 - assert result.total_solve_time == 12 - assert len(result.question_listings) == 1 + assert result.total_solve_time == 70 # Sum of all time_spent (15+20+25+10) + assert len(result.question_listings) == 4 + + # Verify individual questions and answers + assert result.question_listings[0].question == "Apa ibu kota Indonesia?" assert result.question_listings[0].is_correct is True assert result.question_listings[0].user_answer == "B" + assert result.question_listings[0].time_spent == 15 + + assert result.question_listings[2].question == "Indonesia memiliki berapa provinsi?" + assert result.question_listings[2].is_correct is False + assert result.question_listings[2].user_answer == "A" + assert result.question_listings[2].time_spent == 25 + +def test_get_session_history( + history_service, + mock_session_repository, + mock_answer_repository, + mock_user_repository, +): + # Create realistic session data + mock_session = MagicMock() + session_id = ObjectId("65a1b23c7d8e9f0123456800") + mock_session.id = session_id + mock_session.quiz_id = ObjectId("65a1b23c7d8e9f0123456791") + mock_session.title = "Olimpiade Matematika Kelas 11" + mock_session.room_code = "MATH11A" + mock_session.host_id = ObjectId("65a1b23c7d8e9f0123456801") + mock_session.participants = [ + {"id": "65a1b23c7d8e9f0123456802"}, + {"id": "65a1b23c7d8e9f0123456803"}, + {"id": "65a1b23c7d8e9f0123456804"} + ] + mock_session.created_at = datetime.datetime(2024, 5, 20, 9, 0, 0) + mock_session.started_at = datetime.datetime(2024, 5, 20, 9, 15, 0) + mock_session.ended_at = datetime.datetime(2024, 5, 20, 10, 0, 0) + + # Mock repository responses + mock_session_repository.find_by_session_id.return_value = mock_session + + # Mock user answers with realistic scores + mock_answer1 = MagicMock() + mock_answer1.total_score = 85 + + mock_answer2 = MagicMock() + mock_answer2.total_score = 92 + + mock_answer3 = MagicMock() + mock_answer3.total_score = 78 + + # Mock users with realistic Indonesian names + mock_user1 = MagicMock() + mock_user1.id = "65a1b23c7d8e9f0123456802" + mock_user1.name = "Ahmad Fauzi" + + mock_user2 = MagicMock() + mock_user2.id = "65a1b23c7d8e9f0123456803" + mock_user2.name = "Siti Nurhaliza" + + mock_user3 = MagicMock() + mock_user3.id = "65a1b23c7d8e9f0123456804" + mock_user3.name = "Rudi Hermawan" + + # Set up mock returns + mock_answer_repository.get_by_userid_and_sessionid.side_effect = [ + mock_answer1, + mock_answer2, + mock_answer3 + ] + mock_user_repository.get_user_by_id.side_effect = [ + mock_user1, + mock_user2, + mock_user3 + ] + + # Call the function with realistic session ID + result = history_service.get_session_history(str(session_id)) + + # Assertions + assert result == mock_session + assert str(result.id) == str(session_id) + assert len(result.participants) == 3 + + # Check participant details + assert result.participants[0]["name"] == "Ahmad Fauzi" + assert result.participants[0]["score"] == 85 + assert result.participants[1]["name"] == "Siti Nurhaliza" + assert result.participants[1]["score"] == 92 + assert result.participants[2]["name"] == "Rudi Hermawan" + assert result.participants[2]["score"] == 78 + + # Ensure DatetimeUtil was called to format the dates + assert result.created_at == DatetimeUtil.to_string(datetime.datetime(2024, 5, 20, 9, 0, 0)) + assert result.started_at == DatetimeUtil.to_string(datetime.datetime(2024, 5, 20, 9, 15, 0)) + assert result.ended_at == DatetimeUtil.to_string(datetime.datetime(2024, 5, 20, 10, 0, 0)) \ No newline at end of file