"""Unit tests for src/my_deepagent/logging.py — secret scrubbing.""" from __future__ import annotations from typing import Any from my_deepagent.logging import _scrub_processor, scrub, scrub_value _REDACTED = "[REDACTED]" # --------------------------------------------------------------------------- # scrub — individual patterns # --------------------------------------------------------------------------- def test_scrub_openrouter_key() -> None: secret = "sk-or-v1-abc1234567890123456789xyz" assert scrub(secret) == _REDACTED def test_scrub_anthropic_key() -> None: secret = "sk-ant-api03-abcdef1234567890abcdef1234567890xyz" assert scrub(secret) == _REDACTED def test_scrub_openai_project_key() -> None: secret = "sk-proj-abcdefghijklmnopqrstuvwxyz12345" assert scrub(secret) == _REDACTED def test_scrub_openai_general_key() -> None: # must be 30+ chars after "sk-" secret = "sk-abcdefghijklmnopqrstuvwxyz1234567890" assert scrub(secret) == _REDACTED def test_scrub_github_pat() -> None: secret = "ghp_abcdefghijklmnopqrstuvwxyz1234567890" assert scrub(secret) == _REDACTED def test_scrub_bearer_token() -> None: text = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.payload" result = scrub(text) assert _REDACTED in result def test_scrub_plain_text_unchanged() -> None: text = "normal log message with no secrets here" assert scrub(text) == text def test_scrub_partial_match_in_larger_string() -> None: text = f"calling API with key=sk-ant-api03-{'x' * 30}" result = scrub(text) assert _REDACTED in result assert "calling API with key=" in result # --------------------------------------------------------------------------- # scrub_value — recursive # --------------------------------------------------------------------------- def test_scrub_value_dict_scrubs_string_values() -> None: secret = f"sk-or-v1-{'a' * 25}" data: dict[str, Any] = {"key": secret, "n": 42} result = scrub_value(data) assert result["key"] == _REDACTED assert result["n"] == 42 def test_scrub_value_list_scrubs_all_strings() -> None: secret_ant = f"sk-ant-api03-{'b' * 30}" secret_ghp = f"ghp_{'c' * 35}" data: list[Any] = [1, secret_ant, {"k": secret_ghp}] result = scrub_value(data) assert result[0] == 1 assert result[1] == _REDACTED assert result[2]["k"] == _REDACTED def test_scrub_value_non_string_passes_through() -> None: assert scrub_value(42) == 42 assert scrub_value(3.14) == 3.14 assert scrub_value(None) is None assert scrub_value(True) is True def test_scrub_value_tuple_scrubs_strings() -> None: secret = f"sk-or-v1-{'d' * 22}" result = scrub_value((secret, "safe")) assert isinstance(result, tuple) assert result[0] == _REDACTED assert result[1] == "safe" # --------------------------------------------------------------------------- # _scrub_processor # --------------------------------------------------------------------------- def test_scrub_processor_scrubs_event_dict_values() -> None: secret = f"sk-ant-api03-{'e' * 30}" event_dict: dict[str, Any] = { "event": "calling model", "api_key": secret, "model": "claude-3", } result = _scrub_processor(None, "info", event_dict) assert result["api_key"] == _REDACTED assert result["event"] == "calling model" assert result["model"] == "claude-3" def test_scrub_processor_returns_dict() -> None: event_dict: dict[str, Any] = {"event": "no secrets here", "count": 5} result = _scrub_processor(None, "debug", event_dict) assert isinstance(result, dict) assert result["count"] == 5