80bc9c552d
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 33s
CI / helm (pull_request) Successful in 45s
CI / build (pull_request) Successful in 1m4s
CI / lint (pull_request) Successful in 1m13s
CI / typecheck (pull_request) Successful in 1m31s
CI / quality (pull_request) Successful in 1m37s
CI / security (pull_request) Successful in 1m49s
CI / integration_tests (pull_request) Successful in 3m34s
CI / e2e_tests (pull_request) Successful in 5m6s
CI / unit_tests (pull_request) Successful in 6m14s
CI / docker (pull_request) Successful in 1m36s
CI / coverage (pull_request) Successful in 12m27s
CI / status-check (push) Blocked by required conditions
CI / coverage (push) Blocked by required conditions
CI / docker (push) Blocked by required conditions
CI / unit_tests (push) Has started running
CI / status-check (pull_request) Successful in 4s
CI / helm (push) Successful in 31s
CI / build (push) Successful in 53s
CI / lint (push) Successful in 1m3s
CI / quality (push) Successful in 1m13s
CI / typecheck (push) Successful in 1m46s
CI / security (push) Successful in 1m46s
CI / push-validation (push) Successful in 20s
CI / benchmark-publish (push) Failing after 43s
CI / e2e_tests (push) Successful in 4m9s
CI / integration_tests (push) Successful in 6m57s
Fix malformed imports in helper_m1_e2e_verification.py and helper_m4_e2e_cli.py where 'from helpers_common import reset_global_state' was incorrectly inserted inside a parenthesized import block, causing Python syntax errors and breaking the lint and unit_tests CI gates. Also fix import ordering in helper_m2_e2e_verification.py, helper_m5_e2e_context.py, helper_m5_e2e_support.py, and helper_m5_e2e_verification.py to satisfy ruff I001 import-sort rules by placing helpers_common imports in the correct group alongside other local robot/ helper imports. ISSUES CLOSED: #8459
270 lines
8.9 KiB
Python
270 lines
8.9 KiB
Python
"""Robot Framework helper for M5 E2E verification tests.
|
|
|
|
Exercises the complete M5 success criteria sequence:
|
|
1. Large project creation with 10,000+ simulated files
|
|
2. Resource registration and linking to a project
|
|
3. Indexing verification (resource linked, project shows correctly)
|
|
4. Context tier management (hot/warm/cold) via ContextConfig
|
|
5. ACMS v1 context policy set/show with persistence round-trip
|
|
6. Phase view inheritance (default -> strategize -> execute -> apply)
|
|
7. Scoped context output per ACMS phase
|
|
8. Policy clear and inheritance fallback
|
|
|
|
Each subcommand prints a sentinel string on success and exits 0.
|
|
On failure it prints a diagnostic to stderr and exits 1.
|
|
|
|
Usage:
|
|
python robot/helper_m5_e2e_verification.py <command>
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
|
|
# Ensure src is importable when run from workspace root
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
from helper_m5_e2e_context import ( # noqa: E402
|
|
acms_phase_inheritance,
|
|
acms_scoped_context,
|
|
context_policy_clear,
|
|
context_policy_set_show,
|
|
context_tier_config,
|
|
context_view_validation,
|
|
llm_execute_acms_context,
|
|
)
|
|
from helper_m5_e2e_support import ( # noqa: E402
|
|
_create_large_python_repo,
|
|
_create_project,
|
|
_fail,
|
|
_setup_db,
|
|
)
|
|
from helpers_common import reset_global_state # noqa: E402
|
|
|
|
from cleveragents.application.services.repo_indexing_service import ( # noqa: E402
|
|
RepoIndexingService,
|
|
)
|
|
from cleveragents.domain.models.core.repo_index import IndexStatus # noqa: E402
|
|
|
|
# -------------------------------------------------------------------
|
|
# Subcommand: project-create-large
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
def project_create_large() -> None:
|
|
"""Create a project and simulate a 10,000+ file repository.
|
|
|
|
Verifies project creation, resource registration, linking,
|
|
and that the project show reflects the linked resource.
|
|
"""
|
|
reset_global_state()
|
|
proj_repo, link_repo, svc, _sf = _setup_db()
|
|
|
|
proj = _create_project(
|
|
proj_repo,
|
|
"local/large-project",
|
|
description="Large project with 10,000+ file repo",
|
|
)
|
|
if proj.namespaced_name != "local/large-project":
|
|
_fail(f"project name mismatch: {proj.namespaced_name}")
|
|
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
repo_path = Path(tmp) / "large-repo"
|
|
repo_path.mkdir()
|
|
|
|
file_count = _create_large_python_repo(repo_path)
|
|
if file_count < 10_000:
|
|
_fail(f"expected >= 10,000 files, created {file_count}")
|
|
|
|
res = svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/large-repo",
|
|
location=str(repo_path),
|
|
description="Large repository (10K+ files)",
|
|
properties={"path": str(repo_path), "branch": "main"},
|
|
)
|
|
|
|
if not res.resource_id:
|
|
_fail("resource_id is empty")
|
|
|
|
link_repo.create_link(
|
|
project_name="local/large-project",
|
|
resource_id=res.resource_id,
|
|
)
|
|
|
|
links = link_repo.list_links("local/large-project")
|
|
if len(links) != 1:
|
|
_fail(f"expected 1 link, got {len(links)}")
|
|
|
|
fetched = proj_repo.get("local/large-project")
|
|
if fetched.description != "Large project with 10,000+ file repo":
|
|
_fail(f"description mismatch: {fetched.description}")
|
|
|
|
print("m5-project-create-large-ok")
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Subcommand: resource-register-link
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
def resource_register_link() -> None:
|
|
"""Register a git-checkout resource and link it to a project."""
|
|
reset_global_state()
|
|
proj_repo, link_repo, svc, _sf = _setup_db()
|
|
|
|
proj = _create_project(proj_repo, "local/link-test")
|
|
|
|
res1 = svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/repo-alpha",
|
|
location="/tmp/alpha",
|
|
description="Alpha repo",
|
|
properties={"path": "/tmp/alpha", "branch": "main"},
|
|
)
|
|
res2 = svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/repo-beta",
|
|
location="/tmp/beta",
|
|
description="Beta repo",
|
|
properties={"path": "/tmp/beta", "branch": "develop"},
|
|
)
|
|
|
|
link_repo.create_link(
|
|
project_name=proj.namespaced_name,
|
|
resource_id=res1.resource_id,
|
|
)
|
|
link_repo.create_link(
|
|
project_name=proj.namespaced_name,
|
|
resource_id=res2.resource_id,
|
|
)
|
|
|
|
links = link_repo.list_links(proj.namespaced_name)
|
|
if len(links) != 2:
|
|
_fail(f"expected 2 links, got {len(links)}")
|
|
|
|
shown = svc.show_resource("local/repo-alpha")
|
|
if shown.name != "local/repo-alpha":
|
|
_fail(f"resource name mismatch: {shown.name}")
|
|
if shown.resource_type_name != "git-checkout":
|
|
_fail(f"type mismatch: {shown.resource_type_name}")
|
|
|
|
print("m5-resource-register-link-ok")
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Subcommand: indexing-complete
|
|
# -------------------------------------------------------------------
|
|
|
|
|
|
def indexing_complete() -> None:
|
|
"""Verify indexing completes: resource is linked, project shows OK."""
|
|
reset_global_state()
|
|
proj_repo, link_repo, svc, sf = _setup_db()
|
|
|
|
proj = _create_project(proj_repo, "local/idx-project")
|
|
with tempfile.TemporaryDirectory(prefix="m5_idx_repo_") as tmp:
|
|
repo_root = Path(tmp) / "idx-repo"
|
|
repo_root.mkdir(parents=True, exist_ok=True)
|
|
|
|
file_count = _create_large_python_repo(repo_root)
|
|
if file_count < 10_000:
|
|
_fail(f"expected >= 10,000 files, created {file_count}")
|
|
|
|
res = svc.register_resource(
|
|
type_name="git-checkout",
|
|
name="local/idx-repo",
|
|
location=str(repo_root),
|
|
description="Index test",
|
|
properties={"path": str(repo_root), "branch": "main"},
|
|
)
|
|
link_repo.create_link(
|
|
project_name=proj.namespaced_name,
|
|
resource_id=res.resource_id,
|
|
)
|
|
|
|
links = link_repo.list_links(proj.namespaced_name)
|
|
if len(links) != 1:
|
|
_fail(f"expected 1 link, got {len(links)}")
|
|
|
|
index_service = RepoIndexingService(session_factory=sf)
|
|
timeout_seconds = 120.0
|
|
started = time.monotonic()
|
|
idx = index_service.index_resource(
|
|
res.resource_id,
|
|
repo_root,
|
|
timeout_seconds=timeout_seconds,
|
|
)
|
|
elapsed = time.monotonic() - started
|
|
|
|
if elapsed > timeout_seconds:
|
|
_fail(
|
|
"indexing exceeded timeout bound: "
|
|
f"{elapsed:.2f}s > {timeout_seconds:.2f}s"
|
|
)
|
|
if idx.metadata.file_count < 10_000:
|
|
_fail(f"expected >= 10,000 indexed files, got {idx.metadata.file_count}")
|
|
if idx.metadata.status != IndexStatus.READY:
|
|
_fail(f"expected READY status, got {idx.metadata.status}")
|
|
|
|
fetched_res = svc.show_resource("local/idx-repo")
|
|
if fetched_res.resource_id != res.resource_id:
|
|
_fail(f"resource_id mismatch after fetch: {fetched_res.resource_id}")
|
|
if fetched_res.location != str(repo_root):
|
|
_fail(f"location mismatch: {fetched_res.location}")
|
|
|
|
status = index_service.get_index_status(res.resource_id)
|
|
if status is None:
|
|
_fail("index status missing after indexing")
|
|
if status.file_count != idx.metadata.file_count:
|
|
_fail(
|
|
"index status file_count mismatch: "
|
|
f"{status.file_count} != {idx.metadata.file_count}"
|
|
)
|
|
|
|
print("m5-indexing-complete-ok")
|
|
|
|
|
|
# -------------------------------------------------------------------
|
|
# Dispatcher
|
|
# -------------------------------------------------------------------
|
|
|
|
_COMMANDS: dict[str, Callable[[], None]] = {
|
|
"project-create-large": project_create_large,
|
|
"resource-register-link": resource_register_link,
|
|
"indexing-complete": indexing_complete,
|
|
"context-tier-config": context_tier_config,
|
|
"context-policy-set-show": context_policy_set_show,
|
|
"acms-phase-inheritance": acms_phase_inheritance,
|
|
"acms-scoped-context": acms_scoped_context,
|
|
"context-policy-clear": context_policy_clear,
|
|
"context-view-validation": context_view_validation,
|
|
"llm-execute-acms-context": llm_execute_acms_context,
|
|
}
|
|
|
|
|
|
def main() -> int:
|
|
"""Entry point called by Robot Framework ``Run Process``."""
|
|
if len(sys.argv) < 2:
|
|
print(
|
|
f"Usage: helper_m5_e2e_verification.py <{'|'.join(_COMMANDS)}>",
|
|
)
|
|
return 1
|
|
command = sys.argv[1]
|
|
handler = _COMMANDS.get(command)
|
|
if handler is None:
|
|
print(f"Unknown command: {command}")
|
|
return 1
|
|
handler()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|