Merge pull request #6 from atrvd/sergey/fix-test-isolation-ccs-managed-env

test(ci): isolate CCS-managed env vars from host session
This commit is contained in:
Sergey authored and GitHub committed 2026-04-11 17:41:00 +02:00
commit 6038843d9d
3 files changed
+24

No files matched your search

@@ -73,6 +73,8 @@ function stubProcessExit(): void {
beforeEach(async () => {
tempRoot = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'ccs-persist-handler-test-'));
originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR;
// Clear CLAUDE_CONFIG_DIR so scoped CCS_HOME takes effect (leaks from host CCS session)
delete process.env.CLAUDE_CONFIG_DIR;
originalProcessExit = process.exit;
originalFsOpen = fs.promises.open;
originalFsRename = fs.promises.rename;
@@ -12,6 +12,12 @@ import { tmpdir } from 'node:os';
import { spawnSync } from 'node:child_process';
const hookPath = join(process.cwd(), 'lib', 'hooks', 'websearch-transformer.cjs');
/**
* Neutralise CCS_PROFILE_TYPE so shouldSkipHook() does not short-circuit
* when tests run inside a CCS-managed Claude session (where it is 'account').
*/
const NEUTRAL_PROFILE_TYPE = '';
type HookOutput = {
hookSpecificOutput: {
additionalContext: string;
@@ -101,6 +107,7 @@ function runHookWithMockedFetch(mode: 'success' | 'empty' | 'non-result' | 'fail
}),
env: {
...process.env,
CCS_PROFILE_TYPE: NEUTRAL_PROFILE_TYPE,
CCS_WEBSEARCH_ENABLED: '1',
CCS_WEBSEARCH_SKIP: '0',
CCS_WEBSEARCH_BRAVE: '0',
@@ -352,6 +359,7 @@ describe('websearch-transformer hook helpers', () => {
}),
env: {
...process.env,
CCS_PROFILE_TYPE: NEUTRAL_PROFILE_TYPE,
CCS_HOME: ccsHome,
CCS_WEBSEARCH_TRACE: '1',
CCS_WEBSEARCH_TRACE_LAUNCH_ID: 'hook-trace-test',
@@ -428,6 +436,7 @@ describe('websearch-transformer hook helpers', () => {
}),
env: {
...process.env,
CCS_PROFILE_TYPE: NEUTRAL_PROFILE_TYPE,
CCS_HOME: ccsHome,
CCS_WEBSEARCH_TRACE: '1',
CCS_WEBSEARCH_TRACE_FILE: disallowedTracePath,
@@ -509,6 +518,7 @@ global.fetch = async (url) => {
}),
env: {
...process.env,
CCS_PROFILE_TYPE: NEUTRAL_PROFILE_TYPE,
CCS_HOME: ccsHome,
CCS_WEBSEARCH_TRACE: '1',
CCS_WEBSEARCH_TRACE_LAUNCH_ID: 'quota-fallback-test',
@@ -623,6 +633,7 @@ global.fetch = async (url) => {
}),
env: {
...process.env,
CCS_PROFILE_TYPE: NEUTRAL_PROFILE_TYPE,
CCS_HOME: ccsHome,
CCS_WEBSEARCH_TRACE: '1',
CCS_WEBSEARCH_TRACE_LAUNCH_ID: 'cooldown-skip-test',
@@ -723,6 +734,7 @@ global.fetch = async (url) => {
}),
env: {
...process.env,
CCS_PROFILE_TYPE: NEUTRAL_PROFILE_TYPE,
CCS_HOME: ccsHome,
CCS_WEBSEARCH_TRACE: '1',
CCS_WEBSEARCH_TRACE_LAUNCH_ID: 'transient-retry-test',
@@ -30,6 +30,7 @@ let baselineSighupListeners: Array<(...args: unknown[]) => void> = [];
let originalCcsHome: string | undefined;
let originalCcsClaudePath: string | undefined;
let originalDisableAutoUpdater: string | undefined;
let originalClaudeConfigDir: string | undefined;
const realSpawn = childProcess.spawn.bind(childProcess);
const realSpawnSync = childProcess.spawnSync.bind(childProcess);
const realExecSync = childProcess.execSync.bind(childProcess);
@@ -151,10 +152,17 @@ describe('CLAUDECODE environment stripping', () => {
beforeEach(() => {
spawnCalls.length = 0;
process.env.CCS_QUIET = '1';
// Save original env values for restoration in afterEach
originalCcsHome = process.env.CCS_HOME;
originalCcsClaudePath = process.env.CCS_CLAUDE_PATH;
originalDisableAutoUpdater = process.env.DISABLE_AUTOUPDATER;
originalClaudeConfigDir = process.env.CLAUDE_CONFIG_DIR;
// Clear CCS-managed env vars that leak from host sessions
delete process.env.DISABLE_AUTOUPDATER;
delete process.env.CLAUDE_CONFIG_DIR;
baselineSigintListeners = process.listeners('SIGINT');
baselineSigtermListeners = process.listeners('SIGTERM');
baselineSighupListeners = process.listeners('SIGHUP');
@@ -175,6 +183,8 @@ describe('CLAUDECODE environment stripping', () => {
} else {
delete process.env.DISABLE_AUTOUPDATER;
}
if (originalClaudeConfigDir !== undefined) process.env.CLAUDE_CONFIG_DIR = originalClaudeConfigDir;
else delete process.env.CLAUDE_CONFIG_DIR;
for (const listener of process.listeners('SIGINT')) {
if (!baselineSigintListeners.includes(listener)) {