mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 05:45:51 +00:00
343 lines
11 KiB
TypeScript
343 lines
11 KiB
TypeScript
import { describe, expect, test, beforeEach, afterEach } from 'bun:test'
|
|
import {
|
|
isEnvTruthy,
|
|
isEnvDefinedFalsy,
|
|
parseEnvVars,
|
|
hasNodeOption,
|
|
getAWSRegion,
|
|
getDefaultVertexRegion,
|
|
getVertexRegionForModel,
|
|
isBareMode,
|
|
shouldMaintainProjectWorkingDir,
|
|
getClaudeConfigHomeDir,
|
|
} from '../envUtils'
|
|
|
|
// ─── isEnvTruthy ───────────────────────────────────────────────────────
|
|
|
|
describe('isEnvTruthy', () => {
|
|
test("returns true for '1'", () => {
|
|
expect(isEnvTruthy('1')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'true'", () => {
|
|
expect(isEnvTruthy('true')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'TRUE'", () => {
|
|
expect(isEnvTruthy('TRUE')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'yes'", () => {
|
|
expect(isEnvTruthy('yes')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'on'", () => {
|
|
expect(isEnvTruthy('on')).toBe(true)
|
|
})
|
|
|
|
test('returns true for boolean true', () => {
|
|
expect(isEnvTruthy(true)).toBe(true)
|
|
})
|
|
|
|
test("returns false for '0'", () => {
|
|
expect(isEnvTruthy('0')).toBe(false)
|
|
})
|
|
|
|
test("returns false for 'false'", () => {
|
|
expect(isEnvTruthy('false')).toBe(false)
|
|
})
|
|
|
|
test('returns false for empty string', () => {
|
|
expect(isEnvTruthy('')).toBe(false)
|
|
})
|
|
|
|
test('returns false for undefined', () => {
|
|
expect(isEnvTruthy(undefined)).toBe(false)
|
|
})
|
|
|
|
test('returns false for boolean false', () => {
|
|
expect(isEnvTruthy(false)).toBe(false)
|
|
})
|
|
|
|
test("returns true for ' true ' (trimmed)", () => {
|
|
expect(isEnvTruthy(' true ')).toBe(true)
|
|
})
|
|
})
|
|
|
|
// ─── isEnvDefinedFalsy ─────────────────────────────────────────────────
|
|
|
|
describe('isEnvDefinedFalsy', () => {
|
|
test("returns true for '0'", () => {
|
|
expect(isEnvDefinedFalsy('0')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'false'", () => {
|
|
expect(isEnvDefinedFalsy('false')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'no'", () => {
|
|
expect(isEnvDefinedFalsy('no')).toBe(true)
|
|
})
|
|
|
|
test("returns true for 'off'", () => {
|
|
expect(isEnvDefinedFalsy('off')).toBe(true)
|
|
})
|
|
|
|
test('returns true for boolean false', () => {
|
|
expect(isEnvDefinedFalsy(false)).toBe(true)
|
|
})
|
|
|
|
test('returns false for undefined', () => {
|
|
expect(isEnvDefinedFalsy(undefined)).toBe(false)
|
|
})
|
|
|
|
test("returns false for '1'", () => {
|
|
expect(isEnvDefinedFalsy('1')).toBe(false)
|
|
})
|
|
|
|
test("returns false for 'true'", () => {
|
|
expect(isEnvDefinedFalsy('true')).toBe(false)
|
|
})
|
|
|
|
test('returns false for empty string', () => {
|
|
expect(isEnvDefinedFalsy('')).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ─── parseEnvVars ──────────────────────────────────────────────────────
|
|
|
|
describe('parseEnvVars', () => {
|
|
test('parses KEY=VALUE pairs', () => {
|
|
const result = parseEnvVars(['FOO=bar', 'BAZ=qux'])
|
|
expect(result).toEqual({ FOO: 'bar', BAZ: 'qux' })
|
|
})
|
|
|
|
test('handles value with equals sign', () => {
|
|
const result = parseEnvVars(['URL=http://host?a=1&b=2'])
|
|
expect(result).toEqual({ URL: 'http://host?a=1&b=2' })
|
|
})
|
|
|
|
test('returns empty object for undefined', () => {
|
|
expect(parseEnvVars(undefined)).toEqual({})
|
|
})
|
|
|
|
test('returns empty object for empty array', () => {
|
|
expect(parseEnvVars([])).toEqual({})
|
|
})
|
|
|
|
test('throws for missing value', () => {
|
|
expect(() => parseEnvVars(['NOVALUE'])).toThrow(
|
|
'Invalid environment variable format',
|
|
)
|
|
})
|
|
|
|
test('throws for empty key', () => {
|
|
expect(() => parseEnvVars(['=value'])).toThrow(
|
|
'Invalid environment variable format',
|
|
)
|
|
})
|
|
})
|
|
|
|
// ─── hasNodeOption ─────────────────────────────────────────────────────
|
|
|
|
describe('hasNodeOption', () => {
|
|
const saved = process.env.NODE_OPTIONS
|
|
afterEach(() => {
|
|
if (saved === undefined) delete process.env.NODE_OPTIONS
|
|
else process.env.NODE_OPTIONS = saved
|
|
})
|
|
|
|
test('returns true when flag present', () => {
|
|
process.env.NODE_OPTIONS = '--max-old-space-size=4096 --inspect'
|
|
expect(hasNodeOption('--inspect')).toBe(true)
|
|
})
|
|
|
|
test('returns false when flag absent', () => {
|
|
process.env.NODE_OPTIONS = '--max-old-space-size=4096'
|
|
expect(hasNodeOption('--inspect')).toBe(false)
|
|
})
|
|
|
|
test('returns false when NODE_OPTIONS not set', () => {
|
|
delete process.env.NODE_OPTIONS
|
|
expect(hasNodeOption('--inspect')).toBe(false)
|
|
})
|
|
|
|
test('does not match partial flags', () => {
|
|
process.env.NODE_OPTIONS = '--inspect-brk'
|
|
expect(hasNodeOption('--inspect')).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ─── getAWSRegion ──────────────────────────────────────────────────────
|
|
|
|
describe('getAWSRegion', () => {
|
|
const savedRegion = process.env.AWS_REGION
|
|
const savedDefault = process.env.AWS_DEFAULT_REGION
|
|
|
|
afterEach(() => {
|
|
if (savedRegion === undefined) delete process.env.AWS_REGION
|
|
else process.env.AWS_REGION = savedRegion
|
|
if (savedDefault === undefined) delete process.env.AWS_DEFAULT_REGION
|
|
else process.env.AWS_DEFAULT_REGION = savedDefault
|
|
})
|
|
|
|
test('uses AWS_REGION when set', () => {
|
|
process.env.AWS_REGION = 'eu-west-1'
|
|
expect(getAWSRegion()).toBe('eu-west-1')
|
|
})
|
|
|
|
test('falls back to AWS_DEFAULT_REGION', () => {
|
|
delete process.env.AWS_REGION
|
|
process.env.AWS_DEFAULT_REGION = 'ap-northeast-1'
|
|
expect(getAWSRegion()).toBe('ap-northeast-1')
|
|
})
|
|
|
|
test('defaults to us-east-1', () => {
|
|
delete process.env.AWS_REGION
|
|
delete process.env.AWS_DEFAULT_REGION
|
|
expect(getAWSRegion()).toBe('us-east-1')
|
|
})
|
|
})
|
|
|
|
// ─── getDefaultVertexRegion ────────────────────────────────────────────
|
|
|
|
describe('getDefaultVertexRegion', () => {
|
|
const saved = process.env.CLOUD_ML_REGION
|
|
afterEach(() => {
|
|
if (saved === undefined) delete process.env.CLOUD_ML_REGION
|
|
else process.env.CLOUD_ML_REGION = saved
|
|
})
|
|
|
|
test('uses CLOUD_ML_REGION when set', () => {
|
|
process.env.CLOUD_ML_REGION = 'europe-west4'
|
|
expect(getDefaultVertexRegion()).toBe('europe-west4')
|
|
})
|
|
|
|
test('defaults to us-east5', () => {
|
|
delete process.env.CLOUD_ML_REGION
|
|
expect(getDefaultVertexRegion()).toBe('us-east5')
|
|
})
|
|
})
|
|
|
|
// ─── getVertexRegionForModel ───────────────────────────────────────────
|
|
|
|
describe('getVertexRegionForModel', () => {
|
|
const envKeys = [
|
|
'VERTEX_REGION_CLAUDE_HAIKU_4_5',
|
|
'VERTEX_REGION_CLAUDE_4_0_SONNET',
|
|
'VERTEX_REGION_CLAUDE_4_6_SONNET',
|
|
'CLOUD_ML_REGION',
|
|
]
|
|
const saved: Record<string, string | undefined> = {}
|
|
|
|
beforeEach(() => {
|
|
for (const k of envKeys) saved[k] = process.env[k]
|
|
})
|
|
afterEach(() => {
|
|
for (const k of envKeys) {
|
|
if (saved[k] === undefined) delete process.env[k]
|
|
else process.env[k] = saved[k]
|
|
}
|
|
})
|
|
|
|
test('returns model-specific override when set', () => {
|
|
process.env.VERTEX_REGION_CLAUDE_HAIKU_4_5 = 'us-central1'
|
|
expect(getVertexRegionForModel('claude-haiku-4-5-20251001')).toBe(
|
|
'us-central1',
|
|
)
|
|
})
|
|
|
|
test('falls back to default vertex region when override not set', () => {
|
|
delete process.env.VERTEX_REGION_CLAUDE_4_0_SONNET
|
|
delete process.env.CLOUD_ML_REGION
|
|
expect(getVertexRegionForModel('claude-sonnet-4-some-variant')).toBe(
|
|
'us-east5',
|
|
)
|
|
})
|
|
|
|
test('returns default region for unknown model prefix', () => {
|
|
delete process.env.CLOUD_ML_REGION
|
|
expect(getVertexRegionForModel('unknown-model-123')).toBe('us-east5')
|
|
})
|
|
|
|
test('returns default region for undefined model', () => {
|
|
delete process.env.CLOUD_ML_REGION
|
|
expect(getVertexRegionForModel(undefined)).toBe('us-east5')
|
|
})
|
|
})
|
|
|
|
// ─── isBareMode ────────────────────────────────────────────────────────
|
|
|
|
describe('isBareMode', () => {
|
|
const saved = process.env.CLAUDE_CODE_SIMPLE
|
|
const originalArgv = [...process.argv]
|
|
|
|
afterEach(() => {
|
|
if (saved === undefined) delete process.env.CLAUDE_CODE_SIMPLE
|
|
else process.env.CLAUDE_CODE_SIMPLE = saved
|
|
process.argv.length = 0
|
|
process.argv.push(...originalArgv)
|
|
})
|
|
|
|
test('returns true when CLAUDE_CODE_SIMPLE=1', () => {
|
|
process.env.CLAUDE_CODE_SIMPLE = '1'
|
|
expect(isBareMode()).toBe(true)
|
|
})
|
|
|
|
test('returns true when --bare in argv', () => {
|
|
process.argv.push('--bare')
|
|
expect(isBareMode()).toBe(true)
|
|
})
|
|
|
|
test('returns false when neither set', () => {
|
|
delete process.env.CLAUDE_CODE_SIMPLE
|
|
// argv doesn't have --bare by default
|
|
expect(isBareMode()).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ─── shouldMaintainProjectWorkingDir ───────────────────────────────────
|
|
|
|
describe('shouldMaintainProjectWorkingDir', () => {
|
|
const saved = process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR
|
|
|
|
afterEach(() => {
|
|
if (saved === undefined)
|
|
delete process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR
|
|
else process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR = saved
|
|
})
|
|
|
|
test('returns true when set to truthy', () => {
|
|
process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR = '1'
|
|
expect(shouldMaintainProjectWorkingDir()).toBe(true)
|
|
})
|
|
|
|
test('returns false when not set', () => {
|
|
delete process.env.CLAUDE_BASH_MAINTAIN_PROJECT_WORKING_DIR
|
|
expect(shouldMaintainProjectWorkingDir()).toBe(false)
|
|
})
|
|
})
|
|
|
|
// ─── getClaudeConfigHomeDir ────────────────────────────────────────────
|
|
|
|
describe('getClaudeConfigHomeDir', () => {
|
|
const saved = process.env.CLAUDE_CONFIG_DIR
|
|
|
|
afterEach(() => {
|
|
if (saved === undefined) delete process.env.CLAUDE_CONFIG_DIR
|
|
else process.env.CLAUDE_CONFIG_DIR = saved
|
|
})
|
|
|
|
test('uses CLAUDE_CONFIG_DIR when set', () => {
|
|
process.env.CLAUDE_CONFIG_DIR = '/tmp/test-claude'
|
|
// Memoized by CLAUDE_CONFIG_DIR key, so changing env gives fresh value
|
|
expect(getClaudeConfigHomeDir()).toBe('/tmp/test-claude')
|
|
})
|
|
|
|
test('returns a string ending with .claude by default', () => {
|
|
delete process.env.CLAUDE_CONFIG_DIR
|
|
const result = getClaudeConfigHomeDir()
|
|
expect(result).toMatch(/\.claude$/)
|
|
})
|
|
})
|