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>
This commit is contained in:
2026-07-16 18:06:17 +09:00
co-authored by Claude Sonnet 5
commit cee589bb3e
80 changed files with 4695 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
import pytest
from pydantic import ValidationError
from app.models.habit import ALL_WEEKDAYS_MASK, HabitType
from app.schemas.habit import HabitCreate
def _base_kwargs(**overrides):
kwargs = {"name": "습관", "habit_type": HabitType.BUILD}
kwargs.update(overrides)
return kwargs
def test_blank_name_rejected():
with pytest.raises(ValidationError):
HabitCreate(**_base_kwargs(name=" "))
def test_name_is_stripped():
habit = HabitCreate(**_base_kwargs(name=" 아침 운동 "))
assert habit.name == "아침 운동"
def test_blank_condition_text_becomes_none():
habit = HabitCreate(**_base_kwargs(condition_text=" "))
assert habit.condition_text is None
def test_condition_text_is_stripped_when_present():
habit = HabitCreate(**_base_kwargs(condition_text=" 30분 이상 "))
assert habit.condition_text == "30분 이상"
def test_weekdays_mask_zero_rejected():
with pytest.raises(ValidationError):
HabitCreate(**_base_kwargs(weekdays_mask=0))
def test_weekdays_mask_over_max_rejected():
with pytest.raises(ValidationError):
HabitCreate(**_base_kwargs(weekdays_mask=ALL_WEEKDAYS_MASK + 1))
def test_weekdays_mask_single_day_accepted():
habit = HabitCreate(**_base_kwargs(weekdays_mask=0b0000001))
assert habit.weekdays_mask == 0b0000001
def test_weekdays_mask_defaults_to_all_days():
habit = HabitCreate(**_base_kwargs())
assert habit.weekdays_mask == ALL_WEEKDAYS_MASK