154 lines
5.2 KiB
Python
154 lines
5.2 KiB
Python
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
import zipfile
|
|
|
|
|
|
SCRIPT = (
|
|
Path(__file__).resolve().parents[1]
|
|
/ "tools"
|
|
/ "release"
|
|
/ "prepare-runtime-context.py"
|
|
)
|
|
SPEC = importlib.util.spec_from_file_location("prepare_runtime_context", SCRIPT)
|
|
assert SPEC is not None and SPEC.loader is not None
|
|
MODULE = importlib.util.module_from_spec(SPEC)
|
|
sys.modules[SPEC.name] = MODULE
|
|
SPEC.loader.exec_module(MODULE)
|
|
|
|
|
|
class RuntimeImageContextTests(unittest.TestCase):
|
|
def test_builds_deterministic_network_free_context(self) -> None:
|
|
with tempfile.TemporaryDirectory(prefix="govoplan-runtime-context-") as value:
|
|
root = Path(value)
|
|
wheelhouse = root / "input-wheels"
|
|
web = root / "web"
|
|
wheelhouse.mkdir()
|
|
web.mkdir()
|
|
self._wheel(
|
|
wheelhouse / "govoplan_core-1.2.3-py3-none-any.whl",
|
|
package="govoplan-core",
|
|
version="1.2.3",
|
|
module_ids=(),
|
|
)
|
|
self._wheel(
|
|
wheelhouse / "govoplan_files-1.2.3-py3-none-any.whl",
|
|
package="govoplan-files",
|
|
version="1.2.3",
|
|
module_ids=("files",),
|
|
)
|
|
self._wheel(
|
|
wheelhouse / "sqlalchemy-2.0.0-py3-none-any.whl",
|
|
package="SQLAlchemy",
|
|
version="2.0.0",
|
|
module_ids=(),
|
|
)
|
|
(web / "index.html").write_text("<main>GovOPlaN</main>\n", encoding="utf-8")
|
|
|
|
composition = MODULE.prepare_context(
|
|
wheelhouse=wheelhouse,
|
|
web_dist=web,
|
|
output=root / "context",
|
|
required_modules=("files",),
|
|
source_date_epoch=1_700_000_000,
|
|
)
|
|
|
|
self.assertEqual(["files"], composition["python"]["module_ids"])
|
|
self.assertEqual(2, composition["python"]["wheel_count"])
|
|
requirements = (root / "context" / "requirements-runtime.txt").read_text()
|
|
self.assertEqual(
|
|
"govoplan-core[server]==1.2.3\ngovoplan-files==1.2.3\n",
|
|
requirements,
|
|
)
|
|
published = json.loads(
|
|
(
|
|
root
|
|
/ "context"
|
|
/ "web-dist"
|
|
/ ".well-known"
|
|
/ "govoplan-composition.json"
|
|
).read_text()
|
|
)
|
|
self.assertEqual(composition, published)
|
|
|
|
def test_rejects_missing_required_module(self) -> None:
|
|
with tempfile.TemporaryDirectory(prefix="govoplan-runtime-context-") as value:
|
|
root = Path(value)
|
|
wheelhouse = root / "wheels"
|
|
web = root / "web"
|
|
wheelhouse.mkdir()
|
|
web.mkdir()
|
|
self._wheel(
|
|
wheelhouse / "govoplan_core-1.0.0-py3-none-any.whl",
|
|
package="govoplan-core",
|
|
version="1.0.0",
|
|
module_ids=(),
|
|
)
|
|
(web / "index.html").write_text("ok", encoding="utf-8")
|
|
|
|
with self.assertRaisesRegex(MODULE.ContextError, "missing required"):
|
|
MODULE.prepare_context(
|
|
wheelhouse=wheelhouse,
|
|
web_dist=web,
|
|
output=root / "context",
|
|
required_modules=("mail",),
|
|
)
|
|
|
|
def test_rejects_symlinked_web_payload(self) -> None:
|
|
with tempfile.TemporaryDirectory(prefix="govoplan-runtime-context-") as value:
|
|
root = Path(value)
|
|
wheelhouse = root / "wheels"
|
|
web = root / "web"
|
|
wheelhouse.mkdir()
|
|
web.mkdir()
|
|
self._wheel(
|
|
wheelhouse / "govoplan_core-1.0.0-py3-none-any.whl",
|
|
package="govoplan-core",
|
|
version="1.0.0",
|
|
module_ids=(),
|
|
)
|
|
outside = root / "outside"
|
|
outside.write_text("not part of dist", encoding="utf-8")
|
|
(web / "index.html").symlink_to(outside)
|
|
|
|
with self.assertRaisesRegex(MODULE.ContextError, "symlink"):
|
|
MODULE.prepare_context(
|
|
wheelhouse=wheelhouse,
|
|
web_dist=web,
|
|
output=root / "context",
|
|
)
|
|
|
|
@staticmethod
|
|
def _wheel(
|
|
path: Path,
|
|
*,
|
|
package: str,
|
|
version: str,
|
|
module_ids: tuple[str, ...],
|
|
) -> None:
|
|
dist_info = package.replace("-", "_") + f"-{version}.dist-info"
|
|
with zipfile.ZipFile(path, "w") as archive:
|
|
archive.writestr(
|
|
f"{dist_info}/METADATA",
|
|
f"Metadata-Version: 2.1\nName: {package}\nVersion: {version}\n",
|
|
)
|
|
if module_ids:
|
|
rows = "\n".join(
|
|
f"{module_id} = example.module:manifest"
|
|
for module_id in module_ids
|
|
)
|
|
archive.writestr(
|
|
f"{dist_info}/entry_points.txt",
|
|
f"[govoplan.modules]\n{rows}\n",
|
|
)
|
|
archive.writestr(f"{package.replace('-', '_')}/__init__.py", "")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|