"""Authentication flow tests.""" from __future__ import annotations from httpx import AsyncClient from tests.conftest import auth_header, signup async def test_health_is_public(client: AsyncClient): response = await client.get("/health") assert response.status_code == 200 assert response.json()["status"] == "ok" async def test_signup_helper_returns_token_and_user(client: AsyncClient): """There is no public signup endpoint — the test helper is the owner-side creation path — and what it returns must carry the platform role.""" body = await signup(client, "alice@example.com", name="آلیس") assert body["access_token"] assert body["user"]["email"] == "alice@example.com" assert body["user"]["name"] == "آلیس" assert body["user"]["platform_role"] == "manager" assert body["user"]["is_manager"] is True assert body["user"]["is_platform_owner"] is False assert "password" not in str(body).lower() or "password_hash" not in str(body) async def test_signup_helper_never_returns_password_hash(client: AsyncClient): body = await signup(client, "hashcheck@example.com") assert "password_hash" not in body["user"] assert "password" not in body["user"] async def test_public_signup_endpoint_is_gone(client: AsyncClient): """Signup is closed: accounts come from the owner or from an invite link. A stray old client must not silently succeed — the route simply does not exist anymore. """ response = await client.post( "/api/auth/signup", json={"email": "nope@example.com", "password": "password123", "name": ""}, ) assert response.status_code == 404 async def test_login_succeeds_with_correct_password(client: AsyncClient): await signup(client, "bob@example.com", password="password123") response = await client.post( "/api/auth/login", json={"email": "bob@example.com", "password": "password123"} ) assert response.status_code == 200 assert response.json()["access_token"] payload = response.json()["user"] assert payload["platform_role"] == "manager" assert payload["is_platform_owner"] is False async def test_login_fails_with_wrong_password(client: AsyncClient): await signup(client, "carol@example.com", password="password123") response = await client.post( "/api/auth/login", json={"email": "carol@example.com", "password": "wrong-password"} ) assert response.status_code == 401 async def test_login_is_case_insensitive_on_email(client: AsyncClient): await signup(client, "Dave@Example.com", password="password123") response = await client.post( "/api/auth/login", json={"email": "dave@example.com", "password": "password123"} ) assert response.status_code == 200 async def test_me_requires_authentication(client: AsyncClient): response = await client.get("/api/auth/me") assert response.status_code == 401 async def test_me_rejects_garbage_token(client: AsyncClient): response = await client.get("/api/auth/me", headers=auth_header("not-a-real-token")) assert response.status_code == 401 async def test_me_returns_profile_and_organizations(client: AsyncClient): body = await signup(client, "erin@example.com", name="ارین") token = body["access_token"] response = await client.get("/api/auth/me", headers=auth_header(token)) assert response.status_code == 200 payload = response.json() assert payload["user"]["email"] == "erin@example.com" assert payload["organizations"] == [] async def test_logout_is_recorded_and_returns_204(client: AsyncClient): body = await signup(client, "frank@example.com") response = await client.post("/api/auth/logout", headers=auth_header(body["access_token"])) assert response.status_code == 204 async def test_change_password_replaces_the_old_one(client: AsyncClient): """A temp password handed out by the owner must become a private one. Without this, every account created by someone else would live forever on a secret two people know. """ body = await signup(client, "grace@example.com", password="password123") headers = auth_header(body["access_token"]) response = await client.post( "/api/auth/change-password", json={"current_password": "password123", "new_password": "newpassword456"}, headers=headers, ) assert response.status_code == 204, response.text stale = await client.post( "/api/auth/login", json={"email": "grace@example.com", "password": "password123"} ) assert stale.status_code == 401 fresh = await client.post( "/api/auth/login", json={"email": "grace@example.com", "password": "newpassword456"} ) assert fresh.status_code == 200 async def test_change_password_rejects_a_wrong_current_password(client: AsyncClient): body = await signup(client, "heidi@example.com", password="password123") response = await client.post( "/api/auth/change-password", json={"current_password": "not-my-password", "new_password": "newpassword456"}, headers=auth_header(body["access_token"]), ) assert response.status_code == 401 async def test_change_password_rejects_a_short_new_password(client: AsyncClient): body = await signup(client, "ivan@example.com", password="password123") response = await client.post( "/api/auth/change-password", json={"current_password": "password123", "new_password": "123"}, headers=auth_header(body["access_token"]), ) assert response.status_code == 422