"""Tests for auth blueprint (client/blueprints/auth.py). Covers login GET/POST, logout, unauthenticated redirect, and language/theme switch. """ import pytest from unittest.mock import patch class TestLoginPage: """GET /auth/login tests.""" def test_login_page_renders(self, client): """Login page renders successfully with 200 status.""" resp = client.get("/auth/login") assert resp.status_code == 200 assert b"login" in resp.data.lower() or b"Login" in resp.data def test_login_page_redirects_when_already_logged_in(self, logged_in_client): """Already authenticated user is redirected away from login.""" resp = logged_in_client.get("/auth/login") assert resp.status_code == 302 class TestLoginPost: """POST /auth/login tests.""" def test_login_post_success_redirects(self, client, mock_api_client): """Successful login POST sets session and redirects.""" mock_api_client.post.return_value = { "api_key": "new-api-key-abc", "user": { "id": 1, "username": "testuser", "display_name": "Test User", "roles": ["MeasurementTec"], "is_admin": False, "language_pref": "en", "theme_pref": "light", "active": True, }, } # Mock settings call made after login mock_api_client.get.return_value = {} resp = client.post( "/auth/login", data={"username": "testuser", "password": "pass123"}, follow_redirects=False, ) assert resp.status_code == 302 mock_api_client.post.assert_called_once() # Verify session was populated with client.session_transaction() as sess: assert sess.get("api_key") == "new-api-key-abc" assert sess["user"]["username"] == "testuser" def test_login_post_error_shows_error(self, client, mock_api_client): """Failed login returns the login page with error flash.""" mock_api_client.post.return_value = { "error": True, "detail": "Credenziali non valide", } resp = client.post( "/auth/login", data={"username": "bad", "password": "wrong"}, follow_redirects=True, ) assert resp.status_code == 200 # Error message should appear in the response (flashed) assert b"Credenziali non valide" in resp.data or b"login" in resp.data.lower() def test_login_post_empty_fields(self, client, mock_api_client): """Login with empty username/password shows validation error.""" resp = client.post( "/auth/login", data={"username": "", "password": ""}, follow_redirects=True, ) assert resp.status_code == 200 # Should render login again (not crash) class TestLogout: """GET/POST /auth/logout tests.""" def test_logout_clears_session(self, logged_in_client, mock_api_client): """Logout clears session and redirects to login.""" mock_api_client.post.return_value = {} resp = logged_in_client.get("/auth/logout", follow_redirects=False) assert resp.status_code == 302 assert "/auth/login" in resp.headers["Location"] # Session should be cleared with logged_in_client.session_transaction() as sess: assert "api_key" not in sess assert "user" not in sess class TestUnauthenticatedRedirect: """Protected routes redirect unauthenticated users.""" def test_profile_requires_login(self, client): """Profile page redirects to login when not authenticated.""" resp = client.get("/auth/profile", follow_redirects=False) assert resp.status_code == 302 assert "/auth/login" in resp.headers["Location"] class TestLanguageSwitch: """Language and theme switching via app routes.""" def test_set_language_stores_in_session(self, client): """Setting language updates session and redirects back.""" resp = client.get("/set-language/en", follow_redirects=False) assert resp.status_code == 302 with client.session_transaction() as sess: assert sess.get("language") == "en" def test_set_language_invalid_ignored(self, client): """Setting an unsupported language does not crash and still redirects.""" resp = client.get("/set-language/xx", follow_redirects=False) assert resp.status_code == 302 with client.session_transaction() as sess: # Invalid language should NOT be stored assert sess.get("language") != "xx"