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,
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)

View File

@ -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")

View File

@ -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))