مخزن اختصاصی برنامهٔ وب؛ محتوای پوشهٔ web در ریشهٔ مخزن قرار گرفت. تاریخچهٔ پروژهٔ ربات (agy-bot) در این مخزن وجود ندارد و هیچ وابستگی runtime به آن نیست.
99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
"""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_returns_token_and_user(client: AsyncClient):
|
|
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"]["is_platform_owner"] is False
|
|
assert "password" not in str(body).lower() or "password_hash" not in str(body)
|
|
|
|
|
|
async def test_signup_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_signup_rejects_duplicate_email(client: AsyncClient):
|
|
await signup(client, "dup@example.com")
|
|
response = await client.post(
|
|
"/api/auth/signup",
|
|
json={"email": "dup@example.com", "password": "password123", "name": ""},
|
|
)
|
|
assert response.status_code == 409
|
|
assert response.json()["error"]["code"] == "conflict"
|
|
|
|
|
|
async def test_signup_rejects_short_password(client: AsyncClient):
|
|
response = await client.post(
|
|
"/api/auth/signup",
|
|
json={"email": "short@example.com", "password": "123", "name": ""},
|
|
)
|
|
assert response.status_code == 422
|
|
|
|
|
|
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"]
|
|
|
|
|
|
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
|