@ -1,7 +1,9 @@
import pytest
from fastapi import FastAPI , HTTPException , Request
from fastapi . responses import RedirectResponse
from fastapi . staticfiles import AuthStaticFiles
from fastapi . testclient import TestClient
from starlette . responses import HTMLResponse , Response
@pytest . fixture ( scope = " module " )
@ -19,6 +21,11 @@ async def verify_token(request: Request) -> None:
raise HTTPException ( status_code = 401 , detail = " Not authenticated " )
async def _allow_all ( request : Request ) - > None :
""" Auth function that allows all requests. """
pass
@pytest . fixture ( scope = " module " )
def app ( static_dir ) :
app = FastAPI ( )
@ -46,11 +53,6 @@ def app(static_dir):
return app
async def _allow_all ( request : Request ) - > None :
""" Auth function that allows all requests. """
pass
@pytest . fixture ( scope = " module " )
def client ( app ) :
with TestClient ( app ) as c :
@ -61,7 +63,7 @@ def test_private_file_without_auth(client: TestClient):
""" Requesting a private file without auth should return 401. """
response = client . get ( " /private/secret.txt " )
assert response . status_code == 401
assert response . json ( ) == { " detail " : " Not authenticated " }
assert response . text == " Not authenticated "
def test_private_file_with_wrong_token ( client : TestClient ) :
@ -71,7 +73,7 @@ def test_private_file_with_wrong_token(client: TestClient):
headers = { " Authorization " : " Bearer wrong-token " } ,
)
assert response . status_code == 401
assert response . json ( ) == { " detail " : " Not authenticated " }
assert response . text == " Not authenticated "
def test_private_file_with_valid_token ( client : TestClient ) :
@ -121,7 +123,7 @@ def test_auth_headers_forwarded(static_dir):
response = client . get ( " /protected/public.txt " )
assert response . status_code == 401
assert response . headers [ " WWW-Authenticate " ] == " Bearer "
assert response . json ( ) == { " detail " : " Login required " }
assert response . text == " Login required "
def test_cookie_based_auth ( static_dir ) :
@ -149,3 +151,58 @@ def test_cookie_based_auth(static_dir):
response = client . get ( " /dashboard/public.txt " )
assert response . status_code == 200
assert response . text == " public content "
def test_custom_on_error_redirect ( static_dir ) :
""" on_error can redirect to a login page. """
async def deny_all ( request : Request ) - > None :
raise HTTPException ( status_code = 401 , detail = " Unauthorized " )
async def redirect_to_login ( request : Request , exc : HTTPException ) - > Response :
return RedirectResponse ( url = " /login " , status_code = 302 )
app = FastAPI ( )
app . mount (
" /protected " ,
AuthStaticFiles (
directory = str ( static_dir ) ,
auth = deny_all ,
on_error = redirect_to_login ,
) ,
name = " protected " ,
)
with TestClient ( app , follow_redirects = False ) as client :
response = client . get ( " /protected/public.txt " )
assert response . status_code == 302
assert response . headers [ " location " ] == " /login "
def test_custom_on_error_html ( static_dir ) :
""" on_error can return a custom HTML error page. """
async def deny_all ( request : Request ) - > None :
raise HTTPException ( status_code = 403 , detail = " Forbidden " )
async def html_error ( request : Request , exc : HTTPException ) - > Response :
return HTMLResponse (
f " <h1> { exc . status_code } { exc . detail } </h1> " ,
status_code = exc . status_code ,
)
app = FastAPI ( )
app . mount (
" /protected " ,
AuthStaticFiles (
directory = str ( static_dir ) ,
auth = deny_all ,
on_error = html_error ,
) ,
name = " protected " ,
)
with TestClient ( app ) as client :
response = client . get ( " /protected/public.txt " )
assert response . status_code == 403
assert " <h1>403 Forbidden</h1> " in response . text