Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
79e9bf7
test(starlette): Verify request info captured with POST endpoints
alexander-alderman-webb May 15, 2026
1a5d498
fix tests on old starlette versions
alexander-alderman-webb May 15, 2026
d91d454
merge master
alexander-alderman-webb May 15, 2026
3e9648b
fix(starlette): Do not attach eagerly consumed request bodies on stre…
alexander-alderman-webb May 18, 2026
b92156c
update const
alexander-alderman-webb May 18, 2026
f2cedf5
merge
alexander-alderman-webb May 18, 2026
92f1d78
types
alexander-alderman-webb May 18, 2026
6a30714
more types
alexander-alderman-webb May 18, 2026
064ed6a
defensive access
alexander-alderman-webb May 18, 2026
bc8ceb2
patch different form method
alexander-alderman-webb May 18, 2026
e8fb24a
cleanup
alexander-alderman-webb May 18, 2026
962caa4
merge
alexander-alderman-webb May 18, 2026
b029bc0
skip tests when patched method does not exist
alexander-alderman-webb May 18, 2026
5004aae
update docstring
alexander-alderman-webb May 18, 2026
83fa16d
use cached attributes directly
alexander-alderman-webb May 18, 2026
8582cc5
explicit None return
alexander-alderman-webb May 18, 2026
2637c8a
docstring
alexander-alderman-webb May 18, 2026
9d2157e
docstring
alexander-alderman-webb May 18, 2026
b7787d9
handle null JSON
alexander-alderman-webb May 18, 2026
f5ef1a3
test(fastapi): Verify request info captured with POST endpoints
alexander-alderman-webb May 19, 2026
3a2d930
Merge branch 'master' into webb/starlette/request-extractor-tests
alexander-alderman-webb Jun 9, 2026
4f1578f
merge
alexander-alderman-webb Jun 9, 2026
a2b096e
Merge branch 'webb/starlette/request-body-async' into webb/fastapi/re…
alexander-alderman-webb Jun 9, 2026
71b3439
Merge branch 'master' into webb/fastapi/request-extractor-tests
alexander-alderman-webb Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added tests/integrations/fastapi/photo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
154 changes: 153 additions & 1 deletion tests/integrations/fastapi/test_fastapi.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import base64
import json
import logging
import os
import threading
import warnings
from unittest import mock

import fastapi
import pytest
import starlette
from fastapi import FastAPI, HTTPException, Request
from fastapi import Body, FastAPI, File, Form, HTTPException, Request, UploadFile
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.testclient import TestClient

Expand All @@ -22,6 +24,28 @@
FASTAPI_VERSION = parse_version(fastapi.__version__)
STARLETTE_VERSION = parse_version(starlette.__version__)

PICTURE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "photo.jpg")

BODY_JSON = {"some": "json", "for": "testing", "nested": {"numbers": 123}}

BODY_FORM = """--fd721ef49ea403a6\r\nContent-Disposition: form-data; name="username"\r\n\r\nJane\r\n--fd721ef49ea403a6\r\nContent-Disposition: form-data; name="password"\r\n\r\nhello123\r\n--fd721ef49ea403a6\r\nContent-Disposition: form-data; name="photo"; filename="photo.jpg"\r\nContent-Type: image/jpg\r\nContent-Transfer-Encoding: base64\r\n\r\n{{image_data}}\r\n--fd721ef49ea403a6--\r\n""".replace(
"{{image_data}}", str(base64.b64encode(open(PICTURE, "rb").read()))
)

PARSED_FORM = starlette.datastructures.FormData(
[
("username", "Jane"),
("password", "hello123"),
(
"photo",
starlette.datastructures.UploadFile(
filename="photo.jpg",
file=open(PICTURE, "rb"),
),
),
]
)

from tests.integrations.conftest import parametrize_test_configurable_status_codes
from tests.integrations.starlette import test_starlette

Expand Down Expand Up @@ -70,9 +94,137 @@ async def _thread_ids_async():
"active": str(threading.current_thread().ident),
}

@app.post("/body/json")
async def body_json(payload: dict = Body(...)):
capture_message("hi")
return {"status": "ok"}

@app.post("/body/form")
async def body_form(
username: str = Form(...),
password: str = Form(...),
photo: UploadFile = File(...),
):
capture_message("hi")
return {"status": "ok"}

return app


@pytest.mark.asyncio
async def test_request_info_json_body(sentry_init, capture_events):
sentry_init(
traces_sample_rate=1.0,
send_default_pii=True,
integrations=[StarletteIntegration()],
)

app = fastapi_app_factory()
client = TestClient(app)

events = capture_events()

client.post(
"/body/json",
json=BODY_JSON,
headers={
"cookie": "yummy_cookie=choco; tasty_cookie=strawberry",
},
)

(event, transaction_event) = events

assert event["request"]["cookies"] == {
"tasty_cookie": "strawberry",
"yummy_cookie": "choco",
}
assert event["request"]["data"] == BODY_JSON

assert transaction_event["request"]["cookies"] == {
"tasty_cookie": "strawberry",
"yummy_cookie": "choco",
}
assert transaction_event["request"]["data"] == BODY_JSON


@pytest.mark.asyncio
async def test_formdata_request_body(sentry_init, capture_events):
sentry_init(
traces_sample_rate=1.0,
send_default_pii=True,
max_request_body_size="always",
integrations=[StarletteIntegration()],
)

app = fastapi_app_factory()
client = TestClient(app)

events = capture_events()

client.post(
"/body/form",
data=BODY_FORM.encode("utf-8"),
headers={
"content-type": "multipart/form-data; boundary=fd721ef49ea403a6",
},
)

(event, transaction_event) = events
assert event["request"]["data"].keys() == PARSED_FORM.keys()
assert event["request"]["data"]["username"] == PARSED_FORM["username"]
assert event["request"]["data"]["password"] == "[Filtered]"
assert event["request"]["data"]["photo"] == ""
assert event["_meta"]["request"]["data"]["photo"] == {"": {"rem": [["!raw", "x"]]}}

assert transaction_event["request"]["data"].keys() == PARSED_FORM.keys()
assert transaction_event["request"]["data"]["username"] == PARSED_FORM["username"]
assert transaction_event["request"]["data"]["password"] == "[Filtered]"
assert transaction_event["request"]["data"]["photo"] == ""
assert transaction_event["_meta"]["request"]["data"]["photo"] == {
"": {"rem": [["!raw", "x"]]}
}


@pytest.mark.asyncio
async def test_request_body_too_big(sentry_init, capture_events):
sentry_init(
traces_sample_rate=1.0,
send_default_pii=True,
integrations=[StarletteIntegration()],
)

app = fastapi_app_factory()
client = TestClient(app)

events = capture_events()

client.post(
"/body/form",
data=BODY_FORM.encode("utf-8"),
headers={
"content-type": "multipart/form-data; boundary=fd721ef49ea403a6",
"cookie": "yummy_cookie=choco; tasty_cookie=strawberry",
},
)

(event, transaction_event) = events
assert event["request"]["cookies"] == {
"tasty_cookie": "strawberry",
"yummy_cookie": "choco",
}
# Because request is too big only the AnnotatedValue is extracted.
assert event["_meta"]["request"]["data"] == {"": {"rem": [["!config", "x"]]}}

assert transaction_event["request"]["cookies"] == {
"tasty_cookie": "strawberry",
"yummy_cookie": "choco",
}
# Because request is too big only the AnnotatedValue is extracted.
assert transaction_event["_meta"]["request"]["data"] == {
"": {"rem": [["!config", "x"]]}
}


@pytest.mark.asyncio
async def test_response(sentry_init, capture_events):
# FastAPI is heavily based on Starlette so we also need
Expand Down
Loading