From b18f7f78e343425d5fb111a40606ac82001699b4 Mon Sep 17 00:00:00 2001 From: akhdanre Date: Wed, 21 May 2025 11:57:30 +0700 Subject: [PATCH] feat: done fix testing --- ...on_repository.cpython-310-pytest-8.3.4.pyc | Bin 6305 -> 5897 bytes test/repository/test_session_repository.py | 19 +- ..._auth_service.cpython-310-pytest-8.3.4.pyc | Bin 4565 -> 5760 bytes ...story_service.cpython-310-pytest-8.3.4.pyc | Bin 6367 -> 15415 bytes test/service/test_auth_service.py | 182 +++++++++--- test/service/test_history_service.py | 277 ++++++++++++++---- 6 files changed, 366 insertions(+), 112 deletions(-) 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 18afc948e3c82b8762c58c2c9abef01f39e854dd..9c73073083fadba086cffdb3abfa2af6623e6756 100644 GIT binary patch delta 1561 zcmb7EO>7%Q6yCSHUa!~o+Occ9KZ)hKZo-lhk~mdyfzU!B6q2YVX-kb1$IYx^(_|gZ zuA`=`M2JG77Q72YC9#h+K0#w&i9;D*v`9U1xw-0)*4pLO$?6pQN&Hn+K?LP4EwUT2 zq1~UQIe;Q7Mb0P(d3}0_ZANZDnJLNzsO>H(pF*yL{vcRJOGT(KO+Bp~MstK+R7)=v zx#?@`ZDOu9NN0V68t714mQ5OM+oYp7%ml+rTMfr_9II_NED{!0*$--2nZj~e_KRA~ z9mRBsv{z`b<}r*s$Fi~aVUGF9q26cQ;R-HX7p_AWh~NpU;;Ph;Jz+cIK_9vz5xSBm zdQwN|%AV}ZxXN~vfG4*&f9P+CeVOy9ZpZqR3bfNas%5XKQ^5XKOmMVLUCd;_tHfV>$|n&h9lZgaM;SdMGj zF4Bwi`SBAQTa9q_Ns9AAPpeJWo9+~yWxr>#EE^xuv9}s%q6Qw&^K3~O+FgmC1LHXN z(-cAp;c+pbFO4wG?j(*4pFs0vgjYBi0~EzUPa!;VL^hq=A8Ib>emdy>9!v_3`+Bgc zFOoCt^H`GIO?}_{4X_&cI#-$1vP{xgX?JYb*Y2xnVzOF~b~wegT8+NIbFaoKO+psi z3#LmC@bMy@$MRBSw3_U9MX^wRWS|n-=0L5@outj27c_lgXwsf|FwC33(ZG2 zuY?Y|gw>vK|FprA*&mro_5wE?lt!9MBO`KYkloR2Mob5?*oQSQB22Tn+>04x){q0; zQ+%@MTkKY@elXAs7I_dG31SFHKAmTU{PVqV0*+f8jO=65@nw8L!pCIK8drIvx#C)z z=7R^t2~TKZfhRrY!;R5>LYl7`58HMcYn0iYd>-Cp_wq$0=-w!rKVT2n4}qXZH?6 Og7%g5Z>8cZ`QlE<2bgHHgU*blR9lu^+#>fD8iIdC~ith(i9A6>-agz2FFg{ zu7Scv>7k+mR6+F)NQgsnLOD>W3J0VjAtA(x11AnXAu0}B;DWfq%yXb3ijZo_pXPnD zZ{EE5X5M>an@3}{Xw*{hm%rDw^e}Qa)(zj^J@jp~n`Le(g)Rm&O19_zAGZPeMWF7F zYKy=O)^f)}-V~py8S$g~i>ZSO#-BVRAfDO&Nt*)5h_TSL(Tjn3zql3p5c)*YxC*DY zE5^r=dV+ZA>?6x07!aN21!IuRA+c!op39)wuvTiUTya=^WtAT!!d!5x%&Aq&e1w(> zxtzb@cxBIXYgNZ(&A>9|sQAw8hlKde>`9%V&ox#nae3xRT=G+*FZ>Q%q_o#RLV_uQ zvaBqFqXx=`#$XfHfGKNg0D)Rp*0ezL&ieXBh^c|L08Ar(LEY4u?i(8|o5~xCH;jHW zz@7XsT4A!PfPS*oO<yi|x)@pf;yo?ou|cpdSI zl`$#hEtL<7o7TQVIb38TvK;&z+OiV-8M2NLoF^D17!yC39iv+YkJ5^QH0<2!4X5aN zW$qv27ye}yYJ8>WpWu1XZ=Z=6$Xj5kfy(DZ#mH>mwy!{ToPx9x?5Q6);slc-6g`!H zlFX+F&LU*nIO)lsCLkdnW!^~c*06+{?^e-nr%m!|&x$Xi1@U<}y8b0{zQTa1cL0@x zW!2G`#;~qyxZcoN2^&x+6K2FWtp_`RiL#Q(JjUS- z3!kR)e~RBE{x_3rZNu&JlnK31aU8M)J}-Q6`;)eW8kZwRz0oY#r1+|RGC@6%)wz5c z50LM&u#<(pUNR}jKfWO%E)(Pkro_9+iNnnew004)?YqXXq0{g+-_oSGpGtN$lV748 zd)2bLmppW=?BT54d|RF)qEAex((t@kO!XMDWAfsI)R~dj(I?v@2Gkr0^&Y$rH-TSA zKHJ?e=j(38cdH&B6~CvB!x1r%_OtS}I89M663h|2L{KCs5iAm17Z1`yFeH9X_qW() J#Iz&fzX3X 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 38cfac4b00a5dec0390af2616500e3bdd494057f..f16dc6fbf8b6fb2c94c55f1d71fd7064ee676d14 100644 GIT binary patch literal 5760 zcmb7IOLH7o74G-+^gQ&k<(HC-orGi{maMl7F%^?Iyb@Kg2}veE)0*3oTAGKr-D6pz zRYjq!Ou15QC>CBw8>+O+AK)MG3wq0n9aU^V5X^T@_l#SXQkb6VGxs(3b6r=m97!&8X{z6ti zFUGn4VphK(E^_^)tbR#c=6X$B5fh&&jjQ4X@glyjiI+qT-!BXGo>IU1SGu+3x|S5* z_tt{u``zYdVs3d+bNxt7O1FE_`aNIX51M`*)%UtFuB z#@GCgFTKcjx4bai?n>dVd*M2IoDV}^-swa^w1e6h&vVxgx4NCsAI!&t-uJe)eA%R3 zFg1xKDN2=6NKT>1`Uo4J4m}+;TDG`Lt>8WN{_*2ok9|ocO%U-bS zZprS3-;AcX()KoeITiU~G=<&K8M)$mIF_5WOm6KY=i0aww}%#2`di&Fh`Ms8Oq-#_ zElm#Lt4TZTKU5|Qwawf)3ub(>zxuWPc1?MR!%KvQXn&p0A_?e{%u)?AjOXq96m&O_97Nt4S9B;Yy}Nvb?^TVuIx$;?`> zVK5t^rdz7@L{6Z-y8xq|%q%9H%qwg%5N_1n^gEOHeHpCoxLxXSX(hb6 zt*Y>Is`3fm*ouu!ZLbg)V7+Sf>ufA~d zG^rDrWXPuWgbN3FQ-{+Sz3;X9dU3-@2A#Dd^;o-g>qy-lef9ONog4MAcI#G+iZk^v zF=!&zG;IRiBxa7QaB{wkc-S0

eKWNy^fXda~nUk)EFnulbR?vg59GyK61q4TRL8 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 literal 6367 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 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