Files
habit-tracker/tests/test_scheduler_service.py
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

111 lines
4.2 KiB
Python

from datetime import date, datetime, timedelta
from app.models.habit import ALL_WEEKDAYS_MASK, HabitType
from app.models.notification_log import SummaryNotificationLog
from app.schemas.habit import HabitCreate
from app.schemas.push import PushKeys, PushSubscribeRequest
from app.services import habit_service, push_service, scheduler_service
# scheduler_service._tick/_weekly_summary_tick/_monthly_summary_tick은 자체적으로
# app.database.SessionLocal()을 열어 실제 운영 DB에 붙으므로(conftest의 client 픽스처가 lifespan을
# 건너뛰는 이유와 동일) 여기서는 db_session을 직접 주입할 수 있는 _send_period_summaries/
# _claim_summary_slot만 단위 테스트한다.
def _make_habit(db_session, user_id, created_at=None, **overrides):
data = HabitCreate(
name=overrides.pop("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),
)
habit = habit_service.create_habit(db_session, user_id, data)
if created_at is not None:
habit.created_at = created_at
db_session.commit()
db_session.refresh(habit)
return habit
def _subscribe(db_session, user_id, endpoint):
push_service.save_subscription(
db_session, user_id, PushSubscribeRequest(endpoint=endpoint, keys=PushKeys(p256dh="p", auth="a"))
)
# ---- _claim_summary_slot ----
def test_claim_summary_slot_prevents_duplicate(db_session, test_user):
today = date.today()
assert scheduler_service._claim_summary_slot(db_session, test_user.id, "weekly", today) is True
assert scheduler_service._claim_summary_slot(db_session, test_user.id, "weekly", today) is False
assert db_session.query(SummaryNotificationLog).filter_by(user_id=test_user.id).count() == 1
# ---- _send_period_summaries ----
def test_send_period_summaries_sends_push_and_claims_slot(db_session, test_user, monkeypatch):
sent = []
monkeypatch.setattr(push_service, "webpush", lambda **kwargs: sent.append(kwargs))
_subscribe(db_session, test_user.id, "https://push.example.com/x")
today = date.today()
_make_habit(db_session, test_user.id, created_at=datetime.combine(today, datetime.min.time()))
scheduler_service._send_period_summaries(
db_session,
period_type="weekly",
period_start=today,
range_start=today,
range_end=today,
title="이번 주 습관 리포트",
url="/history",
)
assert len(sent) == 1
assert (
db_session.query(SummaryNotificationLog).filter_by(user_id=test_user.id, period_type="weekly").count() == 1
)
def test_send_period_summaries_skips_when_nothing_scheduled_in_range(db_session, test_user, monkeypatch):
sent = []
monkeypatch.setattr(push_service, "webpush", lambda **kwargs: sent.append(kwargs))
_subscribe(db_session, test_user.id, "https://push.example.com/y")
today = date.today()
# 습관이 range_end 이후에 생성되어, 요청한 기간에는 예정된 게 하나도 없다.
_make_habit(db_session, test_user.id, created_at=datetime.combine(today + timedelta(days=1), datetime.min.time()))
scheduler_service._send_period_summaries(
db_session, period_type="weekly", period_start=today, range_start=today, range_end=today, title="t", url="/h"
)
assert sent == []
assert db_session.query(SummaryNotificationLog).count() == 0
def test_send_period_summaries_does_not_resend_when_already_claimed(db_session, test_user, monkeypatch):
sent = []
monkeypatch.setattr(push_service, "webpush", lambda **kwargs: sent.append(kwargs))
_subscribe(db_session, test_user.id, "https://push.example.com/z")
today = date.today()
_make_habit(db_session, test_user.id, created_at=datetime.combine(today, datetime.min.time()))
for _ in range(2):
scheduler_service._send_period_summaries(
db_session,
period_type="weekly",
period_start=today,
range_start=today,
range_end=today,
title="t",
url="/h",
)
assert len(sent) == 1