Add Codeberg releases source

This commit is contained in:
2026-08-08 14:58:11 +02:00
parent ae4a95a337
commit b074bf6aaa
5 changed files with 193 additions and 6 deletions

View File

@@ -120,6 +120,19 @@ def gitlab_entry() -> sideload.AppEntry:
)
@pytest.fixture
def codeberg_entry() -> sideload.AppEntry:
return sideload.AppEntry(
name="CoMaps",
source="codeberg",
app_id="app.comaps",
config={
"url": "https://codeberg.org/comaps/comaps",
"asset_pattern": r"^CoMaps-.*-main-release\.apk$",
},
)
@pytest.fixture
def config(tmp_config: Path) -> sideload.Config:
return sideload.Config.from_file(tmp_config)
@@ -428,6 +441,91 @@ class TestGitHubSource:
assert result.read_bytes() == apk_data
# ── CodebergSource tests ──────────────────────────────────────────────────────
class TestCodebergSource:
CODEBERG_RELEASE = {
"tag_name": "v2026.08.0",
"assets": [
{
"name": "CoMaps-2026.08.0-main-release.apk",
"browser_download_url": "https://codeberg.org/comaps/comaps/releases/download/v2026.08.0/CoMaps-2026.08.0-main-release.apk",
"size": 12345678,
},
{
"name": "CoMaps-2026.08.0-debug.apk",
"browser_download_url": "https://codeberg.org/comaps/comaps/releases/download/v2026.08.0/CoMaps-2026.08.0-debug.apk",
},
{
"name": "CoMaps-2026.08.0-test.apk",
"browser_download_url": "https://codeberg.org/comaps/comaps/releases/download/v2026.08.0/CoMaps-2026.08.0-test.apk",
},
{
"name": "CoMaps-2026.08.0-unsigned.apk",
"browser_download_url": "https://codeberg.org/comaps/comaps/releases/download/v2026.08.0/CoMaps-2026.08.0-unsigned.apk",
},
{
"name": "Source code (zip)",
"browser_download_url": "https://codeberg.org/comaps/comaps/archive/v2026.08.0.zip",
},
],
}
def test_get_latest_release(self, codeberg_entry: sideload.AppEntry):
source = sideload.CodebergSource(codeberg_entry)
with mock.patch("sideload._http_get", return_value=json.dumps(self.CODEBERG_RELEASE).encode()) as http_get:
release = source.get_latest_release()
http_get.assert_called_once_with(
"https://codeberg.org/api/v1/repos/comaps/comaps/releases/latest"
)
assert release.version == "2026.08.0"
assert release.download_url.endswith("CoMaps-2026.08.0-main-release.apk")
assert release.file_size == 12345678
def test_get_latest_release_ambiguous_assets(self, codeberg_entry: sideload.AppEntry):
source = sideload.CodebergSource(codeberg_entry)
release = {
"tag_name": "v1.0",
"assets": [
{"name": "CoMaps-one-main-release.apk", "browser_download_url": "https://example.com/one.apk"},
{"name": "CoMaps-two-main-release.apk", "browser_download_url": "https://example.com/two.apk"},
],
}
with mock.patch("sideload._http_get", return_value=json.dumps(release).encode()):
with pytest.raises(ValueError, match="multiple APK assets match"):
source.get_latest_release()
def test_get_latest_release_no_apk(self, codeberg_entry: sideload.AppEntry):
source = sideload.CodebergSource(codeberg_entry)
release = {"tag_name": "v1.0", "assets": [{"name": "Source code (tar.gz)"}]}
with mock.patch("sideload._http_get", return_value=json.dumps(release).encode()):
with pytest.raises(ValueError, match="no APK asset"):
source.get_latest_release()
@pytest.mark.parametrize(
"url",
[
"http://codeberg.org/comaps/comaps",
"https://example.com/comaps/comaps",
"https://codeberg.org/comaps",
"https://codeberg.org/comaps/comaps/releases",
],
)
def test_invalid_url_raises(self, url: str):
entry = sideload.AppEntry(
name="CoMaps", source="codeberg", app_id="app.comaps", config={"url": url}
)
with pytest.raises(ValueError, match="HTTPS Codeberg repository URL"):
sideload.CodebergSource(entry)
def test_get_latest_release_network_error(self, codeberg_entry: sideload.AppEntry):
source = sideload.CodebergSource(codeberg_entry)
with mock.patch("sideload._http_get", side_effect=RuntimeError("network down")):
with pytest.raises(RuntimeError, match="network down"):
source.get_latest_release()
# ── GitLabSource tests ────────────────────────────────────────────────────────
@@ -645,6 +743,10 @@ class TestMakeSource:
s = sideload._make_source(gitlab_entry)
assert isinstance(s, sideload.GitLabSource)
def test_codeberg(self, codeberg_entry: sideload.AppEntry):
s = sideload._make_source(codeberg_entry)
assert isinstance(s, sideload.CodebergSource)
def test_unknown_source_raises(self):
entry = sideload.AppEntry(name="X", source="unknown", app_id="x", config={})
with pytest.raises(ValueError, match="unknown source type"):