"""Auth flows: registration, login, email verification, password reset, logout.""" from tests.conftest import auth, register, token_from_link async def test_register_issues_session_and_verification_email(client, mailer): resp = await client.post( "/api/v1/auth/register", json={"email": "new@example.com", "password": "password123", "display_name": "New"}, ) assert resp.status_code == 201, resp.text body = resp.json() assert body["token"] assert body["user"]["email"] == "new@example.com" assert body["user"]["email_verified_at"] is None # A verification email was "sent". assert len(mailer.verifications) == 1 assert mailer.verifications[0][0] == "new@example.com" async def test_duplicate_email_conflicts(client): await register(client, "dupe@example.com") resp = await client.post( "/api/v1/auth/register", json={"email": "dupe@example.com", "password": "password123"} ) assert resp.status_code == 409 async def test_login_wrong_password_rejected(client): await register(client, "user@example.com", password="password123") resp = await client.post( "/api/v1/auth/login", json={"email": "user@example.com", "password": "wrong-password"} ) assert resp.status_code == 401 async def test_login_succeeds_and_me_returns_user(client): await register(client, "user2@example.com", password="password123") resp = await client.post( "/api/v1/auth/login", json={"email": "user2@example.com", "password": "password123"} ) assert resp.status_code == 200 token = resp.json()["token"] resp = await client.get("/api/v1/users/me", headers=auth(token)) assert resp.status_code == 200 assert resp.json()["email"] == "user2@example.com" async def test_email_verification(client, mailer): await register(client, "verify@example.com") token = token_from_link(mailer.verifications[0][1]) resp = await client.post("/api/v1/auth/verify-email", json={"token": token}) assert resp.status_code == 204 # Logging in and checking /me shows the address is now verified. login = await client.post( "/api/v1/auth/login", json={"email": "verify@example.com", "password": "password123"} ) me = await client.get("/api/v1/users/me", headers=auth(login.json()["token"])) assert me.json()["email_verified_at"] is not None async def test_password_reset_flow_revokes_old_sessions(client, mailer): old_token = await register(client, "reset@example.com", password="password123") resp = await client.post( "/api/v1/auth/request-password-reset", json={"email": "reset@example.com"} ) assert resp.status_code == 202 reset_token = token_from_link(mailer.resets[0][1]) resp = await client.post( "/api/v1/auth/reset-password", json={"token": reset_token, "new_password": "new-password456"}, ) assert resp.status_code == 204 # Old session is revoked. assert (await client.get("/api/v1/users/me", headers=auth(old_token))).status_code == 401 # Old password no longer works; new one does. assert ( await client.post( "/api/v1/auth/login", json={"email": "reset@example.com", "password": "password123"} ) ).status_code == 401 assert ( await client.post( "/api/v1/auth/login", json={"email": "reset@example.com", "password": "new-password456"}, ) ).status_code == 200 async def test_request_password_reset_unknown_email_still_accepted(client, mailer): resp = await client.post( "/api/v1/auth/request-password-reset", json={"email": "nobody@example.com"} ) assert resp.status_code == 202 assert len(mailer.resets) == 0 # no email sent, but no enumeration either async def test_logout_revokes_session(client): token = await register(client, "logout@example.com") assert (await client.get("/api/v1/users/me", headers=auth(token))).status_code == 200 assert (await client.post("/api/v1/auth/logout", headers=auth(token))).status_code == 204 assert (await client.get("/api/v1/users/me", headers=auth(token))).status_code == 401