DazedTL/tests/test_release_package.py
DazedAnon 57379076bd feat(release): add public release ZIP creation functionality
- Introduced a new button to create a player-ready ZIP of the complete game, excluding translator workspaces, version control files, documentation, backups, saves, and playtest plugins.
- Implemented a background worker to handle ZIP creation without blocking the GUI.
- Added user feedback for the completion and size of the generated ZIP file.
2026-07-26 15:29:24 -05:00

179 lines
6.9 KiB
Python

#!/usr/bin/env python3
"""Tests for player-ready public release archives and workflow buttons."""
from __future__ import annotations
import tempfile
import unittest
import zipfile
from pathlib import Path
from unittest.mock import patch
from PyQt5.QtCore import QSettings
from PyQt5.QtWidgets import QApplication
from util.release_package import ReleasePackageError, create_release_zip
class ReleasePackageTests(unittest.TestCase):
def _write(self, root: Path, relative: str, content: bytes = b"content") -> Path:
path = root / relative
path.parent.mkdir(parents=True, exist_ok=True)
path.write_bytes(content)
return path
def test_release_keeps_game_and_updater_but_omits_tool_artifacts(self):
with tempfile.TemporaryDirectory() as raw:
base = Path(raw)
game = base / "Translated Game"
game.mkdir()
self._write(game, "Game.exe", b"game")
self._write(game, "Data.wolf", b"translated archive")
self._write(game, "Data.wolf.bak", b"original backup")
self._write(game, "Data/manual.txt", b"runtime text")
self._write(game, "Save01.sav", b"translator save")
self._write(game, "Save/Slot1.rpgsave", b"translator save")
self._write(game, ".git/config")
self._write(game, ".dazedtl/images/menu.png")
self._write(game, "wolf_json/manifest.json")
self._write(game, "ace_json/Actors.json")
self._write(game, "scripts/release.py")
self._write(game, "docs/translation-notes.md")
self._write(game, "skills/game.md")
self._write(game, "vocab.txt")
self._write(game, "README.md")
self._write(game, ".gitignore")
self._write(game, ".env", b"key=secret")
self._write(game, "GameUpdate.bat", b"launcher")
self._write(game, "GameUpdate_linux.sh", b"launcher")
self._write(game, "UberWolfCli.exe", b"wolf updater")
self._write(game, "UberWolfCli.LICENSE.txt", b"license")
self._write(game, "gameupdate/patch.ps1", b"patch")
self._write(game, "gameupdate/patch-config.txt", b"repo=game")
self._write(game, "gameupdate/previous_patch_sha.txt", b"private state")
plugins_js = b"""var $plugins =
[
{"name":"CorePlugin","status":true,"parameters":{"label":"keep"}},
{"name":"TLInspector","status":true,"parameters":{}},
{"name":"Forge_MZ","status":true,"parameters":{}}
];
"""
source_plugins = self._write(game, "js/plugins.js", plugins_js)
self._write(game, "js/plugins/CorePlugin.js", b"core")
self._write(game, "js/plugins/TLInspector.js", b"inspector")
self._write(game, "js/plugins/Forge_MZ.js", b"forge")
output = base / "release.zip"
progress = []
result = create_release_zip(
game,
output,
progress=lambda current, total, label: progress.append(
(current, total, label)
),
)
prefix = "Translated Game/"
with zipfile.ZipFile(output) as archive:
names = set(archive.namelist())
archived_plugins = archive.read(prefix + "js/plugins.js").decode("utf-8")
for relative in (
"Game.exe",
"Data.wolf",
"Data/manual.txt",
"GameUpdate.bat",
"GameUpdate_linux.sh",
"UberWolfCli.exe",
"UberWolfCli.LICENSE.txt",
"gameupdate/patch.ps1",
"gameupdate/patch-config.txt",
"js/plugins/CorePlugin.js",
):
self.assertIn(prefix + relative, names)
for relative in (
"Data.wolf.bak",
"Save01.sav",
"Save/Slot1.rpgsave",
".git/config",
".dazedtl/images/menu.png",
"wolf_json/manifest.json",
"ace_json/Actors.json",
"scripts/release.py",
"docs/translation-notes.md",
"skills/game.md",
"vocab.txt",
"README.md",
".gitignore",
".env",
"gameupdate/previous_patch_sha.txt",
"js/plugins/TLInspector.js",
"js/plugins/Forge_MZ.js",
):
self.assertNotIn(prefix + relative, names)
self.assertIn('"name":"CorePlugin"', archived_plugins)
self.assertNotIn("TLInspector", archived_plugins)
self.assertNotIn("Forge_MZ", archived_plugins)
self.assertEqual(source_plugins.read_bytes(), plugins_js)
self.assertEqual(result.output_path, output.resolve())
self.assertEqual(result.sanitized_plugin_lists, 1)
self.assertEqual(progress[-1][:2], (result.files_added, result.files_added))
def test_release_path_must_be_outside_game_folder(self):
with tempfile.TemporaryDirectory() as raw:
game = Path(raw) / "Game"
game.mkdir()
self._write(game, "Game.exe")
output = game / "release.zip"
with self.assertRaisesRegex(ReleasePackageError, "outside the game folder"):
create_release_zip(game, output)
self.assertFalse(output.exists())
def test_zip_suffix_is_added(self):
with tempfile.TemporaryDirectory() as raw:
base = Path(raw)
game = base / "Game"
game.mkdir()
self._write(game, "Game.exe")
result = create_release_zip(game, base / "release")
self.assertEqual(result.output_path.name, "release.zip")
self.assertTrue(result.output_path.is_file())
class ReleaseWorkflowButtonTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.app = QApplication.instance() or QApplication([])
def test_both_workflows_expose_public_release_button(self):
from gui.workflow_tab import WorkflowTab
from gui.wolf_workflow_tab import WolfWorkflowTab
with tempfile.TemporaryDirectory() as raw:
settings = QSettings(str(Path(raw) / "settings.ini"), QSettings.IniFormat)
with (
patch("gui.workflow_tab.QSettings", return_value=settings),
patch("gui.wolf_workflow_tab.QSettings", return_value=settings),
):
rpg = WorkflowTab()
wolf = WolfWorkflowTab()
try:
self.assertEqual(rpg._release_zip_btn.text(), "📦 Create Public Release ZIP")
self.assertEqual(wolf._release_zip_btn.text(), "📦 Create Public Release ZIP")
self.assertIn("GameUpdate", rpg._release_zip_btn.toolTip())
finally:
rpg.close()
wolf.close()
if __name__ == "__main__":
unittest.main()