add day-detail view to monthly history calendar
날짜 셀 클릭 시 그 날 예정된 습관별 완료/실패/미완료 상태를 htmx로 불러와 보여준다. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -407,6 +407,53 @@ def test_get_monthly_summary_does_not_count_failed_as_checked(db_session, test_u
|
||||
assert by_date[date(2026, 3, 5)].checked_count == 0
|
||||
|
||||
|
||||
# ---- get_day_detail ----
|
||||
|
||||
|
||||
def test_get_day_detail_marks_checked_and_failed(db_session, test_user):
|
||||
checked_habit = _make_habit(db_session, test_user.id, name="체크됨", created_at=datetime(2026, 3, 1))
|
||||
failed_habit = _make_habit(db_session, test_user.id, name="실패함", created_at=datetime(2026, 3, 1))
|
||||
_check(db_session, checked_habit.id, date(2026, 3, 5))
|
||||
_fail(db_session, failed_habit.id, date(2026, 3, 5))
|
||||
|
||||
items = log_service.get_day_detail(db_session, test_user.id, date(2026, 3, 5))
|
||||
by_name = {i.name: i for i in items}
|
||||
assert by_name["체크됨"].status == "checked"
|
||||
assert by_name["실패함"].status == "failed"
|
||||
|
||||
|
||||
def test_get_day_detail_marks_past_unlogged_as_missed(db_session, test_user):
|
||||
habit = _make_habit(db_session, test_user.id, created_at=datetime(2026, 3, 1))
|
||||
|
||||
items = log_service.get_day_detail(db_session, test_user.id, date(2026, 3, 5))
|
||||
assert items[0].status == "missed"
|
||||
|
||||
|
||||
def test_get_day_detail_marks_future_unlogged_as_pending(db_session, test_user):
|
||||
habit = _make_habit(db_session, test_user.id)
|
||||
future = date.today() + timedelta(days=1)
|
||||
|
||||
items = log_service.get_day_detail(db_session, test_user.id, future)
|
||||
assert items[0].status == "pending"
|
||||
|
||||
|
||||
def test_get_day_detail_excludes_days_before_habit_created(db_session, test_user):
|
||||
_make_habit(db_session, test_user.id, created_at=datetime(2026, 3, 10))
|
||||
|
||||
items = log_service.get_day_detail(db_session, test_user.id, date(2026, 3, 5))
|
||||
assert items == []
|
||||
|
||||
|
||||
def test_get_day_detail_excludes_unscheduled_weekdays(db_session, test_user):
|
||||
monday_only_mask = 0b0000001 # bit0 = 월요일
|
||||
_make_habit(db_session, test_user.id, created_at=datetime(2026, 3, 1), weekdays_mask=monday_only_mask)
|
||||
|
||||
tuesday = date(2026, 3, 3)
|
||||
assert tuesday.weekday() == 1
|
||||
items = log_service.get_day_detail(db_session, test_user.id, tuesday)
|
||||
assert items == []
|
||||
|
||||
|
||||
# ---- summarize_completion_rate: 미래 날짜 제외 회귀 테스트 ----
|
||||
# CLAUDE.md에 기록된 실제 버그: 미래 날짜를 포함시키면 완료율이 부당하게 낮게 나온다
|
||||
# (실제로 6.2% -> 수정 후 50%가 된 사례).
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
from datetime import date, datetime
|
||||
|
||||
from app.models.habit import ALL_WEEKDAYS_MASK, HabitType
|
||||
from app.schemas.habit import HabitCreate
|
||||
from app.services import habit_service
|
||||
|
||||
|
||||
def _make_habit(db_session, user_id, name="테스트 습관", created_at=None, **overrides):
|
||||
data = HabitCreate(
|
||||
name=name,
|
||||
habit_type=overrides.pop("habit_type", HabitType.BUILD),
|
||||
weekdays_mask=overrides.pop("weekdays_mask", ALL_WEEKDAYS_MASK),
|
||||
)
|
||||
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 test_history_day_detail_shows_scheduled_habit(auth_client, db_session, test_user):
|
||||
_make_habit(db_session, test_user.id, name="아침 스트레칭", created_at=datetime(2026, 3, 1))
|
||||
|
||||
response = auth_client.get("/history/day/2026-03-05")
|
||||
assert response.status_code == 200
|
||||
assert "아침 스트레칭" in response.text
|
||||
|
||||
|
||||
def test_history_day_detail_excludes_other_users_habits(auth_client, db_session, other_user):
|
||||
_make_habit(db_session, other_user.id, name="남의 습관", created_at=datetime(2026, 3, 1))
|
||||
|
||||
response = auth_client.get("/history/day/2026-03-05")
|
||||
assert response.status_code == 200
|
||||
assert "남의 습관" not in response.text
|
||||
|
||||
|
||||
def test_history_day_detail_requires_login(client):
|
||||
response = client.get("/history/day/2026-03-05", follow_redirects=False)
|
||||
assert response.status_code == 303
|
||||
assert response.headers["location"] == "/login"
|
||||
Reference in New Issue
Block a user