import stat
import tempfile
import time
+import typing
from pathlib import Path
import anyio
from starlette.applications import Starlette
from starlette.exceptions import HTTPException
from starlette.middleware import Middleware
-from starlette.middleware.base import BaseHTTPMiddleware
+from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request
+from starlette.responses import Response
from starlette.routing import Mount
from starlette.staticfiles import StaticFiles
+from starlette.testclient import TestClient
+TestClientFactory = typing.Callable[..., TestClient]
-def test_staticfiles(tmpdir, test_client_factory):
+
+def test_staticfiles(tmpdir: Path, test_client_factory: TestClientFactory) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert response.text == "<file content>"
-def test_staticfiles_with_pathlib(tmp_path: Path, test_client_factory):
+def test_staticfiles_with_pathlib(
+ tmp_path: Path, test_client_factory: TestClientFactory
+) -> None:
path = tmp_path / "example.txt"
with open(path, "w") as file:
file.write("<file content>")
assert response.text == "<file content>"
-def test_staticfiles_head_with_middleware(tmpdir, test_client_factory):
+def test_staticfiles_head_with_middleware(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
"""
see https://github.com/encode/starlette/pull/935
"""
with open(path, "w") as file:
file.write("x" * 100)
- async def does_nothing_middleware(request: Request, call_next):
+ async def does_nothing_middleware(
+ request: Request, call_next: RequestResponseEndpoint
+ ) -> Response:
response = await call_next(request)
return response
assert response.headers.get("content-length") == "100"
-def test_staticfiles_with_package(test_client_factory):
+def test_staticfiles_with_package(test_client_factory: TestClientFactory) -> None:
app = StaticFiles(packages=["tests"])
client = test_client_factory(app)
response = client.get("/example.txt")
assert response.text == "123\n"
-def test_staticfiles_post(tmpdir, test_client_factory):
+def test_staticfiles_post(tmpdir: Path, test_client_factory: TestClientFactory) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert response.text == "Method Not Allowed"
-def test_staticfiles_with_directory_returns_404(tmpdir, test_client_factory):
+def test_staticfiles_with_directory_returns_404(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert response.text == "Not Found"
-def test_staticfiles_with_missing_file_returns_404(tmpdir, test_client_factory):
+def test_staticfiles_with_missing_file_returns_404(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert response.text == "Not Found"
-def test_staticfiles_instantiated_with_missing_directory(tmpdir):
+def test_staticfiles_instantiated_with_missing_directory(tmpdir: Path) -> None:
with pytest.raises(RuntimeError) as exc_info:
path = os.path.join(tmpdir, "no_such_directory")
StaticFiles(directory=path)
assert "does not exist" in str(exc_info.value)
-def test_staticfiles_configured_with_missing_directory(tmpdir, test_client_factory):
+def test_staticfiles_configured_with_missing_directory(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "no_such_directory")
app = StaticFiles(directory=path, check_dir=False)
client = test_client_factory(app)
def test_staticfiles_configured_with_file_instead_of_directory(
- tmpdir, test_client_factory
-):
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert "is not a directory" in str(exc_info.value)
-def test_staticfiles_config_check_occurs_only_once(tmpdir, test_client_factory):
+def test_staticfiles_config_check_occurs_only_once(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
app = StaticFiles(directory=tmpdir)
client = test_client_factory(app)
assert not app.config_checked
client.get("/")
-def test_staticfiles_prevents_breaking_out_of_directory(tmpdir):
+def test_staticfiles_prevents_breaking_out_of_directory(tmpdir: Path) -> None:
directory = os.path.join(tmpdir, "foo")
os.mkdir(directory)
assert exc_info.value.detail == "Not Found"
-def test_staticfiles_never_read_file_for_head_method(tmpdir, test_client_factory):
+def test_staticfiles_never_read_file_for_head_method(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert response.headers["content-length"] == "14"
-def test_staticfiles_304_with_etag_match(tmpdir, test_client_factory):
+def test_staticfiles_304_with_etag_match(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
def test_staticfiles_304_with_last_modified_compare_last_req(
- tmpdir, test_client_factory
-):
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
file_last_modified_time = time.mktime(
time.strptime("2013-10-10 23:40:00", "%Y-%m-%d %H:%M:%S")
assert response.content == b"<file content>"
-def test_staticfiles_html_normal(tmpdir, test_client_factory):
+def test_staticfiles_html_normal(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "404.html")
with open(path, "w") as file:
file.write("<h1>Custom not found page</h1>")
assert response.text == "<h1>Custom not found page</h1>"
-def test_staticfiles_html_without_index(tmpdir, test_client_factory):
+def test_staticfiles_html_without_index(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "404.html")
with open(path, "w") as file:
file.write("<h1>Custom not found page</h1>")
assert response.text == "<h1>Custom not found page</h1>"
-def test_staticfiles_html_without_404(tmpdir, test_client_factory):
+def test_staticfiles_html_without_404(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "dir")
os.mkdir(path)
path = os.path.join(path, "index.html")
assert exc_info.value.status_code == 404
-def test_staticfiles_html_only_files(tmpdir, test_client_factory):
+def test_staticfiles_html_only_files(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "hello.html")
with open(path, "w") as file:
file.write("<h1>Hello</h1>")
def test_staticfiles_cache_invalidation_for_deleted_file_html_mode(
- tmpdir, test_client_factory
-):
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path_404 = os.path.join(tmpdir, "404.html")
with open(path_404, "w") as file:
file.write("<p>404 file</p>")
def test_staticfiles_with_invalid_dir_permissions_returns_401(
- tmp_path, test_client_factory
-):
+ tmp_path: Path, test_client_factory: TestClientFactory
+) -> None:
(tmp_path / "example.txt").write_bytes(b"<file content>")
original_mode = tmp_path.stat().st_mode
tmp_path.chmod(original_mode)
-def test_staticfiles_with_missing_dir_returns_404(tmpdir, test_client_factory):
+def test_staticfiles_with_missing_dir_returns_404(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
assert response.text == "Not Found"
-def test_staticfiles_access_file_as_dir_returns_404(tmpdir, test_client_factory):
+def test_staticfiles_access_file_as_dir_returns_404(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
path = os.path.join(tmpdir, "example.txt")
with open(path, "w") as file:
file.write("<file content>")
def test_staticfiles_unhandled_os_error_returns_500(
- tmpdir, test_client_factory, monkeypatch
-):
- def mock_timeout(*args, **kwargs):
+ tmpdir: Path,
+ test_client_factory: TestClientFactory,
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ def mock_timeout(*args: typing.Any, **kwargs: typing.Any) -> None:
raise TimeoutError
path = os.path.join(tmpdir, "example.txt")
assert response.text == "Internal Server Error"
-def test_staticfiles_follows_symlinks(tmpdir, test_client_factory):
+def test_staticfiles_follows_symlinks(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
statics_path = os.path.join(tmpdir, "statics")
os.mkdir(statics_path)
assert response.text == "<h1>Hello</h1>"
-def test_staticfiles_follows_symlink_directories(tmpdir, test_client_factory):
+def test_staticfiles_follows_symlink_directories(
+ tmpdir: Path, test_client_factory: TestClientFactory
+) -> None:
statics_path = os.path.join(tmpdir, "statics")
statics_html_path = os.path.join(statics_path, "html")
os.mkdir(statics_path)
assert response.text == "<h1>Hello</h1>"
-def test_staticfiles_disallows_path_traversal_with_symlinks(tmpdir):
+def test_staticfiles_disallows_path_traversal_with_symlinks(tmpdir: Path) -> None:
statics_path = os.path.join(tmpdir, "statics")
root_source_path = tempfile.mkdtemp()
assert exc_info.value.detail == "Not Found"
-def test_staticfiles_avoids_path_traversal(tmp_path: Path):
+def test_staticfiles_avoids_path_traversal(tmp_path: Path) -> None:
statics_path = tmp_path / "static"
statics_disallow_path = tmp_path / "static_disallow"