chore: import upstream snapshot with attribution
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Guards against CI collecting zero tests under pytest-xdist.
|
||||
|
||||
Two failure modes have produced ``N workers [0 items]`` in CI:
|
||||
|
||||
1. A mangled ``PYTEST_MARKER_EXPR`` (e.g. boolean ``false``) deselects everything.
|
||||
``tests/conftest.py`` forces exit code 5 in that case.
|
||||
|
||||
2. A missing path argument (file/dir deleted but still listed in
|
||||
``.github/workflows/ci.yml``) makes xdist abort collection for the *whole*
|
||||
shard — even when other paths are valid. Seen after ``tests/github`` was
|
||||
removed while ``cli-runtime`` still referenced it.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
_CI_WORKFLOW = _REPO_ROOT / ".github" / "workflows" / "ci.yml"
|
||||
_PATH_RE = re.compile(r"^(tests/\S+|gateway/tests)$")
|
||||
|
||||
|
||||
def test_xdist_empty_marker_exits_no_tests_collected() -> None:
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-m",
|
||||
"pytest",
|
||||
"-n",
|
||||
"2",
|
||||
"-q",
|
||||
"tests/packaging",
|
||||
"-m",
|
||||
"false",
|
||||
],
|
||||
cwd=_REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
assert "0 items" in result.stdout or "0 items" in result.stderr
|
||||
assert result.returncode == 5, (
|
||||
f"expected ExitCode.NO_TESTS_COLLECTED (5), got {result.returncode}\n"
|
||||
f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
|
||||
)
|
||||
|
||||
|
||||
def test_ci_pytest_paths_exist_in_git_tree() -> None:
|
||||
"""Every ``matrix.pytest_paths`` entry must exist in the committed tree.
|
||||
|
||||
Local empty leftover dirs (e.g. ``tests/github/`` with only ``__pycache__``)
|
||||
hide this; CI checkouts do not have them, and a missing path zeros xdist.
|
||||
"""
|
||||
tracked = set(
|
||||
subprocess.check_output(
|
||||
["git", "-C", str(_REPO_ROOT), "ls-tree", "-r", "--name-only", "HEAD"],
|
||||
text=True,
|
||||
).splitlines()
|
||||
)
|
||||
|
||||
def _present(path: str) -> bool:
|
||||
if path in tracked:
|
||||
return True
|
||||
prefix = path.rstrip("/") + "/"
|
||||
return any(entry.startswith(prefix) for entry in tracked)
|
||||
|
||||
workflow = yaml.safe_load(_CI_WORKFLOW.read_text(encoding="utf-8"))
|
||||
missing: list[str] = []
|
||||
for job in workflow.get("jobs", {}).values():
|
||||
matrix = (job.get("strategy") or {}).get("matrix") or {}
|
||||
for entry in matrix.get("include") or []:
|
||||
raw = entry.get("pytest_paths")
|
||||
if not raw:
|
||||
continue
|
||||
shard = entry.get("shard", "?")
|
||||
for token in str(raw).split():
|
||||
if not _PATH_RE.match(token):
|
||||
continue
|
||||
if not _present(token):
|
||||
missing.append(f"{shard}: {token}")
|
||||
|
||||
assert not missing, (
|
||||
"CI pytest_paths missing from git tree (xdist will collect 0 items):\n"
|
||||
+ "\n".join(f" - {item}" for item in missing)
|
||||
)
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Architecture guards for external-system package boundaries."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import subprocess
|
||||
import tomllib
|
||||
from pathlib import Path
|
||||
|
||||
from config.constants.paths import REPO_ROOT
|
||||
|
||||
ROOT = REPO_ROOT
|
||||
FORBIDDEN_TOP_LEVEL_PACKAGES = frozenset({"services", "vendors"})
|
||||
|
||||
|
||||
def _tracked_files(*pathspecs: str) -> list[Path]:
|
||||
result = subprocess.run(
|
||||
["git", "ls-files", "--", *pathspecs],
|
||||
cwd=ROOT,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return [path for line in result.stdout.splitlines() if (path := ROOT / line).exists()]
|
||||
|
||||
|
||||
def _forbidden_imports(path: Path) -> set[str]:
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
|
||||
imports: set[str] = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
imports.update(alias.name for alias in node.names)
|
||||
elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module:
|
||||
imports.add(node.module)
|
||||
return {module for module in imports if module.split(".", 1)[0] in FORBIDDEN_TOP_LEVEL_PACKAGES}
|
||||
|
||||
|
||||
def test_no_tracked_python_imports_from_removed_external_packages() -> None:
|
||||
leaks = {
|
||||
str(path.relative_to(ROOT)): sorted(_forbidden_imports(path))
|
||||
for path in _tracked_files("*.py")
|
||||
if _forbidden_imports(path)
|
||||
}
|
||||
assert leaks == {}
|
||||
|
||||
|
||||
def test_removed_external_packages_have_no_tracked_files() -> None:
|
||||
assert _tracked_files("services", "services/**", "vendors", "vendors/**") == []
|
||||
|
||||
|
||||
def test_tools_registry_does_not_scan_removed_external_packages() -> None:
|
||||
registry_source = (ROOT / "tools/registry.py").read_text(encoding="utf-8")
|
||||
assert '"vendors"' not in registry_source
|
||||
assert '"services"' not in registry_source
|
||||
assert "'vendors'" not in registry_source
|
||||
assert "'services'" not in registry_source
|
||||
|
||||
|
||||
def test_pyproject_does_not_package_removed_external_packages() -> None:
|
||||
pyproject = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))
|
||||
package_includes = set(pyproject["tool"]["setuptools"]["packages"]["find"]["include"])
|
||||
vulture_paths = set(pyproject["tool"]["vulture"]["paths"])
|
||||
|
||||
assert "services*" not in package_includes
|
||||
assert "vendors*" not in package_includes
|
||||
assert "services" not in vulture_paths
|
||||
assert "vendors" not in vulture_paths
|
||||
@@ -0,0 +1,36 @@
|
||||
from pathlib import Path
|
||||
|
||||
from config.constants.paths import REPO_ROOT
|
||||
|
||||
LEGACY_TOKENS = ("tests/test_case_", "test_case=test_case_", "test_orchestrator")
|
||||
|
||||
|
||||
def _scan_paths() -> list[Path]:
|
||||
paths: list[Path] = [
|
||||
REPO_ROOT / "README.md",
|
||||
REPO_ROOT / "pyproject.toml",
|
||||
REPO_ROOT / "Makefile",
|
||||
REPO_ROOT / "tests" / "README.md",
|
||||
]
|
||||
paths.extend((REPO_ROOT / ".github" / "workflows").rglob("*.yml"))
|
||||
paths.extend((REPO_ROOT / "tests" / "e2e").rglob("*.py"))
|
||||
paths.extend((REPO_ROOT / "tests" / "synthetic").rglob("*.py"))
|
||||
return paths
|
||||
|
||||
|
||||
def test_no_legacy_test_catalog_names() -> None:
|
||||
offenders: list[str] = []
|
||||
missing_paths: list[str] = []
|
||||
|
||||
for path in _scan_paths():
|
||||
if not path.exists():
|
||||
missing_paths.append(str(path.relative_to(REPO_ROOT)))
|
||||
continue
|
||||
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for token in LEGACY_TOKENS:
|
||||
if token in text:
|
||||
offenders.append(f"{path.relative_to(REPO_ROOT)} contains '{token}'")
|
||||
|
||||
assert missing_paths == []
|
||||
assert offenders == []
|
||||
@@ -0,0 +1,78 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from config.constants.paths import REPO_ROOT
|
||||
from tests.utils.tracked_sources import tracked_files, tracked_python_files
|
||||
|
||||
ROOT = REPO_ROOT
|
||||
SKIP_DIRS = {
|
||||
".git",
|
||||
".mypy_cache",
|
||||
".pytest_cache",
|
||||
".ruff_cache",
|
||||
".venv",
|
||||
".venv-devcontainer",
|
||||
"__pycache__",
|
||||
"build",
|
||||
"htmlcov",
|
||||
"node_modules",
|
||||
"opensre.egg-info",
|
||||
"plans",
|
||||
"tasks",
|
||||
}
|
||||
TEXT_SUFFIXES = {
|
||||
".cfg",
|
||||
".ini",
|
||||
".json",
|
||||
".md",
|
||||
".mdc",
|
||||
".mdx",
|
||||
".py",
|
||||
".toml",
|
||||
".txt",
|
||||
".yaml",
|
||||
".yml",
|
||||
}
|
||||
|
||||
|
||||
def _iter_repo_text_files() -> list[Path]:
|
||||
files: list[Path] = []
|
||||
for path in tracked_files(str(ROOT)):
|
||||
parts = path.parts
|
||||
if any(part in SKIP_DIRS for part in parts):
|
||||
continue
|
||||
if "site-packages" in parts:
|
||||
continue
|
||||
if not path.is_file():
|
||||
continue
|
||||
if path.name in {"Dockerfile", "Makefile"} or path.suffix in TEXT_SUFFIXES:
|
||||
files.append(path)
|
||||
return files
|
||||
|
||||
|
||||
def test_removed_framework_names_do_not_reappear() -> None:
|
||||
removed = ("lang" + "graph", "lang" + "chain", "lang" + "smith")
|
||||
offenders: list[str] = []
|
||||
|
||||
for path in _iter_repo_text_files():
|
||||
text = path.read_text(encoding="utf-8", errors="ignore").lower()
|
||||
if any(token in text for token in removed):
|
||||
offenders.append(str(path.relative_to(ROOT)))
|
||||
|
||||
assert offenders == []
|
||||
|
||||
|
||||
def test_deleted_app_nodes_package_is_not_referenced_by_python_code() -> None:
|
||||
deleted_package = "app." + "nodes"
|
||||
offenders: list[str] = []
|
||||
|
||||
for path in tracked_python_files(str(ROOT)):
|
||||
rel = path.relative_to(ROOT)
|
||||
if not rel.parts or rel.parts[0] not in {"app", "tests"}:
|
||||
continue
|
||||
text = path.read_text(encoding="utf-8", errors="ignore")
|
||||
if deleted_package in text:
|
||||
offenders.append(str(rel))
|
||||
|
||||
assert offenders == []
|
||||
Reference in New Issue
Block a user