103 lines
3.4 KiB
Python
103 lines
3.4 KiB
Python
"""Composition safety checks (task 9.1).
|
|
|
|
Verifies that the cloud workspace package is purely additive: every existing
|
|
module it composes (``runtime.task``, ``workflow.runner``, ``driver.registry``,
|
|
``api.mcp``) remains unaware of the ``cloud`` package in its source.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import os
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
CLOUD_SOURCE_ROOT = PROJECT_ROOT / "packages" / "cloud-platform" / "cloud"
|
|
|
|
|
|
def _module_source(path: Path) -> str:
|
|
return path.read_text(encoding="utf-8")
|
|
|
|
|
|
def _existing_module_paths() -> list[Path]:
|
|
"""Return source files cloud/ must not edit or import-back into."""
|
|
folders = [
|
|
"core",
|
|
"driver",
|
|
"device",
|
|
"runtime",
|
|
"tools",
|
|
"workflow",
|
|
"agents",
|
|
"storage",
|
|
]
|
|
files: list[Path] = []
|
|
for folder in folders:
|
|
root = PROJECT_ROOT / folder
|
|
if not root.exists():
|
|
continue
|
|
for path in root.rglob("*.py"):
|
|
files.append(path)
|
|
mcp = PROJECT_ROOT / "api" / "mcp.py"
|
|
if mcp.exists():
|
|
files.append(mcp)
|
|
return files
|
|
|
|
|
|
def test_existing_modules_do_not_import_cloud() -> None:
|
|
"""No composed-over module imports ``cloud`` (cloud is one-directional)."""
|
|
offenders: list[str] = []
|
|
for path in _existing_module_paths():
|
|
try:
|
|
source = _module_source(path)
|
|
except OSError:
|
|
continue
|
|
# Look for an actual import of cloud, not the literal word "cloud" in comments.
|
|
for line in source.splitlines():
|
|
stripped = line.strip()
|
|
if stripped.startswith("#"):
|
|
continue
|
|
if (
|
|
"import cloud" in stripped
|
|
or "from cloud" in stripped
|
|
or "import cloud." in stripped
|
|
):
|
|
offenders.append(f"{path}: {stripped}")
|
|
assert not offenders, (
|
|
"cloud/ must compose other packages by import only; the following "
|
|
"existing modules import cloud back (forbidden): " + "; ".join(offenders)
|
|
)
|
|
|
|
|
|
def test_cloud_dispatch_imports_existing_runners_by_name() -> None:
|
|
"""dispatch.py should reference runtime.task.TaskRunner and workflow.runner.WorkflowRunner."""
|
|
dispatch = importlib.import_module("cloud.dispatch")
|
|
source = _module_source(Path(dispatch.__file__)) # type: ignore[arg-type]
|
|
# TaskRunner/WorkflowRunner are referenced via factory callables, not direct
|
|
# imports, so we check for the contract being composed over in docstrings/types.
|
|
assert "TaskRunner" in source or "task_runner_factory" in source
|
|
assert "WorkflowRunner" in source or "workflow_runner_factory" in source
|
|
|
|
|
|
def test_cloud_source_files_exist_only_under_cloud_workspace_member() -> None:
|
|
"""Cloud source files are owned by the cloud-platform workspace member."""
|
|
assert CLOUD_SOURCE_ROOT.exists()
|
|
expected_files = {
|
|
"__init__.py",
|
|
"config.py",
|
|
"pool.py",
|
|
"store.py",
|
|
"scheduler.py",
|
|
"dispatch.py",
|
|
"plugins.py",
|
|
"sdk/__init__.py",
|
|
"sdk/api.py",
|
|
"sdk/client.py",
|
|
"sdk/models.py",
|
|
}
|
|
found: set[str] = set()
|
|
for path in CLOUD_SOURCE_ROOT.rglob("*.py"):
|
|
found.add(str(path.relative_to(CLOUD_SOURCE_ROOT)).replace(os.sep, "/"))
|
|
missing = expected_files - found
|
|
assert not missing, f"missing cloud source files: {sorted(missing)}"
|