Files
tracer-cloud--opensre/tests/tools/test_groundcover_tools.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

181 lines
6.8 KiB
Python

"""Tests for the groundcover investigation tools (logs, traces, query reference)."""
from __future__ import annotations
from typing import Any
from unittest.mock import MagicMock
import pytest
from integrations.groundcover.client import GroundcoverToolResult
from integrations.groundcover.tools import (
get_groundcover_query_reference,
query_groundcover_logs,
query_groundcover_traces,
)
from tests.tools.conftest import BaseToolContract, mock_agent_state
def _ok(data: Any, notes: list[str] | None = None) -> GroundcoverToolResult:
return GroundcoverToolResult(
success=True, tool="t", data=data, text="", notes=notes or [], error=None
)
def _client(result: GroundcoverToolResult) -> MagicMock:
client = MagicMock()
client.call_tool.return_value = result
return client
# ---------------------------------------------------------------------------
# Contract tests
# ---------------------------------------------------------------------------
class TestLogsContract(BaseToolContract):
def get_tool_under_test(self) -> Any:
return query_groundcover_logs.__opensre_registered_tool__
class TestTracesContract(BaseToolContract):
def get_tool_under_test(self) -> Any:
return query_groundcover_traces.__opensre_registered_tool__
class TestReferenceContract(BaseToolContract):
def get_tool_under_test(self) -> Any:
return get_groundcover_query_reference.__opensre_registered_tool__
# ---------------------------------------------------------------------------
# Availability + param extraction
# ---------------------------------------------------------------------------
def test_is_available_requires_connection_and_key() -> None:
rt = query_groundcover_logs.__opensre_registered_tool__
assert rt.is_available({"groundcover": {"connection_verified": True, "api_key": "k"}}) is True
assert rt.is_available({"groundcover": {"connection_verified": True}}) is False
assert rt.is_available({"groundcover": {"_backend": object()}}) is True
assert rt.is_available({}) is False
def test_extract_params_injects_client_not_raw_secrets() -> None:
rt = query_groundcover_logs.__opensre_registered_tool__
params = rt.extract_params(mock_agent_state())
# Credentials are bound into a runtime client object, never exposed as kwargs.
assert "api_key" not in params
assert "mcp_url" not in params
assert "_groundcover_client" in params
assert "limit" in params["query"]
@pytest.mark.parametrize(
"tool_func",
[query_groundcover_logs, query_groundcover_traces, get_groundcover_query_reference],
)
def test_credential_kwargs_are_rejected_by_public_schema(tool_func: Any) -> None:
"""Prompt-injected mcp_url/routing keys must fail validation (security)."""
rt = tool_func.__opensre_registered_tool__
assert rt.input_schema.get("additionalProperties") is False
# Satisfy any required fields so the failure is specifically the rogue key.
payload: dict[str, Any] = dict.fromkeys(rt.input_schema.get("required", []), "x")
payload["mcp_url"] = "https://evil.example.com/api/mcp"
err = rt.validate_public_input(payload)
assert err is not None and "mcp_url" in err
# ---------------------------------------------------------------------------
# Runtime behavior
# ---------------------------------------------------------------------------
def test_logs_unavailable_without_client() -> None:
result = query_groundcover_logs(query="* | limit 10", _groundcover_client=None)
assert result["available"] is False
assert "not configured" in result["error"]
def test_logs_empty_query_returns_needs_query() -> None:
result = query_groundcover_logs(query="", _groundcover_client=_client(_ok([])))
assert result["available"] is True
assert result["data"] == []
assert result["notes"]
def test_logs_happy_path_envelope() -> None:
client = _client(_ok([{"level": "error", "content": "boom"}]))
result = query_groundcover_logs(query="level:error | limit 10", _groundcover_client=client)
assert result["available"] is True
assert result["source"] == "groundcover_logs"
assert result["data"] == [{"level": "error", "content": "boom"}]
assert result["summary"]["returned"] == 1
assert result["time_range"]["period"] == "PT1H"
assert client.call_tool.call_args.args[0] == "query_logs"
def test_logs_truncation_note_sets_truncated() -> None:
client = _client(_ok([{"a": 1}], notes=["Results truncated at 1 rows."]))
result = query_groundcover_logs(query="* | limit 1", _groundcover_client=client)
assert result["truncated"] is True
def test_logs_upstream_error_envelope() -> None:
client = _client(
GroundcoverToolResult(
success=False, tool="query_logs", error="logs query timed out — narrow the time range"
)
)
result = query_groundcover_logs(query="* | limit 10", _groundcover_client=client)
assert result["available"] is False
assert "narrow the time range" in result["error"]
def test_logs_uses_backend_when_injected() -> None:
backend = MagicMock()
backend.query_logs.return_value = {"source": "groundcover_logs", "available": True, "data": []}
result = query_groundcover_logs(
query="level:error | limit 10", period="PT2H", groundcover_backend=backend
)
assert result["available"] is True
# Time-window params must be forwarded to the backend, not dropped.
backend.query_logs.assert_called_once_with(
query="level:error | limit 10", start="", end="", period="PT2H"
)
def test_traces_happy_path() -> None:
client = _client(_ok([{"workload": "checkout", "duration_seconds": 5.2}]))
result = query_groundcover_traces(query="* | limit 10", _groundcover_client=client)
assert result["available"] is True
assert result["source"] == "groundcover_traces"
assert client.call_tool.call_args.args[0] == "query_traces"
def test_reference_returns_text() -> None:
client = MagicMock()
client.get_query_reference.return_value = {
"success": True,
"reference": "# gcQL",
"cached": False,
}
result = get_groundcover_query_reference(_groundcover_client=client)
assert result["available"] is True
assert result["reference"] == "# gcQL"
# ---------------------------------------------------------------------------
# Query-guidance contract: guidance must be visible in metadata
# ---------------------------------------------------------------------------
@pytest.mark.parametrize("tool_func", [query_groundcover_logs, query_groundcover_traces])
def test_signal_tool_descriptions_carry_query_guidance(tool_func: Any) -> None:
rt = tool_func.__opensre_registered_tool__
desc = rt.description.lower()
assert "limit n" in desc
assert "narrow" in desc
assert "get_groundcover_query_reference" in desc
assert "query" in rt.input_schema["properties"]