Files
habit-tracker/tests/test_habit_service.py
T
shinalokandClaude Sonnet 5 cee589bb3e Initial commit: habit tracker PWA with Google OAuth, push notifications
FastAPI + SQLAlchemy/Alembic + MariaDB backend with Jinja2/htmx/Alpine
server-rendered frontend. Multi-user via Google OAuth, daily habit
tracking, monthly/weekly history views, Web Push reminders via
APScheduler, and PWA support (manifest, service worker, offline caching).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 18:06:17 +09:00

134 lines
5.4 KiB
Python

from datetime import date, time
from app.models.habit import ALL_WEEKDAYS_MASK, Habit, HabitStatus, HabitType
from app.models.habit_log import HabitLog
from app.schemas.habit import HabitCreate, HabitUpdate
from app.services import habit_service
def _make_habit(db_session, user_id, name="아침 일찍 일어나기", **overrides):
data = HabitCreate(
name=name,
habit_type=overrides.pop("habit_type", HabitType.BUILD),
weekdays_mask=overrides.pop("weekdays_mask", ALL_WEEKDAYS_MASK),
condition_text=overrides.pop("condition_text", None),
reminder_time=overrides.pop("reminder_time", None),
)
return habit_service.create_habit(db_session, user_id, data)
def test_create_habit_defaults_to_active(db_session, test_user):
habit = _make_habit(db_session, test_user.id)
assert habit.status == HabitStatus.ACTIVE
assert habit.user_id == test_user.id
assert habit.id is not None
def test_list_habits_scoped_to_user(db_session, test_user, other_user):
_make_habit(db_session, test_user.id, name="내 습관")
_make_habit(db_session, other_user.id, name="남의 습관")
mine = habit_service.list_habits(db_session, test_user.id)
assert [h.name for h in mine] == ["내 습관"]
def test_get_habit_returns_none_for_other_users_habit(db_session, test_user, other_user):
other_habit = _make_habit(db_session, other_user.id, name="남의 습관")
assert habit_service.get_habit(db_session, other_habit.id, test_user.id) is None
assert habit_service.get_habit(db_session, other_habit.id, other_user.id) is not None
def test_list_habits_filters_by_type_and_status(db_session, test_user):
build = _make_habit(db_session, test_user.id, name="빌드", habit_type=HabitType.BUILD)
quit_ = _make_habit(db_session, test_user.id, name="퀴트", habit_type=HabitType.QUIT)
habit_service.complete_habit(db_session, quit_)
active_builds = habit_service.list_habits(db_session, test_user.id, habit_type=HabitType.BUILD)
assert [h.id for h in active_builds] == [build.id]
completed = habit_service.list_habits(db_session, test_user.id, status=HabitStatus.COMPLETED)
assert [h.id for h in completed] == [quit_.id]
def test_update_habit_overwrites_fields(db_session, test_user):
habit = _make_habit(db_session, test_user.id, name="원래 이름")
updated = habit_service.update_habit(
db_session,
habit,
HabitUpdate(name="바뀐 이름", habit_type=HabitType.BUILD, weekdays_mask=0b0000001, condition_text=" "),
)
assert updated.name == "바뀐 이름"
assert updated.weekdays_mask == 0b0000001
assert updated.condition_text is None # blank_condition_to_none 검증기 통과 확인
def test_delete_habit_removes_row_and_cascades_logs(db_session, test_user):
habit = _make_habit(db_session, test_user.id)
db_session.add(HabitLog(habit_id=habit.id, log_date=date.today()))
db_session.commit()
habit_service.delete_habit(db_session, habit)
assert db_session.get(Habit, habit.id) is None
assert db_session.query(HabitLog).filter_by(habit_id=habit.id).count() == 0
def test_complete_and_reactivate_habit(db_session, test_user):
habit = _make_habit(db_session, test_user.id)
completed = habit_service.complete_habit(db_session, habit)
assert completed.status == HabitStatus.COMPLETED
assert completed.completed_at is not None
reactivated = habit_service.reactivate_habit(db_session, habit)
assert reactivated.status == HabitStatus.ACTIVE
assert reactivated.completed_at is None
def test_reorder_habits_applies_given_order(db_session, test_user):
a = _make_habit(db_session, test_user.id, name="A")
b = _make_habit(db_session, test_user.id, name="B")
c = _make_habit(db_session, test_user.id, name="C")
habit_service.reorder_habits(db_session, test_user.id, [c.id, a.id, b.id])
ordered = habit_service.list_habits(db_session, test_user.id)
assert [h.name for h in ordered] == ["C", "A", "B"]
def test_reorder_habits_ignores_other_users_ids(db_session, test_user, other_user):
mine = _make_habit(db_session, test_user.id, name="내 것")
others = _make_habit(db_session, other_user.id, name="남의 것")
# 남의 habit_id가 섞여 들어와도 그 습관의 sort_order는 바뀌지 않아야 한다 (IDOR 방지).
habit_service.reorder_habits(db_session, test_user.id, [others.id, mine.id])
assert others.sort_order is None
assert mine.sort_order is not None
def test_list_active_habits_with_reminders_is_not_user_scoped(db_session, test_user, other_user):
with_reminder = _make_habit(db_session, test_user.id, name="알림 있음", reminder_time=time(9, 0))
_make_habit(db_session, other_user.id, name="알림 없음")
result = habit_service.list_active_habits_with_reminders(db_session)
assert [h.id for h in result] == [with_reminder.id]
def test_list_active_user_ids_is_not_user_scoped(db_session, test_user, other_user):
_make_habit(db_session, test_user.id)
_make_habit(db_session, other_user.id)
result = habit_service.list_active_user_ids(db_session)
assert set(result) == {test_user.id, other_user.id}
def test_list_active_user_ids_excludes_completed_only_users(db_session, test_user, other_user):
_make_habit(db_session, test_user.id)
completed_only = _make_habit(db_session, other_user.id)
habit_service.complete_habit(db_session, completed_only)
result = habit_service.list_active_user_ids(db_session)
assert result == [test_user.id]