59 lines
2.2 KiB
Python
59 lines
2.2 KiB
Python
|
|
"""Integration tests for the auth routes.
|
||
|
|
|
||
|
|
Uses FastAPI TestClient with a mocked user store.
|
||
|
|
Does not require a running PostgreSQL.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi.testclient import TestClient
|
||
|
|
from unittest.mock import MagicMock, patch
|
||
|
|
|
||
|
|
from app.infrastructure.auth.user_store import UserRecord
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture
|
||
|
|
def client():
|
||
|
|
"""Return a TestClient with a mocked user store."""
|
||
|
|
mock_store = MagicMock()
|
||
|
|
alice = UserRecord(id="uuid-1", username="alice", hashed_pw="hashed", role="admin", is_active=True)
|
||
|
|
mock_store.authenticate.side_effect = lambda u, p: alice if (u == "alice" and p == "correct") else None
|
||
|
|
|
||
|
|
with patch("app.shared.bootstrap.get_user_store", return_value=mock_store):
|
||
|
|
# Import after patch so the mock is active when routes import bootstrap.
|
||
|
|
from app.api.main import app
|
||
|
|
with TestClient(app, raise_server_exceptions=False) as c:
|
||
|
|
yield c
|
||
|
|
|
||
|
|
|
||
|
|
def test_login_returns_token_for_valid_credentials(client):
|
||
|
|
"""POST /auth/token must return an access_token for valid credentials."""
|
||
|
|
resp = client.post("/api/v1/auth/token", data={"username": "alice", "password": "correct"})
|
||
|
|
assert resp.status_code == 200
|
||
|
|
body = resp.json()
|
||
|
|
assert "access_token" in body
|
||
|
|
assert body["token_type"] == "bearer"
|
||
|
|
|
||
|
|
|
||
|
|
def test_login_returns_401_for_wrong_password(client):
|
||
|
|
"""POST /auth/token must return 401 for wrong password."""
|
||
|
|
resp = client.post("/api/v1/auth/token", data={"username": "alice", "password": "wrong"})
|
||
|
|
assert resp.status_code == 401
|
||
|
|
|
||
|
|
|
||
|
|
def test_me_returns_user_when_authenticated(client):
|
||
|
|
"""GET /auth/me must return user identity when a valid token is provided."""
|
||
|
|
login_resp = client.post("/api/v1/auth/token", data={"username": "alice", "password": "correct"})
|
||
|
|
assert login_resp.status_code == 200, login_resp.text
|
||
|
|
token = login_resp.json()["access_token"]
|
||
|
|
|
||
|
|
me_resp = client.get("/api/v1/auth/me", headers={"Authorization": f"Bearer {token}"})
|
||
|
|
assert me_resp.status_code == 200
|
||
|
|
assert me_resp.json()["username"] == "alice"
|
||
|
|
assert me_resp.json()["role"] == "admin"
|
||
|
|
|
||
|
|
|
||
|
|
def test_me_returns_401_without_token(client):
|
||
|
|
"""GET /auth/me must return 401 when no token is provided."""
|
||
|
|
resp = client.get("/api/v1/auth/me")
|
||
|
|
assert resp.status_code == 401
|