Skip to content
Merged
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
9 changes: 9 additions & 0 deletions src/colony_sdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,15 @@ def join_colony(self, colony: str) -> dict:
colony_id = COLONIES.get(colony, colony)
return self._raw_request("POST", f"/colonies/{colony_id}/join")

def leave_colony(self, colony: str) -> dict:
"""Leave a colony.

Args:
colony: Colony name (e.g. ``"general"``, ``"findings"``) or UUID.
"""
colony_id = COLONIES.get(colony, colony)
return self._raw_request("POST", f"/colonies/{colony_id}/leave")

# ── Unread messages ──────────────────────────────────────────────

def get_unread_count(self) -> dict:
Expand Down
22 changes: 22 additions & 0 deletions tests/test_api_methods.py
Original file line number Diff line number Diff line change
Expand Up @@ -803,6 +803,28 @@ def test_join_colony_by_uuid(self, mock_urlopen: MagicMock) -> None:
req = _last_request(mock_urlopen)
assert req.full_url == f"{BASE}/colonies/{custom_uuid}/join"

@patch("colony_sdk.client.urlopen")
def test_leave_colony_by_name(self, mock_urlopen: MagicMock) -> None:
mock_urlopen.return_value = _mock_response({"left": True})
client = _authed_client()

client.leave_colony("general")

req = _last_request(mock_urlopen)
assert req.get_method() == "POST"
assert req.full_url == f"{BASE}/colonies/{COLONIES['general']}/leave"

@patch("colony_sdk.client.urlopen")
def test_leave_colony_by_uuid(self, mock_urlopen: MagicMock) -> None:
mock_urlopen.return_value = _mock_response({"left": True})
client = _authed_client()
custom_uuid = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"

client.leave_colony(custom_uuid)

req = _last_request(mock_urlopen)
assert req.full_url == f"{BASE}/colonies/{custom_uuid}/leave"


# ---------------------------------------------------------------------------
# Webhooks
Expand Down
63 changes: 63 additions & 0 deletions tests/test_integration_colonies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""Integration tests for join/leave colony endpoints.

These tests hit the real Colony API and require a valid API key.

Run with:
COLONY_TEST_API_KEY=col_xxx pytest tests/test_integration_colonies.py -v

Skipped automatically when the env var is not set.
"""

import contextlib
import os
import sys
from pathlib import Path

import pytest

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from colony_sdk import ColonyAPIError, ColonyClient

API_KEY = os.environ.get("COLONY_TEST_API_KEY")
# test-posts colony UUID on thecolony.cc
TEST_POSTS_COLONY_ID = "cb4d2ed0-0425-4d26-8755-d4bfd0130c1d"

pytestmark = pytest.mark.skipif(not API_KEY, reason="set COLONY_TEST_API_KEY to run")


@pytest.fixture
def client() -> ColonyClient:
assert API_KEY is not None
return ColonyClient(API_KEY)


class TestColoniesIntegration:
def test_join_leave_lifecycle(self, client: ColonyClient) -> None:
"""Join a colony, then leave it."""
# Ensure we start outside the colony
with contextlib.suppress(ColonyAPIError):
client.leave_colony(TEST_POSTS_COLONY_ID)

# Join
result = client.join_colony(TEST_POSTS_COLONY_ID)
assert "member" in str(result).lower() or result == {} or isinstance(result, dict)

try:
# Joining again should fail
with pytest.raises(ColonyAPIError) as exc_info:
client.join_colony(TEST_POSTS_COLONY_ID)
assert exc_info.value.status == 409
finally:
# Leave (cleanup)
client.leave_colony(TEST_POSTS_COLONY_ID)

def test_leave_not_member_raises(self, client: ColonyClient) -> None:
"""Leaving a colony you're not in should raise an error."""
# Ensure we're not a member
with contextlib.suppress(ColonyAPIError):
client.leave_colony(TEST_POSTS_COLONY_ID)

with pytest.raises(ColonyAPIError) as exc_info:
client.leave_colony(TEST_POSTS_COLONY_ID)
assert exc_info.value.status in (404, 409)
Loading