Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/services/compact/autoCompact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ import {
import type { Message } from '../../types/message.js'
import * as realConfig from '../../utils/config.js'

const realContext = await import(
`../../utils/context.js?real=${Date.now()}-${Math.random()}`
)
const realErrors = await import(
`../../utils/errors.js?real=${Date.now()}-${Math.random()}`
)
const realTokens = await import(
`../../utils/tokens.js?real=${Date.now()}-${Math.random()}`
)

const USER_ABORT_MESSAGE = 'API Error: Request was aborted.'

type ImportAutoCompactOptions = {
Expand All @@ -22,6 +32,11 @@ type ImportAutoCompactOptions = {
}

async function importAutoCompact(options: ImportAutoCompactOptions = {}) {
// compact.test.ts uses process-global module stubs. Re-register the real
// dependencies this standalone suite needs before importing autoCompact.
mock.module('../../utils/context.js', () => ({ ...realContext }))
mock.module('../../utils/errors.js', () => ({ ...realErrors }))
mock.module('../../utils/tokens.js', () => ({ ...realTokens }))
mock.module('../../utils/config.js', () => ({
...realConfig,
getGlobalConfig: () => ({ autoCompactEnabled: true }),
Expand Down
112 changes: 112 additions & 0 deletions src/services/compact/compact.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,69 @@ const _realConfigModule = await import(
const _realProjectInstructionsModule = await import(
`../../utils/projectInstructions.js?real=${Date.now()}-${Math.random()}`
)
const _realTokenEstimationModule = await import(
`../tokenEstimation.js?real=${Date.now()}-${Math.random()}`
)
const _realClaudeApiModule = await import(
`../api/claude.js?real=${Date.now()}-${Math.random()}`
)
const _realGrowthBookModule = await import(
`../analytics/growthbook.js?real=${Date.now()}-${Math.random()}`
)
const _realContextModule = await import(
`../../utils/context.js?real=${Date.now()}-${Math.random()}`
)
const _realErrorsModule = await import(
`../../utils/errors.js?real=${Date.now()}-${Math.random()}`
)
const _realTokensModule = await import(
`../../utils/tokens.js?real=${Date.now()}-${Math.random()}`
)

const COMPACT_STUB_MODULES = [
'../analytics/growthbook.js',
'../analytics/index.js',
'../api/claude.js',
'../api/errors.js',
'../api/promptCacheBreakDetection.js',
'../api/withRetry.js',
'../tokenEstimation.js',
'../../bootstrap/state.js',
'../../tools/FileReadTool/FileReadTool.js',
'../../tools/FileReadTool/prompt.js',
'../../tools/ToolSearchTool/ToolSearchTool.js',
'../../utils/attachments.js',
'../../utils/auth.js',
'../../utils/config.js',
'../../utils/context.js',
'../../utils/contextAnalysis.js',
'../../utils/debug.js',
'../../utils/errors.js',
'../../utils/fileStateCache.js',
'../../utils/forkedAgent.js',
'../../utils/hooks.js',
'../../utils/log.js',
'../../utils/memory/types.js',
'../../utils/messages.js',
'../../utils/messages/systemFactories.js',
'../../utils/model/model.js',
'../../utils/model/modelSupportOverrides.js',
'../../utils/path.js',
'../../utils/plans.js',
'../../utils/projectInstructions.js',
'../../utils/sessionActivity.js',
'../../utils/sessionStart.js',
'../../utils/sessionStorage.js',
'../../utils/settings/settings.js',
'../../utils/sleep.js',
'../../utils/slowOperations.js',
'../../utils/systemPromptType.js',
'../../utils/task/diskOutput.js',
'../../utils/tokens.js',
'../../utils/toolSearch.js',
'./grouping.js',
'./prompt.js',
] as const

// ---------------------------------------------------------------------------
// Helpers
Expand Down Expand Up @@ -635,6 +698,12 @@ afterEach(() => {
afterAll(async () => {
mock.restore()
clearProviderEnv()
for (const specifier of COMPACT_STUB_MODULES) {
const realModule = await import(
`${specifier}?real=${Date.now()}-${Math.random()}`
)
mock.module(specifier, () => ({ ...realModule }))
}
// The compact test registers many mock.module() stubs that persist
// process-wide. Restore the real implementations so downstream test files
// (goal controller, runAgent routing, BashTool) get correct behaviour.
Expand Down Expand Up @@ -669,6 +738,49 @@ afterAll(async () => {
mock.module('../../utils/auth.js', () => ({ ..._realAuthModule }))
mock.module('../../utils/path.js', () => ({ ..._realPathModule }))
mock.module('../../utils/config.js', () => ({ ..._realConfigModule }))
// These compact-only stubs affect the standalone microcompact and
// auto-compact tests that run later in the serialized smoke suite.
mock.module('../tokenEstimation.js', () => ({ ..._realTokenEstimationModule }))
mock.module('../api/claude.js', () => ({ ..._realClaudeApiModule }))
mock.module('../analytics/growthbook.js', () => ({ ..._realGrowthBookModule }))
mock.module('../../utils/context.js', () => ({ ..._realContextModule }))
mock.module('../../utils/errors.js', () => ({ ..._realErrorsModule }))
mock.module('../../utils/tokens.js', () => ({ ..._realTokensModule }))
// Keep the cleanup load-bearing: these modules are consumed by standalone
// auto-compact tests when this file happens to run first in the smoke suite.
const [
restoredContext,
restoredErrors,
restoredTokens,
restoredTokenEstimation,
restoredClaudeApi,
restoredGrowthBook,
] = await Promise.all([
import('../../utils/context.js'),
import('../../utils/errors.js'),
import('../../utils/tokens.js'),
import('../tokenEstimation.js'),
import('../api/claude.js'),
import('../analytics/growthbook.js'),
])
expect(restoredContext.getContextWindowForModel).toBe(
_realContextModule.getContextWindowForModel,
)
expect(restoredErrors.hasExactErrorMessage).toBe(
_realErrorsModule.hasExactErrorMessage,
)
expect(restoredTokens.tokenCountWithEstimation).toBe(
_realTokensModule.tokenCountWithEstimation,
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
expect(restoredTokenEstimation.roughTokenCountEstimation).toBe(
_realTokenEstimationModule.roughTokenCountEstimation,
)
expect(restoredClaudeApi.getMaxOutputTokensForModel).toBe(
_realClaudeApiModule.getMaxOutputTokensForModel,
)
expect(restoredGrowthBook.getFeatureValue_CACHED_MAY_BE_STALE).toBe(
_realGrowthBookModule.getFeatureValue_CACHED_MAY_BE_STALE,
)
// projectInstructions: the stub above replaces the whole module with only
// getProjectInstructionFilePaths, so every other export becomes undefined.
// Downstream CLAUDE.md discovery in runAgent.routing.test.ts then crashes in
Expand Down