mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-04-08 10:23:30 +08:00
Merge branch 'main' into main
This commit is contained in:
70
tests/codex-config.test.js
Normal file
70
tests/codex-config.test.js
Normal file
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Tests for `.codex/config.toml` reference defaults.
|
||||
*
|
||||
* Run with: node tests/codex-config.test.js
|
||||
*/
|
||||
|
||||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
console.log(` ✓ ${name}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.log(` ✗ ${name}`);
|
||||
console.log(` Error: ${err.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const repoRoot = path.join(__dirname, '..');
|
||||
const configPath = path.join(repoRoot, '.codex', 'config.toml');
|
||||
const config = fs.readFileSync(configPath, 'utf8');
|
||||
const codexAgentsDir = path.join(repoRoot, '.codex', 'agents');
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
|
||||
if (
|
||||
test('reference config does not pin a top-level model', () => {
|
||||
assert.ok(!/^model\s*=/m.test(config), 'Expected `.codex/config.toml` to inherit the CLI default model');
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (
|
||||
test('reference config does not pin a top-level model provider', () => {
|
||||
assert.ok(
|
||||
!/^model_provider\s*=/m.test(config),
|
||||
'Expected `.codex/config.toml` to inherit the CLI default provider',
|
||||
);
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (
|
||||
test('sample Codex role configs do not use o4-mini', () => {
|
||||
const roleFiles = fs.readdirSync(codexAgentsDir).filter(file => file.endsWith('.toml'));
|
||||
assert.ok(roleFiles.length > 0, 'Expected sample role config files under `.codex/agents`');
|
||||
|
||||
for (const roleFile of roleFiles) {
|
||||
const rolePath = path.join(codexAgentsDir, roleFile);
|
||||
const roleConfig = fs.readFileSync(rolePath, 'utf8');
|
||||
assert.ok(
|
||||
!/^model\s*=\s*"o4-mini"$/m.test(roleConfig),
|
||||
`Expected sample role config to avoid o4-mini: ${roleFile}`,
|
||||
);
|
||||
}
|
||||
})
|
||||
)
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
console.log(`\nPassed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
@@ -98,6 +98,44 @@ function cleanupTestDir(testDir) {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
function normalizeComparablePath(targetPath) {
|
||||
if (!targetPath) return '';
|
||||
|
||||
let normalizedPath = String(targetPath).trim().replace(/\\/g, '/');
|
||||
|
||||
if (/^\/[a-zA-Z]\//.test(normalizedPath)) {
|
||||
normalizedPath = `${normalizedPath[1]}:/${normalizedPath.slice(3)}`;
|
||||
}
|
||||
|
||||
if (/^[a-zA-Z]:\//.test(normalizedPath)) {
|
||||
normalizedPath = `${normalizedPath[0].toUpperCase()}:${normalizedPath.slice(2)}`;
|
||||
}
|
||||
|
||||
try {
|
||||
normalizedPath = fs.realpathSync(normalizedPath);
|
||||
} catch {
|
||||
// Fall through to string normalization when the path cannot be resolved directly.
|
||||
}
|
||||
|
||||
return path.normalize(normalizedPath).replace(/\\/g, '/').replace(/^([a-z]):/, (_, drive) => `${drive.toUpperCase()}:`);
|
||||
}
|
||||
|
||||
function pathsReferToSameLocation(leftPath, rightPath) {
|
||||
const normalizedLeftPath = normalizeComparablePath(leftPath);
|
||||
const normalizedRightPath = normalizeComparablePath(rightPath);
|
||||
|
||||
if (!normalizedLeftPath || !normalizedRightPath) return false;
|
||||
if (normalizedLeftPath === normalizedRightPath) return true;
|
||||
|
||||
try {
|
||||
const leftStats = fs.statSync(normalizedLeftPath);
|
||||
const rightStats = fs.statSync(normalizedRightPath);
|
||||
return leftStats.dev === rightStats.dev && leftStats.ino === rightStats.ino;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function createCommandShim(binDir, baseName, logFile) {
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
|
||||
@@ -155,6 +193,7 @@ async function runTests() {
|
||||
|
||||
let passed = 0;
|
||||
let failed = 0;
|
||||
let skipped = 0;
|
||||
|
||||
const scriptsDir = path.join(__dirname, '..', '..', 'scripts', 'hooks');
|
||||
|
||||
@@ -2148,12 +2187,12 @@ async function runTests() {
|
||||
passed++;
|
||||
else failed++;
|
||||
|
||||
if (
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - detect-project writes project metadata to the registry and project directory');
|
||||
console.log(' (skipped — bash script paths are not Windows-compatible)');
|
||||
skipped++;
|
||||
} else if (
|
||||
await asyncTest('detect-project writes project metadata to the registry and project directory', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' (skipped — bash script paths are not Windows-compatible)');
|
||||
return true;
|
||||
}
|
||||
const testRoot = createTestDir();
|
||||
const homeDir = path.join(testRoot, 'home');
|
||||
const repoDir = path.join(testRoot, 'repo');
|
||||
@@ -2189,9 +2228,9 @@ async function runTests() {
|
||||
|
||||
assert.strictEqual(code, 0, `detect-project should source cleanly, stderr: ${stderr}`);
|
||||
|
||||
const [projectId, projectDir] = stdout.trim().split(/\r?\n/);
|
||||
const [projectId] = stdout.trim().split(/\r?\n/);
|
||||
const registryPath = path.join(homeDir, '.claude', 'homunculus', 'projects.json');
|
||||
const projectMetadataPath = path.join(projectDir, 'project.json');
|
||||
const projectMetadataPath = path.join(homeDir, '.claude', 'homunculus', 'projects', projectId, 'project.json');
|
||||
|
||||
assert.ok(projectId, 'detect-project should emit a project id');
|
||||
assert.ok(fs.existsSync(registryPath), 'projects.json should be created');
|
||||
@@ -2203,7 +2242,13 @@ async function runTests() {
|
||||
assert.ok(registry[projectId], 'registry should contain the detected project');
|
||||
assert.strictEqual(metadata.id, projectId, 'project.json should include the detected id');
|
||||
assert.strictEqual(metadata.name, path.basename(repoDir), 'project.json should include the repo name');
|
||||
assert.strictEqual(fs.realpathSync(metadata.root), fs.realpathSync(repoDir), 'project.json should include the repo root');
|
||||
const normalizedMetadataRoot = normalizeComparablePath(metadata.root);
|
||||
const normalizedRepoDir = normalizeComparablePath(repoDir);
|
||||
assert.ok(normalizedMetadataRoot, 'project.json should include a non-empty repo root');
|
||||
assert.ok(
|
||||
pathsReferToSameLocation(normalizedMetadataRoot, normalizedRepoDir),
|
||||
`project.json should include the repo root (expected ${normalizedRepoDir}, got ${normalizedMetadataRoot})`,
|
||||
);
|
||||
assert.strictEqual(metadata.remote, 'https://github.com/example/ecc-test.git', 'project.json should include the sanitized remote');
|
||||
assert.ok(metadata.created_at, 'project.json should include created_at');
|
||||
assert.ok(metadata.last_seen, 'project.json should include last_seen');
|
||||
@@ -4242,12 +4287,12 @@ async function runTests() {
|
||||
// ── Round 74: session-start.js main().catch handler ──
|
||||
console.log('\nRound 74: session-start.js (main catch — unrecoverable error):');
|
||||
|
||||
if (
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - session-start exits 0 with error message when HOME is non-directory');
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
skipped++;
|
||||
} else if (
|
||||
await asyncTest('session-start exits 0 with error message when HOME is non-directory', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
return;
|
||||
}
|
||||
// HOME=/dev/null makes ensureDir(sessionsDir) throw ENOTDIR,
|
||||
// which propagates to main().catch — the top-level error boundary
|
||||
const result = await runScript(path.join(scriptsDir, 'session-start.js'), '', {
|
||||
@@ -4264,12 +4309,12 @@ async function runTests() {
|
||||
// ── Round 75: pre-compact.js main().catch handler ──
|
||||
console.log('\nRound 75: pre-compact.js (main catch — unrecoverable error):');
|
||||
|
||||
if (
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - pre-compact exits 0 with error message when HOME is non-directory');
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
skipped++;
|
||||
} else if (
|
||||
await asyncTest('pre-compact exits 0 with error message when HOME is non-directory', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
return;
|
||||
}
|
||||
// HOME=/dev/null makes ensureDir(sessionsDir) throw ENOTDIR,
|
||||
// which propagates to main().catch — the top-level error boundary
|
||||
const result = await runScript(path.join(scriptsDir, 'pre-compact.js'), '', {
|
||||
@@ -4286,12 +4331,12 @@ async function runTests() {
|
||||
// ── Round 75: session-end.js main().catch handler ──
|
||||
console.log('\nRound 75: session-end.js (main catch — unrecoverable error):');
|
||||
|
||||
if (
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - session-end exits 0 with error message when HOME is non-directory');
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
skipped++;
|
||||
} else if (
|
||||
await asyncTest('session-end exits 0 with error message when HOME is non-directory', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
return;
|
||||
}
|
||||
// HOME=/dev/null makes ensureDir(sessionsDir) throw ENOTDIR inside main(),
|
||||
// which propagates to runMain().catch — the top-level error boundary
|
||||
const result = await runScript(path.join(scriptsDir, 'session-end.js'), '{}', {
|
||||
@@ -4308,12 +4353,12 @@ async function runTests() {
|
||||
// ── Round 76: evaluate-session.js main().catch handler ──
|
||||
console.log('\nRound 76: evaluate-session.js (main catch — unrecoverable error):');
|
||||
|
||||
if (
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - evaluate-session exits 0 with error message when HOME is non-directory');
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
skipped++;
|
||||
} else if (
|
||||
await asyncTest('evaluate-session exits 0 with error message when HOME is non-directory', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
return;
|
||||
}
|
||||
// HOME=/dev/null makes ensureDir(learnedSkillsPath) throw ENOTDIR,
|
||||
// which propagates to main().catch — the top-level error boundary
|
||||
const result = await runScript(path.join(scriptsDir, 'evaluate-session.js'), '{}', {
|
||||
@@ -4330,12 +4375,12 @@ async function runTests() {
|
||||
// ── Round 76: suggest-compact.js main().catch handler ──
|
||||
console.log('\nRound 76: suggest-compact.js (main catch — double-failure):');
|
||||
|
||||
if (
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' - suggest-compact exits 0 with error when TMPDIR is non-directory');
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
skipped++;
|
||||
} else if (
|
||||
await asyncTest('suggest-compact exits 0 with error when TMPDIR is non-directory', async () => {
|
||||
if (process.platform === 'win32') {
|
||||
console.log(' (skipped — /dev/null not available on Windows)');
|
||||
return;
|
||||
}
|
||||
// TMPDIR=/dev/null causes openSync to fail (ENOTDIR), then the catch
|
||||
// fallback writeFile also fails, propagating to main().catch
|
||||
const result = await runScript(path.join(scriptsDir, 'suggest-compact.js'), '', {
|
||||
@@ -4807,7 +4852,8 @@ Some random content without the expected ### Context to Load section
|
||||
console.log('\n=== Test Results ===');
|
||||
console.log(`Passed: ${passed}`);
|
||||
console.log(`Failed: ${failed}`);
|
||||
console.log(`Total: ${passed + failed}\n`);
|
||||
console.log(`Skipped: ${skipped}`);
|
||||
console.log(`Total: ${passed + failed + skipped}\n`);
|
||||
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user