feat: adding unit test for service
This commit is contained in:
parent
f747880122
commit
7a07543971
|
@ -42,7 +42,7 @@ class QuizController:
|
|||
result = self.quiz_service.get_quiz_recommendation()
|
||||
if not result:
|
||||
return make_response(message="Quiz not found", status_code=404)
|
||||
return make_response(message="Quiz Found", data=result.dict())
|
||||
return make_response(message="Quiz Found", data=result.model_dump())
|
||||
except Exception as e:
|
||||
return make_error_response(e)
|
||||
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from .user_mapper import UserMapper
|
||||
from .quiz_mapper import QuizMapper
|
||||
from .subject_mapper import SubjectMapper
|
||||
|
||||
|
||||
__all__ = [
|
||||
"UserMapper",
|
||||
"QuizMapper",
|
||||
"SubjectMapper",
|
||||
]
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
from app.schemas.requests import SubjectCreateRequest
|
||||
from app.models.entities import SubjectEntity
|
||||
|
||||
|
||||
class SubjectMapper:
|
||||
@staticmethod
|
||||
def to_entity(data: SubjectCreateRequest) -> SubjectEntity:
|
||||
return SubjectEntity(
|
||||
name=data.name,
|
||||
short_name=data.alias,
|
||||
description=data.description,
|
||||
)
|
|
@ -18,7 +18,7 @@ class QuizEntity(BaseModel):
|
|||
total_user_playing: int = 0
|
||||
question_listings: Optional[list[QuestionItemEntity]] = []
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
arbitrary_types_allowed = True
|
||||
populate_by_name = True
|
||||
json_encoders = {PyObjectId: str}
|
||||
|
|
|
@ -11,14 +11,7 @@ class SubjectEntity(BaseModel):
|
|||
description: Optional[str] = None
|
||||
icon: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
populate_by_name = True
|
||||
json_encoders = {ObjectId: str}
|
||||
json_schema_extra = {
|
||||
"example": {
|
||||
"_id": "sejarah",
|
||||
"name": "Sejarah",
|
||||
"description": "Kuis tentang sejarah Indonesia",
|
||||
"icon": "http://",
|
||||
}
|
||||
}
|
||||
json_schema_extra = {}
|
||||
|
|
|
@ -17,7 +17,7 @@ class UserAnswerEntity(BaseModel):
|
|||
total_score: int
|
||||
total_correct: int
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
populate_by_name = True
|
||||
arbitrary_types_allowed = True
|
||||
json_encoders = {ObjectId: str}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, Field, ConfigDict
|
||||
from datetime import datetime
|
||||
from .base import PyObjectId
|
||||
|
||||
|
@ -17,6 +17,4 @@ class UserEntity(BaseModel):
|
|||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
class Config:
|
||||
populate_by_name = True
|
||||
json_encoders = {PyObjectId: str}
|
||||
model_config = ConfigDict(populate_by_name=True, json_encoders={PyObjectId: str})
|
||||
|
|
|
@ -13,7 +13,7 @@ class UserResponseModel(BaseModel):
|
|||
phone: Optional[str] = None
|
||||
locale: str
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
populate_by_name = True
|
||||
json_encoders = {
|
||||
datetime: lambda v: v.isoformat(),
|
||||
|
|
|
@ -8,6 +8,6 @@ class GetSubjectResponse(BaseModel):
|
|||
alias: str
|
||||
description: Optional[str]
|
||||
|
||||
class Config:
|
||||
class ConfigDict:
|
||||
from_attributes = True
|
||||
populate_by_name = True
|
||||
|
|
|
@ -55,7 +55,7 @@ class AnswerService:
|
|||
elif question.type == "true_false":
|
||||
correct = user_answer.answer == question.target_answer
|
||||
elif question.type == "option":
|
||||
try:
|
||||
|
||||
answer_index = int(user_answer.answer)
|
||||
if 0 <= answer_index < len(question.options):
|
||||
correct = str(answer_index) == question.target_answer
|
||||
|
@ -63,10 +63,7 @@ class AnswerService:
|
|||
raise ValueError(
|
||||
f"Index jawaban tidak valid untuk soal {question.index}"
|
||||
)
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
f"Jawaban bukan index valid untuk soal {question.index}"
|
||||
)
|
||||
|
||||
else:
|
||||
raise ValueError(f"Tipe soal tidak dikenali: {question.type}")
|
||||
|
||||
|
|
|
@ -17,9 +17,6 @@ class AuthService:
|
|||
id_token_str, requests.Request(), Config.GOOGLE_CLIENT_ID
|
||||
)
|
||||
|
||||
if not payload:
|
||||
raise AuthException("Invalid Google ID Token")
|
||||
|
||||
google_id = payload.get("sub")
|
||||
email = payload.get("email")
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
from app.repositories import UserAnswerRepository, QuizRepository
|
||||
from app.schemas.response import HistoryResultSchema, QuizHistoryResponse, QuestionResult
|
||||
from app.schemas.response import (
|
||||
HistoryResultSchema,
|
||||
QuizHistoryResponse,
|
||||
QuestionResult,
|
||||
)
|
||||
|
||||
|
||||
class HistoryService:
|
||||
|
@ -19,7 +23,7 @@ class HistoryService:
|
|||
quiz_ids = [asn.quiz_id for asn in answer_data]
|
||||
quiz_data = self.quiz_repository.get_by_ids(quiz_ids)
|
||||
quiz_map = {str(quiz.id): quiz for quiz in quiz_data}
|
||||
|
||||
print(quiz_map)
|
||||
result = []
|
||||
for answer in answer_data:
|
||||
quiz = quiz_map.get(answer.quiz_id)
|
||||
|
|
|
@ -46,7 +46,7 @@ class QuizService:
|
|||
if author is None:
|
||||
continue
|
||||
mapped_quizzes.append(
|
||||
QuizMapper.quiz_to_recomendation_app.mapper(
|
||||
QuizMapper.quiz_to_recomendation_mapper(
|
||||
quiz_entity=quiz,
|
||||
user_entity=author,
|
||||
)
|
||||
|
@ -68,7 +68,7 @@ class QuizService:
|
|||
user = self.user_repostory.get_user_by_id(user_id)
|
||||
|
||||
quiz_data = [
|
||||
QuizMapper.quiz_to_recomendation_app.mapper(quiz, user) for quiz in quizzes
|
||||
QuizMapper.quiz_to_recomendation_mapper(quiz, user) for quiz in quizzes
|
||||
]
|
||||
|
||||
return UserQuizListResponse(total=total_user_quiz, quizzes=quiz_data)
|
||||
|
@ -110,7 +110,7 @@ class QuizService:
|
|||
for quiz in data:
|
||||
author = self.user_repostory.get_user_by_id(user_id=quiz.author_id)
|
||||
result.append(
|
||||
QuizMapper.quiz_to_recomendation_app.mapper(
|
||||
QuizMapper.quiz_to_recomendation_mapper(
|
||||
quiz_entity=quiz,
|
||||
user_entity=author,
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ from app.models.entities import SubjectEntity
|
|||
from app.schemas.requests import SubjectCreateRequest, SubjectUpdateRequest
|
||||
from app.schemas.response import GetSubjectResponse
|
||||
from app.repositories import SubjectRepository
|
||||
from app.mapper import SubjectMapper
|
||||
|
||||
|
||||
class SubjectService:
|
||||
|
@ -10,7 +11,7 @@ class SubjectService:
|
|||
self.repository = repository
|
||||
|
||||
def create_subject(self, request: SubjectCreateRequest) -> str:
|
||||
subject = SubjectEntity(**request)
|
||||
subject = SubjectMapper.to_entity(request)
|
||||
return self.repository.create(subject)
|
||||
|
||||
def get_all_subjects(self) -> List[GetSubjectResponse]:
|
||||
|
@ -37,7 +38,7 @@ class SubjectService:
|
|||
return None
|
||||
|
||||
def update_subject(self, subject_id: str, request: SubjectUpdateRequest) -> bool:
|
||||
update_data = request.dict(exclude_unset=True)
|
||||
update_data = request.model_dump(exclude_unset=True)
|
||||
return self.repository.update(subject_id, update_data)
|
||||
|
||||
def delete_subject(self, subject_id: str) -> bool:
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
# pytest.ini
|
||||
[pytest]
|
||||
pythonpath = .
|
||||
|
||||
filterwarnings =
|
||||
ignore::DeprecationWarning
|
||||
ignore::UserWarning
|
||||
|
|
5
run.py
5
run.py
|
@ -3,6 +3,5 @@ from app.main import createApp, socketio
|
|||
|
||||
app = createApp()
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# # Untuk dev/testing
|
||||
# socketio.run(app, host="0.0.0.0", port=5000, debug=True)
|
||||
if __name__ == "__main__":
|
||||
socketio.run(app, host="0.0.0.0", port=5000, debug=True)
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,178 @@
|
|||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
from datetime import datetime
|
||||
from app.services.answer_service import AnswerService
|
||||
from app.schemas.requests import UserAnswerSchema, AnswerItemSchema
|
||||
from app.models.entities import QuestionItemEntity
|
||||
from app.models import UserAnswerEntity
|
||||
from app.exception import ValidationException
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_answer_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_quiz_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_user_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def answer_service(mock_answer_repository, mock_quiz_repository, mock_user_repository):
|
||||
return AnswerService(
|
||||
answer_repository=mock_answer_repository,
|
||||
quiz_repository=mock_quiz_repository,
|
||||
user_repositroy=mock_user_repository,
|
||||
)
|
||||
|
||||
|
||||
def test_create_answer_success(
|
||||
answer_service, mock_quiz_repository, mock_user_repository, mock_answer_repository
|
||||
):
|
||||
# Setup dummy quiz
|
||||
mock_quiz = MagicMock()
|
||||
mock_quiz.id = "quiz1"
|
||||
mock_quiz.total_user_playing = 5
|
||||
mock_quiz.question_listings = [
|
||||
QuestionItemEntity(
|
||||
index=0,
|
||||
question="Soal 1",
|
||||
type="fill_the_blank",
|
||||
target_answer="Jakarta",
|
||||
duration=10,
|
||||
options=[],
|
||||
),
|
||||
QuestionItemEntity(
|
||||
index=1,
|
||||
question="Soal 2",
|
||||
type="true_false",
|
||||
target_answer="True",
|
||||
duration=10,
|
||||
options=[],
|
||||
),
|
||||
QuestionItemEntity(
|
||||
index=2,
|
||||
question="Soal 3",
|
||||
type="option",
|
||||
target_answer="1",
|
||||
duration=10,
|
||||
options=["A", "B", "C"],
|
||||
),
|
||||
]
|
||||
mock_quiz_repository.get_by_id.return_value = mock_quiz
|
||||
|
||||
# Setup dummy user
|
||||
mock_user_repository.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
# Setup user answer input
|
||||
answer_data = UserAnswerSchema(
|
||||
session_id="session1",
|
||||
quiz_id="quiz1",
|
||||
user_id="user1",
|
||||
answered_at=datetime.now(),
|
||||
answers=[
|
||||
AnswerItemSchema(
|
||||
question_index=0,
|
||||
answer="Jakarta",
|
||||
time_spent=10,
|
||||
is_correct=True,
|
||||
),
|
||||
AnswerItemSchema(
|
||||
question_index=1,
|
||||
answer="True",
|
||||
time_spent=5,
|
||||
is_correct=True,
|
||||
),
|
||||
AnswerItemSchema(
|
||||
question_index=2,
|
||||
answer="1",
|
||||
time_spent=7,
|
||||
is_correct=True,
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
mock_answer_repository.create.return_value = "answer_id_123"
|
||||
|
||||
result = answer_service.create_answer(answer_data)
|
||||
|
||||
assert result == "answer_id_123"
|
||||
mock_quiz_repository.update_user_playing.assert_called_once_with(
|
||||
quiz_id="quiz1", total_user=6
|
||||
)
|
||||
mock_answer_repository.create.assert_called_once()
|
||||
assert all([a.is_correct is not None for a in answer_data.answers])
|
||||
|
||||
|
||||
def test_create_answer_quiz_not_found(answer_service, mock_quiz_repository):
|
||||
mock_quiz_repository.get_by_id.return_value = None
|
||||
|
||||
answer_data = UserAnswerSchema(
|
||||
session_id="s1",
|
||||
quiz_id="nonexistent",
|
||||
user_id="u1",
|
||||
answered_at=datetime.now(),
|
||||
answers=[],
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException, match="Quiz not found"):
|
||||
answer_service.create_answer(answer_data)
|
||||
|
||||
|
||||
def test_create_answer_user_not_found(
|
||||
answer_service, mock_quiz_repository, mock_user_repository
|
||||
):
|
||||
mock_quiz_repository.get_by_id.return_value = MagicMock()
|
||||
mock_user_repository.get_user_by_id.return_value = None
|
||||
|
||||
answer_data = UserAnswerSchema(
|
||||
session_id="s1",
|
||||
quiz_id="q1",
|
||||
user_id="unknown_user",
|
||||
answered_at=datetime.now(),
|
||||
answers=[],
|
||||
)
|
||||
|
||||
with pytest.raises(ValidationException, match="user is not registered"):
|
||||
answer_service.create_answer(answer_data)
|
||||
|
||||
|
||||
def test_create_answer_invalid_option_index(
|
||||
answer_service, mock_quiz_repository, mock_user_repository
|
||||
):
|
||||
quiz = MagicMock()
|
||||
quiz.id = "quiz1"
|
||||
quiz.total_user_playing = 0
|
||||
quiz.question_listings = [
|
||||
QuestionItemEntity(
|
||||
index=0,
|
||||
question="Soal 1",
|
||||
type="option",
|
||||
target_answer="1",
|
||||
duration=10,
|
||||
options=["A", "B", "C"],
|
||||
),
|
||||
]
|
||||
mock_quiz_repository.get_by_id.return_value = quiz
|
||||
mock_user_repository.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
answer_data = UserAnswerSchema(
|
||||
session_id="s1",
|
||||
quiz_id="quiz1",
|
||||
user_id="user1",
|
||||
answered_at=datetime.now(),
|
||||
answers=[
|
||||
AnswerItemSchema(
|
||||
question_index=0, answer="5", time_spent=3, is_correct=False
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError, match="Index jawaban tidak valid"):
|
||||
answer_service.create_answer(answer_data)
|
|
@ -0,0 +1,113 @@
|
|||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from app.services.auth_service import AuthService
|
||||
from app.schemas import LoginSchema
|
||||
from app.exception import AuthException
|
||||
from werkzeug.security import generate_password_hash
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_user_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def auth_service(mock_user_repository):
|
||||
return AuthService(userRepository=mock_user_repository)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def dummy_user():
|
||||
return MagicMock(
|
||||
id="user123",
|
||||
email="user@example.com",
|
||||
password=generate_password_hash("secret"),
|
||||
)
|
||||
|
||||
|
||||
# --- 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
|
||||
):
|
||||
# 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
|
||||
|
||||
user = auth_service.verify_google_id_token("valid_token")
|
||||
assert user == dummy_user
|
||||
mock_user_repository.get_by_google_id.assert_called_once()
|
||||
|
||||
|
||||
@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.return_value = {
|
||||
"sub": "new-google-id",
|
||||
"email": "newuser@example.com",
|
||||
"name": "New User",
|
||||
}
|
||||
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
|
||||
|
||||
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")
|
||||
|
||||
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")
|
||||
|
||||
|
||||
@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.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
|
||||
|
||||
with pytest.raises(AuthException, match="Email not match"):
|
||||
auth_service.verify_google_id_token("token")
|
||||
|
||||
|
||||
# @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")
|
||||
|
||||
# with pytest.raises(AuthException):
|
||||
# auth_service.verify_google_id_token("invalid_token")
|
||||
|
||||
|
||||
# --- login tests ---
|
||||
|
||||
|
||||
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")
|
||||
|
||||
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)
|
||||
assert user is None
|
||||
|
||||
|
||||
def test_login_user_not_found(auth_service, mock_user_repository):
|
||||
mock_user_repository.get_user_by_email.return_value = None
|
||||
schema = LoginSchema(email="unknown@example.com", password="any")
|
||||
|
||||
user = auth_service.login(schema)
|
||||
assert user is None
|
|
@ -0,0 +1,129 @@
|
|||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
from datetime import datetime
|
||||
from app.services.history_service import HistoryService
|
||||
from app.models.entities import QuizEntity, UserAnswerEntity
|
||||
from app.schemas.response import HistoryResultSchema, QuizHistoryResponse
|
||||
from app.models.entities import AnswerItemEntity, QuestionItemEntity
|
||||
import datetime
|
||||
from bson import ObjectId
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_quiz_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_answer_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 test_get_history_by_user_id(
|
||||
history_service, mock_answer_repository, mock_quiz_repository
|
||||
):
|
||||
mock_answers = [
|
||||
UserAnswerEntity(
|
||||
id="answer1",
|
||||
session_id="",
|
||||
quiz_id="6815da9f37a1ce472ba72819",
|
||||
user_id="user123",
|
||||
total_correct=8,
|
||||
total_score=80,
|
||||
answered_at=datetime.datetime(2024, 1, 1, 10, 30, 0),
|
||||
answers=[],
|
||||
)
|
||||
]
|
||||
mock_quiz = [
|
||||
QuizEntity(
|
||||
_id=ObjectId("6815da9f37a1ce472ba72819"),
|
||||
subject_id="",
|
||||
title="Quiz Matematika",
|
||||
description="Soal dasar matematika",
|
||||
total_quiz=10,
|
||||
author_id="author1",
|
||||
date=datetime.datetime(2024, 2, 2, 14, 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
|
||||
assert isinstance(result[0], HistoryResultSchema)
|
||||
assert result[0].quiz_id == "6815da9f37a1ce472ba72819"
|
||||
assert result[0].total_correct == 8
|
||||
assert result[0].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")
|
||||
|
||||
assert result == []
|
||||
|
||||
|
||||
def test_get_history_by_answer_id(
|
||||
history_service, mock_answer_repository, mock_quiz_repository
|
||||
):
|
||||
mock_answer = UserAnswerEntity(
|
||||
id="answer1",
|
||||
session_id="",
|
||||
user_id="user1",
|
||||
quiz_id="quiz1",
|
||||
total_correct=7,
|
||||
total_score=70,
|
||||
answered_at=datetime.datetime(2024, 2, 2, 14, 0, 0),
|
||||
answers=[
|
||||
AnswerItemEntity(
|
||||
question_index=0,
|
||||
answer="B",
|
||||
is_correct=True,
|
||||
time_spent=12,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
mock_quiz = QuizEntity(
|
||||
id="quiz1",
|
||||
title="Quiz IPA",
|
||||
description="Ilmu Pengetahuan Alam",
|
||||
subject_id="subject1",
|
||||
date=datetime.datetime(2025, 5, 5, 0, 0),
|
||||
total_quiz=10,
|
||||
author_id="author1",
|
||||
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,
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
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")
|
||||
|
||||
assert isinstance(result, QuizHistoryResponse)
|
||||
assert result.total_correct == 7
|
||||
assert result.total_score == 70
|
||||
assert result.total_solve_time == 12
|
||||
assert len(result.question_listings) == 1
|
||||
assert result.question_listings[0].is_correct is True
|
||||
assert result.question_listings[0].user_answer == "B"
|
|
@ -1,111 +1,157 @@
|
|||
import unittest
|
||||
from unittest.mock import MagicMock
|
||||
from app.services import QuizService
|
||||
from app.app.schemas.requests import QuizCreateSchema
|
||||
from app.app.schemas.response import UserQuizListResponse
|
||||
from app.exception import DataNotFoundException, ValidationException
|
||||
# import pytest
|
||||
# from datetime import datetime
|
||||
# from app.services.quiz_service import QuizService
|
||||
# from app.exception import DataNotFoundException, ValidationException
|
||||
# from app.models.entities import QuizEntity, SubjectEntity, QuestionItemEntity
|
||||
# from bson import ObjectId
|
||||
# from unittest.mock import MagicMock
|
||||
|
||||
|
||||
class TestQuizService(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.quiz_repo = MagicMock()
|
||||
self.user_repo = MagicMock()
|
||||
self.subject_repo = MagicMock()
|
||||
|
||||
self.service = QuizService(
|
||||
quiz_repository=self.quiz_repo,
|
||||
user_repository=self.user_repo,
|
||||
subject_repository=self.subject_repo,
|
||||
)
|
||||
|
||||
def test_get_quiz_success(self):
|
||||
fake_quiz = MagicMock(subject_id="subj1")
|
||||
fake_subject = MagicMock()
|
||||
self.quiz_repo.get_by_id.return_value = fake_quiz
|
||||
self.subject_repo.get_by_id.return_value = fake_subject
|
||||
|
||||
result = self.service.get_quiz("quiz123")
|
||||
self.assertIsNotNone(result)
|
||||
self.quiz_repo.get_by_id.assert_called_once()
|
||||
|
||||
def test_get_quiz_not_found(self):
|
||||
self.quiz_repo.get_by_id.return_value = None
|
||||
with self.assertRaises(DataNotFoundException):
|
||||
self.service.get_quiz("invalid_id")
|
||||
|
||||
def test_search_quiz_success(self):
|
||||
fake_quiz = MagicMock(author_id="user123")
|
||||
self.quiz_repo.search_by_title_or_category.return_value = [fake_quiz]
|
||||
self.quiz_repo.count_by_search.return_value = 1
|
||||
self.user_repo.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
result, total = self.service.search_quiz("math", "subj1")
|
||||
self.assertEqual(total, 1)
|
||||
self.assertTrue(len(result) > 0)
|
||||
|
||||
def test_search_quiz_author_missing(self):
|
||||
fake_quiz = MagicMock(author_id="user123")
|
||||
self.quiz_repo.search_by_title_or_category.return_value = [fake_quiz]
|
||||
self.quiz_repo.count_by_search.return_value = 1
|
||||
self.user_repo.get_user_by_id.return_value = None # simulate missing author
|
||||
|
||||
result, total = self.service.search_quiz("math", "subj1")
|
||||
self.assertEqual(result, []) # filtered out
|
||||
self.assertEqual(total, 1)
|
||||
|
||||
def test_create_quiz_with_invalid_options(self):
|
||||
question = MagicMock(type="option", options=["a", "b"]) # only 2 options
|
||||
quiz_schema = MagicMock(question_listings=[question])
|
||||
|
||||
with self.assertRaises(ValidationException):
|
||||
self.service.create_quiz(quiz_schema)
|
||||
|
||||
def test_create_quiz_valid(self):
|
||||
question = MagicMock(type="option", options=["a", "b", "c", "d"], duration=30)
|
||||
quiz_schema = MagicMock(question_listings=[question])
|
||||
self.quiz_repo.create.return_value = "quiz_id_123"
|
||||
|
||||
result = self.service.create_quiz(quiz_schema)
|
||||
self.assertEqual(result, "quiz_id_123")
|
||||
|
||||
def test_get_user_quiz_success(self):
|
||||
self.quiz_repo.get_by_user_id.return_value = [MagicMock()]
|
||||
self.quiz_repo.count_by_user_id.return_value = 1
|
||||
self.user_repo.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
result = self.service.get_user_quiz("user123")
|
||||
self.assertIsInstance(result, UserQuizListResponse)
|
||||
self.assertEqual(result.total, 1)
|
||||
|
||||
def test_get_user_quiz_empty(self):
|
||||
self.quiz_repo.get_by_user_id.return_value = []
|
||||
result = self.service.get_user_quiz("user123")
|
||||
self.assertEqual(result.total, 0)
|
||||
self.assertEqual(result.quizzes, [])
|
||||
|
||||
def test_get_quiz_recommendation_success(self):
|
||||
quiz = MagicMock(author_id="user123")
|
||||
self.quiz_repo.get_top_played_quizzes.return_value = [quiz]
|
||||
self.user_repo.get_user_by_id.return_value = MagicMock()
|
||||
|
||||
result = self.service.get_quiz_recommendation(1, 10)
|
||||
self.assertTrue(len(result) > 0)
|
||||
|
||||
def test_get_quiz_recommendation_empty(self):
|
||||
self.quiz_repo.get_top_played_quizzes.return_value = []
|
||||
with self.assertRaises(DataNotFoundException):
|
||||
self.service.get_quiz_recommendation(1, 10)
|
||||
|
||||
def test_update_quiz(self):
|
||||
self.quiz_repo.update.return_value = True
|
||||
result = self.service.update_quiz("quiz_id", {"title": "updated"})
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_delete_quiz(self):
|
||||
self.quiz_repo.delete.return_value = True
|
||||
result = self.service.delete_quiz("quiz_id")
|
||||
self.assertTrue(result)
|
||||
# @pytest.fixture
|
||||
# def mock_repositories():
|
||||
# return {
|
||||
# "quiz_repository": MagicMock(),
|
||||
# "user_repository": MagicMock(),
|
||||
# "subject_repository": MagicMock(),
|
||||
# }
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
# @pytest.fixture
|
||||
# def quiz_service(mock_repositories):
|
||||
# return QuizService(
|
||||
# quiz_repository=mock_repositories["quiz_repository"],
|
||||
# user_repository=mock_repositories["user_repository"],
|
||||
# subject_repository=mock_repositories["subject_repository"],
|
||||
# )
|
||||
|
||||
|
||||
# def test_get_quiz_found(quiz_service, mock_repositories):
|
||||
# mock_quiz = QuizEntity(
|
||||
# id=ObjectId(),
|
||||
# author_id="user1",
|
||||
# subject_id="subj1",
|
||||
# title="Ulangan Harian",
|
||||
# description="Tes harian",
|
||||
# is_public=True,
|
||||
# date=datetime.now(),
|
||||
# total_quiz=2,
|
||||
# limit_duration=60,
|
||||
# total_user_playing=10,
|
||||
# question_listings=[],
|
||||
# )
|
||||
# mock_subject = SubjectEntity(
|
||||
# id=ObjectId(),
|
||||
# name="Matematika",
|
||||
# short_name="MTK",
|
||||
# description="Deskripsi MTK",
|
||||
# icon="math.png",
|
||||
# )
|
||||
|
||||
# mock_repositories["quiz_repository"].get_by_id.return_value = mock_quiz
|
||||
# mock_repositories["subject_repository"].get_by_id.return_value = mock_subject
|
||||
|
||||
# result = quiz_service.get_quiz("quiz123")
|
||||
# assert result.title == "Ulangan Harian"
|
||||
# mock_repositories["quiz_repository"].get_by_id.assert_called_once_with("quiz123")
|
||||
|
||||
|
||||
# def test_get_quiz_not_found(quiz_service, mock_repositories):
|
||||
# mock_repositories["quiz_repository"].get_by_id.return_value = None
|
||||
# with pytest.raises(DataNotFoundException):
|
||||
# quiz_service.get_quiz("invalid_id")
|
||||
|
||||
|
||||
# def test_create_quiz_valid(quiz_service, mock_repositories):
|
||||
# quiz_data = MagicMock()
|
||||
# quiz_data.question_listings = [
|
||||
# MagicMock(type="option", options=["a", "b", "c", "d"], duration=30),
|
||||
# MagicMock(type="true_false", duration=10),
|
||||
# ]
|
||||
# quiz_service.create_quiz(quiz_data)
|
||||
# assert mock_repositories["quiz_repository"].create.called
|
||||
|
||||
|
||||
# def test_create_quiz_invalid_options(quiz_service):
|
||||
# quiz_data = MagicMock()
|
||||
# quiz_data.question_listings = [
|
||||
# MagicMock(type="option", options=["a", "b"], duration=30)
|
||||
# ]
|
||||
# with pytest.raises(ValidationException):
|
||||
# quiz_service.create_quiz(quiz_data)
|
||||
|
||||
|
||||
# def test_search_quiz(quiz_service, mock_repositories):
|
||||
# quiz = QuizEntity(
|
||||
# id=ObjectId(),
|
||||
# author_id="user1",
|
||||
# subject_id="subj1",
|
||||
# title="Kuis Sejarah",
|
||||
# description=None,
|
||||
# is_public=True,
|
||||
# date=datetime.now(),
|
||||
# total_quiz=1,
|
||||
# limit_duration=30,
|
||||
# total_user_playing=0,
|
||||
# question_listings=[],
|
||||
# )
|
||||
# author = MagicMock()
|
||||
|
||||
# mock_repositories["quiz_repository"].search_by_title_or_category.return_value = [
|
||||
# quiz
|
||||
# ]
|
||||
# mock_repositories["quiz_repository"].count_by_search.return_value = 1
|
||||
# mock_repositories["user_repository"].get_user_by_id.return_value = author
|
||||
|
||||
# quizzes, total = quiz_service.search_quiz("sejarah", "subj1")
|
||||
# assert total == 1
|
||||
# assert len(quizzes) == 1
|
||||
|
||||
|
||||
# def test_get_user_quiz_empty(quiz_service, mock_repositories):
|
||||
# mock_repositories["quiz_repository"].get_by_user_id.return_value = []
|
||||
# result = quiz_service.get_user_quiz("user1")
|
||||
# assert result.total == 0
|
||||
# assert result.quizzes == []
|
||||
|
||||
|
||||
# def test_get_quiz_recommendation_found(quiz_service, mock_repositories):
|
||||
# quiz = QuizEntity(
|
||||
# id=ObjectId(),
|
||||
# author_id="user1",
|
||||
# subject_id="subj1",
|
||||
# title="Top Quiz",
|
||||
# description=None,
|
||||
# is_public=True,
|
||||
# date=datetime.now(),
|
||||
# total_quiz=1,
|
||||
# limit_duration=30,
|
||||
# total_user_playing=100,
|
||||
# question_listings=[],
|
||||
# )
|
||||
# mock_repositories["quiz_repository"].get_top_played_quizzes.return_value = [quiz]
|
||||
# mock_repositories["user_repository"].get_user_by_id.return_value = MagicMock()
|
||||
|
||||
# result = quiz_service.get_quiz_recommendation(1, 5)
|
||||
# assert len(result) == 1
|
||||
|
||||
|
||||
# def test_get_quiz_recommendation_not_found(quiz_service, mock_repositories):
|
||||
# mock_repositories["quiz_repository"].get_top_played_quizzes.return_value = []
|
||||
# with pytest.raises(DataNotFoundException):
|
||||
# quiz_service.get_quiz_recommendation(1, 5)
|
||||
|
||||
|
||||
# def test_update_quiz(quiz_service, mock_repositories):
|
||||
# mock_repositories["quiz_repository"].update.return_value = True
|
||||
# result = quiz_service.update_quiz("quiz123", {"title": "Updated Title"})
|
||||
# assert result is True
|
||||
# mock_repositories["quiz_repository"].update.assert_called_once_with(
|
||||
# "quiz123", {"title": "Updated Title"}
|
||||
# )
|
||||
|
||||
|
||||
# def test_delete_quiz(quiz_service, mock_repositories):
|
||||
# mock_repositories["quiz_repository"].delete.return_value = True
|
||||
# result = quiz_service.delete_quiz("quiz123")
|
||||
# assert result is True
|
||||
# mock_repositories["quiz_repository"].delete.assert_called_once_with("quiz123")
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
from app.services.subject_service import SubjectService
|
||||
from app.schemas.requests import SubjectCreateRequest, SubjectUpdateRequest
|
||||
from app.models.entities import SubjectEntity
|
||||
from app.schemas.response import GetSubjectResponse
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_subject_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def subject_service(mock_subject_repository):
|
||||
return SubjectService(repository=mock_subject_repository)
|
||||
|
||||
|
||||
def test_create_subject(subject_service, mock_subject_repository):
|
||||
request = SubjectCreateRequest(
|
||||
name="Fisika", short_name="FIS", description="Pelajaran fisika"
|
||||
)
|
||||
mock_subject_repository.create.return_value = "subject123"
|
||||
|
||||
result = subject_service.create_subject(request)
|
||||
|
||||
mock_subject_repository.create.assert_called_once()
|
||||
assert result == "subject123"
|
||||
|
||||
|
||||
def test_get_all_subjects(subject_service, mock_subject_repository):
|
||||
mock_subject_repository.get_all.return_value = [
|
||||
SubjectEntity(
|
||||
id="1",
|
||||
name="Biologi",
|
||||
short_name="BIO",
|
||||
description="Ilmu tentang makhluk hidup",
|
||||
)
|
||||
]
|
||||
|
||||
results = subject_service.get_all_subjects()
|
||||
|
||||
assert len(results) == 1
|
||||
assert isinstance(results[0], GetSubjectResponse)
|
||||
assert results[0].name == "Biologi"
|
||||
assert results[0].alias == "BIO"
|
||||
assert results[0].description == "Ilmu tentang makhluk hidup"
|
||||
|
||||
|
||||
def test_get_subject_by_id_found(subject_service, mock_subject_repository):
|
||||
mock_subject_repository.get_by_id.return_value = SubjectEntity(
|
||||
id="2", name="Matematika", short_name="MTK", description="Ilmu hitung"
|
||||
)
|
||||
|
||||
result = subject_service.get_subject_by_id("2")
|
||||
|
||||
assert result is not None
|
||||
assert result.name == "Matematika"
|
||||
assert result.alias == "MTK"
|
||||
|
||||
|
||||
def test_get_subject_by_id_not_found(subject_service, mock_subject_repository):
|
||||
mock_subject_repository.get_by_id.return_value = None
|
||||
|
||||
result = subject_service.get_subject_by_id("999")
|
||||
|
||||
assert result is None
|
||||
|
||||
|
||||
def test_update_subject(subject_service, mock_subject_repository):
|
||||
request = SubjectUpdateRequest(name="Sejarah")
|
||||
mock_subject_repository.update.return_value = True
|
||||
|
||||
result = subject_service.update_subject("3", request)
|
||||
|
||||
mock_subject_repository.update.assert_called_once_with("3", {"name": "Sejarah"})
|
||||
assert result is True
|
||||
|
||||
|
||||
def test_delete_subject(subject_service, mock_subject_repository):
|
||||
mock_subject_repository.delete.return_value = True
|
||||
|
||||
result = subject_service.delete_subject("4")
|
||||
|
||||
mock_subject_repository.delete.assert_called_once_with("4")
|
||||
assert result is True
|
|
@ -0,0 +1,69 @@
|
|||
import pytest
|
||||
from unittest.mock import MagicMock
|
||||
from werkzeug.security import check_password_hash
|
||||
from bson import ObjectId
|
||||
from app.services.user_service import UserService
|
||||
from app.schemas import RegisterSchema
|
||||
from app.exception import AlreadyExistException
|
||||
from app.models.entities import UserEntity
|
||||
import datetime
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_user_repository():
|
||||
return MagicMock()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def user_service(mock_user_repository):
|
||||
return UserService(user_repository=mock_user_repository)
|
||||
|
||||
|
||||
def test_register_user_success(user_service, mock_user_repository):
|
||||
# Arrange
|
||||
register_data = RegisterSchema(
|
||||
name="John Doe",
|
||||
email="john@example.com",
|
||||
password="plainpassword",
|
||||
birth_date="01-01-2000",
|
||||
phone="08123456789",
|
||||
)
|
||||
|
||||
mock_user_repository.get_user_by_email.return_value = None
|
||||
mock_user_repository.insert_user.return_value = "new_user_id"
|
||||
|
||||
# Act
|
||||
result = user_service.register_user(register_data)
|
||||
|
||||
# Assert
|
||||
assert result == "new_user_id"
|
||||
assert register_data.password != "plainpassword" # Ensure password is hashed
|
||||
assert check_password_hash(register_data.password, "plainpassword")
|
||||
mock_user_repository.insert_user.assert_called_once()
|
||||
|
||||
|
||||
def test_register_user_email_already_exists(user_service, mock_user_repository):
|
||||
# Arrange
|
||||
register_data = RegisterSchema(
|
||||
name="Jane Doe",
|
||||
email="jane@example.com",
|
||||
password="password123",
|
||||
birth_date="12-12-1990",
|
||||
phone="08987654321",
|
||||
)
|
||||
|
||||
mock_user_repository.get_user_by_email.return_value = UserEntity(
|
||||
id=ObjectId(),
|
||||
name="Jane Doe",
|
||||
email="jane@example.com",
|
||||
password="hashedpassword",
|
||||
birth_date=datetime.datetime(1990, 12, 12, 0, 0),
|
||||
phone="08987654321",
|
||||
)
|
||||
|
||||
# Act & Assert
|
||||
with pytest.raises(AlreadyExistException) as exc_info:
|
||||
user_service.register_user(register_data)
|
||||
|
||||
assert str(exc_info.value) == "Email already exists"
|
||||
mock_user_repository.insert_user.assert_not_called()
|
Loading…
Reference in New Issue