Files
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

128 lines
4.3 KiB
Python

"""Tests for tool trace formatting and redaction helpers."""
from __future__ import annotations
from typing import Any
from platform.observability.trace.redaction import (
format_json_preview,
format_tool_trace_entry,
redact_sensitive,
)
SENSITIVE_KEYS = [
"api_key",
"api-key",
"apiKey",
"token",
"secret",
"password",
"credential",
"authorization",
"auth_header",
]
def test_redact_sensitive_recurses_into_nested_collections() -> None:
value: dict[str, Any] = {
**dict.fromkeys(SENSITIVE_KEYS, "do-not-leak"),
"_internal": object(),
"backend": object(),
"mcp_backend": object(),
"nested": {"token": "secret-token", "safe": "visible"},
"items": [{"password": "hidden"}, ("public", {"_client": object()})],
}
redacted = redact_sensitive(value)
for key in SENSITIVE_KEYS:
assert redacted[key] == "[redacted]"
assert redacted["_internal"] == "[runtime object]"
assert redacted["backend"] == "[runtime object]"
assert redacted["mcp_backend"] == "[runtime object]"
assert redacted["nested"] == {"token": "[redacted]", "safe": "visible"}
assert redacted["items"] == [
{"password": "[redacted]"},
["public", {"_client": "[runtime object]"}],
]
def test_redact_sensitive_handles_scalars_and_regex_precedence() -> None:
for scalar in (123, "plain"):
assert redact_sensitive(scalar) == scalar
assert redact_sensitive(None) is None
assert redact_sensitive(True) is True
assert redact_sensitive({"_token": "secret-token"}) == {"_token": "[redacted]"}
# substring match — "access_token_count" contains "token" -> redacted
assert redact_sensitive({"access_token_count": "val"}) == {"access_token_count": "[redacted]"}
# no sensitive substring -> not redacted
assert redact_sensitive({"count_only": "val"}) == {"count_only": "val"}
def test_format_json_preview_redacts_truncates_and_stringifies() -> None:
preview = format_json_preview(
{"query": "service:error", "credential": "super-secret", "count": 2}
)
assert '"query": "service:error"' in preview
assert '"credential": "[redacted]"' in preview
assert '"count": 2' in preview
assert "super-secret" not in preview
truncated = format_json_preview({"message": "x" * 100}, max_chars=50)
assert len(truncated) <= 50
assert truncated.endswith("\n... [truncated]")
stringified = format_json_preview({"values": {1, 2}})
assert '"values":' in stringified
assert "1" in stringified
assert "2" in stringified
def test_format_tool_trace_entry_populates_fields_and_collapses_previews() -> None:
assert format_tool_trace_entry({"tool_name": "primary", "key": "fallback"}).startswith(
"- `primary`"
)
assert format_tool_trace_entry({"key": "fallback"}).startswith("- `fallback`")
assert format_tool_trace_entry({}).startswith("- `tool` (iteration None)")
assert format_tool_trace_entry({"loop_iteration": -1}).startswith("- `tool` (seed)")
assert format_tool_trace_entry({"loop_iteration": 2}).startswith("- `tool` (iteration 2)")
entry = {
"tool_name": "kubernetes_logs",
"loop_iteration": 3,
"tool_args": {"namespace": "prod", "token": "hidden"},
"data": {"status": "ok", "events": [1, 2]},
}
formatted = format_tool_trace_entry(entry)
assert formatted.startswith("- `kubernetes_logs` (iteration 3)")
assert "namespace" in formatted
assert "[redacted]" in formatted
assert "hidden" not in formatted
assert "status" in formatted
assert "events" in formatted
assert formatted.count("\n") == 2
def test_format_tool_trace_entry_handles_empty_trace_record_and_output_limit() -> None:
formatted = format_tool_trace_entry({})
assert formatted.startswith("- `tool` (iteration None)")
assert "\n input: `{}`" in formatted
assert "\n output: `" in formatted
assert formatted.count("\n") == 2
limited = format_tool_trace_entry(
{
"tool_name": "large_tool",
"loop_iteration": 1,
"data": {"output": "x" * 200},
},
max_output_chars=60,
)
assert limited.startswith("- `large_tool` (iteration 1)")
assert "... [truncated]" in limited
assert limited.count("\n") == 2