Files

59 lines
1.9 KiB
Python
Raw Permalink Normal View History

"""Per-account / per-host connection caps (refactor §4).
The provider holds a per-account (≤4) and per-host semaphore slot for the whole
lifetime of an open socket, so a worker can never open hundreds of simultaneous
connections to one account or host.
"""
from __future__ import annotations
import asyncio
import pytest
from app.providers.imap_smtp.provider import _PER_ACCOUNT_CONN_CAP, ImapSmtpProvider, ProviderConfig
def _cfg(username: str, host: str) -> ProviderConfig:
return ProviderConfig(
imap_host=host,
imap_port=993,
imap_tls="ssl",
smtp_host=host,
smtp_port=465,
smtp_tls="ssl",
username=username,
password="pw",
)
async def test_per_account_connection_cap() -> None:
cfg = _cfg("user@example.com", "imap.example.com")
providers = [ImapSmtpProvider(cfg) for _ in range(_PER_ACCOUNT_CONN_CAP + 1)]
# Fill the account's slots (host cap defaults to 50, so account is the limiter).
for provider in providers[:_PER_ACCOUNT_CONN_CAP]:
await provider._acquire_conn_slots()
# The next acquire blocks until a slot frees.
with pytest.raises(TimeoutError):
await asyncio.wait_for(providers[-1]._acquire_conn_slots(), timeout=0.2)
providers[0]._release_conn_slots()
await asyncio.wait_for(providers[-1]._acquire_conn_slots(), timeout=0.5) # now succeeds
for provider in providers[1:]:
provider._release_conn_slots()
async def test_distinct_accounts_do_not_share_slots() -> None:
# Saturating account A must not block account B (independent semaphores).
a = [ImapSmtpProvider(_cfg("a@example.com", "imap.example.com")) for _ in range(4)]
for provider in a:
await provider._acquire_conn_slots()
b = ImapSmtpProvider(_cfg("b@example.com", "imap.example.com"))
await asyncio.wait_for(b._acquire_conn_slots(), timeout=0.5)
b._release_conn_slots()
for provider in a:
provider._release_conn_slots()