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
23 changes: 19 additions & 4 deletions tests/test_wavey_gist.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import requests

from utils.wavey_gist import upload_to_gist
from utils.wavey_gist import DEFAULT_GIST_TITLE, PRIMARY_FILE_NAME, upload_to_gist


def _mock_response(*, post_json: dict | None = None) -> MagicMock:
Expand All @@ -32,7 +32,11 @@ def test_successful_upload(self, mock_post: MagicMock) -> None:

payload = mock_post.call_args[1]["json"]
self.assertEqual(payload["title"], "Test")
self.assertEqual(payload["markdown"], "# Test\n\nSome **markdown**")
# Wavey Gist now expects a `files` snapshot — the legacy `markdown` field
# is rejected with HTTP 400. README.md is the preferred primary filename
# (https://gist.wavey.info/llms.txt).
self.assertNotIn("markdown", payload)
self.assertEqual(payload["files"][PRIMARY_FILE_NAME]["content"], "# Test\n\nSome **markdown**")
self.assertEqual(mock_post.call_args[1]["headers"]["Authorization"], "Bearer test-key")

@patch.dict("utils.wavey_gist.os.environ", {"WAVEY_GIST_API_KEY": "test-key"})
Expand All @@ -42,8 +46,8 @@ def test_no_title_sends_raw_content(self, mock_post: MagicMock) -> None:

upload_to_gist("Content only")
payload = mock_post.call_args[1]["json"]
self.assertEqual(payload["title"], "Monitoring Details")
self.assertEqual(payload["markdown"], "Content only")
self.assertEqual(payload["title"], DEFAULT_GIST_TITLE)
self.assertEqual(payload["files"][PRIMARY_FILE_NAME]["content"], "Content only")

@patch.dict("utils.wavey_gist.os.environ", {}, clear=True)
@patch("utils.wavey_gist.requests.post")
Expand All @@ -57,6 +61,17 @@ def test_missing_url_returns_empty(self, mock_post: MagicMock) -> None:
mock_post.return_value = _mock_response(post_json={"id": "abc123"})
self.assertEqual(upload_to_gist("x"), "")

@patch.dict("utils.wavey_gist.os.environ", {"WAVEY_GIST_API_KEY": "test-key"})
@patch("utils.wavey_gist.requests.post")
def test_http_error_returns_empty(self, mock_post: MagicMock) -> None:
# E.g. the legacy `markdown` field — the API now rejects unknown fields
# with HTTP 400. The function must keep alerting and return "" so the
# caller can fall back to the in-Telegram summary.
mock_response = MagicMock()
mock_response.raise_for_status.side_effect = requests.HTTPError("400 Client Error")
mock_post.return_value = mock_response
self.assertEqual(upload_to_gist("x"), "")

@patch.dict("utils.wavey_gist.os.environ", {"WAVEY_GIST_API_KEY": "test-key"})
@patch("utils.wavey_gist.requests.post")
def test_request_failure_returns_empty(self, mock_post: MagicMock) -> None:
Expand Down
21 changes: 16 additions & 5 deletions utils/wavey_gist.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,18 @@

WAVEY_GIST_API_URL = "https://api.wavey.info/api/v1/gists"
DEFAULT_GIST_TITLE = "Monitoring Details"
# Wavey Gist picks `README.md` as the primary file when present, so the rendered
# page opens with our content. See https://gist.wavey.info/llms.txt.
PRIMARY_FILE_NAME = "README.md"


def upload_to_gist(content: str, title: str = "") -> str:
"""Publish markdown ``content`` to Wavey Gist and return the rendered-page URL.

Args:
content: The markdown text to upload.
title: Optional title, prepended as a top-level markdown heading.
title: Optional title, prepended as a top-level markdown heading and used
as the gist's display title.

Returns:
The URL of the created gist, or an empty string on failure.
Expand All @@ -31,23 +35,30 @@ def upload_to_gist(content: str, title: str = "") -> str:
return ""

markdown = f"# {title}\n\n{content}" if title else content
# Wavey Gist's create endpoint now expects a `files` snapshot — the legacy
# `title` + `markdown` fields are rejected with HTTP 400. See
# https://gist.wavey.info/llms.txt.
payload: dict = {
"title": title or DEFAULT_GIST_TITLE,
"files": {PRIMARY_FILE_NAME: {"content": markdown}},
}

try:
response = requests.post(
WAVEY_GIST_API_URL,
json={"title": title or DEFAULT_GIST_TITLE, "markdown": markdown},
json=payload,
headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
timeout=10,
)
response.raise_for_status()
payload = response.json()
body = response.json()
except (requests.RequestException, ValueError) as e:
logger.warning("Failed to upload to Wavey Gist: %s", e)
return ""

url = payload.get("url", "")
url = body.get("url", "")
if not url:
logger.warning("Wavey Gist response did not include a URL: %s", payload)
logger.warning("Wavey Gist response did not include a URL: %s", body)
return ""

logger.info("Uploaded gist to %s", url)
Expand Down