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