Fix server threading, tag URLs, and tag date resolution

- Switch to ThreadingHTTPServer to handle concurrent requests
- Catch BrokenPipeError on response writes to suppress noisy tracebacks
- Change tag URL from /tags/ to /releases/tag/
- Resolve tag dates via commit SHA from tags API + commits endpoint (works for public repos without auth)
- Update tests to match new tag date resolution approach
This commit is contained in:
2026-07-28 12:25:04 +02:00
parent 7aeecc1e15
commit e1bd1ad9eb
4 changed files with 39 additions and 54 deletions
+14 -28
View File
@@ -61,24 +61,18 @@ class TestFetchTags:
def test_fetch_tags(self, mock_api):
mock_api.get("https://api.github.com/repos/owner/repo/tags").mock(
return_value=httpx.Response(200, json=[
{"name": "v1.0.0", "zipball_url": "https://api.github.com/repos/owner/repo/zipball/v1.0.0"},
{"name": "v0.9.0", "zipball_url": "https://api.github.com/repos/owner/repo/zipball/v0.9.0"},
{"name": "v1.0.0", "commit": {"sha": "aaa111"}},
{"name": "v0.9.0", "commit": {"sha": "bbb222"}},
])
)
mock_api.get("https://api.github.com/repos/owner/repo/git/ref/tags/v1.0.0").mock(
mock_api.get("https://api.github.com/repos/owner/repo/commits/aaa111").mock(
return_value=httpx.Response(200, json={
"object": {
"type": "commit",
"author": {"date": "2025-01-15T10:30:00Z"},
}
"commit": {"author": {"date": "2025-01-15T10:30:00Z"}}
})
)
mock_api.get("https://api.github.com/repos/owner/repo/git/ref/tags/v0.9.0").mock(
mock_api.get("https://api.github.com/repos/owner/repo/commits/bbb222").mock(
return_value=httpx.Response(200, json={
"object": {
"type": "commit",
"author": {"date": "2025-01-10T08:00:00Z"},
}
"commit": {"author": {"date": "2025-01-10T08:00:00Z"}}
})
)
@@ -91,26 +85,18 @@ class TestFetchTags:
assert tags[0]["title"] == "v1.0.0"
assert tags[0]["body"] == ""
assert tags[0]["published_at"] == "2025-01-15T10:30:00Z"
assert tags[0]["html_url"] == "https://github.com/owner/repo/tags/v1.0.0"
assert tags[0]["html_url"] == "https://github.com/owner/repo/releases/tag/v1.0.0"
def test_fetch_tags_with_tag_object(self, mock_api):
"""When a tag points to a tag object, follow to the commit."""
"""Commit date is fetched directly from commit SHA in tags API."""
mock_api.get("https://api.github.com/repos/owner/repo/tags").mock(
return_value=httpx.Response(200, json=[
{"name": "v2.0.0", "zipball_url": "https://api.github.com/..."},
{"name": "v2.0.0", "commit": {"sha": "abc123"}},
])
)
mock_api.get("https://api.github.com/repos/owner/repo/git/ref/tags/v2.0.0").mock(
mock_api.get("https://api.github.com/repos/owner/repo/commits/abc123").mock(
return_value=httpx.Response(200, json={
"object": {
"type": "tag",
"sha": "abc123",
}
})
)
mock_api.get("https://api.github.com/repos/owner/repo/git/commits/abc123").mock(
return_value=httpx.Response(200, json={
"author": {"date": "2025-02-01T12:00:00Z"}
"commit": {"author": {"date": "2025-02-01T12:00:00Z"}}
})
)
@@ -121,13 +107,13 @@ class TestFetchTags:
assert tags[0]["published_at"] == "2025-02-01T12:00:00Z"
def test_fetch_tags_fallback_date(self, mock_api):
"""When git ref fails, use current time as fallback."""
"""When commit API fails, use current time as fallback."""
mock_api.get("https://api.github.com/repos/owner/repo/tags").mock(
return_value=httpx.Response(200, json=[
{"name": "v1.0.0", "zipball_url": "https://api.github.com/..."},
{"name": "v1.0.0", "commit": {"sha": "xyz789"}},
])
)
mock_api.get("https://api.github.com/repos/owner/repo/git/ref/tags/v1.0.0").mock(
mock_api.get("https://api.github.com/repos/owner/repo/commits/xyz789").mock(
return_value=httpx.Response(404)
)