fix(bluesky): surface real error instead of misleading "auth failed"

When Cloudflare blocks requests to bsky.social or public.api.bsky.app
with a 403, the error was swallowed by a generic except clause and
reported as "Bluesky auth failed" - misleading users into thinking
their credentials were wrong.

Now _create_session() preserves the specific error in _session_error,
and search_bluesky() surfaces it. Cloudflare 403s get a clear message
about network-level blocks. Actual 401s say "Invalid credentials".

Closes #69

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Van Horn
2026-03-15 22:13:28 -07:00
parent 7ffa508bb4
commit 0c6358ab98
2 changed files with 46 additions and 9 deletions
+24 -3
View File
@@ -126,6 +126,7 @@ class TestCreateSession(unittest.TestCase):
mock_request.side_effect = Exception("connection refused")
token = bluesky._create_session("user.bsky.social", "app-pw")
self.assertIsNone(token)
self.assertIn("connection refused", bluesky._session_error)
@patch("lib.bluesky.http.request")
def test_returns_none_on_missing_jwt(self, mock_request):
@@ -152,12 +153,32 @@ class TestSearchBlueskyAuth(unittest.TestCase):
self.assertIn("not configured", result["error"])
@patch("lib.bluesky.http.request")
def test_auth_failure_returns_error(self, mock_request):
mock_request.side_effect = Exception("401 Unauthorized")
def test_auth_failure_returns_specific_error(self, mock_request):
mock_request.side_effect = Exception("connection refused")
config = {"BSKY_HANDLE": "user.bsky.social", "BSKY_APP_PASSWORD": "pw"}
result = bluesky.search_bluesky("test", "2026-01-01", "2026-03-09", config=config)
self.assertEqual(result["posts"], [])
self.assertIn("auth failed", result["error"])
self.assertIn("connection refused", result["error"])
self.assertNotIn("auth failed", result["error"])
@patch("lib.bluesky.http.request")
def test_cloudflare_403_returns_network_error(self, mock_request):
from lib.http import HTTPError
mock_request.side_effect = HTTPError("HTTP 403: Forbidden", 403, "<html>Cloudflare</html>")
config = {"BSKY_HANDLE": "user.bsky.social", "BSKY_APP_PASSWORD": "pw"}
result = bluesky.search_bluesky("test", "2026-01-01", "2026-03-09", config=config)
self.assertEqual(result["posts"], [])
self.assertIn("Cloudflare", result["error"])
self.assertIn("network", result["error"].lower())
@patch("lib.bluesky.http.request")
def test_401_returns_credentials_error(self, mock_request):
from lib.http import HTTPError
mock_request.side_effect = HTTPError("HTTP 401: Unauthorized", 401, "")
config = {"BSKY_HANDLE": "user.bsky.social", "BSKY_APP_PASSWORD": "pw"}
result = bluesky.search_bluesky("test", "2026-01-01", "2026-03-09", config=config)
self.assertEqual(result["posts"], [])
self.assertIn("Invalid credentials", result["error"])
@patch("lib.bluesky.http.request")
def test_successful_search_passes_bearer(self, mock_request):