Files
cleveragents-core/robot/helper_m5_acms_smoke.py
T
HAL9000 7aa50ac489 fix(e2e): restore M5/WF14 tests broken by JSON envelope and stale config
- Add NO_COLOR=1 to E2E Suite Setup and Test Environment to prevent
  Rich from injecting ANSI escape codes into JSON output, which breaks
  Extract JSON From Stdout and JSON assertions in M5/WF14 acceptance tests.

- Add reset_global_state() calls to all M1/M2/M4/M5 E2E verification
  helpers to clear Settings singleton, DI container, provider registry,
  and SQLAlchemy engine cache between parallel pabot worker runs.

- Propagate NO_COLOR=1 at suite-setup level in E2E and integration
  test resource files so all Run Process subprocesses inherit clean
  terminal output mode.

Root causes:
1. Missing NO_COLOR=1 caused Rich ANSI codes in CLI JSON output,
   making json.loads() failures in robot Extract JSON From Stdout
2. Stale Settings singleton carried provider keys and DB paths between
   pabot workers, causing UNIQUE constraint violations and stale config
   lookups in context policy and server mode E2E tests.

Related: PR #9912
2026-05-01 00:59:02 +00:00

152 lines
4.6 KiB
Python

"""Robot Framework helper for M5 ACMS pipeline smoke tests."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from helpers_common import reset_global_state
from cleveragents.domain.models.core.context_policy import (
ContextView,
ProjectContextPolicy,
)
_FIXTURES_DIR = Path(__file__).resolve().parents[1] / "features" / "fixtures" / "m5"
# ---------------------------------------------------------------------------
# Robot keywords
# ---------------------------------------------------------------------------
def load_acms_context_policy_fixture() -> dict[str, Any]:
"""Load the ACMS context policy fixture file."""
reset_global_state()
fixture_path = _FIXTURES_DIR / "acms_context_policy.json"
return json.loads(fixture_path.read_text(encoding="utf-8"))
def load_large_project_context_fixture() -> dict[str, Any]:
"""Load the large project context fixture file."""
reset_global_state()
fixture_path = _FIXTURES_DIR / "large_project_context.json"
return json.loads(fixture_path.read_text(encoding="utf-8"))
def load_context_analysis_fixture() -> dict[str, Any]:
"""Load the context analysis results fixture file."""
reset_global_state()
fixture_path = _FIXTURES_DIR / "context_analysis_results.json"
return json.loads(fixture_path.read_text(encoding="utf-8"))
def resolve_view_for_phase(phase: str) -> dict[str, Any]:
"""Resolve a view from an empty policy for the given phase."""
reset_global_state()
policy = ProjectContextPolicy()
view = policy.resolve_view(phase)
return {
"include_paths": view.include_paths,
"exclude_paths": view.exclude_paths,
"include_resources": view.include_resources,
"exclude_resources": view.exclude_resources,
"max_file_size": view.max_file_size,
"max_total_size": view.max_total_size,
}
def resolve_strategize_inheriting_default() -> dict[str, Any]:
"""Resolve strategize view that inherits from default."""
reset_global_state()
policy = ProjectContextPolicy(
default_view=ContextView(
include_paths=["src/**/*.py"],
exclude_paths=["**/__pycache__/**"],
max_file_size=262144,
),
)
view = policy.resolve_view("strategize")
return {
"include_paths": view.include_paths,
"max_file_size": view.max_file_size,
}
def resolve_strategize_with_override() -> dict[str, Any]:
"""Resolve strategize view with explicit override."""
reset_global_state()
policy = ProjectContextPolicy(
default_view=ContextView(
include_paths=["src/**/*.py"],
max_file_size=262144,
),
strategize_view=ContextView(
include_paths=["src/**/*.py", "docs/**/*.md"],
max_file_size=131072,
),
)
view = policy.resolve_view("strategize")
return {
"include_paths": view.include_paths,
"max_file_size": view.max_file_size,
}
def check_budget_enforcement(
max_size: int,
oversized_file: int,
within_file: int,
) -> dict[str, bool]:
"""Check if files exceed or fit within file size budget."""
reset_global_state()
view = ContextView(max_file_size=int(max_size))
limit = view.max_file_size
assert limit is not None
return {
"oversized": int(oversized_file) > limit,
"within_budget": int(within_file) <= limit,
}
def check_total_budget_enforcement(
max_total: int,
oversized_total: int,
within_total: int,
) -> dict[str, bool]:
"""Check if aggregate context exceeds total size budget."""
reset_global_state()
view = ContextView(max_total_size=int(max_total))
limit = view.max_total_size
assert limit is not None
return {
"oversized": int(oversized_total) > limit,
"within_budget": int(within_total) <= limit,
}
def attempt_resolve_invalid_phase(phase: str) -> dict[str, str]:
"""Try resolving an invalid phase and capture the error."""
reset_global_state()
policy = ProjectContextPolicy()
try:
policy.resolve_view(phase)
return {"error": "False", "message": ""}
except ValueError as exc:
return {"error": "True", "message": str(exc)}
def create_multi_project_context(
count_a: int,
count_b: int,
) -> dict[str, int]:
"""Simulate independent context for two projects."""
reset_global_state()
proj_a = [{"path": f"src/a_{i}.py", "size": 1024} for i in range(int(count_a))]
proj_b = [{"path": f"src/b_{i}.py", "size": 1024} for i in range(int(count_b))]
return {
"proj_a_count": len(proj_a),
"proj_b_count": len(proj_b),
}