"""US-003: Authentication and security tests.""" import pytest from fastapi.testclient import TestClient class TestLogin: def test_login_success(self, client: TestClient): resp = client.post("/api/auth/login", json={ "username": "admin", "password": "admin123" }) assert resp.status_code == 200 data = resp.json() assert "access_token" in data assert len(data["access_token"]) > 0 def test_login_wrong_password(self, client: TestClient): resp = client.post("/api/auth/login", json={ "username": "admin", "password": "wrongpassword" }) assert resp.status_code == 401 assert "Incorrect username or password" in resp.json()["detail"] def test_login_nonexistent_user(self, client: TestClient): resp = client.post("/api/auth/login", json={ "username": "nonexistent_user_xyz", "password": "password" }) assert resp.status_code == 401 def test_login_empty_username(self, client: TestClient): resp = client.post("/api/auth/login", json={ "username": "", "password": "password" }) assert resp.status_code in (401, 422) class TestRegister: def test_register_new_user(self, client: TestClient): resp = client.post("/api/auth/register", json={ "username": "testuser_001", "password": "testpass123" }) assert resp.status_code == 201 assert resp.json()["username"] == "testuser_001" def test_register_duplicate(self, client: TestClient): client.post("/api/auth/register", json={ "username": "dup_user", "password": "testpass123" }) resp = client.post("/api/auth/register", json={ "username": "dup_user", "password": "testpass123" }) assert resp.status_code == 409 assert "already exists" in resp.json()["detail"] def test_register_missing_password(self, client: TestClient): resp = client.post("/api/auth/register", json={"username": "user"}) assert resp.status_code == 422 class TestTokenAuth: def test_me_with_valid_token(self, client: TestClient, auth_token): resp = client.get( "/api/auth/me", headers={"Authorization": f"Bearer {auth_token}"} ) assert resp.status_code == 200 assert resp.json()["username"] == "admin" def test_me_without_token(self, client: TestClient): resp = client.get("/api/auth/me") assert resp.status_code in (401, 403) def test_me_with_invalid_token(self, client: TestClient): resp = client.get( "/api/auth/me", headers={"Authorization": "Bearer invalid.token.here"} ) assert resp.status_code in (401, 403) def test_me_with_malformed_header(self, client: TestClient): resp = client.get( "/api/auth/me", headers={"Authorization": "NotBearer token"} ) assert resp.status_code in (401, 403) def test_me_with_expired_token(self, client: TestClient): """Token with past expiration should be rejected.""" expired = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTcwMDAwMDAwMH0.fake" resp = client.get( "/api/auth/me", headers={"Authorization": f"Bearer {expired}"} ) assert resp.status_code in (401, 403) def test_login_then_access_protected(self, client: TestClient): login_resp = client.post("/api/auth/login", json={ "username": "admin", "password": "admin123" }) token = login_resp.json()["access_token"] resp = client.get( "/api/auth/me", headers={"Authorization": f"Bearer {token}"} ) assert resp.status_code == 200 assert resp.json()["username"] == "admin" class TestPasswordHashing: def test_bcrypt_not_plaintext(self, client: TestClient): """Passwords should be hashed, not stored as plaintext.""" from auth.service import hash_password, verify_password hashed = hash_password("test_password") assert hashed != "test_password" assert verify_password("test_password", hashed) assert not verify_password("wrong_password", hashed)