Files
tracer-cloud--opensre/tests/synthetic/eks/test_suite_axis2.py
T
wehub-resource-sync 4b6817381b
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
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

152 lines
6.1 KiB
Python

"""Axis 2 adversarial test suite for synthetic Kubernetes RCA scenarios.
Differences from test_suite.py (Axis 1):
1. Uses SelectiveEKSBackend + SelectiveDatadogBackend instead of the
straight fixture-backed versions.
- Each selective backend records every tool invocation into an audit log
so tests can assert the agent called the right methods.
2. Asserts two additional dimensions from ReasoningScore:
- ruling_out_ok: the agent's output contains all ruling_out_keywords
declared in the scenario's answer.yml (proves it dismissed alternatives).
- queries_ok: the agent invoked all required_queries entries (proves it
checked the right evidence source before concluding).
3. Runs all scenarios with ruling_out_keywords or required_queries declared.
Scenarios without Axis 2 fields are skipped from the Axis 2-specific
assertions. Higher difficulty scenarios are wrapped with xfail(strict=False)
so expected failures do not gate CI but passes are recorded as xpass.
Run with:
pytest -m axis2 tests/synthetic/eks/test_suite_axis2.py -v
"""
from __future__ import annotations
import pytest
from config.config import has_credentials_for_active_llm_provider
from tests.synthetic.eks.run_suite import run_scenario, score_reasoning
from tests.synthetic.eks.scenario_loader import load_all_scenarios
from tests.synthetic.mock_datadog_backend.selective_backend import SelectiveDatadogBackend
from tests.synthetic.mock_eks_backend.selective_backend import SelectiveEKSBackend
_ALL_SCENARIOS = load_all_scenarios()
_LLM_ATTEMPTS = 2
_SYNTHETIC_SKIP_LLM = (
"SKIPPED: missing API key for LLM_PROVIDER "
"(suite uses mock EKS/Datadog backends; configure the key for your selected provider)"
)
# Difficulty threshold above which the LLM is expected to struggle.
# Failures at or above this difficulty are the gap signal —
# they should not gate CI (strict=False xfail).
_XFAIL_DIFFICULTY = 3
def _axis2_scenarios() -> list:
"""Return pytest params for all Axis 2 scenarios.
Scenarios at difficulty >= _XFAIL_DIFFICULTY are wrapped with
pytest.mark.xfail(strict=False) so that:
- Failures keep CI green (expected, part of the gap metric).
- Passes are recorded as bonuses (xpass).
"""
params = []
for f in _ALL_SCENARIOS:
if not (f.answer_key.ruling_out_keywords or f.answer_key.required_queries):
continue
if f.metadata.scenario_difficulty >= _XFAIL_DIFFICULTY:
params.append(
pytest.param(
f,
id=f.scenario_id,
marks=pytest.mark.xfail(
strict=False,
reason=(
f"difficulty={f.metadata.scenario_difficulty}: "
"expected to challenge real LLMs — failure is the gap signal"
),
),
)
)
else:
params.append(pytest.param(f, id=f.scenario_id))
return params
def _should_assert_trajectory(fixture, actual_category: str) -> bool:
"""Keep exact trajectory assertions for the lower-difficulty tiers only."""
return fixture.metadata.scenario_difficulty < _XFAIL_DIFFICULTY and actual_category != "healthy"
def _run_axis2_scenario_test(fixture) -> None:
"""Run Axis 2 scenario with real LLM and selective backends, then assert reasoning."""
if not has_credentials_for_active_llm_provider():
pytest.skip(_SYNTHETIC_SKIP_LLM)
failures: list[str] = []
for attempt in range(1, _LLM_ATTEMPTS + 1):
eks_backend = SelectiveEKSBackend(fixture)
datadog_backend = SelectiveDatadogBackend(fixture)
final_state, score = run_scenario(
fixture,
use_mock_backends=True,
eks_backend=eks_backend,
datadog_backend=datadog_backend,
)
queried_tools = list(eks_backend.queried_tools) + list(datadog_backend.queried_tools)
reasoning = score_reasoning(fixture, final_state, queried_tools=queried_tools)
try:
assert final_state["root_cause"], f"{fixture.scenario_id}: agent produced no root_cause"
assert score.passed is True, (
f"{fixture.scenario_id} FAILED: {score.failure_reason}\n"
f" actual_category={score.actual_category!r} "
f" missing_keywords={score.missing_keywords}"
)
if (
_should_assert_trajectory(fixture, score.actual_category)
and score.trajectory is not None
):
assert score.trajectory.sequencing_ok, (
f"{fixture.scenario_id} TRAJECTORY FAIL: "
f"sequencing={score.trajectory.sequencing_ok} "
f"calibration={score.trajectory.calibration_ok}\n"
f" expected={score.trajectory.expected_sequence}\n"
f" actual={score.trajectory.actual_sequence}"
)
if reasoning is not None:
# Keep query coverage as the hard gate. The ruling-out score is
# still computed and surfaced in reports, but exact phrasing in
# free-form model outputs is too variable to be CI-stable.
assert reasoning.queries_ok, (
f"{fixture.scenario_id} REASONING FAIL — agent never invoked: "
f"{reasoning.missing_queries}\n"
f" queried_tools audit log: "
f"eks={sorted(eks_backend.unique_queried_tools)} "
f"datadog={sorted(datadog_backend.unique_queried_tools)}"
)
return
except AssertionError as exc:
failures.append(f"attempt {attempt}/{_LLM_ATTEMPTS}: {exc}")
raise AssertionError("\n\n".join(failures))
_AXIS2_PARAMS = _axis2_scenarios()
@pytest.mark.axis2
@pytest.mark.skipif(not _AXIS2_PARAMS, reason="no Axis 2 K8s scenarios yet")
@pytest.mark.parametrize("fixture", _AXIS2_PARAMS or [None])
def test_axis2_scenario(fixture) -> None:
"""Axis 2 adversarial test: selective backends + reasoning quality checks."""
_run_axis2_scenario_test(fixture)