Files
govoplan-core/tests/test_wheel_runtime.py

144 lines
5.0 KiB
Python

from __future__ import annotations
import json
import os
from pathlib import Path
import subprocess
import sys
import sysconfig
import tempfile
import unittest
class WheelRuntimeTests(unittest.TestCase):
def test_built_wheel_contains_and_runs_core_migrations_without_source_checkout(self) -> None:
repository_root = Path(__file__).resolve().parents[1]
access_repository = repository_root.parent / "govoplan-access"
self.assertTrue(access_repository.is_dir(), "wheel runtime check requires the mandatory Access repository")
with tempfile.TemporaryDirectory(prefix="govoplan-core-wheel-") as directory:
temporary_root = Path(directory)
wheel_directory = temporary_root / "wheels"
wheel_directory.mkdir()
self._run(
sys.executable,
"-m",
"pip",
"wheel",
"--no-deps",
"--wheel-dir",
str(wheel_directory),
str(repository_root),
cwd=temporary_root,
)
self._run(
sys.executable,
"-m",
"pip",
"wheel",
"--no-deps",
"--wheel-dir",
str(wheel_directory),
str(access_repository),
cwd=temporary_root,
)
core_wheels = tuple(wheel_directory.glob("govoplan_core-*.whl"))
access_wheels = tuple(wheel_directory.glob("govoplan_access-*.whl"))
self.assertEqual(1, len(core_wheels))
self.assertEqual(1, len(access_wheels))
virtual_environment = temporary_root / "venv"
self._run(sys.executable, "-m", "venv", str(virtual_environment), cwd=temporary_root)
venv_python = virtual_environment / "bin" / "python"
self._run(
str(venv_python),
"-m",
"pip",
"install",
"--no-deps",
str(core_wheels[0]),
str(access_wheels[0]),
cwd=temporary_root,
)
database_path = temporary_root / "wheel-runtime.db"
probe = self._run(
str(venv_python),
"-c",
self._probe_script(),
str(database_path),
cwd=temporary_root,
env={
**os.environ,
# Reuse only third-party test dependencies from the parent
# environment. Editable .pth files are not processed from
# PYTHONPATH, so Core itself can only come from the wheel.
"PYTHONPATH": sysconfig.get_path("purelib"),
"GOVOPLAN_CORE_SOURCE_ROOT": "",
"GOVOPLAN_ENABLED_MODULES": "",
},
)
result = json.loads(probe.stdout)
installed_package = Path(result["package_file"])
runtime_root = Path(result["runtime_root"])
self.assertTrue(installed_package.is_relative_to(virtual_environment))
self.assertTrue(runtime_root.is_relative_to(virtual_environment))
self.assertNotEqual(repository_root, runtime_root)
self.assertTrue((runtime_root / "alembic.ini").is_file())
self.assertTrue((runtime_root / "alembic" / "env.py").is_file())
self.assertIn("4f2a9c8e7b6d", result["heads"])
self.assertIn("core_scopes", result["tables"])
self.assertIn("core_system_settings", result["tables"])
@staticmethod
def _run(*command: str, cwd: Path, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]:
result = subprocess.run(
command,
cwd=cwd,
env=env,
check=False,
capture_output=True,
text=True,
)
if result.returncode:
raise AssertionError(
f"Command failed ({result.returncode}): {' '.join(command)}\n"
f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
)
return result
@staticmethod
def _probe_script() -> str:
return """
import json
from pathlib import Path
import sys
from alembic import command
from alembic.script import ScriptDirectory
from sqlalchemy import create_engine, inspect
import govoplan_core
from govoplan_core.db.migrations import _repo_root, alembic_config
database_url = f"sqlite:///{Path(sys.argv[1])}"
config = alembic_config(database_url=database_url, enabled_modules=())
command.upgrade(config, "heads")
script = ScriptDirectory.from_config(config)
engine = create_engine(database_url)
try:
with engine.connect() as connection:
tables = sorted(inspect(connection).get_table_names())
finally:
engine.dispose()
print(json.dumps({
"package_file": govoplan_core.__file__,
"runtime_root": str(_repo_root()),
"heads": sorted(script.get_heads()),
"tables": tables,
}))
"""
if __name__ == "__main__":
unittest.main()