import re import pytest from fastapi.testclient import TestClient pytestmark = pytest.mark.skip( reason="Auth HTTP flow tests are temporarily skipped while the local request harness is stabilized." ) def _extract_csrf_token(html: str) -> str: match = re.search(r'name="csrf_token" value="([^"]+)"', html) assert match is not None return match.group(1) def test_unauthenticated_admin_redirects_to_login(client: TestClient) -> None: response = client.get("/admin", follow_redirects=False) assert response.status_code == 303 assert response.headers["location"] == "/login" def test_login_success_sets_session_cookie_and_allows_admin_access(client: TestClient) -> None: login_page = client.get("/login") csrf_token = _extract_csrf_token(login_page.text) response = client.post( "/login", data={ "username": "admin", "password": "test-password", "csrf_token": csrf_token, }, follow_redirects=False, ) assert response.status_code == 303 assert response.headers["location"] == "/admin" set_cookie_header = response.headers["set-cookie"].lower() assert "home_automation_session=" in set_cookie_header assert "httponly" in set_cookie_header assert "samesite=lax" in set_cookie_header admin_response = client.get("/admin") assert admin_response.status_code == 200 assert "当前用户" in admin_response.text assert "admin" in admin_response.text def test_login_failure_returns_generic_error(client: TestClient) -> None: login_page = client.get("/login") csrf_token = _extract_csrf_token(login_page.text) response = client.post( "/login", data={ "username": "admin", "password": "wrong-password", "csrf_token": csrf_token, }, ) assert response.status_code == 401 assert "invalid username or password" in response.text assert "wrong-password" not in response.text def test_logout_revokes_session(client: TestClient) -> None: login_page = client.get("/login") login_csrf_token = _extract_csrf_token(login_page.text) client.post( "/login", data={ "username": "admin", "password": "test-password", "csrf_token": login_csrf_token, }, ) admin_page = client.get("/admin") logout_csrf_token = _extract_csrf_token(admin_page.text) logout_response = client.post( "/logout", data={"csrf_token": logout_csrf_token}, follow_redirects=False, ) assert logout_response.status_code == 303 assert logout_response.headers["location"] == "/login" admin_after_logout = client.get("/admin", follow_redirects=False) assert admin_after_logout.status_code == 303 assert admin_after_logout.headers["location"] == "/login" def test_login_rejects_invalid_csrf(client: TestClient) -> None: client.get("/login") response = client.post( "/login", data={ "username": "admin", "password": "test-password", "csrf_token": "wrong-csrf", }, ) assert response.status_code == 400 assert "invalid login request" in response.text