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
31 changes: 29 additions & 2 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ jobs:
STREAM_BASE_URL: ${{ vars.STREAM_BASE_URL }}
STREAM_API_KEY: ${{ vars.STREAM_API_KEY }}
STREAM_API_SECRET: ${{ secrets.STREAM_API_SECRET }}
STREAM_CHAT_API_KEY: ${{ vars.STREAM_CHAT_API_KEY }}
STREAM_CHAT_API_SECRET: ${{ secrets.STREAM_CHAT_API_SECRET }}
STREAM_CHAT_BASE_URL: ${{ vars.STREAM_CHAT_BASE_URL }}
timeout-minutes: 30
steps:
- name: Checkout
Expand All @@ -85,7 +88,31 @@ jobs:
run: |
echo "STREAM_API_KEY is set: ${{ env.STREAM_API_KEY != '' }}"
echo "STREAM_API_SECRET is set: ${{ env.STREAM_API_SECRET != '' }}"
echo "STREAM_CHAT_API_KEY is set: ${{ env.STREAM_CHAT_API_KEY != '' }}"
echo "STREAM_CHAT_API_SECRET is set: ${{ env.STREAM_CHAT_API_SECRET != '' }}"
echo "STREAM_BASE_URL is set: ${{ env.STREAM_BASE_URL != '' }}"
- name: Run tests
run: uv run pytest -m "${{ inputs.marker }}" tests/ getstream/
echo "STREAM_CHAT_BASE_URL is set: ${{ env.STREAM_CHAT_BASE_URL != '' }}"
- name: Run non-video tests
env:
STREAM_API_KEY: ${{ vars.STREAM_CHAT_API_KEY }}
STREAM_API_SECRET: ${{ secrets.STREAM_CHAT_API_SECRET }}
STREAM_BASE_URL: ${{ vars.STREAM_CHAT_BASE_URL }}
run: |
uv run pytest -m "${{ inputs.marker }}" tests/ getstream/ \
--ignore=tests/rtc \
--ignore=tests/test_video_examples.py \
--ignore=tests/test_video_integration.py \
--ignore=tests/test_video_openai.py \
--ignore=tests/test_signaling.py \
--ignore=tests/test_audio_stream_track.py \
--ignore=getstream/video
- name: Run video tests
run: |
uv run pytest -m "${{ inputs.marker }}" \
tests/rtc \
tests/test_video_examples.py \
tests/test_video_integration.py \
tests/test_video_openai.py \
tests/test_signaling.py \
tests/test_audio_stream_track.py

8 changes: 7 additions & 1 deletion getstream/feeds/rest_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,13 +725,19 @@ def add_comments_batch(
def query_comments(
self,
filter: Dict[str, object],
id_around: Optional[str] = None,
limit: Optional[int] = None,
next: Optional[str] = None,
prev: Optional[str] = None,
sort: Optional[str] = None,
) -> StreamResponse[QueryCommentsResponse]:
json = QueryCommentsRequest(
filter=filter, limit=limit, next=next, prev=prev, sort=sort
filter=filter,
id_around=id_around,
limit=limit,
next=next,
prev=prev,
sort=sort,
).to_dict()
return self.post(
"/api/v2/feeds/comments/query", QueryCommentsResponse, json=json
Expand Down
6 changes: 5 additions & 1 deletion getstream/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1787,7 +1787,8 @@ class AsyncExportErrorEvent(DataClassJsonMixin):
task_id: str = dc_field(metadata=dc_config(field_name="task_id"))
custom: Dict[str, object] = dc_field(metadata=dc_config(field_name="custom"))
type: str = dc_field(
default="export.moderation_logs.error", metadata=dc_config(field_name="type")
default="export.bulk_image_moderation.error",
metadata=dc_config(field_name="type"),
)
received_at: Optional[datetime] = dc_field(
default=None,
Expand Down Expand Up @@ -16024,6 +16025,9 @@ class QueryCommentReactionsResponse(DataClassJsonMixin):
@dataclass
class QueryCommentsRequest(DataClassJsonMixin):
filter: Dict[str, object] = dc_field(metadata=dc_config(field_name="filter"))
id_around: Optional[str] = dc_field(
default=None, metadata=dc_config(field_name="id_around")
)
limit: Optional[int] = dc_field(
default=None, metadata=dc_config(field_name="limit")
)
Expand Down
Binary file added tests/assets/test_upload.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions tests/assets/test_upload.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello world test file content
6 changes: 2 additions & 4 deletions tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,8 @@ def wait_for_task(client, task_id, timeout_ms=10000, poll_interval_ms=1000):
start_time = time.time() * 1000 # Convert to milliseconds
while True:
response = client.get_task(id=task_id)
if response.data.status == "completed":
if response.data.status in ("completed", "failed"):
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait_for_task silently returns on task failure

Low Severity

wait_for_task now returns successfully when status is "failed", treating it the same as "completed". While current callers happen to assert status == "completed" afterward, any future caller that simply uses the return value without checking the status will silently accept a failed task. The function name and docstring imply it waits for successful completion, creating a subtle contract mismatch.

Fix in Cursor Fix in Web

return response
if (time.time() * 1000) - start_time > timeout_ms:
raise TimeoutError(
f"Task {task_id} did not complete within {timeout_ms} seconds"
)
raise TimeoutError(f"Task {task_id} did not complete within {timeout_ms}ms")
time.sleep(poll_interval_ms / 1000.0)
75 changes: 75 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import functools
import uuid
import pytest
import os
from dotenv import load_dotenv
Expand All @@ -12,6 +13,9 @@
async_client,
)

from getstream import Stream
from getstream.models import UserRequest, ChannelInput

__all__ = [
"client",
"call",
Expand All @@ -20,9 +24,80 @@
"test_feed",
"get_feed",
"async_client",
"channel",
"random_user",
"random_users",
"server_user",
]


@pytest.fixture
def random_user(client: Stream):
user_id = str(uuid.uuid4())
response = client.update_users(
users={user_id: UserRequest(id=user_id, name=user_id)}
)
assert user_id in response.data.users
yield response.data.users[user_id]
try:
client.delete_users(
user_ids=[user_id], user="hard", conversations="hard", messages="hard"
)
except Exception:
pass


@pytest.fixture
def random_users(client: Stream):
users = []
user_ids = []
for _ in range(3):
uid = str(uuid.uuid4())
user_ids.append(uid)
users.append(UserRequest(id=uid, name=uid))
response = client.update_users(users={u.id: u for u in users})
yield [response.data.users[uid] for uid in user_ids]
try:
client.delete_users(
user_ids=user_ids, user="hard", conversations="hard", messages="hard"
)
except Exception:
pass


@pytest.fixture
def server_user(client: Stream):
user_id = str(uuid.uuid4())
response = client.update_users(
users={user_id: UserRequest(id=user_id, name="server-admin")}
)
assert user_id in response.data.users
yield response.data.users[user_id]
try:
client.delete_users(
user_ids=[user_id], user="hard", conversations="hard", messages="hard"
)
except Exception:
pass


@pytest.fixture
def channel(client: Stream, random_user):
channel_id = str(uuid.uuid4())
ch = client.chat.channel("messaging", channel_id)
ch.get_or_create(
data=ChannelInput(
created_by_id=random_user.id,
custom={"test": True, "language": "python"},
)
)
yield ch
try:
client.chat.delete_channels(cids=[f"messaging:{channel_id}"], hard_delete=True)
except Exception:
pass


@pytest.fixture(scope="session", autouse=True)
def load_env():
load_dotenv()
Expand Down
Loading
Loading