Skip to content

Commit 9696f13

Browse files
authored
Merge pull request #14 from TheColonyCC/fix/unfollow-and-integration-tests
2 parents fe87938 + a43ed77 commit 9696f13

4 files changed

Lines changed: 98 additions & 13 deletions

File tree

src/colony_sdk/client.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -388,23 +388,20 @@ def update_profile(self, **fields: str) -> dict:
388388
# ── Following ────────────────────────────────────────────────────
389389

390390
def follow(self, user_id: str) -> dict:
391-
"""Follow a user. If already following, this unfollows them (toggle).
391+
"""Follow a user.
392392
393393
Args:
394-
user_id: The UUID of the user to follow/unfollow.
394+
user_id: The UUID of the user to follow.
395395
"""
396396
return self._raw_request("POST", f"/users/{user_id}/follow")
397397

398398
def unfollow(self, user_id: str) -> dict:
399399
"""Unfollow a user.
400400
401-
This is an alias for :meth:`follow` since the API toggles the
402-
follow state. Provided for readability.
403-
404401
Args:
405402
user_id: The UUID of the user to unfollow.
406403
"""
407-
return self.follow(user_id)
404+
return self._raw_request("DELETE", f"/users/{user_id}/follow")
408405

409406
# ── Notifications ───────────────────────────────────────────────
410407

tests/test_api_methods.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,35 @@ def test_update_profile(self, mock_urlopen: MagicMock) -> None:
686686
assert body == {"bio": "New bio", "lightning_address": "me@getalby.com"}
687687

688688

689+
# ---------------------------------------------------------------------------
690+
# Following
691+
# ---------------------------------------------------------------------------
692+
693+
694+
class TestFollowing:
695+
@patch("colony_sdk.client.urlopen")
696+
def test_follow(self, mock_urlopen: MagicMock) -> None:
697+
mock_urlopen.return_value = _mock_response({"status": "following"})
698+
client = _authed_client()
699+
700+
client.follow("u1")
701+
702+
req = _last_request(mock_urlopen)
703+
assert req.get_method() == "POST"
704+
assert req.full_url == f"{BASE}/users/u1/follow"
705+
706+
@patch("colony_sdk.client.urlopen")
707+
def test_unfollow(self, mock_urlopen: MagicMock) -> None:
708+
mock_urlopen.return_value = _mock_response({})
709+
client = _authed_client()
710+
711+
client.unfollow("u1")
712+
713+
req = _last_request(mock_urlopen)
714+
assert req.get_method() == "DELETE"
715+
assert req.full_url == f"{BASE}/users/u1/follow"
716+
717+
689718
# ---------------------------------------------------------------------------
690719
# Notifications
691720
# ---------------------------------------------------------------------------

tests/test_client.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,11 @@ def test_follow_calls_correct_endpoint():
118118
assert callable(client.follow)
119119

120120

121-
def test_unfollow_aliases_follow():
122-
"""unfollow() should be an alias for follow()."""
121+
def test_unfollow_is_separate_method():
122+
"""unfollow() should be a distinct method from follow()."""
123123
client = ColonyClient("col_test")
124+
assert callable(client.unfollow)
124125
assert client.unfollow.__func__ is not client.follow.__func__
125-
# But unfollow delegates to follow internally — check source
126-
import inspect
127-
128-
source = inspect.getsource(client.unfollow)
129-
assert "self.follow(user_id)" in source
130126

131127

132128
def test_api_error_exported():

tests/test_integration_follow.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Integration tests for follow/unfollow endpoints.
2+
3+
These tests hit the real Colony API and require a valid API key.
4+
5+
Run with:
6+
COLONY_TEST_API_KEY=col_xxx pytest tests/test_integration_follow.py -v
7+
8+
Skipped automatically when the env var is not set.
9+
"""
10+
11+
import contextlib
12+
import os
13+
import sys
14+
from pathlib import Path
15+
16+
import pytest
17+
18+
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
19+
20+
from colony_sdk import ColonyAPIError, ColonyClient
21+
22+
API_KEY = os.environ.get("COLONY_TEST_API_KEY")
23+
# ColonistOne's user ID on thecolony.cc
24+
COLONIST_ONE_ID = "324ab98e-955c-4274-bd30-8570cbdf58f1"
25+
26+
pytestmark = pytest.mark.skipif(not API_KEY, reason="set COLONY_TEST_API_KEY to run")
27+
28+
29+
@pytest.fixture
30+
def client() -> ColonyClient:
31+
assert API_KEY is not None
32+
return ColonyClient(API_KEY)
33+
34+
35+
class TestFollowIntegration:
36+
def test_follow_unfollow_lifecycle(self, client: ColonyClient) -> None:
37+
"""Follow a user, then unfollow them."""
38+
# Ensure we start unfollowed (ignore errors if already unfollowed)
39+
with contextlib.suppress(ColonyAPIError):
40+
client.unfollow(COLONIST_ONE_ID)
41+
42+
# Follow
43+
result = client.follow(COLONIST_ONE_ID)
44+
assert result.get("status") == "following"
45+
46+
try:
47+
# Following again should fail with 409
48+
with pytest.raises(ColonyAPIError) as exc_info:
49+
client.follow(COLONIST_ONE_ID)
50+
assert exc_info.value.status == 409
51+
finally:
52+
# Unfollow (cleanup)
53+
client.unfollow(COLONIST_ONE_ID)
54+
55+
def test_unfollow_not_following_raises(self, client: ColonyClient) -> None:
56+
"""Unfollowing a user you don't follow should raise an error."""
57+
# Ensure we're not following
58+
with contextlib.suppress(ColonyAPIError):
59+
client.unfollow(COLONIST_ONE_ID)
60+
61+
with pytest.raises(ColonyAPIError) as exc_info:
62+
client.unfollow(COLONIST_ONE_ID)
63+
assert exc_info.value.status in (404, 409)

0 commit comments

Comments
 (0)