mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-07-09 17:19:15 +00:00
fix: fix and improve test isolation and collision with environment (#530)
* fix: fix and improve test isolation and collision with environment * docs: remove unnecessary comment * test(env-isolation): clear CODEBURN_FORCE_MACOS_MAJOR and pin TZ Two env vars read in src/ were not isolated: CODEBURN_FORCE_MACOS_MAJOR (now cleared so it cannot leak between tests) and TZ (now pinned to UTC, since clearing it falls back to the OS zone and would shift date buckets versus a clean CI runner). --------- Co-authored-by: AgentSeal <hello@agentseal.org>
This commit is contained in:
parent
60410a2632
commit
75c32e6d65
26 changed files with 122 additions and 207 deletions
|
|
@ -16,9 +16,6 @@ describe('Antigravity CLI statusLine hook installer', () => {
|
|||
const settingsPath = join(dir, 'settings.json')
|
||||
const binDir = join(dir, 'bin')
|
||||
const codeburnPath = join(binDir, process.platform === 'win32' ? 'codeburn.cmd' : 'codeburn')
|
||||
const oldSettingsPath = process.env['CODEBURN_ANTIGRAVITY_SETTINGS_PATH']
|
||||
const oldCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
const oldPath = process.env.PATH
|
||||
await mkdir(binDir, { recursive: true })
|
||||
await writeFile(codeburnPath, process.platform === 'win32' ? '@echo off\r\n' : '#!/bin/sh\n')
|
||||
await chmod(codeburnPath, 0o755)
|
||||
|
|
@ -29,12 +26,6 @@ describe('Antigravity CLI statusLine hook installer', () => {
|
|||
try {
|
||||
await run(dir, settingsPath)
|
||||
} finally {
|
||||
if (oldSettingsPath === undefined) delete process.env['CODEBURN_ANTIGRAVITY_SETTINGS_PATH']
|
||||
else process.env['CODEBURN_ANTIGRAVITY_SETTINGS_PATH'] = oldSettingsPath
|
||||
if (oldCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = oldCacheDir
|
||||
if (oldPath === undefined) delete process.env.PATH
|
||||
else process.env.PATH = oldPath
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ beforeEach(() => {
|
|||
|
||||
afterEach(async () => {
|
||||
vi.useRealTimers()
|
||||
delete process.env['CODEBURN_CACHE_DIR']
|
||||
if (existsSync(TMP_CACHE_ROOT)) {
|
||||
await rm(TMP_CACHE_ROOT, { recursive: true, force: true })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -429,7 +429,6 @@ describe('DeepSeek v4 models resolve to pricing', () => {
|
|||
})
|
||||
|
||||
it('keeps bundled DeepSeek v4 fallback entries when runtime pricing cache is stale', async () => {
|
||||
const previousCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
const cacheRoot = await mkdtemp(join(tmpdir(), 'codeburn-pricing-cache-'))
|
||||
|
||||
try {
|
||||
|
|
@ -455,11 +454,6 @@ describe('DeepSeek v4 models resolve to pricing', () => {
|
|||
expect(getModelCosts('deepseek-v4-pro')!.inputCostPerToken).toBe(4.35e-7)
|
||||
expect(getModelCosts('deepseek-v4-flash')!.inputCostPerToken).toBe(1.4e-7)
|
||||
} finally {
|
||||
if (previousCacheDir === undefined) {
|
||||
delete process.env['CODEBURN_CACHE_DIR']
|
||||
} else {
|
||||
process.env['CODEBURN_CACHE_DIR'] = previousCacheDir
|
||||
}
|
||||
await rm(cacheRoot, { recursive: true, force: true })
|
||||
await loadPricing()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ import {
|
|||
detectBloatedClaudeMd,
|
||||
detectUnusedMcp,
|
||||
detectBashBloat,
|
||||
detectGhostAgents,
|
||||
detectGhostSkills,
|
||||
detectGhostCommands,
|
||||
loadMcpConfigs,
|
||||
scanJsonlFile,
|
||||
|
|
@ -217,16 +215,6 @@ describe('detectUnusedMcp', () => {
|
|||
// ============================================================================
|
||||
|
||||
describe('detectBashBloat', () => {
|
||||
const originalEnv = process.env['BASH_MAX_OUTPUT_LENGTH']
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env['BASH_MAX_OUTPUT_LENGTH']
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
if (originalEnv !== undefined) process.env['BASH_MAX_OUTPUT_LENGTH'] = originalEnv
|
||||
})
|
||||
|
||||
it('flags when env var is unset (uses default 30K)', () => {
|
||||
const finding = detectBashBloat()
|
||||
expect(finding).not.toBeNull()
|
||||
|
|
|
|||
|
|
@ -92,16 +92,12 @@ describe.skipIf(!isSqliteAvailable())(
|
|||
let tmpHome: string
|
||||
let tmpCache: string
|
||||
let dbPath: string
|
||||
let prevHome: string | undefined
|
||||
let prevCache: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpHome = await mkdtemp(join(tmpdir(), 'cb-otel-agg-home-'))
|
||||
tmpCache = await mkdtemp(join(tmpdir(), 'cb-otel-agg-cache-'))
|
||||
dbPath = join(tmpHome, 'agent-traces.db')
|
||||
|
||||
prevHome = process.env['HOME']
|
||||
prevCache = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['HOME'] = tmpHome
|
||||
process.env['CODEBURN_CACHE_DIR'] = tmpCache
|
||||
|
||||
|
|
@ -116,10 +112,6 @@ describe.skipIf(!isSqliteAvailable())(
|
|||
afterEach(async () => {
|
||||
clearSessionCache()
|
||||
vi.unstubAllEnvs()
|
||||
if (prevHome === undefined) delete process.env['HOME']
|
||||
else process.env['HOME'] = prevHome
|
||||
if (prevCache === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = prevCache
|
||||
await rm(tmpHome, { recursive: true, force: true })
|
||||
await rm(tmpCache, { recursive: true, force: true })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -7,13 +7,9 @@ import { parseAllSessions } from '../src/parser.js'
|
|||
import type { DateRange } from '../src/types.js'
|
||||
|
||||
let tmpDir: string
|
||||
let originalConfigDir: string | undefined
|
||||
let originalDesktopSessionsDir: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await mkdtemp(join(tmpdir(), 'claude-cwd-test-'))
|
||||
originalConfigDir = process.env['CLAUDE_CONFIG_DIR']
|
||||
originalDesktopSessionsDir = process.env['CODEBURN_DESKTOP_SESSIONS_DIR']
|
||||
process.env['CLAUDE_CONFIG_DIR'] = tmpDir
|
||||
// Point desktop sessions at an empty subdir by default so real sessions
|
||||
// on the developer's machine do not bleed into the unit tests.
|
||||
|
|
@ -21,16 +17,6 @@ beforeEach(async () => {
|
|||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (originalConfigDir === undefined) {
|
||||
delete process.env['CLAUDE_CONFIG_DIR']
|
||||
} else {
|
||||
process.env['CLAUDE_CONFIG_DIR'] = originalConfigDir
|
||||
}
|
||||
if (originalDesktopSessionsDir === undefined) {
|
||||
delete process.env['CODEBURN_DESKTOP_SESSIONS_DIR']
|
||||
} else {
|
||||
process.env['CODEBURN_DESKTOP_SESSIONS_DIR'] = originalDesktopSessionsDir
|
||||
}
|
||||
await rm(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -10,24 +10,16 @@ import type { DateRange } from '../src/types.js'
|
|||
|
||||
let home: string
|
||||
let cacheDir: string
|
||||
let previousHome: string | undefined
|
||||
let previousCacheDir: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
home = await mkdtemp(join(tmpdir(), 'codeburn-gemini-home-'))
|
||||
cacheDir = await mkdtemp(join(tmpdir(), 'codeburn-gemini-cache-'))
|
||||
previousHome = process.env['HOME']
|
||||
previousCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['HOME'] = home
|
||||
process.env['CODEBURN_CACHE_DIR'] = cacheDir
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
clearSessionCache()
|
||||
if (previousHome === undefined) delete process.env['HOME']
|
||||
else process.env['HOME'] = previousHome
|
||||
if (previousCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = previousCacheDir
|
||||
await rm(home, { recursive: true, force: true })
|
||||
await rm(cacheDir, { recursive: true, force: true })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ beforeEach(async () => {
|
|||
|
||||
afterEach(async () => {
|
||||
clearSessionCache()
|
||||
delete process.env['CLAUDE_CONFIG_DIR']
|
||||
await rm(home, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -20,25 +20,18 @@ function makeRange(): DateRange {
|
|||
}
|
||||
|
||||
let tmpDirs: string[] = []
|
||||
let originalConfigDir: string | undefined
|
||||
|
||||
beforeAll(async () => {
|
||||
await loadPricing()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
originalConfigDir = process.env['CLAUDE_CONFIG_DIR']
|
||||
setLocalModelSavings({})
|
||||
setModelAliases({})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
delete (Object.prototype as Record<string, unknown>).calls
|
||||
if (originalConfigDir === undefined) {
|
||||
delete process.env['CLAUDE_CONFIG_DIR']
|
||||
} else {
|
||||
process.env['CLAUDE_CONFIG_DIR'] = originalConfigDir
|
||||
}
|
||||
clearSessionCache()
|
||||
while (tmpDirs.length > 0) {
|
||||
const d = tmpDirs.pop()
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@ const CWD = '/Users/test/codexonlyproxied'
|
|||
let tmpDirs: string[] = []
|
||||
|
||||
afterEach(async () => {
|
||||
delete process.env['CODEX_HOME']
|
||||
delete process.env['CLAUDE_CONFIG_DIR']
|
||||
while (tmpDirs.length > 0) {
|
||||
const d = tmpDirs.pop()
|
||||
if (d) await rm(d, { recursive: true, force: true })
|
||||
|
|
|
|||
|
|
@ -19,8 +19,6 @@ const MERGE_CWD = '/Users/test/proxiedmerge'
|
|||
let tmpDirs: string[] = []
|
||||
|
||||
afterEach(async () => {
|
||||
delete process.env['CLAUDE_CONFIG_DIR']
|
||||
delete process.env['CODEX_HOME']
|
||||
while (tmpDirs.length > 0) {
|
||||
const d = tmpDirs.pop()
|
||||
if (d) await rm(d, { recursive: true, force: true })
|
||||
|
|
|
|||
|
|
@ -127,14 +127,12 @@ const makeRange = (): DateRange => ({ start: RANGE_START, end: RANGE_END })
|
|||
const FIXTURE_CWD = '/private/var/eywa-proxy-fixture/acme'
|
||||
|
||||
let tmpDirs: string[] = []
|
||||
let originalConfigDir: string | undefined
|
||||
|
||||
beforeAll(async () => {
|
||||
await loadPricing()
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
originalConfigDir = process.env['CLAUDE_CONFIG_DIR']
|
||||
setProxyPaths([])
|
||||
setLocalModelSavings({})
|
||||
setModelAliases({})
|
||||
|
|
@ -143,8 +141,6 @@ beforeEach(() => {
|
|||
|
||||
afterEach(async () => {
|
||||
setProxyPaths([])
|
||||
if (originalConfigDir === undefined) delete process.env['CLAUDE_CONFIG_DIR']
|
||||
else process.env['CLAUDE_CONFIG_DIR'] = originalConfigDir
|
||||
clearSessionCache()
|
||||
while (tmpDirs.length > 0) {
|
||||
const d = tmpDirs.pop()
|
||||
|
|
|
|||
|
|
@ -172,15 +172,11 @@ function totalOutput(projects: Awaited<ReturnType<typeof parseAllSessions>>): nu
|
|||
// ── Common env setup ──────────────────────────────────────────────────────
|
||||
let tmpHome: string
|
||||
let tmpCache: string
|
||||
let prevHome: string | undefined
|
||||
let prevCache: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpHome = await mkdtemp(join(tmpdir(), 'cb-parser-test-home-'))
|
||||
tmpCache = await mkdtemp(join(tmpdir(), 'cb-parser-test-cache-'))
|
||||
|
||||
prevHome = process.env['HOME']
|
||||
prevCache = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['HOME'] = tmpHome
|
||||
process.env['CODEBURN_CACHE_DIR'] = tmpCache
|
||||
|
||||
|
|
@ -194,11 +190,6 @@ afterEach(async () => {
|
|||
clearSessionCache()
|
||||
vi.unstubAllEnvs()
|
||||
|
||||
if (prevHome === undefined) delete process.env['HOME']
|
||||
else process.env['HOME'] = prevHome
|
||||
if (prevCache === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = prevCache
|
||||
|
||||
_synthSources = []
|
||||
|
||||
await rm(tmpHome, { recursive: true, force: true })
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ describe('getPlanUsage', () => {
|
|||
|
||||
it('keeps the provider-specific parser filter for one active plan', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-plan-usage-test-'))
|
||||
const previousHome = process.env['HOME']
|
||||
process.env['HOME'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -217,18 +216,12 @@ describe('getPlanUsage', () => {
|
|||
expect(usages).toHaveLength(1)
|
||||
expect(usages[0]?.spentApiEquivalentUsd).toBe(80)
|
||||
} finally {
|
||||
if (previousHome === undefined) {
|
||||
delete process.env['HOME']
|
||||
} else {
|
||||
process.env['HOME'] = previousHome
|
||||
}
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('computes multiple active plan usages from one all-provider parse', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-plan-usage-test-'))
|
||||
const previousHome = process.env['HOME']
|
||||
process.env['HOME'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -344,11 +337,6 @@ describe('getPlanUsage', () => {
|
|||
expect(usages.map(usage => usage.plan.provider)).toEqual(['claude', 'codex'])
|
||||
expect(usages.map(usage => usage.spentApiEquivalentUsd)).toEqual([100, 50])
|
||||
} finally {
|
||||
if (previousHome === undefined) {
|
||||
delete process.env['HOME']
|
||||
} else {
|
||||
process.env['HOME'] = previousHome
|
||||
}
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ describe('plan presets', () => {
|
|||
describe('plan config persistence', () => {
|
||||
it('round-trips per-provider plans and clears one provider at a time', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-plan-test-'))
|
||||
const previousHome = process.env['HOME']
|
||||
process.env['HOME'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -74,18 +73,12 @@ describe('plan config persistence', () => {
|
|||
expect(await readPlan()).toBeUndefined()
|
||||
expect(await readPlans()).toEqual({})
|
||||
} finally {
|
||||
if (previousHome === undefined) {
|
||||
delete process.env['HOME']
|
||||
} else {
|
||||
process.env['HOME'] = previousHome
|
||||
}
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('reads legacy single-plan config as a provider-keyed plan map', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-plan-test-'))
|
||||
const previousHome = process.env['HOME']
|
||||
process.env['HOME'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -107,18 +100,12 @@ describe('plan config persistence', () => {
|
|||
resetDay: 3,
|
||||
})
|
||||
} finally {
|
||||
if (previousHome === undefined) {
|
||||
delete process.env['HOME']
|
||||
} else {
|
||||
process.env['HOME'] = previousHome
|
||||
}
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('drops a hand-edited all plan when provider-specific plans are present', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-plan-test-'))
|
||||
const previousHome = process.env['HOME']
|
||||
process.env['HOME'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -144,18 +131,12 @@ describe('plan config persistence', () => {
|
|||
expect(plans.claude).toMatchObject({ id: 'claude-max', provider: 'claude' })
|
||||
expect(await readPlan()).toMatchObject({ id: 'claude-max', provider: 'claude' })
|
||||
} finally {
|
||||
if (previousHome === undefined) {
|
||||
delete process.env['HOME']
|
||||
} else {
|
||||
process.env['HOME'] = previousHome
|
||||
}
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('does not allow an all-provider plan to overlap provider-specific plans', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-plan-test-'))
|
||||
const previousHome = process.env['HOME']
|
||||
process.env['HOME'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -191,11 +172,6 @@ describe('plan config persistence', () => {
|
|||
})
|
||||
expect((await readPlans()).claude).toBeUndefined()
|
||||
} finally {
|
||||
if (previousHome === undefined) {
|
||||
delete process.env['HOME']
|
||||
} else {
|
||||
process.env['HOME'] = previousHome
|
||||
}
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -8,18 +8,12 @@ import type { DateRange } from '../src/types.js'
|
|||
let home: string
|
||||
let cacheDir: string
|
||||
let vibeHome: string
|
||||
let originalHome: string | undefined
|
||||
let originalCacheDir: string | undefined
|
||||
let originalVibeHome: string | undefined
|
||||
let clearParserCache: (() => void) | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
home = await mkdtemp(join(tmpdir(), 'codeburn-turn-group-home-'))
|
||||
cacheDir = await mkdtemp(join(tmpdir(), 'codeburn-turn-group-cache-'))
|
||||
vibeHome = await mkdtemp(join(tmpdir(), 'codeburn-turn-group-vibe-'))
|
||||
originalHome = process.env['HOME']
|
||||
originalCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
originalVibeHome = process.env['VIBE_HOME']
|
||||
process.env['HOME'] = home
|
||||
process.env['CODEBURN_CACHE_DIR'] = cacheDir
|
||||
process.env['VIBE_HOME'] = vibeHome
|
||||
|
|
@ -29,12 +23,6 @@ afterEach(async () => {
|
|||
clearParserCache?.()
|
||||
clearParserCache = undefined
|
||||
vi.resetModules()
|
||||
if (originalHome === undefined) delete process.env['HOME']
|
||||
else process.env['HOME'] = originalHome
|
||||
if (originalCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = originalCacheDir
|
||||
if (originalVibeHome === undefined) delete process.env['VIBE_HOME']
|
||||
else process.env['VIBE_HOME'] = originalVibeHome
|
||||
await rm(home, { recursive: true, force: true })
|
||||
await rm(cacheDir, { recursive: true, force: true })
|
||||
await rm(vibeHome, { recursive: true, force: true })
|
||||
|
|
|
|||
|
|
@ -263,7 +263,6 @@ describe('antigravity provider helpers', () => {
|
|||
|
||||
it('captures exact Antigravity CLI statusLine usage as fallback calls', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-'))
|
||||
const oldCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['CODEBURN_CACHE_DIR'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -317,15 +316,12 @@ describe('antigravity provider helpers', () => {
|
|||
expect(calls[0]!.projectPath).toBeUndefined()
|
||||
expect(calls[0]!.costUSD).toBeGreaterThan(0)
|
||||
} finally {
|
||||
if (oldCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = oldCacheDir
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('skips statusLine fallback calls when RPC cache already covered the conversation', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-rpc-dedup-'))
|
||||
const oldCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['CODEBURN_CACHE_DIR'] = dir
|
||||
|
||||
try {
|
||||
|
|
@ -354,15 +350,12 @@ describe('antigravity provider helpers', () => {
|
|||
|
||||
expect(calls).toEqual([])
|
||||
} finally {
|
||||
if (oldCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = oldCacheDir
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('skips singleton statusLine snapshots and deltas monotonic usage', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-runs-'))
|
||||
const oldCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['CODEBURN_CACHE_DIR'] = dir
|
||||
|
||||
const basePayload = {
|
||||
|
|
@ -409,15 +402,12 @@ describe('antigravity provider helpers', () => {
|
|||
])
|
||||
expect(calls.map(call => call.cachedInputTokens)).toEqual([0, 0])
|
||||
} finally {
|
||||
if (oldCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = oldCacheDir
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('treats non-monotonic statusLine usage as a new request snapshot', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'codeburn-antigravity-statusline-reset-'))
|
||||
const oldCacheDir = process.env['CODEBURN_CACHE_DIR']
|
||||
process.env['CODEBURN_CACHE_DIR'] = dir
|
||||
|
||||
const payload = (
|
||||
|
|
@ -458,8 +448,6 @@ describe('antigravity provider helpers', () => {
|
|||
[200, 30, 500],
|
||||
])
|
||||
} finally {
|
||||
if (oldCacheDir === undefined) delete process.env['CODEBURN_CACHE_DIR']
|
||||
else process.env['CODEBURN_CACHE_DIR'] = oldCacheDir
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,19 +18,12 @@ type TestDb = {
|
|||
}
|
||||
|
||||
let tmpRoot: string
|
||||
let originalEnv: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpRoot = await mkdtemp(join(tmpdir(), 'crush-test-'))
|
||||
originalEnv = process.env['CRUSH_GLOBAL_DATA']
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (originalEnv === undefined) {
|
||||
delete process.env['CRUSH_GLOBAL_DATA']
|
||||
} else {
|
||||
process.env['CRUSH_GLOBAL_DATA'] = originalEnv
|
||||
}
|
||||
await rm(tmpRoot, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,12 @@ import type { DateRange } from '../../src/types.js'
|
|||
const skipReason = isSqliteAvailable() ? null : 'node:sqlite not available — needs Node 22+; skipping'
|
||||
|
||||
let tmpDir: string
|
||||
let savedBudget: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await mkdtemp(join(tmpdir(), 'cursor-cap-'))
|
||||
savedBudget = process.env['CODEBURN_CURSOR_MAX_BUBBLES']
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (savedBudget === undefined) delete process.env['CODEBURN_CURSOR_MAX_BUBBLES']
|
||||
else process.env['CODEBURN_CURSOR_MAX_BUBBLES'] = savedBudget
|
||||
await rm(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import { createDevinProvider } from '../../src/providers/devin.js'
|
|||
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
||||
|
||||
let tmpDir: string
|
||||
const originalHome = process.env['HOME']
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await mkdtemp(join(tmpdir(), 'devin-provider-'))
|
||||
|
|
@ -16,8 +15,6 @@ beforeEach(async () => {
|
|||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (originalHome === undefined) delete process.env['HOME']
|
||||
else process.env['HOME'] = originalHome
|
||||
await rm(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -7,20 +7,12 @@ import { createMistralVibeProvider } from '../../src/providers/mistral-vibe.js'
|
|||
import type { ParsedProviderCall } from '../../src/providers/types.js'
|
||||
|
||||
let tmpDir: string
|
||||
let originalVibeHome: string | undefined
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await mkdtemp(join(tmpdir(), 'mistral-vibe-test-'))
|
||||
originalVibeHome = process.env['VIBE_HOME']
|
||||
delete process.env['VIBE_HOME']
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
if (originalVibeHome === undefined) {
|
||||
delete process.env['VIBE_HOME']
|
||||
} else {
|
||||
process.env['VIBE_HOME'] = originalVibeHome
|
||||
}
|
||||
await rm(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, it, expect, afterEach, beforeEach } from 'vitest'
|
||||
import { describe, it, expect, afterEach } from 'vitest'
|
||||
import { mkdtemp, mkdir, cp, rm } from 'fs/promises'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
|
|
@ -28,19 +28,9 @@ function makeRange(offsetMs: number): DateRange {
|
|||
|
||||
describe('HIGH-1 prototype pollution via unchecked bracket-assign', () => {
|
||||
const tmpDirs: string[] = []
|
||||
let originalConfigDir: string | undefined
|
||||
|
||||
beforeEach(() => {
|
||||
originalConfigDir = process.env['CLAUDE_CONFIG_DIR']
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
delete (Object.prototype as Record<string, unknown>).calls
|
||||
if (originalConfigDir === undefined) {
|
||||
delete process.env['CLAUDE_CONFIG_DIR']
|
||||
} else {
|
||||
process.env['CLAUDE_CONFIG_DIR'] = originalConfigDir
|
||||
}
|
||||
while (tmpDirs.length > 0) {
|
||||
const d = tmpDirs.pop()
|
||||
if (d) await rm(d, { recursive: true, force: true })
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ beforeEach(() => {
|
|||
})
|
||||
|
||||
afterEach(async () => {
|
||||
delete process.env['CODEBURN_CACHE_DIR']
|
||||
if (existsSync(TMP_DIR)) await rm(TMP_DIR, { recursive: true })
|
||||
})
|
||||
|
||||
|
|
@ -171,11 +170,8 @@ describe('computeEnvFingerprint', () => {
|
|||
|
||||
it('changes when env var changes', () => {
|
||||
const before = computeEnvFingerprint('claude')
|
||||
const orig = process.env['CLAUDE_CONFIG_DIR']
|
||||
process.env['CLAUDE_CONFIG_DIR'] = '/tmp/different'
|
||||
const after = computeEnvFingerprint('claude')
|
||||
if (orig === undefined) delete process.env['CLAUDE_CONFIG_DIR']
|
||||
else process.env['CLAUDE_CONFIG_DIR'] = orig
|
||||
expect(before).not.toBe(after)
|
||||
})
|
||||
|
||||
|
|
|
|||
110
tests/setup/env-isolation.ts
Normal file
110
tests/setup/env-isolation.ts
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// Vitest setup file: isolates every test from the developer's shell environment.
|
||||
//
|
||||
// codeburn discovers sessions through a long list of provider-specific env
|
||||
// vars (CLAUDE_CONFIG_DIR, CODEX_HOME, CRUSH_GLOBAL_DATA, …) and via HOME /
|
||||
// XDG_* / APPDATA / LOCALAPPDATA. Without this file, any value set in the
|
||||
// developer's shell (e.g. CLAUDE_CONFIG_DIRS=/Users/me/.claude:…) bleeds into
|
||||
// fixture-based tests: the parser reads the developer's REAL sessions instead
|
||||
// of the temp-dir fixture, producing nonsense totals and false failures that
|
||||
// pass on a clean CI runner.
|
||||
//
|
||||
// What this file does:
|
||||
// 1. Mints an empty sandbox temp dir once per worker.
|
||||
// 2. REDIRECTED vars (HOME / XDG_* / APPDATA / LOCALAPPDATA) point at the
|
||||
// sandbox so any fallback to homedir() / platform defaults lands in an
|
||||
// empty filesystem.
|
||||
// 3. CLEARED vars (every provider's explicit override) are deleted so a test
|
||||
// that does NOT set one gets "unconfigured" rather than the dev's value.
|
||||
// 4. PRESERVED vars (PATH, COLUMNS, …) are snapshotted from the dev's shell
|
||||
// and restored every test. We can't wipe them - Node uses PATH for spawn
|
||||
// and module resolution, terminal code uses COLUMNS - but a test that
|
||||
// mutates them shouldn't leak the change into the next test.
|
||||
// 5. Re-asserts the above before EVERY test (global beforeEach), so a test
|
||||
// that mutates an env var doesn't leak its value into the next test.
|
||||
// Tests can freely set process.env['HOME'] = customDir without saving the
|
||||
// previous value - the next test gets a fresh sandbox baseline.
|
||||
//
|
||||
// CAVEAT: env vars set in a test file's beforeAll() get overwritten by this
|
||||
// file's beforeEach before each test runs. Use beforeEach (not beforeAll) when
|
||||
// the test body depends on a specific env var value.
|
||||
|
||||
import { mkdtempSync } from 'fs'
|
||||
import { tmpdir } from 'os'
|
||||
import { join } from 'path'
|
||||
import { beforeEach } from 'vitest'
|
||||
|
||||
const sandbox = mkdtempSync(join(tmpdir(), 'codeburn-test-env-'))
|
||||
|
||||
const REDIRECTED = [
|
||||
'HOME',
|
||||
'XDG_CONFIG_HOME',
|
||||
'XDG_DATA_HOME',
|
||||
'XDG_CACHE_HOME',
|
||||
'XDG_STATE_HOME',
|
||||
'APPDATA',
|
||||
'LOCALAPPDATA',
|
||||
] as const
|
||||
|
||||
const CLEARED = [
|
||||
// Provider session-discovery dirs
|
||||
'CLAUDE_CONFIG_DIR',
|
||||
'CLAUDE_CONFIG_DIRS',
|
||||
'CODEX_HOME',
|
||||
'CRUSH_GLOBAL_DATA',
|
||||
'CODEBUFF_DATA_DIR',
|
||||
'FACTORY_DIR',
|
||||
'GOOSE_PATH_ROOT',
|
||||
'GROK_HOME',
|
||||
'KIRO_HOME',
|
||||
'KIMI_SHARE_DIR',
|
||||
'MUX_ROOT',
|
||||
'QWEN_DATA_DIR',
|
||||
'VIBE_HOME',
|
||||
'WARP_DB_PATH',
|
||||
'ZS_DATA_DIR',
|
||||
// codeburn override dirs / paths
|
||||
'CODEBURN_CACHE_DIR',
|
||||
'CODEBURN_COPILOT_OTEL_DB',
|
||||
'CODEBURN_COPILOT_SESSION_STATE_DIR',
|
||||
'CODEBURN_COPILOT_WS_STORAGE_DIR',
|
||||
'CODEBURN_DESKTOP_SESSIONS_DIR',
|
||||
'CODEBURN_MUX_DIR',
|
||||
'CODEBURN_ANTIGRAVITY_SETTINGS_PATH',
|
||||
// codeburn behavior toggles (set by the dev to tweak local runs)
|
||||
'CODEBURN_COPILOT_DISABLE_OTEL',
|
||||
'CODEBURN_TZ',
|
||||
'CODEBURN_VERBOSE',
|
||||
'CODEBURN_CURSOR_MAX_BUBBLES',
|
||||
'CODEBURN_FORCE_MACOS_MAJOR',
|
||||
// Provider model/credential overrides
|
||||
'KIMI_MODEL_NAME',
|
||||
'AI_GATEWAY_API_KEY',
|
||||
'VERCEL_OIDC_TOKEN',
|
||||
// Read by detectBashBloat - a dev's real shell limit must not bleed in
|
||||
'BASH_MAX_OUTPUT_LENGTH',
|
||||
] as const
|
||||
|
||||
// Snapshotted from the dev's shell and restored every test. These can't be
|
||||
// wiped (Node needs PATH for spawn / module resolution, dashboard/table layout
|
||||
// reads COLUMNS) but a test that mutates them shouldn't leak.
|
||||
const PRESERVED = ['PATH', 'COLUMNS'] as const
|
||||
const preservedSnapshot = new Map<string, string | undefined>()
|
||||
for (const key of PRESERVED) preservedSnapshot.set(key, process.env[key])
|
||||
|
||||
function applyIsolation(): void {
|
||||
for (const key of REDIRECTED) process.env[key] = sandbox
|
||||
for (const key of CLEARED) delete process.env[key]
|
||||
for (const key of PRESERVED) {
|
||||
const original = preservedSnapshot.get(key)
|
||||
if (original === undefined) delete process.env[key]
|
||||
else process.env[key] = original
|
||||
}
|
||||
// Pin the timezone so date grouping is deterministic regardless of the dev's
|
||||
// shell TZ. Clearing it is not enough (Node falls back to the OS zone); a
|
||||
// non-UTC TZ would otherwise shift day buckets versus a clean CI runner. A
|
||||
// test that needs a specific zone can still set process.env.TZ in beforeEach.
|
||||
process.env.TZ = 'UTC'
|
||||
}
|
||||
|
||||
applyIsolation()
|
||||
beforeEach(applyIsolation)
|
||||
|
|
@ -1,39 +1,13 @@
|
|||
import { describe, expect, it, beforeAll, afterAll } from 'vitest'
|
||||
import { mkdtemp, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { describe, expect, it, beforeAll } from 'vitest'
|
||||
import { buildMenubarPayloadForRange } from '../src/usage-aggregator.js'
|
||||
import { getDateRange } from '../src/cli-date.js'
|
||||
import { loadPricing } from '../src/models.js'
|
||||
|
||||
describe('buildMenubarPayloadForRange', () => {
|
||||
// Point HOME / config at an empty temp dir so the payload is built from an
|
||||
// empty dataset. This keeps the test deterministic and fast regardless of how
|
||||
// much real session data the developer's machine has for "today" (parsing a
|
||||
// heavy day previously pushed this past its timeout).
|
||||
const saved: Record<string, string | undefined> = {}
|
||||
let tmp: string
|
||||
|
||||
beforeAll(async () => {
|
||||
tmp = await mkdtemp(join(tmpdir(), 'codeburn-agg-test-'))
|
||||
for (const key of ['HOME', 'CLAUDE_CONFIG_DIR', 'XDG_CONFIG_HOME', 'CODEBURN_CACHE_DIR']) {
|
||||
saved[key] = process.env[key]
|
||||
}
|
||||
process.env['HOME'] = tmp
|
||||
process.env['CLAUDE_CONFIG_DIR'] = join(tmp, '.claude')
|
||||
process.env['XDG_CONFIG_HOME'] = join(tmp, '.config')
|
||||
process.env['CODEBURN_CACHE_DIR'] = join(tmp, 'cache')
|
||||
await loadPricing()
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
for (const [key, value] of Object.entries(saved)) {
|
||||
if (value === undefined) delete process.env[key]
|
||||
else process.env[key] = value
|
||||
}
|
||||
await rm(tmp, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
it('returns a valid payload and skips optimize findings when optimize:false', async () => {
|
||||
const payload = await buildMenubarPayloadForRange(getDateRange('today'), { provider: 'all', optimize: false })
|
||||
expect(typeof payload.current.label).toBe('string')
|
||||
|
|
|
|||
10
vitest.config.ts
Normal file
10
vitest.config.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
// Runs once per worker before any test. Scrubs the developer's shell so
|
||||
// session-discovery env vars (CLAUDE_CONFIG_DIRS, HOME, XDG_*, every
|
||||
// provider-specific *_HOME) don't bleed real local data into fixtures.
|
||||
setupFiles: ['./tests/setup/env-isolation.ts'],
|
||||
},
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue