public site and containers for that.
Build and Publish Container / Publish Linux container (push) Failing after 1m52s

This commit is contained in:
2026-10-02 12:57:22 -04:00
parent 86a187b08a
commit 7ac541def5
25 changed files with 3265 additions and 4 deletions
+256
View File
@@ -0,0 +1,256 @@
from __future__ import annotations
import tempfile
import unittest
import xml.etree.ElementTree as ET
from pathlib import Path
from unittest.mock import Mock, patch
from fastapi.testclient import TestClient
from progression_backend.app import create_app
from progression_backend.app import latest_windows_release_url
from progression_backend.config import Settings
from progression_backend.database import Database
from progression_backend.modlist import (
ModListItem,
core_items,
generate_rml,
parse_active_mods,
workshop_items,
)
from progression_backend.refresh import RefreshService
from progression_backend.steam_api import WorkshopDetails
from progression_backend.workshop import SteamCmdBatchResult, parse_about_xml, utc_now
class FakeSteam:
def get_collections(self, collection_ids: list[str]) -> dict[str, list[str]]:
return {collection_id: ["123456"] for collection_id in collection_ids}
def get_item_details(self, workshop_ids: list[str]) -> dict[str, WorkshopDetails]:
return {
workshop_id: WorkshopDetails(
workshop_id=workshop_id,
title="Test & Mod",
time_updated=123,
file_size=456,
consumer_app_id=294100,
result=1,
)
for workshop_id in workshop_ids
}
class FakeRunner:
def __init__(self, content_dir: Path):
self.content_dir = content_dir
def available(self) -> bool:
return True
def run_batch(self, workshop_ids: list[str]) -> SteamCmdBatchResult:
for workshop_id in workshop_ids:
about = self.content_dir / workshop_id / "About"
about.mkdir(parents=True)
(about / "About.xml").write_text(
"<ModMetaData><name>Test &amp; Mod</name>"
"<packageId>Author.Test</packageId>"
"<steamAppId>294100</steamAppId></ModMetaData>",
encoding="utf-8",
)
timestamp = utc_now()
return SteamCmdBatchResult(
workshop_ids=workshop_ids,
started_at=timestamp,
finished_at=timestamp,
elapsed_seconds=0.1,
exit_code=0,
status="completed",
failed_ids=set(),
output_tail="Success",
)
class BackendTests(unittest.TestCase):
@patch("progression_backend.app.requests.get")
def test_latest_windows_release_includes_prereleases(self, get: Mock) -> None:
response = Mock()
response.json.return_value = [
{
"tag_name": "0.2.2",
"published_at": "2026-06-13T02:37:15Z",
"draft": False,
"prerelease": True,
"assets": [
{
"name": "ProgressionLoader.exe",
"browser_download_url": "https://git.hudsonriggs.systems/HRiggs/ProgressionMods/releases/download/0.2.2/ProgressionLoader.exe",
}
],
}
]
get.return_value = response
self.assertEqual(
latest_windows_release_url(),
"https://git.hudsonriggs.systems/HRiggs/ProgressionMods/releases/download/0.2.2/ProgressionLoader.exe",
)
response.raise_for_status.assert_called_once_with()
@patch("progression_backend.app.requests.get")
def test_latest_windows_release_prefers_stable_build(self, get: Mock) -> None:
response = Mock()
response.json.return_value = [
{
"published_at": "2026-07-01T00:00:00Z",
"draft": False,
"prerelease": True,
"assets": [
{
"name": "ProgressionLoader.exe",
"browser_download_url": "https://git.hudsonriggs.systems/HRiggs/ProgressionMods/releases/download/0.3.0-rc1/ProgressionLoader.exe",
}
],
},
{
"published_at": "2026-06-30T00:00:00Z",
"draft": False,
"prerelease": False,
"assets": [
{
"name": "ProgressionLoader.exe",
"browser_download_url": "https://git.hudsonriggs.systems/HRiggs/ProgressionMods/releases/download/0.2.3/ProgressionLoader.exe",
}
],
},
]
get.return_value = response
self.assertEqual(
latest_windows_release_url(),
"https://git.hudsonriggs.systems/HRiggs/ProgressionMods/releases/download/0.2.3/ProgressionLoader.exe",
)
def test_about_parser_normalizes_package_id(self) -> None:
with tempfile.TemporaryDirectory() as directory:
about = Path(directory) / "About"
about.mkdir()
(about / "About.xml").write_text(
"<ModMetaData><name>A &amp; B</name><packageId>Author.Mod</packageId></ModMetaData>",
encoding="utf-8",
)
parsed = parse_about_xml(Path(directory))
self.assertEqual(parsed.package_id, "author.mod")
self.assertEqual(parsed.display_name, "A & B")
def test_rml_uses_workshop_id_and_escapes_names(self) -> None:
payload = generate_rml(
[
*core_items(["royalty"]),
ModListItem("author.mod", "123456", "A & B"),
],
"1.6",
)
root = ET.fromstring(payload)
self.assertEqual(root.findtext("meta/gameVersion"), "1.6")
self.assertEqual(
[node.text for node in root.findall("meta/modSteamIds/li")],
["0", "0", "123456"],
)
self.assertIn(b"A &amp; B", payload)
def test_homebrew_parser_reads_active_mods(self) -> None:
items = parse_active_mods(
b"<ModsConfigData><activeMods><li>ludeon.rimworld</li>"
b"<li>author.mod</li></activeMods></ModsConfigData>"
)
self.assertEqual(
[item.package_id for item in items], ["ludeon.rimworld", "author.mod"]
)
def test_refresh_persists_download_analytics(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
settings = Settings(
host="127.0.0.1",
port=8000,
database_path=root / "progression.db",
steamcmd_path="steamcmd",
workshop_content_dir=root / "workshop" / "294100",
steamcmd_batch_size=25,
steamcmd_timeout_seconds=60,
refresh_interval_seconds=3600,
refresh_on_startup=False,
game_version="1.6",
admin_token=None,
collections={"Core": "1", "Content": "2", "Cosmetics": "3"},
static_dir=root,
)
database = Database(settings.database_path)
database.initialize()
service = RefreshService(
settings,
database,
steam=FakeSteam(),
runner=FakeRunner(settings.workshop_content_dir),
)
service.refresh("test-run", "test")
run = database.fetch_one(
"SELECT * FROM refresh_runs WHERE id = ?", ("test-run",)
)
self.assertIsNotNone(run)
self.assertEqual(run["status"], "completed", run)
self.assertEqual(run["unique_items"], 1)
self.assertEqual(run["downloads_succeeded"], 1)
mod = database.fetch_one("SELECT * FROM mods WHERE workshop_id = '123456'")
self.assertEqual(mod["package_id"], "author.test")
self.assertEqual(mod["steam_app_id"], "294100")
generated = workshop_items([mod])
self.assertEqual(generated[0].workshop_id, "123456")
def test_api_serves_dashboard_and_reports_empty_modlist(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
project_root = Path(__file__).resolve().parents[1]
settings = Settings(
host="127.0.0.1",
port=8000,
database_path=root / "progression.db",
steamcmd_path="missing-steamcmd",
workshop_content_dir=root / "workshop" / "294100",
steamcmd_batch_size=25,
steamcmd_timeout_seconds=60,
refresh_interval_seconds=3600,
refresh_on_startup=False,
game_version="1.6",
admin_token=None,
collections={"Core": "1", "Content": "2", "Cosmetics": "3"},
static_dir=project_root / "progression_backend" / "static",
)
with TestClient(create_app(settings)) as client:
self.assertEqual(client.get("/").status_code, 200)
self.assertEqual(client.get("/art/GameTitle.png").status_code, 200)
self.assertEqual(client.get("/api/health").json()["status"], "ok")
self.assertIn("rml_updated_at", client.get("/api/status").json())
response = client.get("/api/modlists/vanilla")
self.assertEqual(response.status_code, 409)
release_url = (
"https://git.hudsonriggs.systems/HRiggs/ProgressionMods/"
"releases/download/0.2.2/ProgressionLoader.exe"
)
with patch(
"progression_backend.app.latest_windows_release_url",
return_value=release_url,
):
response = client.get(
"/api/downloads/windows", follow_redirects=False
)
self.assertEqual(response.status_code, 307)
self.assertEqual(response.headers["location"], release_url)
if __name__ == "__main__":
unittest.main()