Skip to content

Commit 6d09c19

Browse files
test(cli): neutralize ambient client home overrides in the C runner
The CLI resolves each client's config home from its own environment variable before falling back to $HOME — cbm_codex_config_dir() honours $CODEX_HOME and ignores the home_dir it is handed. A test that redirects only HOME therefore does not isolate those clients: it resolves, reads and WRITES the developer's real configuration. On a machine with $CODEX_HOME exported (any Codex or Orca user) 27 of 269 cli tests failed for that reason alone. Every one of them asserted a successful rc while the CLI was reporting genuine failures against the ambient config: an unbalanced managed marker left in the real config.toml, an AGENTS.md that is a symlink and is refused by the O_NOFOLLOW writer, and user-modified agent profiles that are correctly preserved. The behaviour under test passed in all of them; only the rc was poisoned. Worse than the red: the suite mutated that real config, installing and removing SKILL.md under the user's Codex home. The individual tests that already neutralize CODEX_HOME are exactly the ones that pass, and the file carries a comment admitting one suite was "green only via env leaked from earlier suites". Rather than extend those ad-hoc per-test lists again, neutralize the whole inventory once in the runner, and reuse the inventory the shell fixtures are already required to satisfy. tests/test_smoke_fixture_contract.sh grows one check so the two cannot drift: a client isolated in the smoke scripts but forgotten in the C runner now fails the contract. Verified on macOS arm64 with $CODEX_HOME and $OPENCODE_CONFIG_DIR exported: cli went from 241 passed / 28 failed to 268 passed / 0 failed, with no change outside tests/. Signed-off-by: Gabriel Gonzalez Pineda <64bits@gmail.com>
1 parent bb2b990 commit 6d09c19

2 files changed

Lines changed: 77 additions & 29 deletions

File tree

tests/test_main.c

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ static bool tf_invoked_as_windows_git_module(void) {
5454
* Inspect the actual loaded module so the copied git.exe probe cannot fall
5555
* through into the ordinary test runner when argv[0] is merely "git". */
5656
wchar_t image[32768];
57-
DWORD image_length =
58-
GetModuleFileNameW(NULL, image, (DWORD)(sizeof(image) / sizeof(image[0])));
57+
DWORD image_length = GetModuleFileNameW(NULL, image, (DWORD)(sizeof(image) / sizeof(image[0])));
5958
if (image_length == 0 || image_length >= (DWORD)(sizeof(image) / sizeof(image[0]))) {
6059
return false;
6160
}
@@ -86,8 +85,7 @@ static bool tf_invoked_as_blocking_git(const char *argv0) {
8685
base = cursor + 1;
8786
}
8887
}
89-
return strcmp(base, "git") == 0 || strcmp(base, "git.exe") == 0 ||
90-
strcmp(base, "GIT.EXE") == 0;
88+
return strcmp(base, "git") == 0 || strcmp(base, "git.exe") == 0 || strcmp(base, "GIT.EXE") == 0;
9189
#endif
9290
}
9391

@@ -165,6 +163,38 @@ static void tf_cleanup_cache_sentinel(void) {
165163
}
166164
}
167165

166+
/* Client home overrides the CLI honours BEFORE $HOME, so redirecting HOME alone
167+
* does not isolate them: cbm_codex_config_dir() and its siblings return the
168+
* ambient path and the suite resolves against the developer's real config —
169+
* reading its state and writing to it. Same inventory the shell fixtures are
170+
* already required to neutralize (tests/test_smoke_fixture_contract.sh), kept
171+
* in one place so a new client cannot be isolated in the smoke scripts and
172+
* forgotten here. A test that exercises one of these sets it after setup. */
173+
static const char *const tf_client_home_overrides[] = {
174+
"CLAUDE_CONFIG_DIR",
175+
"CODEX_HOME",
176+
"KIRO_HOME",
177+
"HERMES_HOME",
178+
"QWEN_HOME",
179+
"CLINE_DATA_DIR",
180+
"OPENCLAW_HOME",
181+
"OPENCLAW_STATE_DIR",
182+
"OPENCLAW_PROFILE",
183+
"OPENCLAW_CONFIG_PATH",
184+
"OPENCLAW_WORKSPACE_DIR",
185+
"OPENCODE_CONFIG",
186+
"OPENCODE_CONFIG_DIR",
187+
"COPILOT_HOME",
188+
"CRUSH_GLOBAL_CONFIG",
189+
"VIBE_HOME",
190+
"GLAB_CONFIG_DIR",
191+
"KIMI_CODE_HOME",
192+
"CBM_CONTINUE_CONFIG_PATH",
193+
"CBM_TRAE_CONFIG_PATH",
194+
"CBM_ROO_CONFIG_PATH",
195+
"CBM_CODY_CONFIG_PATH",
196+
};
197+
168198
static bool tf_setup_cache_sentinel(void) {
169199
snprintf(tf_home_sentinel, sizeof(tf_home_sentinel), "/tmp/cbm-test-home-XXXXXX");
170200
if (!cbm_mkdtemp(tf_home_sentinel)) {
@@ -175,6 +205,10 @@ static bool tf_setup_cache_sentinel(void) {
175205
* override keeps both conventions pointed at the same isolated tree. */
176206
cbm_setenv("HOME", tf_home_sentinel, 1);
177207
cbm_unsetenv("CBM_CACHE_DIR");
208+
for (size_t i = 0U; i < sizeof(tf_client_home_overrides) / sizeof(tf_client_home_overrides[0]);
209+
i++) {
210+
cbm_unsetenv(tf_client_home_overrides[i]);
211+
}
178212
atexit(tf_cleanup_cache_sentinel);
179213
return true;
180214
}

tests/test_smoke_fixture_contract.sh

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,44 @@ require(
6969
"smoke-local.sh must not reserve/release a port or launch a separate http.server",
7070
)
7171
72+
# Every environment variable the CLI honours ahead of $HOME. A fixture that
73+
# redirects only HOME still resolves these to the developer's real config, so
74+
# both the shell fixtures and the C runner must neutralize the whole set.
75+
CLIENT_HOME_OVERRIDES = (
76+
"CLAUDE_CONFIG_DIR",
77+
"CODEX_HOME",
78+
"KIRO_HOME",
79+
"HERMES_HOME",
80+
"QWEN_HOME",
81+
"CLINE_DATA_DIR",
82+
"OPENCLAW_HOME",
83+
"OPENCLAW_STATE_DIR",
84+
"OPENCLAW_PROFILE",
85+
"OPENCLAW_CONFIG_PATH",
86+
"OPENCLAW_WORKSPACE_DIR",
87+
"OPENCODE_CONFIG",
88+
"OPENCODE_CONFIG_DIR",
89+
"COPILOT_HOME",
90+
"CRUSH_GLOBAL_CONFIG",
91+
"VIBE_HOME",
92+
"GLAB_CONFIG_DIR",
93+
"KIMI_CODE_HOME",
94+
"CBM_CONTINUE_CONFIG_PATH",
95+
"CBM_TRAE_CONFIG_PATH",
96+
"CBM_ROO_CONFIG_PATH",
97+
"CBM_CODY_CONFIG_PATH",
98+
)
99+
100+
# The C suite exercises the same install/uninstall paths as the shell fixtures,
101+
# so it needs the same neutralization — otherwise a green run on a developer
102+
# machine only proves the ambient config happened to be writable.
103+
test_main = read("tests/test_main.c")
104+
for variable in CLIENT_HOME_OVERRIDES:
105+
require(
106+
f'"{variable}"' in test_main,
107+
f"tests/test_main.c must neutralize ambient {variable}",
108+
)
109+
72110
for relative, source in (
73111
("scripts/smoke-local.sh", smoke_local),
74112
("test-infrastructure/vm/vm-smoke.sh", vm_smoke),
@@ -89,31 +127,7 @@ for relative, source in (
89127
'wait "$SERVER_PID"' in source,
90128
f"{relative} cleanup must reap the fixture-server process",
91129
)
92-
for variable in (
93-
"CLAUDE_CONFIG_DIR",
94-
"CODEX_HOME",
95-
"KIRO_HOME",
96-
"HERMES_HOME",
97-
"QWEN_HOME",
98-
"CLINE_DATA_DIR",
99-
"OPENCLAW_HOME",
100-
"OPENCLAW_STATE_DIR",
101-
"OPENCLAW_PROFILE",
102-
"OPENCLAW_CONFIG_PATH",
103-
"OPENCLAW_WORKSPACE_DIR",
104-
"OPENCODE_CONFIG",
105-
"OPENCODE_CONFIG_DIR",
106-
"COPILOT_HOME",
107-
"CRUSH_GLOBAL_CONFIG",
108-
"VIBE_HOME",
109-
"GLAB_CONFIG_DIR",
110-
"KIMI_CODE_HOME",
111-
"CBM_CONTINUE_CONFIG_PATH",
112-
"CBM_TRAE_CONFIG_PATH",
113-
"CBM_ROO_CONFIG_PATH",
114-
"CBM_CODY_CONFIG_PATH",
115-
"CBM_TEST_WINDOWS_USER_PATH_RUN_ID",
116-
):
130+
for variable in CLIENT_HOME_OVERRIDES + ("CBM_TEST_WINDOWS_USER_PATH_RUN_ID",):
117131
require(
118132
f"-u {variable}" in source,
119133
f"{relative} must neutralize ambient {variable}",

0 commit comments

Comments
 (0)