2 changed files with 95 additions and 33 deletions
@ -0,0 +1,50 @@ |
|||||
|
import sys |
||||
|
import threading |
||||
|
|
||||
|
from fastapi import APIRouter, FastAPI |
||||
|
from fastapi.routing import _IncludedRouter |
||||
|
from fastapi.testclient import TestClient |
||||
|
|
||||
|
N_ROUTES = 120 |
||||
|
N_THREADS = 6 |
||||
|
|
||||
|
|
||||
|
def build_app() -> FastAPI: |
||||
|
app = FastAPI() |
||||
|
router = APIRouter() |
||||
|
for i in range(N_ROUTES): |
||||
|
|
||||
|
def endpoint(i: int = i) -> dict[str, int]: |
||||
|
return {"i": i} |
||||
|
|
||||
|
router.add_api_route(f"/r{i}", endpoint, methods=["GET"]) |
||||
|
app.include_router(router) |
||||
|
return app |
||||
|
|
||||
|
|
||||
|
def test_concurrent_first_requests_do_not_corrupt_candidate_cache() -> None: |
||||
|
app = build_app() |
||||
|
old_interval = sys.getswitchinterval() |
||||
|
sys.setswitchinterval(1e-5) |
||||
|
try: |
||||
|
barrier = threading.Barrier(N_THREADS) |
||||
|
statuses: list[int] = [] |
||||
|
|
||||
|
def worker() -> None: |
||||
|
client = TestClient(app) |
||||
|
barrier.wait() |
||||
|
statuses.append(client.get(f"/r{N_ROUTES - 1}").status_code) |
||||
|
|
||||
|
threads = [threading.Thread(target=worker) for _ in range(N_THREADS)] |
||||
|
for thread in threads: |
||||
|
thread.start() |
||||
|
for thread in threads: |
||||
|
thread.join() |
||||
|
finally: |
||||
|
sys.setswitchinterval(old_interval) |
||||
|
|
||||
|
assert statuses == [200] * N_THREADS |
||||
|
included = next( |
||||
|
route for route in app.router.routes if isinstance(route, _IncludedRouter) |
||||
|
) |
||||
|
assert len(included.effective_candidates()) == N_ROUTES |
||||
Loading…
Reference in new issue