Skip to content

Commit fa67e36

Browse files
test(fastapi): Verify request info capture with POST endpoints (#6287)
Adapt POST request tests with JSON and FormData bodies from the Starlette integration test suite.
1 parent f655e66 commit fa67e36

2 files changed

Lines changed: 153 additions & 1 deletion

File tree

20.5 KB
Loading

tests/integrations/fastapi/test_fastapi.py

Lines changed: 153 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
import base64
12
import json
23
import logging
4+
import os
35
import threading
46
import warnings
57
from unittest import mock
68

79
import fastapi
810
import pytest
911
import starlette
10-
from fastapi import FastAPI, HTTPException, Request
12+
from fastapi import Body, FastAPI, File, Form, HTTPException, Request, UploadFile
1113
from fastapi.middleware.trustedhost import TrustedHostMiddleware
1214
from fastapi.testclient import TestClient
1315

@@ -22,6 +24,28 @@
2224
FASTAPI_VERSION = parse_version(fastapi.__version__)
2325
STARLETTE_VERSION = parse_version(starlette.__version__)
2426

27+
PICTURE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "photo.jpg")
28+
29+
BODY_JSON = {"some": "json", "for": "testing", "nested": {"numbers": 123}}
30+
31+
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(
32+
"{{image_data}}", str(base64.b64encode(open(PICTURE, "rb").read()))
33+
)
34+
35+
PARSED_FORM = starlette.datastructures.FormData(
36+
[
37+
("username", "Jane"),
38+
("password", "hello123"),
39+
(
40+
"photo",
41+
starlette.datastructures.UploadFile(
42+
filename="photo.jpg",
43+
file=open(PICTURE, "rb"),
44+
),
45+
),
46+
]
47+
)
48+
2549
from tests.integrations.conftest import parametrize_test_configurable_status_codes
2650
from tests.integrations.starlette import test_starlette
2751

@@ -70,9 +94,137 @@ async def _thread_ids_async():
7094
"active": str(threading.current_thread().ident),
7195
}
7296

97+
@app.post("/body/json")
98+
async def body_json(payload: dict = Body(...)):
99+
capture_message("hi")
100+
return {"status": "ok"}
101+
102+
@app.post("/body/form")
103+
async def body_form(
104+
username: str = Form(...),
105+
password: str = Form(...),
106+
photo: UploadFile = File(...),
107+
):
108+
capture_message("hi")
109+
return {"status": "ok"}
110+
73111
return app
74112

75113

114+
@pytest.mark.asyncio
115+
async def test_request_info_json_body(sentry_init, capture_events):
116+
sentry_init(
117+
traces_sample_rate=1.0,
118+
send_default_pii=True,
119+
integrations=[StarletteIntegration()],
120+
)
121+
122+
app = fastapi_app_factory()
123+
client = TestClient(app)
124+
125+
events = capture_events()
126+
127+
client.post(
128+
"/body/json",
129+
json=BODY_JSON,
130+
headers={
131+
"cookie": "yummy_cookie=choco; tasty_cookie=strawberry",
132+
},
133+
)
134+
135+
(event, transaction_event) = events
136+
137+
assert event["request"]["cookies"] == {
138+
"tasty_cookie": "strawberry",
139+
"yummy_cookie": "choco",
140+
}
141+
assert event["request"]["data"] == BODY_JSON
142+
143+
assert transaction_event["request"]["cookies"] == {
144+
"tasty_cookie": "strawberry",
145+
"yummy_cookie": "choco",
146+
}
147+
assert transaction_event["request"]["data"] == BODY_JSON
148+
149+
150+
@pytest.mark.asyncio
151+
async def test_formdata_request_body(sentry_init, capture_events):
152+
sentry_init(
153+
traces_sample_rate=1.0,
154+
send_default_pii=True,
155+
max_request_body_size="always",
156+
integrations=[StarletteIntegration()],
157+
)
158+
159+
app = fastapi_app_factory()
160+
client = TestClient(app)
161+
162+
events = capture_events()
163+
164+
client.post(
165+
"/body/form",
166+
data=BODY_FORM.encode("utf-8"),
167+
headers={
168+
"content-type": "multipart/form-data; boundary=fd721ef49ea403a6",
169+
},
170+
)
171+
172+
(event, transaction_event) = events
173+
assert event["request"]["data"].keys() == PARSED_FORM.keys()
174+
assert event["request"]["data"]["username"] == PARSED_FORM["username"]
175+
assert event["request"]["data"]["password"] == "[Filtered]"
176+
assert event["request"]["data"]["photo"] == ""
177+
assert event["_meta"]["request"]["data"]["photo"] == {"": {"rem": [["!raw", "x"]]}}
178+
179+
assert transaction_event["request"]["data"].keys() == PARSED_FORM.keys()
180+
assert transaction_event["request"]["data"]["username"] == PARSED_FORM["username"]
181+
assert transaction_event["request"]["data"]["password"] == "[Filtered]"
182+
assert transaction_event["request"]["data"]["photo"] == ""
183+
assert transaction_event["_meta"]["request"]["data"]["photo"] == {
184+
"": {"rem": [["!raw", "x"]]}
185+
}
186+
187+
188+
@pytest.mark.asyncio
189+
async def test_request_body_too_big(sentry_init, capture_events):
190+
sentry_init(
191+
traces_sample_rate=1.0,
192+
send_default_pii=True,
193+
integrations=[StarletteIntegration()],
194+
)
195+
196+
app = fastapi_app_factory()
197+
client = TestClient(app)
198+
199+
events = capture_events()
200+
201+
client.post(
202+
"/body/form",
203+
data=BODY_FORM.encode("utf-8"),
204+
headers={
205+
"content-type": "multipart/form-data; boundary=fd721ef49ea403a6",
206+
"cookie": "yummy_cookie=choco; tasty_cookie=strawberry",
207+
},
208+
)
209+
210+
(event, transaction_event) = events
211+
assert event["request"]["cookies"] == {
212+
"tasty_cookie": "strawberry",
213+
"yummy_cookie": "choco",
214+
}
215+
# Because request is too big only the AnnotatedValue is extracted.
216+
assert event["_meta"]["request"]["data"] == {"": {"rem": [["!config", "x"]]}}
217+
218+
assert transaction_event["request"]["cookies"] == {
219+
"tasty_cookie": "strawberry",
220+
"yummy_cookie": "choco",
221+
}
222+
# Because request is too big only the AnnotatedValue is extracted.
223+
assert transaction_event["_meta"]["request"]["data"] == {
224+
"": {"rem": [["!config", "x"]]}
225+
}
226+
227+
76228
@pytest.mark.asyncio
77229
async def test_response(sentry_init, capture_events):
78230
# FastAPI is heavily based on Starlette so we also need

0 commit comments

Comments
 (0)