Files
cleveragents-core/robot/helper_m4_e2e_verification.py
hurui200320 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
test(e2e): validate M4 acceptance criteria for v3.3.0 milestone closure
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
2026-03-13 14:03:37 +08:00

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())