Files
orcs-code/src/utils/openclaudePaths.test.ts
Nourrisse Florian a00b7928de fix: strip comments before scanning for missing imports (#676)
* fix: strip comments before scanning for missing imports

The scanForMissingImports regex matched require() and import() patterns
inside JSDoc comments, causing false-positive missing module detection.
A documented path like `require('./commands/proactive.js')` in a comment
was resolved from the wrong directory, marked as missing, then the global
onResolve handler intercepted ALL imports of that specifier — including
valid ones — replacing them with truthy noop stubs that broke runtime.

Strip block (/* */) and line (//) comments from source before scanning.

* fix: repair 10 pre-existing test failures

- promptIdentity.test.ts: define MACRO global (ISSUES_EXPLAINER etc.)
  for test mode where Bun.define build-time replacements aren't active
- context.test.ts: clear OPENAI_MODEL env var in each test — the user's
  environment (e.g. OPENAI_MODEL=github_copilot/gpt-5.4) polluted the
  provider-qualified lookup, returning wrong context windows
- openclaudePaths.test.ts: set CLAUDE_CONFIG_DIR to force .openclaude
  path when ~/.openclaude doesn't exist on the test machine
2026-04-15 19:42:26 +08:00

147 lines
4.5 KiB
TypeScript

import { afterEach, describe, expect, mock, test } from 'bun:test'
import * as fsPromises from 'fs/promises'
import { homedir } from 'os'
import { join } from 'path'
const originalEnv = { ...process.env }
const originalArgv = [...process.argv]
async function importFreshEnvUtils() {
return import(`./envUtils.ts?ts=${Date.now()}-${Math.random()}`)
}
async function importFreshSettings() {
return import(`./settings/settings.ts?ts=${Date.now()}-${Math.random()}`)
}
async function importFreshLocalInstaller() {
return import(`./localInstaller.ts?ts=${Date.now()}-${Math.random()}`)
}
afterEach(() => {
process.env = { ...originalEnv }
process.argv = [...originalArgv]
mock.restore()
})
describe('OpenClaude paths', () => {
test('defaults user config home to ~/.openclaude', async () => {
delete process.env.CLAUDE_CONFIG_DIR
const { resolveClaudeConfigHomeDir } = await importFreshEnvUtils()
expect(
resolveClaudeConfigHomeDir({
homeDir: homedir(),
openClaudeExists: true,
legacyClaudeExists: false,
}),
).toBe(join(homedir(), '.openclaude'))
})
test('falls back to ~/.claude when legacy config exists and ~/.openclaude does not', async () => {
delete process.env.CLAUDE_CONFIG_DIR
const { resolveClaudeConfigHomeDir } = await importFreshEnvUtils()
expect(
resolveClaudeConfigHomeDir({
homeDir: homedir(),
openClaudeExists: false,
legacyClaudeExists: true,
}),
).toBe(join(homedir(), '.claude'))
})
test('uses CLAUDE_CONFIG_DIR override when provided', async () => {
process.env.CLAUDE_CONFIG_DIR = '/tmp/custom-openclaude'
const { getClaudeConfigHomeDir, resolveClaudeConfigHomeDir } =
await importFreshEnvUtils()
expect(getClaudeConfigHomeDir()).toBe('/tmp/custom-openclaude')
expect(
resolveClaudeConfigHomeDir({
configDirEnv: '/tmp/custom-openclaude',
}),
).toBe('/tmp/custom-openclaude')
})
test('project and local settings paths use .openclaude', async () => {
const { getRelativeSettingsFilePathForSource } = await importFreshSettings()
expect(getRelativeSettingsFilePathForSource('projectSettings')).toBe(
'.openclaude/settings.json',
)
expect(getRelativeSettingsFilePathForSource('localSettings')).toBe(
'.openclaude/settings.local.json',
)
})
test('local installer uses openclaude wrapper path', async () => {
// Force .openclaude config home so the test doesn't fall back to
// ~/.claude when ~/.openclaude doesn't exist on this machine.
process.env.CLAUDE_CONFIG_DIR = join(homedir(), '.openclaude')
const { getLocalClaudePath } = await importFreshLocalInstaller()
expect(getLocalClaudePath()).toBe(
join(homedir(), '.openclaude', 'local', 'openclaude'),
)
})
test('local installation detection matches .openclaude path', async () => {
const { isManagedLocalInstallationPath } =
await importFreshLocalInstaller()
expect(
isManagedLocalInstallationPath(
`${join(homedir(), '.openclaude', 'local')}/node_modules/.bin/openclaude`,
),
).toBe(true)
})
test('local installation detection still matches legacy .claude path', async () => {
const { isManagedLocalInstallationPath } =
await importFreshLocalInstaller()
expect(
isManagedLocalInstallationPath(
`${join(homedir(), '.claude', 'local')}/node_modules/.bin/openclaude`,
),
).toBe(true)
})
test('candidate local install dirs include both openclaude and legacy claude paths', async () => {
const { getCandidateLocalInstallDirs } = await importFreshLocalInstaller()
expect(
getCandidateLocalInstallDirs({
configHomeDir: join(homedir(), '.openclaude'),
homeDir: homedir(),
}),
).toEqual([
join(homedir(), '.openclaude', 'local'),
join(homedir(), '.claude', 'local'),
])
})
test('legacy local installs are detected when they still expose the claude binary', async () => {
mock.module('fs/promises', () => ({
...fsPromises,
access: async (path: string) => {
if (
path === join(homedir(), '.claude', 'local', 'node_modules', '.bin', 'claude')
) {
return
}
throw Object.assign(new Error('ENOENT'), { code: 'ENOENT' })
},
}))
const { getDetectedLocalInstallDir, localInstallationExists } =
await importFreshLocalInstaller()
expect(await localInstallationExists()).toBe(true)
expect(await getDetectedLocalInstallDir()).toBe(
join(homedir(), '.claude', 'local'),
)
})
})