Skip to content

Commit 7b98f3a

Browse files
authored
Merge branch 'master' into fix/836-starlette-1.0-compatibility
2 parents 832f057 + 4591d64 commit 7b98f3a

2 files changed

Lines changed: 33 additions & 3 deletions

File tree

setup.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
author_email="support@scoutapm.com",
4949
license="MIT",
5050
zip_safe=False,
51-
python_requires=">=3.8, <4",
51+
python_requires=">=3.10, <4",
5252
packages=packages,
5353
package_dir={str(""): str("src")},
5454
ext_modules=ext_modules,
@@ -83,8 +83,6 @@
8383
"Operating System :: MacOS",
8484
"Operating System :: POSIX",
8585
"Operating System :: POSIX :: Linux",
86-
"Programming Language :: Python :: 3.8",
87-
"Programming Language :: Python :: 3.9",
8886
"Programming Language :: Python :: 3.10",
8987
"Programming Language :: Python :: 3.11",
9088
"Programming Language :: Python :: 3.12",

tests/integration/test_starlette.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ async def crash(request):
5858
async def return_error(request):
5959
return PlainTextResponse("Something went wrong", status_code=503)
6060

61+
async def return_unauthorized(request):
62+
return PlainTextResponse("Unauthorized", status_code=401)
63+
6164
async def background_jobs(request):
6265
def sync_noop():
6366
pass
@@ -85,6 +88,7 @@ async def __call__(self, scope, receive, send):
8588
Route("/sync-hello/", endpoint=SyncHelloEndpoint),
8689
Route("/crash/", endpoint=crash),
8790
Route("/return-error/", endpoint=return_error),
91+
Route("/return-unauthorized/", endpoint=return_unauthorized),
8892
Route("/background-jobs/", endpoint=background_jobs),
8993
Route("/instance-app/", endpoint=InstanceApp()),
9094
]
@@ -474,3 +478,31 @@ async def test_instance_app(tracked_requests):
474478
"Controller/tests.integration.test_starlette."
475479
+ "app_with_scout.<locals>.InstanceApp"
476480
)
481+
482+
483+
@pytest.mark.asyncio
484+
async def test_return_unauthorized_not_tagged_as_error(tracked_requests):
485+
"""
486+
Verify that a 401 Unauthorized response is tracked but NOT tagged as an
487+
error. Only 5xx responses should be tagged as errors. This is the correct
488+
behavior when, for example, a FastAPI OAuth2 dependency rejects an empty
489+
bearer token: the 401 is a normal client error, not a server error.
490+
491+
See: https://github.com/scoutapp/scout_apm_python/issues/838
492+
"""
493+
with app_with_scout() as app:
494+
communicator = ApplicationCommunicator(
495+
app, asgi_http_scope(path="/return-unauthorized/")
496+
)
497+
await communicator.send_input({"type": "http.request"})
498+
response_start = await communicator.receive_output()
499+
await communicator.receive_output()
500+
501+
assert response_start["type"] == "http.response.start"
502+
assert response_start["status"] == 401
503+
assert len(tracked_requests) == 1
504+
tracked_request = tracked_requests[0]
505+
assert len(tracked_request.complete_spans) == 1
506+
assert tracked_request.tags["path"] == "/return-unauthorized/"
507+
# 401 must NOT be tagged as an error — only 5xx responses are errors
508+
assert "error" not in tracked_request.tags

0 commit comments

Comments
 (0)