diff --git a/tests/README.md b/tests/README.md index f45a8a831..90cd2379d 100644 --- a/tests/README.md +++ b/tests/README.md @@ -61,6 +61,7 @@ All tests live directly in `integration/`; shared mechanics live in `utils/`. | `test_case_06_*`, `test_case_08_*` | Pass a provider or model-location override to managed Codex after configure and from fresh state | ug rejects the override before Codex starts and preserves agent-owned state | | `test_case_10_*`, `test_case_12_*` | Disable discovery and pass a provider or model-location override to managed Codex | ug still rejects both configured and fresh launches | | `test_ug_configure_managed_codex_catalog_fallback` | Configure from an injected managed response containing a GPT model absent from Codex's bundled catalog | Actionable metadata warning; conservative catalog entry for the unknown model; real Codex prompt on the valid default model | +| `test_managed_fixture_codex_http_headers_in_managed_file` | Interactive PTY configure with injected managed `http_headers` for Codex | The specified header (`x-databricks-workspace`) lands in `model_providers.Databricks.http_headers` in `/etc/codex/managed_config.toml` with the exact admin value | | `test_managed_fixture_claude_mps_defaults_accompany_discovery`, `test_managed_fixture_claude_parent_schema_defaults_accompany_discovery` | Launch Claude from injected managed defaults with MPS and Unity Catalog discovery | Both generated settings files retain every admin-authored default alongside the source header; only UC Opus/Sonnet family ids gain `[1m]` | | `test_managed_fixture_claude_model_lifecycle`, `test_managed_fixture_codex_model_lifecycle` | Configure across no config -> static A -> static B -> MPS -> no config (stub-injected, `null` for no-config; MPS via a real provider service) | Each agent's model files reconcile to each static config (removed models pruned); switching to an MPS and a workspace with no managed config both clear ug's managed model settings so no stale list is enforced | | `test_ug_installed_wheel_exposes_help_and_version` | Invoke freshly installed console command | Package version matches; public help works | @@ -72,9 +73,9 @@ All tests live directly in `integration/`; shared mechanics live in `utils/`. With both agents selected there are **46 live cases** (6 interactive TUI cases), **4 managed-workspace cases** (marker `managed`, run against a separate workspace that publishes a CodingAgentConfig), **1 two-workspace case** (marker `workspace_switch`), -**36 managed-fixture cases** (marker `managed_fixture`, with only +**37 managed-fixture cases** (marker `managed_fixture`, with only the CodingAgentConfig input injected), and **5 installation checks**. The 24 numbered scenarios -cover configured and fresh state across the Claude and Codex managed-discovery matrix; twelve +cover configured and fresh state across the Claude and Codex managed-discovery matrix; thirteen existing collected cases cover focused model, MCP, skills, and lifecycle shapes. Parametrization varies argument spelling or routing mode, never hides the agent/provider in the test name. Duplicate boot-only cases diff --git a/tests/integration/README.md b/tests/integration/README.md index 2ac6a4ddf..be2e6386c 100644 --- a/tests/integration/README.md +++ b/tests/integration/README.md @@ -178,10 +178,10 @@ checks** with both agents. A separate **4 managed-workspace cases** (one per age re-configure, and a cache-TTL journey; marker `managed`) run against a workspace that publishes a CodingAgentConfig; see "Managed-workspace journeys" below. One **`workspace_switch` case** uses two real workspaces and checks skills MCP cleanup and a completed Claude task. -A further **36 `managed_fixture` +A further **37 `managed_fixture` cases** use `UCODE_MANAGED_CONFIG_STUB`. Twenty-four explicit configured/fresh Claude and Codex discovery and source-override journeys fetch the published config once per agent, replace that -agent's static source with its dedicated MPS, and reuse the result. Twelve existing collected cases +agent's static source with its dedicated MPS, and reuse the result. Thirteen existing collected cases cover focused model, MCP, skills, and lifecycle shapes, including per-agent model reconciliation and managed skill cleanup. The two Claude default-model cases launch with injected MPS and Unity Catalog sources and verify both generated settings files retain all admin-authored family defaults. diff --git a/tests/integration/test_ug_configure_managed_mcp.py b/tests/integration/test_ug_configure_managed_mcp.py index 93268805f..b3d66f572 100644 --- a/tests/integration/test_ug_configure_managed_mcp.py +++ b/tests/integration/test_ug_configure_managed_mcp.py @@ -93,3 +93,46 @@ def test_managed_fixture_codex_mcp_written_to_managed_file(live_session, workspa personal = tomllib.loads(personal_path.read_text()) if personal_path.exists() else {} personal_servers = personal.get("mcp_servers") or {} assert not any("github" in name.lower() for name in personal_servers), personal_servers + + +@pytest.mark.managed_fixture +@pytest.mark.codex +def test_managed_fixture_codex_http_headers_in_managed_file(live_session, workspace, tmp_path): + """Scenario: an interactive configure with managed http_headers writes them into the OS-managed file. + + Scope: managed-config content assertion in the same family as the MCP/skills/models + managed_fixture tests — verifies the http_headers field flows from the injected + CodingAgentConfig through `ug configure codex` into the OS-managed + /etc/codex/managed_config.toml under [model_providers.Databricks.http_headers]. + + Expected: after an interactive PTY configure, /etc/codex/managed_config.toml contains + model_providers.Databricks.http_headers with the admin-specified header + x-databricks-workspace = "eng-ml-inference", exactly as the injected managed config + dictates. + """ + session = live_session + managed_header_key = "x-databricks-workspace" + managed_header_value = "eng-ml-inference" + config = build_coding_agent_config( + "CODING_AGENT_CODEX", + build_codex_agent_config( + models=[CODEX_MODEL], + http_headers={managed_header_key: managed_header_value}, + ), + ) + set_managed_config_stub(session, tmp_path, config) + command = [str(session.binary), "configure", "--workspace", workspace, "--skip-upgrade"] + with ConfigureTerminal(session, "codex", command, "managed-http-headers-codex") as configure: + configure.finish(timeout=300) + + managed = tomllib.loads(session.run(CODEX_MANAGED_CONFIG_PATH, binary="cat", timeout=30).stdout) + provider = (managed.get("model_providers") or {}).get("Databricks") or {} + headers = provider.get("http_headers") or {} + assert managed_header_key in headers, ( + f"Expected header {managed_header_key!r} in model_providers.Databricks.http_headers; " + f"got: {headers!r}\nFull managed config: {managed!r}" + ) + assert headers[managed_header_key] == managed_header_value, ( + f"Expected {managed_header_key!r} = {managed_header_value!r}, " + f"got {headers[managed_header_key]!r}" + ) diff --git a/tests/integration/utils/managed.py b/tests/integration/utils/managed.py index 423ebcf80..fbeb86630 100644 --- a/tests/integration/utils/managed.py +++ b/tests/integration/utils/managed.py @@ -115,13 +115,20 @@ def build_claude_agent_config( return {"agent": "CODING_AGENT_CLAUDE_CODE", "config": config} -def build_codex_agent_config(*, models: list[str], smart_routing: bool = False) -> dict: +def build_codex_agent_config( + *, + models: list[str], + smart_routing: bool = False, + http_headers: dict[str, str] | None = None, +) -> dict: config = { "models": {"model_services": models}, "default_models": {"default_model": models[0]}, } if smart_routing: config["smart_routing"] = {"enabled": True} + if http_headers is not None: + config["http_headers"] = http_headers return {"agent": "CODING_AGENT_CODEX", "config": config}