Files
ccs/tests/shared/fixtures/test-environment.js
T
Tam Nhu Tran 3af554275e test: isolate shared-state test suites for CI stability
- add dependency injection to export-command, binary-manager,
  codex-plan-compatibility, config-command, and usage-syncer
- override process.exit in api-export test to prevent silent
  termination in Bun's parallel test runner
- harden test-environment bootstrap with process.env isolation
- fix auth middleware to avoid config upgrade during checks
2026-03-25 16:41:41 -04:00

247 lines
7.0 KiB
JavaScript

#!/usr/bin/env node
'use strict';
/**
* Test Environment Isolation
*
* Provides isolated ~/.ccs/ directory for tests to prevent
* interference with the user's actual CCS configuration.
*
* Usage:
* const { createTestEnvironment } = require('../fixtures/test-environment');
*
* describe('my tests', () => {
* let testEnv;
*
* before(() => {
* testEnv = createTestEnvironment();
* });
*
* after(() => {
* testEnv.cleanup();
* });
*
* it('test something', () => {
* // Tests run with CCS_HOME pointing to temp directory
* // Your test code here
* });
* });
*/
const fs = require('fs');
const path = require('path');
const os = require('os');
let bootstrappedTestHome;
const originalHomedir = os.homedir;
let homedirPatched = false;
function getEffectiveTestHome() {
return process.env.CCS_HOME || process.env.HOME || process.env.USERPROFILE || bootstrappedTestHome || originalHomedir();
}
function patchHomedirForTests() {
if (homedirPatched) {
return;
}
os.homedir = () => getEffectiveTestHome();
homedirPatched = true;
}
function createIsolatedTestHome(prefix = 'ccs-test-home-') {
const testHome = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
fs.mkdirSync(path.join(testHome, '.ccs'), { recursive: true });
fs.mkdirSync(path.join(testHome, '.claude'), { recursive: true });
fs.mkdirSync(path.join(testHome, '.config'), { recursive: true });
fs.mkdirSync(path.join(testHome, '.cache'), { recursive: true });
fs.mkdirSync(path.join(testHome, '.state'), { recursive: true });
return testHome;
}
function ensureGlobalTestEnvironment() {
if (bootstrappedTestHome) {
return bootstrappedTestHome;
}
const testHome = createIsolatedTestHome();
process.env.HOME = testHome;
process.env.USERPROFILE = testHome;
process.env.CCS_HOME = testHome;
process.env.XDG_CONFIG_HOME = path.join(testHome, '.config');
process.env.XDG_CACHE_HOME = path.join(testHome, '.cache');
process.env.XDG_STATE_HOME = path.join(testHome, '.state');
process.env.CCS_TEST_BOOTSTRAP_HOME = testHome;
bootstrappedTestHome = testHome;
patchHomedirForTests();
return bootstrappedTestHome;
}
/**
* Create an isolated test environment
* Sets CCS_HOME to a temporary directory and provides cleanup
*
* @returns {object} Test environment with paths and cleanup function
*/
function createTestEnvironment() {
// Create unique temp directory for this test run
const testHome = createIsolatedTestHome('ccs-test-');
const testCcsDir = path.join(testHome, '.ccs');
// Store original environment
const originalHome = process.env.HOME;
const originalCcsHome = process.env.CCS_HOME;
const originalUserProfile = process.env.USERPROFILE;
const originalXdgConfigHome = process.env.XDG_CONFIG_HOME;
const originalXdgCacheHome = process.env.XDG_CACHE_HOME;
const originalXdgStateHome = process.env.XDG_STATE_HOME;
// Keep HOME-family env vars aligned so code that still consults os.homedir()
// or XDG defaults stays inside the isolated test sandbox.
process.env.HOME = testHome;
process.env.USERPROFILE = testHome;
process.env.CCS_HOME = testHome;
process.env.XDG_CONFIG_HOME = path.join(testHome, '.config');
process.env.XDG_CACHE_HOME = path.join(testHome, '.cache');
process.env.XDG_STATE_HOME = path.join(testHome, '.state');
patchHomedirForTests();
// Return environment object
return {
/** Path to the test home directory (like ~) */
testHome,
/** Path to the test .ccs directory (like ~/.ccs) */
ccsDir: testCcsDir,
/** Original HOME value for reference */
originalHome,
/**
* Get a path within the test CCS directory
* @param {...string} parts - Path segments
* @returns {string} Full path
*/
getCcsPath(...parts) {
return path.join(testCcsDir, ...parts);
},
/**
* Create a file in the test CCS directory
* @param {string} relativePath - Path relative to .ccs/
* @param {string|object} content - File content (objects are JSON stringified)
*/
createFile(relativePath, content) {
const fullPath = path.join(testCcsDir, relativePath);
const dir = path.dirname(fullPath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
const data = typeof content === 'object' ? JSON.stringify(content, null, 2) : content;
fs.writeFileSync(fullPath, data);
},
/**
* Read a file from the test CCS directory
* @param {string} relativePath - Path relative to .ccs/
* @param {boolean} [asJson=false] - Parse as JSON
* @returns {string|object} File content
*/
readFile(relativePath, asJson = false) {
const fullPath = path.join(testCcsDir, relativePath);
const content = fs.readFileSync(fullPath, 'utf8');
return asJson ? JSON.parse(content) : content;
},
/**
* Check if a file exists in the test CCS directory
* @param {string} relativePath - Path relative to .ccs/
* @returns {boolean}
*/
fileExists(relativePath) {
return fs.existsSync(path.join(testCcsDir, relativePath));
},
/**
* Clean up the test environment and restore original settings
*/
cleanup() {
// Restore original environment
if (originalHome !== undefined) {
process.env.HOME = originalHome;
} else {
delete process.env.HOME;
}
if (originalUserProfile !== undefined) {
process.env.USERPROFILE = originalUserProfile;
} else {
delete process.env.USERPROFILE;
}
if (originalCcsHome !== undefined) {
process.env.CCS_HOME = originalCcsHome;
} else {
delete process.env.CCS_HOME;
}
if (originalXdgConfigHome !== undefined) {
process.env.XDG_CONFIG_HOME = originalXdgConfigHome;
} else {
delete process.env.XDG_CONFIG_HOME;
}
if (originalXdgCacheHome !== undefined) {
process.env.XDG_CACHE_HOME = originalXdgCacheHome;
} else {
delete process.env.XDG_CACHE_HOME;
}
if (originalXdgStateHome !== undefined) {
process.env.XDG_STATE_HOME = originalXdgStateHome;
} else {
delete process.env.XDG_STATE_HOME;
}
// Clean up temp directory
try {
fs.rmSync(testHome, { recursive: true, force: true });
} catch (err) {
// Ignore cleanup errors
console.warn(`[test-environment] Cleanup warning: ${err.message}`);
}
}
};
}
/**
* Get the CCS home directory (respects CCS_HOME env var)
* This should be used instead of os.homedir() for CCS paths
*
* @returns {string} Home directory path
*/
function getCcsHome() {
return process.env.CCS_HOME || os.homedir();
}
/**
* Get the CCS directory path
*
* @returns {string} Path to .ccs directory
*/
function getCcsDir() {
return path.join(getCcsHome(), '.ccs');
}
module.exports = {
createTestEnvironment,
ensureGlobalTestEnvironment,
getCcsHome,
getCcsDir
};
if (process.env.CCS_TEST_DISABLE_GLOBAL_BOOTSTRAP !== '1') {
ensureGlobalTestEnvironment();
}