@@ -136,6 +136,37 @@ def test_callback_new_user_creates_user_and_sets_cookie(client):
136136 assert "httponly" in set_cookie_header .lower ()
137137
138138
139+ def test_callback_new_user_creates_profile_with_github_username (client , db ):
140+ """初回ログイン: User + OAuthAccount + Profile が同一トランザクションで作成され、
141+ Profile.github_username が GitHub login 名で設定される (Issue #71)。"""
142+ state = _valid_state ()
143+ client .cookies .set ("oauth_state" , state )
144+ mock_client = _mock_httpx_client ()
145+
146+ with patch ("app.api.endpoints.auth.httpx.AsyncClient" , return_value = mock_client ):
147+ res = client .get (f"/api/v1/auth/github/callback?code=fake_code&state={ state } " )
148+
149+ assert res .status_code == 302
150+
151+ # JWT デコードして user_id を取得
152+ import jwt as _jwt
153+ from app .core .config import settings as _settings
154+
155+ token = res .cookies .get ("access_token" )
156+ assert token is not None
157+ payload = _jwt .decode (
158+ token , _settings .JWT_SECRET_KEY , algorithms = [_settings .JWT_ALGORITHM ]
159+ )
160+ user_id = int (payload ["sub" ])
161+
162+ # Profile が作成されていることを確認
163+ from app .crud .profile import get_profile_by_user_id
164+
165+ profile = get_profile_by_user_id (db , user_id )
166+ assert profile is not None
167+ assert profile .github_username == FAKE_GITHUB_USER ["login" ]
168+
169+
139170def test_callback_new_user_username_collision (client , db ):
140171 """GitHub のログイン名がすでに使われている場合は username に ID を付与。"""
141172 state = _valid_state ()
@@ -202,6 +233,50 @@ def test_callback_existing_user_updates_token(client):
202233 assert "access_token" in res2 .cookies
203234
204235
236+ def test_callback_existing_user_without_profile_creates_profile (client , db ):
237+ """既存ユーザーで Profile が欠損している場合、GitHub OAuth ログイン時に
238+ Profile を自動作成する(移行ケース対応: Issue #71)。"""
239+ # 既存ユーザーを Profile なしで準備(移行前データを模倣)
240+ from app .crud .user import create_user as db_create_user
241+ from app .crud .oauth_account import create_oauth_account
242+ from app .schemas .user import UserCreate
243+ from app .schemas .oauth_account import OAuthAccountCreate
244+
245+ existing_user = db_create_user (
246+ db , UserCreate (username = "legacy_user" , password = None )
247+ )
248+ # OAuthAccount は存在するが Profile は存在しない状態
249+ create_oauth_account (
250+ db ,
251+ OAuthAccountCreate (
252+ user_id = existing_user .id ,
253+ provider = "github" ,
254+ provider_user_id = str (FAKE_GITHUB_USER ["id" ]),
255+ access_token = "old_token" ,
256+ ),
257+ )
258+ db .commit ()
259+
260+ # Profile が存在しないことを確認
261+ from app .crud .profile import get_profile_by_user_id
262+
263+ assert get_profile_by_user_id (db , existing_user .id ) is None
264+
265+ # OAuth callback でログイン
266+ state = _valid_state ()
267+ client .cookies .set ("oauth_state" , state )
268+ mock_client = _mock_httpx_client ()
269+ with patch ("app.api.endpoints.auth.httpx.AsyncClient" , return_value = mock_client ):
270+ res = client .get (f"/api/v1/auth/github/callback?code=fake_code&state={ state } " )
271+
272+ assert res .status_code == 302
273+
274+ # Profile が自動作成されたことを確認
275+ profile = get_profile_by_user_id (db , existing_user .id )
276+ assert profile is not None
277+ assert profile .github_username == FAKE_GITHUB_USER ["login" ]
278+
279+
205280# ---------------------------------------------------------------------------
206281# GET /auth/github/callback - エラー系
207282# ---------------------------------------------------------------------------
0 commit comments