Files
local-ci-cd-system/tests/python/test_winimage.py
T
Simone c257c931c3 refactor(deploy-win): drop UUP dump, take ISO via --iso-url / --win-iso
On a Linux host the UUP converter only produces the RTM install.wim (build
26100.1), so Windows Update had to install the entire cumulative anyway —
the UUP download/convert machinery added a slow ~5 GB step and several host
tools (aria2/wimtools/cabextract/chntpw) for no benefit.

Replace it: deploy-win now takes the install ISO directly via `--iso-url`
(urllib download + cache by file name under <CI_ROOT>/iso/, optional
--iso-sha256) or a local `--win-iso` (exactly one required). Patch + deploy
flow is unchanged. Supply an already-updated ISO (e.g. a massgrave link) to
skip the WU step entirely.

- Remove winimage/uup.py and its tests.
- Trim setup-host-linux.sh deps to p7zip-full + xorriso + genisoimage.
- Update RUNBOOK §5 and the Windows-host port plan. Coverage 93.6%.
2026-06-09 18:42:22 +02:00

110 lines
4.2 KiB
Python

"""Unit tests for the winimage subpackage (autounattend, iso_patch)."""
from __future__ import annotations
import subprocess
from pathlib import Path
from typing import Any
import click
import pytest
from ci_orchestrator.winimage import autounattend as au
from ci_orchestrator.winimage import iso_patch as ip
# ── autounattend ───────────────────────────────────────────────────────────
def test_render_autounattend_no_placeholder() -> None:
xml = au.render_autounattend(au.WinUnattendParams())
assert "{{" not in xml and "}}" not in xml
assert "TVRH6-WHNXV-R9WG3-9XRFY-MY832" in xml
def test_render_post_install_substitutes() -> None:
ps = au.render_post_install(au.WinUnattendParams(admin_password="Secret123"))
assert "Secret123" in ps and "{{" not in ps
assert "install_complete.flag" in ps
def test_validate_computer_name_ok() -> None:
au.validate_computer_name("WINBUILD-2025")
@pytest.mark.parametrize("bad", ["bad name", "toolongcomputername01", "has_underscore"])
def test_validate_computer_name_bad(bad: str) -> None:
with pytest.raises(ValueError, match="invalid computer name"):
au.validate_computer_name(bad)
def test_apply_detects_leftover() -> None:
with pytest.raises(ValueError, match="unsubstituted placeholder"):
au._apply("hello {{MISSING}}", {})
# ── iso_patch ──────────────────────────────────────────────────────────────
def test_read_volid_fallback(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
monkeypatch.setattr(ip.shutil, "which", lambda _n: None)
iso = tmp_path / "x.iso"
iso.write_bytes(b"x")
assert ip._read_volid(iso) == "WINSERVER"
def test_check_free_space_raises(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
iso = tmp_path / "x.iso"
iso.write_bytes(b"x" * 1000)
monkeypatch.setattr(
ip.shutil, "disk_usage",
lambda _p: type("DU", (), {"free": 10})(),
)
with pytest.raises(click.ClickException, match="insufficient space"):
ip._check_free_space(iso, tmp_path)
def test_patch_iso_happy(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
base = tmp_path / "base.iso"
base.write_bytes(b"x" * 1000)
out = tmp_path / "out.iso"
monkeypatch.setattr(ip, "_require_xorriso", lambda: "xorriso")
monkeypatch.setattr(ip, "_require_7z", lambda: "7z")
monkeypatch.setattr(ip, "_read_volid", lambda _p: "WINSERVER")
monkeypatch.setattr(ip, "_check_free_space", lambda *_a: None)
def fake_run(argv: list[str], **_kw: Any) -> subprocess.CompletedProcess[str]:
# simulate 7z extraction creating the efi boot file in the tree
if argv[0] == "7z" and "x" in argv:
outflag = next(a for a in argv if a.startswith("-o"))
tree = Path(outflag[2:])
boot = tree / ip._EFI_BOOT_NOPROMPT
boot.parent.mkdir(parents=True, exist_ok=True)
boot.write_bytes(b"boot")
elif "mkisofs" in argv:
out.write_bytes(b"iso")
return subprocess.CompletedProcess(argv, 0, "", "")
monkeypatch.setattr(ip.subprocess, "run", fake_run)
result = ip.patch_iso(
base, autounattend_xml="<x/>", post_install_ps1="echo hi", out_iso=out,
work_dir=tmp_path,
)
assert result == out and out.is_file()
def test_patch_iso_missing_efi_boot(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
base = tmp_path / "base.iso"
base.write_bytes(b"x" * 1000)
monkeypatch.setattr(ip, "_require_xorriso", lambda: "xorriso")
monkeypatch.setattr(ip, "_require_7z", lambda: "7z")
monkeypatch.setattr(ip, "_check_free_space", lambda *_a: None)
monkeypatch.setattr(
ip.subprocess, "run",
lambda argv, **_k: subprocess.CompletedProcess(argv, 0, "", ""),
)
with pytest.raises(click.ClickException, match="no-prompt"):
ip.patch_iso(
base, autounattend_xml="<x/>", post_install_ps1="x",
out_iso=tmp_path / "out.iso", work_dir=tmp_path,
)