"""Tests for the upstream-error diagnostic-dump gating. The dump can contain cleartext prompt/tool/system content, so it must be OFF by default, never written in stateless mode, and content-redacted unless the operator explicitly opts in to full content. """ from __future__ import annotations import inspect from types import SimpleNamespace import pytest pytest.importorskip("fastapi") from headroom.proxy.handlers._debug_dump import _debug_dump_mode, _redact_debug_value def _config(stateless: bool = False) -> SimpleNamespace: return SimpleNamespace(stateless=stateless) def test_debug_dump_off_by_default(monkeypatch): monkeypatch.delenv("HEADROOM_DEBUG_DUMP", raising=False) assert _debug_dump_mode(_config()) == "off" @pytest.mark.parametrize("value", ["1", "true", "yes", "on", "redacted", "REDACTED"]) def test_debug_dump_opt_in_redacted(monkeypatch, value): monkeypatch.setenv("HEADROOM_DEBUG_DUMP", value) assert _debug_dump_mode(_config()) == "redacted" @pytest.mark.parametrize("value", ["full", "all", "content"]) def test_debug_dump_opt_in_full(monkeypatch, value): monkeypatch.setenv("HEADROOM_DEBUG_DUMP", value) assert _debug_dump_mode(_config()) == "full" def test_debug_dump_unknown_value_is_off(monkeypatch): monkeypatch.setenv("HEADROOM_DEBUG_DUMP", "maybe") assert _debug_dump_mode(_config()) == "off" def test_stateless_forces_dump_off_even_when_opted_in(monkeypatch): # Stateless mode must win over any opt-in: no filesystem writes, period. monkeypatch.setenv("HEADROOM_DEBUG_DUMP", "full") assert _debug_dump_mode(_config(stateless=True)) == "off" def test_redact_elides_long_strings_keeps_structure(): payload = { "role": "user", "type": "text", "id": "msg_123", "text": "secret prompt content " * 20, # long → redacted "blocks": [ {"type": "tool_use", "name": "search", "input": "x" * 500}, {"type": "text", "text": "short"}, ], } out = _redact_debug_value(payload) # Short structural fields preserved: assert out["role"] == "user" assert out["type"] == "text" assert out["id"] == "msg_123" assert out["blocks"][0]["name"] == "search" assert out["blocks"][1]["text"] == "short" # Long content elided to a length placeholder (no original content leaks): assert out["text"].startswith("