headroom/tests/test_proxy
JD Davis 8a1d38bc5d
fix(proxy): complete stateless Responses and buffered CCR lifecycle (#2997)
## Description

Consolidates the related OpenAI Responses ZDR/stateless continuation and
buffered CCR response-lifecycle corrections on current main. It
preserves client storage policy, makes Headroom-owned continuations
stateless across HTTP and WebSocket, and prevents buffered streaming
paths from committing a false HTTP 200 before the real upstream outcome
is known.

Closes #2675

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Code refactoring (no functional changes)

## Changes Made

- Preserves explicit and omitted Responses `store` policy instead of
forcing provider storage or disabling memory tools.
- Replays normalized input, replayable outputs, encrypted reasoning
content, and Headroom function outputs without `previous_response_id`.
- Applies the same stateless continuation policy to HTTP and WebSocket.
- Prevents transparent memory execution after client-visible WebSocket
output.
- Delays buffered CCR ASGI status/headers until the operation resolves
for Anthropic Messages and OpenAI Responses.
- Preserves real 429/5xx status and retry headers.
- Converts malformed non-JSON/non-SSE upstream 200 replies to a
sanitized 502 protocol error.
- Preserves valid JSON-to-SSE synthesis and existing SSE adaptation.
- Removes unreachable task cleanup left behind after replacing the old
keepalive polling loop with a direct awaited operation.

## Testing

- [x] Unit tests pass (`pytest`)
- [x] Linting passes (`ruff check .`)
- [x] Type checking passes (`mypy headroom`)
- [x] New tests added for new functionality
- [x] Manual testing performed

### Test Output

```text
118 passed across the changed HTTP/WS ZDR, lifecycle, and both-provider CCR suites
11526 tests collected with no collection errors
ruff check .: All checks passed
ruff format --check .: 1411 files already formatted
mypy headroom/proxy/handlers/anthropic.py headroom/proxy/handlers/openai.py:
Success: no issues found in 2 source files
```

Exact-head CI is entirely green on
`cbc2739c0c`.

## Real Behavior Proof

- Environment: macOS arm64/Python 3.13 locally; GitHub-hosted Ubuntu
matrix pending.
- Exact command / steps: exercise `store=false` Responses memory calls
over HTTP and WebSocket; exercise buffered Anthropic and Responses
requests returning successful JSON/SSE, delayed 429 responses,
exceptions, and malformed successful bodies; invoke returned ASGI
responses and inspect emitted status, headers, and body order.
- Observed result: stateless continuations omit provider response IDs
and retain `store=false`; no ASGI start event is emitted before the
buffered outcome; real failures preserve status/headers; malformed 200
responses become sanitized 502 errors.
- Not tested: live ZDR tenant and live Anthropic/OpenAI upstream
credentials are unavailable in repository CI; wire contracts are
exercised through deterministic upstream doubles.

## Runtime Rollout Safety

- Rollout-managed feature(s): Responses memory continuation and buffered
CCR handling.
- Minimum rollout channel: normal patch release after full CI
qualification.
- Stable/default behavior changed: memory continuation no longer
requires provider storage; buffered CCR waits before committing response
status.
- Kill switch / disable path: disable memory/CCR using existing proxy
configuration (`--no-ccr` for CCR); ordinary non-buffered paths are
unchanged.
- Unsafe override required: none.
- Qualification impact: full Python matrix plus focused HTTP/WS
lifecycle suites must pass; patch coverage must not rely on unreachable
cleanup.
- Rollback path: human revert of this PR restores prior
continuation/buffering behavior; no persisted data migration is
introduced.

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review — exact-head CI is entirely
green

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation — inline
protocol/lifecycle documentation; no separate user guide required
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my
feature works
- [x] New and existing unit tests pass locally with my changes
- [x] I did **not** edit `CHANGELOG.md` — it is generated by
release-please from my Conventional Commit PR title (a CI guard enforces
this)

## Screenshots (if applicable)

Not applicable; proxy protocol behavior.

## Additional Notes

Human review only. No merge or auto-merge is configured. This supersedes
narrower #2995 and incorporates the complete intent of #2705, #2959, and
#2968 without falsely closing those PRs. It does not claim the broader
event-level streaming-splice guarantees requested by #1877. Refreshed
from main after #2996; the MCP cap `mcp>=1.28.1,<2.0.0` is preserved.
2026-08-13 21:12:38 -05:00
..
test_anthropic_buffered_timeout.py fix(proxy/anthropic): None-guard usage token counts on the direct buffered path (#2434) 2026-08-12 00:07:51 -05:00
test_anthropic_ccr_deferred_injection.py fix(ccr): verify a scanned marker's hash before advertising it (#2908) 2026-08-13 11:46:21 -05:00
test_anthropic_ccr_raise.py fix(anthropic): CCR exception must re-raise, not silently swallow (#838) 2026-06-10 21:11:46 -05:00
test_anthropic_recount_and_reparse_safety.py perf(proxy): bound upstream calls and hot-path costs (#2852) 2026-08-09 16:24:33 -07:00
test_anthropic_streaming_ccr_retrieve.py fix(proxy): complete stateless Responses and buffered CCR lifecycle (#2997) 2026-08-13 21:12:38 -05:00
test_anthropic_upstream_header.py fix(proxy): honor x-headroom-base-url on /v1/messages route (#1763) 2026-07-09 12:43:40 -05:00
test_background_compression.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_bedrock_passthrough.py feat(proxy): compress AWS Bedrock InvokeModel requests via configurable upstream (#720) 2026-06-16 14:56:16 -05:00
test_bedrock_sse_ping.py fix: emit SSE ping before message_start on Bedrock streaming path (issue #902) (#1080) 2026-08-04 21:41:58 -05:00
test_cc_switch_reconciler.py feat(proxy): cc-switch reconciler — keep Headroom in the request path alongside cc-switch (#1030) 2026-06-17 08:41:16 -05:00
test_ccr_frozen_prefix_coupling.py fix(proxy): stop toggling headroom_retrieve in the Anthropic tools array (#2672) 2026-08-03 16:18:11 -07:00
test_compression_failure_action.py fix(codex): fail open for proxy compression timeout 2026-06-04 14:04:49 +05:30
test_compression_timeout_config.py feat(proxy): make COMPRESSION_TIMEOUT_SECONDS configurable via env (#946) (#991) 2026-06-16 21:10:01 -05:00
test_compute_turn_id.py fix(proxy): strip cache_control before hashing turn_id 2026-04-23 16:04:57 +02:00
test_gemini_savings_profile.py fix(proxy/gemini): keep streaming-parity baseline so eligible_pct can't exceed 100 (#2824) 2026-08-06 19:21:56 -07:00
test_header_safe_transforms.py feat(transforms): attribute read_lifecycle + smart_crush tags (#249) 2026-06-11 11:51:26 -05:00
test_mcp_stats_aggregation.py fix(proxy): Strands MCP bundle + backend path fixes + Codex fail-closed protection 2026-05-21 11:00:14 -07:00
test_model_router.py feat(proxy): add opt-in cost-aware model router (#1706) (#2205) 2026-07-15 19:58:17 +00:00
test_model_router_wiring.py feat(proxy): add opt-in cost-aware model router (#1706) (#2205) 2026-07-15 19:58:17 +00:00
test_openai_backend_path.py fix(proxy): skip max_tokens rename for backend-routed openai chat (#2401) 2026-07-18 16:47:10 -07:00
test_openai_chat_ccr_injection.py fix(ccr): avoid injecting tool on chat streaming 2026-08-11 16:18:57 -07:00
test_openai_chat_savings_profile.py fix(proxy/openai): None-guard usage token counts on the chat path (#2431) 2026-07-19 22:15:22 -07:00
test_openai_responses_ccr.py fix(proxy): complete stateless Responses and buffered CCR lifecycle (#2997) 2026-08-13 21:12:38 -05:00
test_openai_stream_usage_option.py fix(proxy/openai): respect explicit stream_options.include_usage (#2026) 2026-07-13 00:43:16 -04:00
test_openai_transport_path_prefix.py fix(codex): OpenCode Zen telemetry attribution (#1648) 2026-07-07 11:35:21 -05:00
test_openai_upstream_header.py fix(proxy): preserve sub-path in X-Headroom-Base-Url custom upstream (#2037) (#2127) 2026-07-13 19:53:45 -04:00
test_phase3_byte_identity.py perf(compression): take large cold-start contexts off the synchronous kompress path (#1171) (#1298) 2026-06-23 10:48:06 -05:00
test_request_logger.py feat(proxy): log compressed messages alongside original request (#261) 2026-06-11 19:02:54 -05:00
test_settings_fresh_process_precedence.py fix(proxy): dedupe Codex WS request logging for accurate mixed-provider dashboards (#2189) 2026-07-15 18:18:34 +00:00
test_settings_store.py fix(settings): accept documented HEADROOM_* env names as settings keys (#2833) 2026-08-11 17:22:27 -05:00
test_tool_search_repair_after_turn_hooks.py fix(proxy/anthropic): repair headroom_retrieve history references the tools array cannot support (#2876) 2026-08-13 15:05:07 -05:00
test_transformations_feed.py fix(proxy): scope CORS to loopback + gate operator/content endpoints (#1226) 2026-06-21 00:50:55 -07:00