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
+79
View File
@@ -0,0 +1,79 @@
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from app.database import Base, get_db
from app.main import app
from app.models.user import User
from app.security import create_session_token
from app.config import settings
@pytest.fixture()
def engine():
eng = create_engine(
"sqlite:///:memory:",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
@event.listens_for(eng, "connect")
def _enable_fk(dbapi_connection, _):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
Base.metadata.create_all(eng)
yield eng
Base.metadata.drop_all(eng)
eng.dispose()
@pytest.fixture()
def db_session(engine):
session_factory = sessionmaker(bind=engine, autoflush=False, autocommit=False)
session = session_factory()
try:
yield session
finally:
session.close()
@pytest.fixture()
def client(db_session):
def _override_get_db():
yield db_session
app.dependency_overrides[get_db] = _override_get_db
# TestClient를 컨텍스트 매니저(`with`)로 쓰지 않으므로 lifespan(스케줄러 기동)이 실행되지 않는다 —
# 테스트가 실제 운영 MariaDB에 붙는 APScheduler 백그라운드 잡을 우연히 건드리지 않게 하기 위함.
test_client = TestClient(app)
yield test_client
app.dependency_overrides.clear()
@pytest.fixture()
def test_user(db_session):
user = User(google_sub="test-sub-1", email="tester@example.com", name="테스터")
db_session.add(user)
db_session.commit()
db_session.refresh(user)
return user
@pytest.fixture()
def other_user(db_session):
user = User(google_sub="test-sub-2", email="other@example.com", name="다른유저")
db_session.add(user)
db_session.commit()
db_session.refresh(user)
return user
@pytest.fixture()
def auth_client(client, test_user):
token = create_session_token(test_user.id)
client.cookies.set(settings.session_cookie_name, token)
return client