155 lines
6.3 KiB
Python
155 lines
6.3 KiB
Python
import importlib.util
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
import zipfile
|
|
|
|
import pytest
|
|
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[2] / "scripts/package_source.py"
|
|
spec = importlib.util.spec_from_file_location("package_source", SCRIPT)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
|
|
|
|
def git(root, *args):
|
|
result = subprocess.run(
|
|
["git", "-C", str(root), *args], capture_output=True, text=True, timeout=30
|
|
)
|
|
assert result.returncode == 0, result.stderr
|
|
return result.stdout.strip()
|
|
|
|
|
|
@pytest.fixture
|
|
def repository(tmp_path):
|
|
if not shutil.which("git"):
|
|
pytest.skip("Git required")
|
|
root = tmp_path / "source"
|
|
root.mkdir()
|
|
git(root, "init", "-b", "main")
|
|
git(root, "config", "user.name", "Fixture")
|
|
git(root, "config", "user.email", "fixture@example.test")
|
|
keep = ["start.bat", ".env.example", "uv.lock", "apps/showcase_ui/src/main.ts"]
|
|
tracked_builds = [
|
|
"artemis/resources/showcase_ui/main.js",
|
|
"packages/artemis-accessibility-helper/ArtemisAccessibilityHelper.apk",
|
|
"packages/artemis-accessibility-helper/debug.keystore",
|
|
]
|
|
omit = [
|
|
".env",
|
|
".env.local",
|
|
".venv/pyvenv.cfg",
|
|
"nested/node_modules/foo.js",
|
|
"apps/showcase_ui/dist/main.js",
|
|
"__pycache__/a.pyc",
|
|
"traces/data.db",
|
|
".roo/mcp.json",
|
|
]
|
|
for name in keep + tracked_builds + omit:
|
|
target = root / name
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
target.write_text("fixture")
|
|
(root / ".gitignore").write_text(
|
|
".env*\n!.env.example\n.venv/\nnode_modules/\ndist/\ntraces/\n.roo/\n__pycache__/\n"
|
|
)
|
|
git(root, "add", "--", ".gitignore", *keep, *tracked_builds)
|
|
git(root, "commit", "-m", "initial")
|
|
git(root, "remote", "add", "eapil", "https://git.example.invalid/team/project.git")
|
|
git(root, "config", "branch.main.remote", "eapil")
|
|
git(root, "config", "branch.main.merge", "refs/heads/main")
|
|
return root, keep, tracked_builds, omit
|
|
|
|
|
|
def test_archive_is_updatable_clean_checkout_without_local_data(repository, tmp_path):
|
|
root, keep, tracked_builds, omit = repository
|
|
output = tmp_path / "artifacts/source.zip"
|
|
count, digest = module.package_source(root, output)
|
|
assert count > len(keep)
|
|
assert len(digest) == 64
|
|
with zipfile.ZipFile(output) as archive:
|
|
assert archive.testzip() is None
|
|
names = set(archive.namelist())
|
|
assert {f"artemis/{name}" for name in keep}.issubset(names)
|
|
assert not {f"artemis/{name}" for name in tracked_builds + omit} & names
|
|
assert "artemis/.git/config" in names
|
|
assert "artemis/.git/index" in names
|
|
assert not any(name.startswith("artemis/.git/logs/") for name in names)
|
|
assert not any(name.startswith("artemis/.git/hooks/") for name in names)
|
|
archive.extractall(tmp_path / "unpacked")
|
|
extracted = tmp_path / "unpacked/artemis"
|
|
assert git(extracted, "symbolic-ref", "--short", "HEAD") == "main"
|
|
assert git(extracted, "rev-parse", "HEAD") == git(root, "rev-parse", "HEAD")
|
|
assert git(extracted, "status", "--porcelain") == ""
|
|
assert git(extracted, "remote", "get-url", "origin") == (
|
|
"https://git.example.invalid/team/project.git"
|
|
)
|
|
assert git(extracted, "config", "--get", "branch.main.remote") == "origin"
|
|
(extracted / tracked_builds[0]).parent.mkdir(parents=True)
|
|
(extracted / tracked_builds[0]).write_text("new bundle")
|
|
assert git(extracted, "status", "--porcelain") == ""
|
|
|
|
|
|
def test_unpacked_archive_fast_forwards(repository, tmp_path):
|
|
root, _, tracked_builds, _ = repository
|
|
bare = tmp_path / "upstream.git"
|
|
bare.mkdir()
|
|
git(bare, "init", "--bare")
|
|
git(root, "remote", "add", "test-upstream", str(bare))
|
|
git(root, "push", "test-upstream", "main")
|
|
archive = tmp_path / "source.zip"
|
|
module.package_source(root, archive)
|
|
with zipfile.ZipFile(archive) as contents:
|
|
contents.extractall(tmp_path / "unpacked")
|
|
extracted = tmp_path / "unpacked/artemis"
|
|
git(extracted, "remote", "set-url", "origin", str(bare))
|
|
built_asset = extracted / tracked_builds[0]
|
|
built_asset.parent.mkdir(parents=True)
|
|
built_asset.write_text("generated after setup")
|
|
(root / "start.bat").write_text("updated")
|
|
git(root, "add", "--", "start.bat")
|
|
git(root, "commit", "-m", "update launcher")
|
|
git(root, "push", "test-upstream", "main")
|
|
from scripts.update_installation import update_source
|
|
|
|
assert update_source(extracted) == 10
|
|
assert (extracted / "start.bat").read_text() == "updated"
|
|
assert git(extracted, "status", "--porcelain") == ""
|
|
|
|
|
|
def test_packaging_rejects_uncommitted_changes_and_credentials(repository, tmp_path):
|
|
root, _, _, _ = repository
|
|
(root / "start.bat").write_text("local change")
|
|
with pytest.raises(ValueError, match="Commit tracked changes"):
|
|
module.package_source(root, tmp_path / "source.zip")
|
|
(root / "start.bat").write_text("fixture")
|
|
git(
|
|
root,
|
|
"remote",
|
|
"set-url",
|
|
"eapil",
|
|
"https://user:secret@git.example.invalid/team/project.git",
|
|
)
|
|
with pytest.raises(ValueError, match="credential-free HTTPS"):
|
|
module.package_source(root, tmp_path / "source.zip")
|
|
|
|
|
|
def test_archive_checks_out_chinese_batch_with_crlf_and_clean_git(repository, tmp_path):
|
|
root, _, _, _ = repository
|
|
project_root = Path(__file__).resolve().parents[2]
|
|
(root / ".gitattributes").write_bytes((project_root / ".gitattributes").read_bytes())
|
|
# The Git blob uses LF; the archive must contain the CRLF working-tree form.
|
|
content = (project_root / "start-cn.bat").read_bytes().replace(b"\r\n", b"\n")
|
|
(root / "start-cn.bat").write_bytes(content)
|
|
git(root, "add", "--", ".gitattributes", "start-cn.bat")
|
|
git(root, "commit", "-m", "Windows launcher attributes")
|
|
archive_path = tmp_path / "source.zip"
|
|
module.package_source(root, archive_path)
|
|
with zipfile.ZipFile(archive_path) as archive:
|
|
packed = archive.read("artemis/start-cn.bat")
|
|
assert b"\r\n" in packed
|
|
assert b"\n" not in packed.replace(b"\r\n", b"")
|
|
assert packed.decode("utf-8").startswith("@echo off\r\n")
|
|
archive.extractall(tmp_path / "unpacked")
|
|
assert git(tmp_path / "unpacked/artemis", "status", "--porcelain") == ""
|