Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 55 additions & 7 deletions tests/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import datetime
import functools
import inspect
import time
from abc import ABC

Expand Down Expand Up @@ -52,14 +54,19 @@ def _is_transient_error(exc: Exception) -> bool:
if isinstance(exc, (httpx.ReadTimeout, httpx.ConnectTimeout, httpx.ConnectError)):
return True
if isinstance(exc, StreamAPIException):
body = ""
try:
body = exc.http_response.text or ""
except Exception:
pass
if "upstream connect error" in body or "disconnect" in body:
if exc.status_code in (429, 502, 503, 504):
return True
if exc.status_code in (502, 503, 504):
msg = str(exc).lower()
if any(
phrase in msg
for phrase in (
"upstream connect error",
"disconnect",
"maximum number of",
"rate limit",
"too many",
)
):
return True
return False

Expand All @@ -85,3 +92,44 @@ def wrapper(*args, **kwargs):
return wrapper

return decorator


_BUILTIN_CHANNEL_TYPES = frozenset(
{"messaging", "livestream", "team", "gaming", "commerce"}
)
_STALE_THRESHOLD = datetime.timedelta(minutes=2)


def cleanup_channel_types(func):
"""Decorator that deletes stale test channel types before the test runs.

Frees slots toward the 50-type limit without interfering with parallel
runners (only removes types older than 2 minutes). Expects a ``client``
pytest fixture parameter.
"""

@functools.wraps(func)
def wrapper(*args, **kwargs):
# Resolve 'client' from pytest kwargs or positional args
client = kwargs.get("client")
if client is None:
sig = inspect.signature(func)
params = list(sig.parameters)
idx = params.index("client") if "client" in params else 0
client = args[idx]

now = datetime.datetime.now(datetime.timezone.utc)
resp = client.chat.list_channel_types()
for name, config in resp.data.channel_types.items():
if name in _BUILTIN_CHANNEL_TYPES:
continue
if config and (now - config.created_at) > _STALE_THRESHOLD:
try:
client.chat.delete_channel_type(name=name)
except Exception:
pass
time.sleep(2)

return func(*args, **kwargs)

return wrapper
10 changes: 10 additions & 0 deletions tests/test_chat_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
QueryFutureChannelBansPayload,
SortParamRequest,
)
from tests.base import cleanup_channel_types, retry_on_transient_error


def test_get_app_settings(client: Stream):
Expand Down Expand Up @@ -389,6 +390,8 @@ def test_query_future_channel_bans(client: Stream, random_users):
pass


@retry_on_transient_error()
@cleanup_channel_types
def test_create_channel_type(client: Stream):
"""Create a channel type with custom settings."""
type_name = f"testtype{uuid.uuid4().hex[:8]}"
Expand All @@ -413,6 +416,8 @@ def test_create_channel_type(client: Stream):
pass


@retry_on_transient_error()
@cleanup_channel_types
def test_update_channel_type_mark_messages_pending(client: Stream):
"""Update a channel type with mark_messages_pending=True."""
type_name = f"testtype{uuid.uuid4().hex[:8]}"
Expand Down Expand Up @@ -445,6 +450,8 @@ def test_update_channel_type_mark_messages_pending(client: Stream):
pass


@retry_on_transient_error()
@cleanup_channel_types
def test_update_channel_type_push_notifications(client: Stream):
"""Update a channel type with push_notifications=False."""
type_name = f"testtype{uuid.uuid4().hex[:8]}"
Expand Down Expand Up @@ -477,6 +484,8 @@ def test_update_channel_type_push_notifications(client: Stream):
pass


@retry_on_transient_error()
@cleanup_channel_types
def test_delete_channel_type(client: Stream):
"""Create and delete a channel type with retry."""
type_name = f"testdeltype{uuid.uuid4().hex[:8]}"
Expand Down Expand Up @@ -540,6 +549,7 @@ def test_get_rate_limits_specific_endpoints(client: Stream):
assert info.remaining >= 0


@retry_on_transient_error()
def test_event_hooks_sqs_sns(client: Stream):
"""Test setting SQS, SNS, and pending_message event hooks."""
# Save original hooks to restore later
Expand Down