feat: done fix testing

This commit is contained in:
akhdanre 2025-05-21 11:57:30 +07:00
parent ecf61c6c1d
commit b18f7f78e3
6 changed files with 366 additions and 112 deletions

View File

@ -26,7 +26,7 @@ def generate_session_entity() -> SessionEntity:
ended_at=None, ended_at=None,
is_active=True, is_active=True,
participan_limit=5, participan_limit=5,
participants=["user1"], participants=[{"id": "kajsdflkj234"}],
current_question_index=0, 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): def test_find_by_session_id_not_found(repository):
nonexistent_id = str(ObjectId())
repository.collection.find_one.return_value = None 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 assert result is None
@ -72,17 +72,6 @@ def test_find_by_session_code_found(repository):
assert result.quiz_id == "quiz1" 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): def test_add_participant(repository):
repository.collection.update_one.return_value = MagicMock(modified_count=1) repository.collection.update_one.return_value = MagicMock(modified_count=1)

View File

@ -4,6 +4,9 @@ from app.services.auth_service import AuthService
from app.schemas import LoginSchema from app.schemas import LoginSchema
from app.exception import AuthException from app.exception import AuthException
from werkzeug.security import generate_password_hash 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 @pytest.fixture
@ -17,97 +20,180 @@ def auth_service(mock_user_repository):
@pytest.fixture @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( return MagicMock(
id="user123", id="6507a32a1f3b5e2dc8a45bf9", email="budi@mail.com", name="Budi Santoso"
email="user@example.com",
password=generate_password_hash("secret"),
) )
# --- verify_google_id_token tests --- # --- verify_google_id_token tests ---
@patch("app.services.auth_service.id_token.verify_oauth2_token") @patch("app.services.auth_service.id_token.verify_oauth2_token")
def test_verify_google_existing_user( 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 # Simulate valid token
mock_verify.return_value = {"sub": "google-id-123", "email": "user@example.com"} mock_verify.return_value = {
mock_user_repository.get_by_google_id.return_value = dummy_user "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") # Setup the mapper to return our expected response
assert user == dummy_user with patch(
mock_user_repository.get_by_google_id.assert_called_once() "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") @patch("app.services.auth_service.id_token.verify_oauth2_token")
def test_verify_google_new_user( 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 = { # Setup the Google payload with natural data
"sub": "new-google-id", google_payload = {
"email": "newuser@example.com", "sub": "117239875612345678901",
"name": "New User", "email": "dewi@mail.com",
"name": "Dewi Sartika",
} }
mock_user_repository.get_by_google_id.return_value = None mock_verify.return_value = google_payload
mock_user_repository.insert_user.return_value = "new-user-id"
mock_user_repository.get_user_by_id.return_value = dummy_user
# 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( with patch(
"app.services.auth_service.UserMapper.from_google_payload" "app.services.auth_service.UserMapper.from_google_payload"
) as mock_mapper: ) as mock_from_google:
mock_mapper.return_value = dummy_user with patch(
user = auth_service.verify_google_id_token("new_token") "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 # Call the method
mock_user_repository.insert_user.assert_called_once() user = auth_service.verify_google_id_token("google_token_string")
mock_user_repository.get_user_by_id.assert_called_once_with(user_id="new-user-id")
# 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") @patch("app.services.auth_service.id_token.verify_oauth2_token")
def test_verify_google_email_mismatch( 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"} # Setup mismatched email scenario with natural data
dummy_user.email = "correct@example.com" mock_verify.return_value = {
mock_user_repository.get_by_google_id.return_value = dummy_user "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"): 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") @patch("app.services.auth_service.id_token.verify_oauth2_token")
# def test_verify_google_invalid_token(mock_verify, auth_service): def test_verify_google_invalid_token(mock_verify, auth_service):
# mock_verify.side_effect = ValueError("Invalid token") # Setup token verification failure
mock_verify.side_effect = ValueError("Token tidak valid")
# with pytest.raises(AuthException): # Test should raise exception
# auth_service.verify_google_id_token("invalid_token") with pytest.raises(Exception):
auth_service.verify_google_id_token("invalid_token_string")
# --- login tests --- # --- 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
def test_login_success(auth_service, mock_user_repository, dummy_user): # Call login with natural data
mock_user_repository.get_user_by_email.return_value = dummy_user schema = LoginSchema(email="budi@mail.com", password="rahasia123")
schema = LoginSchema(email="user@example.com", password="secret")
user = auth_service.login(schema) user = auth_service.login(schema)
assert user.email == "user@example.com"
assert user.password is None # 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_wrong_password(auth_service, mock_user_repository, dummy_user): def test_login_wrong_password(auth_service, mock_user_repository, dummy_user_entity):
mock_user_repository.get_user_by_email.return_value = dummy_user # Setup repository with correct user
schema = LoginSchema(email="user@example.com", password="wrong") 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) user = auth_service.login(schema)
# Assertions
assert user is None 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): 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 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) user = auth_service.login(schema)
# Assertions
assert user is None assert user is None
mock_user_repository.get_user_by_email.assert_called_once_with("tidak_ada@mail.com")

View File

@ -7,49 +7,85 @@ from app.schemas.response import HistoryResultSchema, QuizHistoryResponse
from app.models.entities import AnswerItemEntity, QuestionItemEntity from app.models.entities import AnswerItemEntity, QuestionItemEntity
import datetime import datetime
from bson import ObjectId from bson import ObjectId
from app.helpers import DatetimeUtil
@pytest.fixture @pytest.fixture
def mock_quiz_repository(): def mock_quiz_repository():
return MagicMock() return MagicMock()
@pytest.fixture @pytest.fixture
def mock_answer_repository(): def mock_answer_repository():
return MagicMock() return MagicMock()
@pytest.fixture
def mock_session_repository():
return MagicMock()
@pytest.fixture @pytest.fixture
def history_service(mock_quiz_repository, mock_answer_repository): def mock_user_repository():
return HistoryService( return MagicMock()
quiz_repository=mock_quiz_repository, answer_repository=mock_answer_repository
)
@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( def test_get_history_by_user_id(
history_service, mock_answer_repository, mock_quiz_repository history_service, mock_answer_repository, mock_quiz_repository
): ):
# Create realistic user answers with proper ObjectId format
mock_answers = [ mock_answers = [
UserAnswerEntity( UserAnswerEntity(
id="answer1", id=ObjectId("65a1b23c7d8e9f0123456789"),
session_id="", session_id="65a1b23c7d8e9f0123456790",
quiz_id="6815da9f37a1ce472ba72819", quiz_id="65a1b23c7d8e9f0123456791",
user_id="user123", user_id="65a1b23c7d8e9f0123456792",
total_correct=8, total_correct=8,
total_score=80, 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=[], answers=[],
) )
] ]
# Create realistic quiz data with proper ObjectId format
mock_quiz = [ mock_quiz = [
QuizEntity( QuizEntity(
_id=ObjectId("6815da9f37a1ce472ba72819"), _id=ObjectId("65a1b23c7d8e9f0123456791"),
subject_id="", subject_id="65a1b23c7d8e9f0123456796",
title="Quiz Matematika", title="Persiapan UN Matematika Kelas 12",
description="Soal dasar matematika", description="Quiz untuk mempersiapkan Ujian Nasional Matematika SMA",
total_quiz=10, total_quiz=10,
author_id="author1", author_id="65a1b23c7d8e9f0123456797",
date=datetime.datetime(2024, 2, 2, 14, 0, 0), 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=[], question_listings=[],
) )
] ]
@ -57,60 +93,107 @@ def test_get_history_by_user_id(
mock_answer_repository.get_by_user.return_value = mock_answers mock_answer_repository.get_by_user.return_value = mock_answers
mock_quiz_repository.get_by_ids.return_value = mock_quiz mock_quiz_repository.get_by_ids.return_value = mock_quiz
result = history_service.get_history_by_user_id("user123") # Test with realistic user ID
result = history_service.get_history_by_user_id("65a1b23c7d8e9f0123456792")
assert len(result) == 1 # Assertions
assert len(result) == 2
assert isinstance(result[0], HistoryResultSchema) 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_correct == 8
assert result[0].total_question == 10 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): def test_get_history_by_user_id_no_data(history_service, mock_answer_repository):
mock_answer_repository.get_by_user.return_value = [] mock_answer_repository.get_by_user.return_value = []
result = history_service.get_history_by_user_id("65a1b23c7d8e9f0123456792")
result = history_service.get_history_by_user_id("user123")
assert result == [] assert result == []
def test_get_history_by_answer_id( def test_get_history_by_answer_id(
history_service, mock_answer_repository, mock_quiz_repository history_service, mock_answer_repository, mock_quiz_repository
): ):
# Create a realistic answer with detailed answer items
mock_answer = UserAnswerEntity( mock_answer = UserAnswerEntity(
id="answer1", id=ObjectId("65a1b23c7d8e9f0123456799"),
session_id="", session_id="65a1b23c7d8e9f0123456790",
user_id="user1", user_id="65a1b23c7d8e9f0123456792",
quiz_id="quiz1", quiz_id="65a1b23c7d8e9f0123456791",
total_correct=7, total_correct=7,
total_score=70, total_score=70,
answered_at=datetime.datetime(2024, 2, 2, 14, 0, 0), answered_at=datetime.datetime(2024, 5, 18, 14, 22, 30),
answers=[ answers=[
AnswerItemEntity( AnswerItemEntity(
question_index=0, question_index=0,
answer="B", answer="B",
is_correct=True, 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( mock_quiz = QuizEntity(
id="quiz1", id=ObjectId("65a1b23c7d8e9f0123456791"),
title="Quiz IPA", title="Geografi Indonesia",
description="Ilmu Pengetahuan Alam", description="Quiz tentang letak geografis dan kondisi alam Indonesia",
subject_id="subject1", subject_id="65a1b23c7d8e9f0123456796",
date=datetime.datetime(2025, 5, 5, 0, 0), date=datetime.datetime(2024, 5, 1, 8, 0, 0),
total_quiz=10, total_quiz=10,
author_id="author1", author_id="65a1b23c7d8e9f0123456797",
question_listings=[ question_listings=[
QuestionItemEntity( QuestionItemEntity(
index=0, index=0,
question="Apa ibu kota Indonesia?", question="Apa ibu kota Indonesia?",
type="multiple_choice", type="multiple_choice",
target_answer="B", target_answer="B",
options=["A. Surabaya", "B. Jakarta", "C. Bandung"], options=["A. Surabaya", "B. Jakarta", "C. Bandung", "D. Medan"],
duration=20, 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,
) )
], ],
) )
@ -118,12 +201,108 @@ def test_get_history_by_answer_id(
mock_answer_repository.get_by_id.return_value = mock_answer mock_answer_repository.get_by_id.return_value = mock_answer
mock_quiz_repository.get_by_id.return_value = mock_quiz 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 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_correct == 7
assert result.total_score == 70 assert result.total_score == 70
assert result.total_solve_time == 12 assert result.total_solve_time == 70 # Sum of all time_spent (15+20+25+10)
assert len(result.question_listings) == 1 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].is_correct is True
assert result.question_listings[0].user_answer == "B" 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))