173 lines
6.4 KiB
Python
173 lines
6.4 KiB
Python
import hashlib
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from tools.beta_release_lib import validate_complete_mapping
|
|
|
|
|
|
def test_manifest_rejects_an_unclassified_or_missing_mod() -> None:
|
|
with pytest.raises(ValueError, match="missing=.*alpha.jar.*unknown=.*beta.jar"):
|
|
validate_complete_mapping(
|
|
{"alpha.jar", "beta.jar"},
|
|
{"beta.jar": "UNCLASSIFIED"},
|
|
)
|
|
|
|
|
|
def test_manifest_accepts_only_known_sides_and_exact_names() -> None:
|
|
validate_complete_mapping(
|
|
{"alpha.jar", "beta.jar"},
|
|
{"alpha.jar": "client", "beta.jar": "server"},
|
|
)
|
|
with pytest.raises(ValueError, match="extra=.*gamma.jar"):
|
|
validate_complete_mapping(
|
|
{"alpha.jar"}, {"alpha.jar": "both", "gamma.jar": "client"},
|
|
)
|
|
|
|
|
|
def test_write_keybind_options_keeps_only_version_and_key_lines(tmp_path: Path) -> None:
|
|
from tools.beta_release_lib import write_keybind_options
|
|
|
|
source = tmp_path / "source-options.txt"
|
|
destination = tmp_path / "options.txt"
|
|
source.write_text(
|
|
"version:3465\nrenderDistance:32\nkey_key.tacz.reload.desc:key.keyboard.r\n"
|
|
"fov:0.0\nkey_key.forward:key.keyboard.w\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
write_keybind_options(source, destination)
|
|
|
|
assert destination.read_text(encoding="utf-8") == (
|
|
"version:3465\nkey_key.tacz.reload.desc:key.keyboard.r\nkey_key.forward:key.keyboard.w\n"
|
|
)
|
|
|
|
|
|
def test_copy_classified_tree_never_copies_wrong_side_or_world(tmp_path: Path) -> None:
|
|
from tools.beta_release_lib import copy_classified_tree
|
|
|
|
source = tmp_path / "source"
|
|
(source / "minecraft/mods").mkdir(parents=True)
|
|
(source / "minecraft/saves/world").mkdir(parents=True)
|
|
(source / "minecraft/mods/client.jar").write_bytes(b"client")
|
|
(source / "minecraft/mods/both.jar").write_bytes(b"both")
|
|
(source / "minecraft/mods/server.jar").write_bytes(b"server")
|
|
(source / "minecraft/saves/world/level.dat").write_bytes(b"world")
|
|
|
|
destination = tmp_path / "client"
|
|
copy_classified_tree(
|
|
source,
|
|
destination,
|
|
"client",
|
|
{"client.jar": "client", "both.jar": "both", "server.jar": "server"},
|
|
[],
|
|
)
|
|
|
|
assert (destination / "minecraft/mods/client.jar").is_file()
|
|
assert (destination / "minecraft/mods/both.jar").is_file()
|
|
assert not (destination / "minecraft/mods/server.jar").exists()
|
|
assert not (destination / "minecraft/saves").exists()
|
|
|
|
|
|
def test_validate_client_stage_requires_prism_root_and_rejects_world(tmp_path: Path) -> None:
|
|
from tools.beta_release_lib import validate_client_stage
|
|
|
|
stage = tmp_path / "client-stage"
|
|
(stage / "minecraft/saves/world").mkdir(parents=True)
|
|
(stage / "minecraft/saves/world/level.dat").write_bytes(b"world")
|
|
(stage / "minecraft/mods").mkdir()
|
|
(stage / "minecraft/options.txt").write_text("version:3465\n", encoding="utf-8")
|
|
(stage / "instance.cfg").write_text("InstanceType=OneSix\n", encoding="utf-8")
|
|
(stage / "mmc-pack.json").write_text("{}\n", encoding="utf-8")
|
|
|
|
with pytest.raises(ValueError, match="forbidden client path.*minecraft/saves"):
|
|
validate_client_stage(stage, {})
|
|
|
|
|
|
def test_sanitize_client_stage_removes_server_credentials_and_personal_cache(
|
|
tmp_path: Path,
|
|
) -> None:
|
|
from tools.beta_release_lib import sanitize_client_stage
|
|
|
|
stage = tmp_path / "client-stage"
|
|
credentials = stage / "minecraft/config/Discord-Integration.toml"
|
|
cache = stage / "minecraft/config/voicechat/username-cache.json"
|
|
shader_selection = stage / "minecraft/config/oculus.properties"
|
|
skin_provider = stage / "minecraft/config/skinrestorer/config.json"
|
|
credentials.parent.mkdir(parents=True)
|
|
cache.parent.mkdir(parents=True)
|
|
skin_provider.parent.mkdir(parents=True)
|
|
credentials.write_text("botToken=do-not-publish\n", encoding="utf-8")
|
|
cache.write_text('{"player":"do-not-publish"}\n', encoding="utf-8")
|
|
shader_selection.write_text("shaderPack=local-only\n", encoding="utf-8")
|
|
skin_provider.write_text('{"apiKey":"do-not-publish"}\n', encoding="utf-8")
|
|
|
|
sanitize_client_stage(stage)
|
|
|
|
assert not credentials.exists()
|
|
assert not cache.exists()
|
|
assert not shader_selection.exists()
|
|
assert not skin_provider.exists()
|
|
|
|
|
|
def test_validate_server_stage_rejects_client_jar_and_requires_target_world(tmp_path: Path) -> None:
|
|
from tools.beta_release_lib import validate_server_stage
|
|
|
|
stage = tmp_path / "server-stage"
|
|
(stage / "mods").mkdir(parents=True)
|
|
(stage / "mods/client.jar").write_bytes(b"client")
|
|
(stage / "eula.txt").write_text("eula=false\n", encoding="utf-8")
|
|
(stage / "server.properties").write_text("level-name=world\n", encoding="utf-8")
|
|
|
|
with pytest.raises(ValueError, match="server JAR mismatch|missing target world"):
|
|
validate_server_stage(stage, {"client.jar": "client"}, "world")
|
|
|
|
|
|
def test_forge_request_declares_a_user_agent() -> None:
|
|
from tools.build_beta_release import forge_request
|
|
|
|
request = forge_request("https://example.invalid/forge-installer.jar")
|
|
|
|
assert request.get_header("User-agent") == "EscapeFromFuncraftBetaBuilder/1.0"
|
|
|
|
|
|
def test_forge_install_env_prepends_compatibility_library_path(
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
from tools.build_beta_release import forge_install_env
|
|
|
|
monkeypatch.setenv("LD_LIBRARY_PATH", "/system/lib")
|
|
|
|
env = forge_install_env(tmp_path / "legacy-zlib")
|
|
|
|
assert env is not None
|
|
assert env["LD_LIBRARY_PATH"] == f"{tmp_path / 'legacy-zlib'}:/system/lib"
|
|
|
|
|
|
def test_sha256_file_streams_without_reading_the_archive_at_once(
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
from tools.beta_release_lib import sha256_file
|
|
|
|
archive = tmp_path / "large-archive.bin"
|
|
payload = b"release-data" * 100_000
|
|
archive.write_bytes(payload)
|
|
|
|
def fail_read_bytes(_: Path) -> bytes:
|
|
raise AssertionError("release archives must not be read into memory at once")
|
|
|
|
monkeypatch.setattr(Path, "read_bytes", fail_read_bytes)
|
|
|
|
assert sha256_file(archive) == hashlib.sha256(payload).hexdigest()
|
|
|
|
|
|
def test_source_mod_names_includes_only_mod_jars(tmp_path: Path) -> None:
|
|
from tools.build_beta_release import source_mod_names
|
|
|
|
mods = tmp_path / "minecraft/mods"
|
|
mods.mkdir(parents=True)
|
|
(mods / "required.jar").write_bytes(b"jar")
|
|
(mods / "notes.txt").write_text("ignore", encoding="utf-8")
|
|
|
|
assert source_mod_names(tmp_path) == {"required.jar"}
|