"""Secret-redaction helper regression tests. Locks in the patterns that surface from real provider error paths: Telegram bot URLs in aiohttp.ClientError messages, Authorization Bearer tokens in Matrix/ntfy responses, Discord/Slack webhook tokens, URL userinfo, and common ?token= query params. """ from __future__ import annotations import pytest from notify_bridge_core.notifications.redact import redact, redact_exc @pytest.mark.parametrize( "raw,expected_substr,not_in", [ ( "Cannot connect to host api.telegram.org/bot1234567:AABBCC-secret-token/sendMessage", "api.telegram.org/bot***", "AABBCC-secret-token", ), ( "Authorization: Bearer ey.JhbGciOiJIUzI1NiJ9.payload.sig", "Bearer ***", "ey.JhbGciOiJIUzI1NiJ9", ), ( "POST https://discord.com/api/webhooks/12345/abcdefg-token failed", "discord.com/api/webhooks/12345/***", "abcdefg-token", ), ( "POST https://hooks.slack.com/services/T01/B02/zzzzz failed", "hooks.slack.com/services/T01/B02/***", "zzzzz", ), ( "fetch http://user:supersecret@example.com/foo", "http://***@example.com/foo", "supersecret", ), ( "https://api.example.com/x?token=mytoken123&extra=ok", "token=***", "mytoken123", ), ], ) def test_redact_known_secrets(raw: str, expected_substr: str, not_in: str) -> None: out = redact(raw) assert expected_substr in out assert not_in not in out def test_redact_idempotent() -> None: once = redact("Bearer abcdefghij1234567890") twice = redact(once) assert once == twice def test_redact_exc_returns_str() -> None: err = RuntimeError("Bearer abcdefghij1234567890") out = redact_exc(err) assert isinstance(out, str) assert "Bearer ***" in out assert "abcdefghij1234567890" not in out def test_redact_handles_non_string() -> None: # Coercion path should not raise. out = redact(12345) # type: ignore[arg-type] assert out == "12345"