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
25 changes: 24 additions & 1 deletion tests/tools/test_skills_hub_clawhub.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
#!/usr/bin/env python3

import io
import unittest
import zipfile
from unittest.mock import patch

import httpx

from tools.skills_hub import ClawHubSource, SkillMeta


class _MockResponse:
def __init__(self, status_code=200, json_data=None, text="", headers=None):
def __init__(self, status_code=200, json_data=None, text="", headers=None, content=b""):
self.status_code = status_code
self._json_data = json_data
self.text = text
self.headers = headers or {}
self.content = content

def json(self):
return self._json_data
Expand Down Expand Up @@ -264,6 +269,24 @@ def side_effect(url, *args, **kwargs):
self.assertIsNotNone(bundle)
self.assertEqual(bundle.files["SKILL.md"], "# Skill")

@patch("tools.skills_hub.time.sleep")
@patch("tools.skills_hub.httpx.get")
def test_download_zip_retries_http_error_before_success(self, mock_get, mock_sleep):
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, "w") as zf:
zf.writestr("SKILL.md", "# Skill")

mock_get.side_effect = [
httpx.ConnectTimeout("temporary timeout"),
_MockResponse(status_code=200, content=zip_buffer.getvalue()),
]

files = self.src._download_zip("caldav-calendar", "1.0.1")

self.assertEqual(files, {"SKILL.md": "# Skill"})
self.assertEqual(mock_get.call_count, 2)
mock_sleep.assert_called_once_with(1)

@patch("tools.skills_hub.check_website_access", return_value=None)
@patch("tools.skills_hub.is_safe_url")
@patch("tools.skills_hub.httpx.get")
Expand Down
11 changes: 11 additions & 0 deletions tools/skills_hub.py
Original file line number Diff line number Diff line change
Expand Up @@ -2587,6 +2587,17 @@ def _download_zip(self, slug: str, version: str) -> Dict[str, str]:
logger.warning("ClawHub returned invalid ZIP for %s v%s", slug, version)
return files
except httpx.HTTPError as exc:
if attempt < max_retries - 1:
logger.debug(
"ClawHub ZIP download failed for %s v%s, retrying (attempt %d/%d): %s",
slug,
version,
attempt + 1,
max_retries,
exc,
)
time.sleep(1)
continue
logger.debug("ClawHub ZIP download failed for %s v%s: %s", slug, version, exc)
return files

Expand Down