317d015260
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 16s
CI / build (pull_request) Successful in 17s
CI / quality (pull_request) Successful in 18s
CI / e2e_tests (pull_request) Successful in 27s
CI / security (pull_request) Successful in 38s
CI / typecheck (pull_request) Successful in 39s
CI / unit_tests (pull_request) Successful in 3m0s
CI / docker (pull_request) Successful in 10s
CI / integration_tests (pull_request) Successful in 3m29s
CI / coverage (pull_request) Successful in 5m54s
CI / lint (push) Successful in 13s
CI / build (push) Successful in 14s
CI / quality (push) Successful in 17s
CI / security (push) Successful in 38s
CI / e2e_tests (push) Successful in 36s
CI / typecheck (push) Successful in 39s
CI / benchmark-regression (push) Has been skipped
CI / unit_tests (push) Successful in 2m59s
CI / integration_tests (push) Successful in 3m24s
CI / docker (push) Successful in 38s
CI / coverage (push) Successful in 5m54s
CI / benchmark-publish (push) Successful in 19m34s
CI / benchmark-regression (pull_request) Successful in 36m21s
Strengthen M4 E2E CLI acceptance tests and address all review findings from the self-review on PR #681 (3 blocking, 7 non-blocking issues). Blocking fixes: - Remove tautological domain assertions from cli_plan_execute() that verified values the test itself constructed via _mock_parent_plan and could never fail. Replace with a comment documenting that subplan info cannot be verified from CLI output alone. - Fix unguarded positional arg access in plan_diff() that would raise IndexError if the production CLI passes plan_id as a keyword argument. Now uses kwargs-with-positional-fallback pattern. - Split the 1074-line helper_m4_e2e_verification.py into four focused modules under the CONTRIBUTING.md 500-line limit: helper_m4_e2e_common.py (237 lines) — constants, helpers, factories helper_m4_e2e_domain.py (495 lines) — domain model tests helper_m4_e2e_cli.py (426 lines) — CLI integration tests helper_m4_e2e_verification.py (81 lines) — thin dispatcher Non-blocking improvements: - Add ProjectLink isinstance type guard before iterating project_links. - Wrap all assert_called_once*() calls in try/except with _fail() pattern to produce standardised FAIL: diagnostics instead of raw AssertionError. - Make project_links extraction symmetric with action_name (kwargs + positional fallback at index 1). - Replace broad "execute" substring match with word-boundary regex r"\bexecute\b" to avoid false positives from incidental substrings. - Extract _make_subplan_status() factory, _assert_exit_code() and _assert_mock_called_once*() wrappers to eliminate DRY violations (SubplanStatus construction repeated 3x, exit-code checks 4x). - Replace non-deterministic datetime.now() calls with frozen constant _FROZEN_NOW = datetime(2026, 3, 1, 12, 0, 0) for deterministic fixtures. - Rename generic _DECISION_ULID_N constants to descriptive names (_DECISION_PROMPT_DEF, _DECISION_STRATEGY, _DECISION_PARALLEL_SPAWN, _DECISION_SPAWN_API, _DECISION_SPAWN_UI). Quality gates: - lint: PASS - format: PASS (1404 files unchanged) - typecheck: PASS (0 errors) - unit_tests: 10,431 scenarios, 0 failures - integration_tests: 1,452 tests, 0 failures - coverage_report: PASS (98%, threshold 97%) - security_scan: PASS - dead_code: PASS - docs: PASS - build: PASS ISSUES CLOSED: #495
110 lines
3.9 KiB
Python
110 lines
3.9 KiB
Python
"""Robot Framework helper for M4 E2E verification tests.
|
|
|
|
Exercises the M4 success criteria for subplans and parallel execution:
|
|
1. Plan spawns multiple subplans during Execute
|
|
2. View subplan tree via plan tree
|
|
3. Verify merged results via plan diff
|
|
4. Parallel subplan execution respects max_parallel
|
|
5. Three-way merge combines non-conflicting changes
|
|
6. Merge conflicts are surfaced correctly
|
|
7. Parent plan tracks all subplan statuses
|
|
8. CLI plan use creates plan with subplan config
|
|
9. CLI plan execute transitions plan with subplans
|
|
10. CLI plan tree displays subplan hierarchy
|
|
11. CLI plan execute aborts on read-only plan (error path)
|
|
12. CLI plan use aborts on unavailable action (error path)
|
|
13. CLI plan diff aborts on missing changeset (error path)
|
|
14. CLI plan tree handles zero decisions (error path)
|
|
|
|
CLI-facing tests (plan-diff, cli-plan-use, cli-plan-execute, cli-plan-tree)
|
|
invoke the real ``agents`` CLI via subprocess without mocking.
|
|
Domain-level tests exercise real application code directly.
|
|
|
|
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_m4_e2e_verification.py <command>
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import sys
|
|
from collections.abc import Callable
|
|
from pathlib import Path
|
|
|
|
# Ensure the src directory is on the import path.
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.append(_SRC)
|
|
|
|
from helpers_common import reset_global_state # noqa: E402
|
|
|
|
# Command → (module_name, function_name) dispatch table.
|
|
# Modules are imported lazily so that running a domain-only command
|
|
# (e.g. ``merge-clean``) does not load the CLI module's heavy imports
|
|
# (``typer.testing``, ``cleveragents.cli.commands.plan``).
|
|
_COMMAND_MAP: dict[str, tuple[str, str]] = {
|
|
# Domain tests
|
|
"spawn-subplans": ("helper_m4_e2e_domain", "spawn_subplans"),
|
|
"plan-tree": ("helper_m4_e2e_domain", "plan_tree"),
|
|
"parallel-max": ("helper_m4_e2e_domain", "parallel_max"),
|
|
"parent-tracking": ("helper_m4_e2e_domain", "parent_tracking"),
|
|
# Merge tests
|
|
"merge-clean": ("helper_m4_e2e_merge", "merge_clean"),
|
|
"merge-conflict": ("helper_m4_e2e_merge", "merge_conflict"),
|
|
# CLI happy-path tests
|
|
"plan-diff": ("helper_m4_e2e_cli", "plan_diff"),
|
|
"cli-plan-use": ("helper_m4_e2e_cli", "cli_plan_use"),
|
|
"cli-plan-execute": ("helper_m4_e2e_cli", "cli_plan_execute"),
|
|
"cli-plan-tree": ("helper_m4_e2e_cli", "cli_plan_tree"),
|
|
# CLI error-path tests
|
|
"cli-plan-execute-readonly": (
|
|
"helper_m4_e2e_cli_errors",
|
|
"cli_plan_execute_readonly",
|
|
),
|
|
"cli-plan-use-not-found": ("helper_m4_e2e_cli_errors", "cli_plan_use_not_found"),
|
|
"cli-plan-diff-no-changeset": (
|
|
"helper_m4_e2e_cli_errors",
|
|
"cli_plan_diff_no_changeset",
|
|
),
|
|
"cli-plan-tree-empty": ("helper_m4_e2e_cli_errors", "cli_plan_tree_empty"),
|
|
}
|
|
|
|
|
|
def _resolve_handler(command: str) -> Callable[[], None] | None:
|
|
"""Lazily import the handler for *command*.
|
|
|
|
Only the module containing the requested command is loaded,
|
|
avoiding unnecessary import overhead for unrelated modules.
|
|
"""
|
|
entry = _COMMAND_MAP.get(command)
|
|
if entry is None:
|
|
return None
|
|
module_name, func_name = entry
|
|
module = importlib.import_module(module_name)
|
|
handler: Callable[[], None] = getattr(module, func_name)
|
|
return handler
|
|
|
|
|
|
def main() -> int:
|
|
"""Entry point called by Robot Framework ``Run Process``."""
|
|
if len(sys.argv) < 2:
|
|
print(
|
|
f"Usage: helper_m4_e2e_verification.py <{'|'.join(_COMMAND_MAP)}>",
|
|
)
|
|
return 1
|
|
command = sys.argv[1]
|
|
handler = _resolve_handler(command)
|
|
if handler is None:
|
|
print(f"Unknown command: {command}")
|
|
return 1
|
|
reset_global_state()
|
|
handler()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|