feat: adding unit test for service

This commit is contained in:
akhdanre 2025-05-08 01:55:44 +07:00
parent f747880122
commit 7a07543971
28 changed files with 776 additions and 148 deletions

View File

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

View File

@ -1,8 +1,10 @@
from .user_mapper import UserMapper
from .quiz_mapper import QuizMapper
from .subject_mapper import SubjectMapper
__all__ = [
"UserMapper",
"QuizMapper",
"SubjectMapper",
]

View File

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

View File

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

View File

@ -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 = {}

View File

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

View File

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

View File

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

View File

@ -8,6 +8,6 @@ class GetSubjectResponse(BaseModel):
alias: str
description: Optional[str]
class Config:
class ConfigDict:
from_attributes = True
populate_by_name = True

View File

@ -55,18 +55,15 @@ 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
else:
raise ValueError(
f"Index jawaban tidak valid untuk soal {question.index}"
)
except ValueError:
answer_index = int(user_answer.answer)
if 0 <= answer_index < len(question.options):
correct = str(answer_index) == question.target_answer
else:
raise ValueError(
f"Jawaban bukan index valid untuk soal {question.index}"
f"Index jawaban tidak valid untuk soal {question.index}"
)
else:
raise ValueError(f"Tipe soal tidak dikenali: {question.type}")

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
# pytest.ini
[pytest]
pythonpath = .
filterwarnings =
ignore::DeprecationWarning
ignore::UserWarning

5
run.py
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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