hasari-api / services /backend /tests /test_auth_flow.py
erdoganpeker's picture
v0.3.0 — multimodal vehicle damage MVP
e327f0d
"""
Auth flow E2E testleri.
Akis: register -> login -> me -> refresh -> me (yeni token) -> invalid token -> 401
Bu testler /auth/* router'inin tam islevselligini kapsar; security.py'daki
JWT verify + auth.py'deki dependency chain'i birlikte calistirir.
"""
from __future__ import annotations
import httpx
import pytest
# ---------------- register ----------------
async def test_register_creates_user_and_returns_tokens(
async_client: httpx.AsyncClient, reset_user_store
):
r = await async_client.post(
"/auth/register",
json={
"email": "alice@test.example.com",
"password": "strong-pass-1234",
"full_name": "Alice",
},
)
assert r.status_code == 201, r.text
body = r.json()
assert body["token_type"] == "bearer"
assert body["access_token"]
assert body["refresh_token"]
assert body["access_token"] != body["refresh_token"]
assert body["expires_in"] > 0
async def test_register_duplicate_email_returns_409(
async_client: httpx.AsyncClient, reset_user_store
):
payload = {"email": "dup@test.example.com", "password": "strong-pass-1234"}
r1 = await async_client.post("/auth/register", json=payload)
assert r1.status_code == 201
r2 = await async_client.post("/auth/register", json=payload)
assert r2.status_code == 409
assert "zaten" in r2.json()["detail"].lower()
async def test_register_weak_password_rejected(
async_client: httpx.AsyncClient, reset_user_store
):
# password min_length=8 (models.py)
r = await async_client.post(
"/auth/register",
json={"email": "weak@test.example.com", "password": "short"},
)
assert r.status_code == 422 # pydantic validation
# ---------------- login ----------------
async def test_login_with_valid_credentials(
async_client: httpx.AsyncClient, reset_user_store
):
await async_client.post(
"/auth/register",
json={"email": "bob@test.example.com", "password": "strong-pass-1234"},
)
r = await async_client.post(
"/auth/login",
json={"email": "bob@test.example.com", "password": "strong-pass-1234"},
)
assert r.status_code == 200
body = r.json()
assert body["access_token"]
assert body["refresh_token"]
async def test_login_with_wrong_password_returns_401(
async_client: httpx.AsyncClient, reset_user_store
):
await async_client.post(
"/auth/register",
json={"email": "bob2@test.example.com", "password": "strong-pass-1234"},
)
r = await async_client.post(
"/auth/login",
json={"email": "bob2@test.example.com", "password": "WRONG-PASSWORD"},
)
assert r.status_code == 401
async def test_login_with_nonexistent_email_returns_401(
async_client: httpx.AsyncClient, reset_user_store
):
r = await async_client.post(
"/auth/login",
json={"email": "ghost@test.example.com", "password": "anything-12345"},
)
assert r.status_code == 401
# ---------------- me ----------------
async def test_me_returns_current_user(
async_client: httpx.AsyncClient, reset_user_store
):
reg = await async_client.post(
"/auth/register",
json={
"email": "carol@test.example.com",
"password": "strong-pass-1234",
"full_name": "Carol",
},
)
token = reg.json()["access_token"]
r = await async_client.get(
"/auth/me",
headers={"Authorization": f"Bearer {token}"},
)
assert r.status_code == 200, r.text
body = r.json()
assert body["email"] == "carol@test.example.com"
assert body["full_name"] == "Carol"
assert body["role"] == "user"
assert body["is_active"] is True
# ---------------- refresh ----------------
async def test_refresh_returns_new_token_pair_and_new_token_works(
async_client: httpx.AsyncClient, reset_user_store
):
reg = await async_client.post(
"/auth/register",
json={"email": "dave@test.example.com", "password": "strong-pass-1234"},
)
refresh_token = reg.json()["refresh_token"]
old_access = reg.json()["access_token"]
# /auth/refresh
r = await async_client.post(
"/auth/refresh",
json={"refresh_token": refresh_token},
)
assert r.status_code == 200, r.text
new_pair = r.json()
assert new_pair["access_token"]
assert new_pair["refresh_token"]
# JTI farkli olmali — token degismis
assert new_pair["access_token"] != old_access
# Yeni access token ile /auth/me cagrisi gecmeli
me = await async_client.get(
"/auth/me",
headers={"Authorization": f"Bearer {new_pair['access_token']}"},
)
assert me.status_code == 200
assert me.json()["email"] == "dave@test.example.com"
async def test_refresh_with_invalid_token_returns_401(
async_client: httpx.AsyncClient, reset_user_store
):
r = await async_client.post(
"/auth/refresh",
json={"refresh_token": "this.is.not.a.valid.jwt.token"},
)
assert r.status_code == 401
async def test_refresh_with_access_token_type_rejected(
async_client: httpx.AsyncClient, reset_user_store
):
"""Access token, refresh endpoint'inde kullanilamamali (type=access != refresh)."""
reg = await async_client.post(
"/auth/register",
json={"email": "eve@test.example.com", "password": "strong-pass-1234"},
)
access = reg.json()["access_token"]
r = await async_client.post(
"/auth/refresh",
json={"refresh_token": access},
)
assert r.status_code == 401
# ---------------- invalid token -> 401 ----------------
async def test_me_with_invalid_token_returns_401(
async_client: httpx.AsyncClient, reset_user_store, monkeypatch
):
# Dev mode bypass'i devre disi birak: API_KEYS dolu yap
from config import settings
monkeypatch.setattr(settings, "api_keys", ["valid-key-123"])
monkeypatch.setattr(settings, "environment", "production")
r = await async_client.get(
"/auth/me",
headers={"Authorization": "Bearer not-a-valid-jwt"},
)
assert r.status_code == 401
async def test_me_with_tampered_token_returns_401(
async_client: httpx.AsyncClient, reset_user_store, monkeypatch
):
from config import settings
monkeypatch.setattr(settings, "api_keys", ["valid-key-123"])
monkeypatch.setattr(settings, "environment", "production")
reg = await async_client.post(
"/auth/register",
json={"email": "frank@test.example.com", "password": "strong-pass-1234"},
)
token = reg.json()["access_token"]
# Son karakteri degistir -> imza bozulur
tampered = token[:-2] + ("AA" if token[-2:] != "AA" else "BB")
r = await async_client.get(
"/auth/me",
headers={"Authorization": f"Bearer {tampered}"},
)
assert r.status_code == 401
async def test_me_without_token_in_prod_mode_returns_401(
async_client: httpx.AsyncClient, reset_user_store, monkeypatch
):
from config import settings
monkeypatch.setattr(settings, "api_keys", ["valid-key-123"])
monkeypatch.setattr(settings, "environment", "production")
r = await async_client.get("/auth/me")
assert r.status_code == 401