headroom/tests/test_cli
Focused Instability 9f712ccbd7
fix(wrap): percent-encode non-ASCII cwd names in X-Headroom-Project header (#1071)
## Description

Non-ASCII directory names (Chinese, Japanese, Korean, Cyrillic) caused
an immediate API error when using `headroom wrap claude`:

```
API Error: Header 'X-Headroom-Project' has invalid value: '第二大脑共享'
```

RFC 7230 requires HTTP header values to be visible ASCII only. The raw
cwd basename was being sent directly, breaking the entire session before
the first token.

Closes #1069

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)

## Changes Made

- `headroom/cli/wrap.py` — `_project_name_from_cwd()`: percent-encode
non-ASCII chars via `urllib.parse.quote(name, safe="-_.() ")` so the
header value is always ASCII-safe
- `headroom/proxy/savings_tracker.py` — `sanitize_project_name()`:
`urllib.parse.unquote()` before cleanup so the stored/displayed project
name is the original Unicode directory name

ASCII-only project names are unaffected (quote/unquote is a no-op for
them).

## Testing

- [x] Unit tests pass (`pytest`)
- [x] New tests added for new functionality

### Test Output

```text
tests/test_cli/test_wrap_helpers.py::TestApplyProjectHeaderEnv::test_non_ascii_cwd_name_is_percent_encoded PASSED
tests/test_cli/test_wrap_helpers.py::TestApplyProjectHeaderEnv::test_non_ascii_cwd_header_is_ascii_safe PASSED
tests/test_proxy_project_savings.py::test_sanitize_project_name_decodes_percent_encoded_non_ascii PASSED
======================== 15 passed, 1 warning in 0.42s =========================
```

## Real Behavior Proof

- Environment: macOS 15, Python 3.11.9, headroom dev install from source
- Exact command / steps: `mkdir /tmp/test-中文-项目 && cd /tmp/test-中文-项目`,
then run `.venv/bin/pytest
tests/test_cli/test_wrap_helpers.py::TestApplyProjectHeaderEnv::test_non_ascii_cwd_header_is_ascii_safe
-v` — header_value.encode("ascii") passes without UnicodeEncodeError
- Observed result: `X-Headroom-Project` header contains percent-encoded
ASCII (`test-%E4%B8%AD%E6%96%87-%E9%A1%B9%E7%9B%AE`); proxy decodes back
to `test-中文-项目` for storage
- Not tested: live end-to-end wrap session with a real Claude API key

## Review Readiness

- [x] I have performed a self-review
- [x] This PR is ready for human review

## Checklist

- [x] My code follows the project's style guidelines
- [x] I have performed a self-review of my code
- [x] I have added tests that prove my fix is effective
- [x] New and existing unit tests pass locally with my changes

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 11:19:43 -05:00
..
test_copilot_auth.py fix: support Copilot Business subscription auth (#641) 2026-06-12 20:46:38 -05:00
test_init_cli.py fix(codex): poll /wham/usage for subscription limits (handshake no longer sends x-codex-* headers) (#924) 2026-06-12 17:03:14 -05:00
test_install_cli.py test: expand persistent install coverage 2026-04-11 18:24:15 -05:00
test_main_help_version.py feat(cli): add Docker-native install flow and parity docs 2026-04-10 23:27:24 -05:00
test_mcp.py fix(mcp): auto-register headroom MCP server in wrap claude/codex and init -g 2026-05-08 17:18:32 -07:00
test_serena_disable.py fix(serena): stop the Serena dashboard popup and make --no-serena actually disable Serena (#1003) 2026-06-14 23:32:46 -07:00
test_serena_migrate.py fix(serena): migrate stale Headroom-installed Serena entry on re-wrap (#1008) 2026-06-16 15:17:13 -05:00
test_unwrap_claude.py fix: register serena mcp during wrap 2026-05-09 23:05:59 -07:00
test_wrap_aider.py feat(proxy): per-project savings breakdown on the dashboard (claude, codex, aider, copilot, cursor) (#803) 2026-06-10 21:04:45 -05:00
test_wrap_bridge.py feat: add lean-ctx context tool support 2026-05-11 17:54:17 -04:00
test_wrap_codex.py fix(codex): retag thread providers so history menu stays whole across the proxy boundary (#1034) 2026-06-16 15:13:21 -05:00
test_wrap_continue.py fix(cli): G1 remediation — non-string clobber, per-model systemMessage, openhands gate 2026-05-25 11:54:06 -07:00
test_wrap_copilot.py feat: add Copilot BYOK provider wrapper utilities and CLI support (#1041) 2026-06-16 12:07:10 -07:00
test_wrap_goose.py refactor(cli): factor shared wrap-subcommand scaffolding 2026-05-26 11:22:50 -07:00
test_wrap_helpers.py fix(wrap): percent-encode non-ASCII cwd names in X-Headroom-Project header (#1071) 2026-06-18 11:19:43 -05:00
test_wrap_hintfile_agents.py refactor(cli): factor shared wrap-subcommand scaffolding 2026-05-26 11:22:50 -07:00
test_wrap_openclaw.py fix: stabilize codex compression, stats, and proxy lifecycle 2026-05-09 13:47:53 -07:00
test_wrap_openhands.py fix(cli): G1 remediation — non-string clobber, per-model systemMessage, openhands gate 2026-05-25 11:54:06 -07:00
test_wrap_persistent.py fix(wrap): track shared proxy clients with markers (#877) 2026-06-11 19:42:43 -05:00
test_wrap_rtk_metrics.py fix(observability): G3 remediation — bound cardinality + wire dead metrics 2026-05-24 10:41:56 -07:00
test_wrap_vibe.py feat: Add support for Mistral Vibe CLI (#935) 2026-06-16 14:59:51 -05:00