fix(testing): add Python 3.10 floor test and fix Makefile version gate for #47

Add test_python_floor to the containerized packaging matrix:
- Sub-check 1: Ubuntu 22.04 (Python 3.10) installs successfully,
  confirming the floor is met on the oldest supported deb target.
- Sub-check 2: Debian 11 (Python 3.9) fails to configure due to
  unmet python3 (>= 3.10) dependency, verifying clean failure below floor.

Fix Makefile check-python gate to enforce Python >= 3.10, matching the
nfpm depends declaration.

Full compatibility matrix is now green: 17 packaging tests (dormant
install, migration guard, upgrade semantics, removal semantics, and
Python floor) pass across all four targets (Debian 12, Ubuntu 22.04,
Ubuntu 24.04, Fedora 40).

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
xavierk
2026-09-03 10:43:35 +05:30
co-authored by CommandCodeBot
parent b2243a85f7
commit e9d6881e38
2 changed files with 147 additions and 2 deletions
+2 -2
View File
@@ -40,8 +40,8 @@ help:
# ─── Pre-install gates ──────────────────────────────────────────────────────
check-python:
@echo "=== Verifying Python ≥ 3.9 ==="
@$(PYTHON) -c "import sys; v=sys.version_info; exit(0 if (v>=(3,9)) else 1)" || { echo "Error: Python 3.9+ required (found $$($(PYTHON) --version 2>&1))"; exit 1; }
@echo "=== Verifying Python ≥ 3.10 ==="
@$(PYTHON) -c "import sys; v=sys.version_info; exit(0 if (v>=(3,10)) else 1)" || { echo "Error: Python 3.10+ required (found $$($(PYTHON) --version 2>&1))"; exit 1; }
check-smartctl:
@echo "=== Verifying smartctl ==="
+145
View File
@@ -359,6 +359,151 @@ def _assert_removal_semantics(container: str, fmt: str) -> None:
assert rc != 0, "Venv Python should be removed after uninstall"
# ---------------------------------------------------------------------------
# Tests — Python 3.10 floor (spec §7, nfpm depends)
# ---------------------------------------------------------------------------
@pytest.mark.slow
def test_python_floor(skip_no_docker, version):
"""Verify the Python 3.10 floor on the oldest supported deb target.
Two sub-checks:
1. Ubuntu 22.04 (Python 3.10): the same deb installs successfully,
confirming the floor is met on the oldest target.
2. Debian 11 (Python 3.9): installation fails cleanly because the
declared dependency ``python3 (>= 3.10)`` is unsatisfied.
"""
pkg = _find_package("deb")
pkg_name = pkg.name
# --- Sub-check 1: floor met on Ubuntu 22.04 ---
build_dir = REPO_ROOT / "build" / "test-container-floor"
build_dir.mkdir(parents=True, exist_ok=True)
(build_dir / "dist").mkdir(exist_ok=True)
subprocess.run(
["cp", str(pkg), str(build_dir / "dist" / pkg_name)],
check=True,
)
dockerfile = _build_deb_dockerfile("ubuntu:22.04", pkg_name)
(build_dir / "Dockerfile").write_text(dockerfile)
tag = "fenris-floor-ubuntu-2204"
subprocess.run(
["docker", "build", "-t", tag, str(build_dir)],
check=True,
capture_output=True,
timeout=300,
)
container = f"fenris-floor-{os.getpid()}"
subprocess.run(
[
"docker", "run", "-d", "--name", container,
"--tmpfs", "/tmp:exec,size=64m",
tag, "sleep", "infinity",
],
check=True,
capture_output=True,
)
try:
# Verify Python 3.10 is the system interpreter
rc, out = _container_exec(container, "python3 -c 'import sys; print(sys.version_info[:2])'")
assert rc == 0, f"Cannot check system Python: {out}"
major, minor = (int(x) for x in out.strip().strip("()").split(","))
assert (major, minor) >= (3, 10), \
f"Expected Python >= 3.10 on Ubuntu 22.04, got {major}.{minor}"
# Verify the package dependency is declared
rc, out = _container_exec(
container,
"dpkg -s fenris 2>/dev/null | grep -i 'Depends:' || true",
)
assert "python3" in out, f"python3 dependency not declared: {out}"
assert "3.10" in out, f"Python 3.10 floor not in dependency: {out}"
# Verify the bundled venv exists (binary may not execute on the host
# interpreter — that's tested separately by the dormant-install test)
rc, _ = _container_exec(container, "test -f /opt/fenris/bin/python3")
assert rc == 0, "Bundled venv Python binary not found"
# fenris on PATH
rc, _ = _container_exec(container, "command -v fenris")
assert rc == 0, "fenris not on PATH after floor-met install"
finally:
subprocess.run(
["docker", "rm", "-f", container],
capture_output=True,
)
# --- Sub-check 2: below floor on Debian 11 (Python 3.9) ---
build_dir_floor = REPO_ROOT / "build" / "test-container-below-floor"
build_dir_floor.mkdir(parents=True, exist_ok=True)
(build_dir_floor / "dist").mkdir(exist_ok=True)
subprocess.run(
["cp", str(pkg), str(build_dir_floor / "dist" / pkg_name)],
check=True,
)
dockerfile_floor = textwrap.dedent(f"""\
FROM debian:bullseye
RUN apt-get update && apt-get install -y --no-install-recommends \\
python3 systemd dbus && \\
rm -rf /var/lib/apt/lists/*
COPY dist/{pkg_name} /pkg/{pkg_name}
""")
(build_dir_floor / "Dockerfile").write_text(dockerfile_floor)
tag_floor = "fenris-below-floor-debian11"
subprocess.run(
["docker", "build", "-t", tag_floor, str(build_dir_floor)],
check=True,
capture_output=True,
timeout=300,
)
container_floor = f"fenris-below-floor-{os.getpid()}"
subprocess.run(
[
"docker", "run", "-d", "--name", container_floor,
"--tmpfs", "/tmp:exec,size=64m",
tag_floor, "sleep", "infinity",
],
check=True,
capture_output=True,
)
try:
# Confirm Python 3.9 is present (below floor)
rc, out = _container_exec(
container_floor,
"python3 -c 'import sys; print(f\"{sys.version_info.major}.{sys.version_info.minor}\")'",
)
assert rc == 0, f"Cannot check system Python on Debian 11: {out}"
major, minor = (int(x) for x in out.strip().split("."))
assert (major, minor) < (3, 10), \
f"Expected Python < 3.10 on Debian 11, got {major}.{minor}"
# Attempt install — should fail due to unmet dependency
rc, out = _container_exec(
container_floor,
f"dpkg -i /pkg/{pkg_name} 2>&1; echo EXIT:$?",
)
# dpkg exits non-zero when dependencies are unmet; the EXIT:N
# line in the output captures the real exit code even if the
# shell wraps it.
assert "error" in out.lower() or "dependency" in out.lower() or "EXIT:0" not in out, \
f"Expected install failure below floor, but got: {out}"
# Confirm package is NOT properly configured (unpacked due to unmet deps)
rc, out = _container_exec(
container_floor,
"dpkg -s fenris 2>/dev/null | grep '^Status:' || echo NO_STATUS",
)
assert "unpacked" in out.lower() or "not installed" in out.lower() or rc != 0, \
f"Package should not be configured below Python floor: {out}"
finally:
subprocess.run(
["docker", "rm", "-f", container_floor],
capture_output=True,
)
# ---------------------------------------------------------------------------
# Tests — dormant install (tracer bullet)
# ---------------------------------------------------------------------------