perf: isolate authentication database work (#311)
This commit is contained in:
1 parent
9189d13db9
commit
d026bbd400
2 files changed
+170
-5
No files matched your search
@@ -430,6 +430,171 @@ async def test_session_registry_latency_does_not_block_the_event_loop(access_con
|
||||
assert heartbeat_elapsed < 0.08
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_sign_in_throttle_lookup_does_not_block_the_event_loop(
|
||||
access_control, monkeypatch
|
||||
):
|
||||
class SlowAttempts:
|
||||
def retry_after(self, source):
|
||||
time.sleep(0.15)
|
||||
return 17
|
||||
|
||||
monkeypatch.setattr(main, "_login_attempt_store", lambda: SlowAttempts())
|
||||
transport = httpx.ASGITransport(app=main.app, client=("203.0.113.10", 1234))
|
||||
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
|
||||
sign_in = asyncio.create_task(
|
||||
client.post("/api/v1/session", json={"access_token": "wrong"})
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
started = time.perf_counter()
|
||||
await asyncio.sleep(0.01)
|
||||
heartbeat_elapsed = time.perf_counter() - started
|
||||
response = await sign_in
|
||||
|
||||
assert response.status_code == 429
|
||||
assert response.headers["retry-after"] == "17"
|
||||
assert heartbeat_elapsed < 0.08
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_failed_sign_in_recording_does_not_block_the_event_loop(
|
||||
access_control, monkeypatch
|
||||
):
|
||||
class SlowAttempts:
|
||||
def retry_after(self, source):
|
||||
time.sleep(0.02)
|
||||
return 0
|
||||
|
||||
def record_failure(self, source):
|
||||
time.sleep(0.15)
|
||||
|
||||
monkeypatch.setattr(main, "_login_attempt_store", lambda: SlowAttempts())
|
||||
transport = httpx.ASGITransport(app=main.app, client=("203.0.113.11", 1234))
|
||||
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
|
||||
sign_in = asyncio.create_task(
|
||||
client.post("/api/v1/session", json={"access_token": "wrong"})
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
started = time.perf_counter()
|
||||
await asyncio.sleep(0.04)
|
||||
heartbeat_elapsed = time.perf_counter() - started
|
||||
response = await sign_in
|
||||
|
||||
assert response.status_code == 401
|
||||
assert heartbeat_elapsed < 0.08
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_successful_sign_in_throttle_clear_does_not_block_the_event_loop(
|
||||
access_control, monkeypatch
|
||||
):
|
||||
class SlowAttempts:
|
||||
def retry_after(self, source):
|
||||
time.sleep(0.02)
|
||||
return 0
|
||||
|
||||
def clear(self, source):
|
||||
time.sleep(0.15)
|
||||
|
||||
monkeypatch.setattr(main, "_login_attempt_store", lambda: SlowAttempts())
|
||||
transport = httpx.ASGITransport(app=main.app, client=("203.0.113.12", 1234))
|
||||
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
|
||||
sign_in = asyncio.create_task(
|
||||
client.post(
|
||||
"/api/v1/session",
|
||||
json={"access_token": "correct horse battery staple"},
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
started = time.perf_counter()
|
||||
await asyncio.sleep(0.04)
|
||||
heartbeat_elapsed = time.perf_counter() - started
|
||||
response = await sign_in
|
||||
|
||||
assert response.status_code == 200
|
||||
assert heartbeat_elapsed < 0.08
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_session_activation_does_not_block_the_event_loop(
|
||||
access_control, monkeypatch
|
||||
):
|
||||
class Attempts:
|
||||
def retry_after(self, source):
|
||||
time.sleep(0.02)
|
||||
return 0
|
||||
|
||||
def clear(self, source):
|
||||
return None
|
||||
|
||||
class SlowStore:
|
||||
def activate(self, session_id, expires_at):
|
||||
time.sleep(0.15)
|
||||
|
||||
monkeypatch.setattr(main, "_login_attempt_store", lambda: Attempts())
|
||||
monkeypatch.setattr(main.dashboard_auth, "_session_store", lambda now=None: SlowStore())
|
||||
transport = httpx.ASGITransport(app=main.app, client=("203.0.113.13", 1234))
|
||||
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
|
||||
sign_in = asyncio.create_task(
|
||||
client.post(
|
||||
"/api/v1/session",
|
||||
json={"access_token": "correct horse battery staple"},
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
started = time.perf_counter()
|
||||
await asyncio.sleep(0.04)
|
||||
heartbeat_elapsed = time.perf_counter() - started
|
||||
response = await sign_in
|
||||
|
||||
assert response.status_code == 200
|
||||
assert any(
|
||||
"stackchain_session=" in value
|
||||
for value in response.headers.get_list("set-cookie")
|
||||
)
|
||||
assert heartbeat_elapsed < 0.08
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_single_session_revocation_does_not_block_the_event_loop(
|
||||
access_control, monkeypatch
|
||||
):
|
||||
transport = httpx.ASGITransport(app=main.app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="https://test") as client:
|
||||
await client.post(
|
||||
"/api/v1/session",
|
||||
json={"access_token": "correct horse battery staple"},
|
||||
)
|
||||
csrf = client.cookies["stackchain_csrf"]
|
||||
|
||||
class SlowStore:
|
||||
def is_active(self, session_id, expires_at):
|
||||
time.sleep(0.02)
|
||||
return True
|
||||
|
||||
def revoke(self, session_id):
|
||||
time.sleep(0.15)
|
||||
|
||||
monkeypatch.setattr(
|
||||
main.dashboard_auth, "_session_store", lambda now=None: SlowStore()
|
||||
)
|
||||
sign_out = asyncio.create_task(
|
||||
client.delete(
|
||||
"/api/v1/session",
|
||||
headers={"Origin": "https://test", "X-CSRF-Token": csrf},
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
started = time.perf_counter()
|
||||
await asyncio.sleep(0.04)
|
||||
heartbeat_elapsed = time.perf_counter() - started
|
||||
response = await sign_out
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["authenticated"] is False
|
||||
assert heartbeat_elapsed < 0.08
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_session_registry_read_failure_fails_closed_before_gitea(
|
||||
access_control, monkeypatch
|
||||
|
||||
Reference in new issue
Block a user