mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-11 03:13:12 +00:00
test(cliproxy): cover auto compact windows
This commit is contained in:
1 parent
b0732613a3
commit
c107a6752c
3 files changed
+203
-16
No files matched your search
@@ -75,6 +75,18 @@ describe('model-catalog compatibility lookups', () => {
|
|||||||
expect(catalog?.models[0]?.extendedContext).toBeUndefined();
|
expect(catalog?.models[0]?.extendedContext).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('maps positive integer live context length into contextWindow metadata', () => {
|
||||||
|
const catalog = mergeCatalog('xai', [
|
||||||
|
{
|
||||||
|
id: 'grok-4.3',
|
||||||
|
display_name: 'Grok 4.3 Live',
|
||||||
|
context_length: 1_000_000,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(catalog?.models[0]?.contextWindow).toBe(1_000_000);
|
||||||
|
});
|
||||||
|
|
||||||
it('does not re-add stale static-only models when live catalog data is present', () => {
|
it('does not re-add stale static-only models when live catalog data is present', () => {
|
||||||
const catalog = mergeCatalog('gemini', [
|
const catalog = mergeCatalog('gemini', [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { afterEach, beforeEach, describe, expect, it } from 'bun:test';
|
||||||
|
|
||||||
|
import { invalidateConfigCache } from '../../../config/config-loader-facade';
|
||||||
|
import { clearConfigCache } from '../base-config-loader';
|
||||||
|
import { applyClaudeAutoCompactWindow } from '../env-builder';
|
||||||
|
import { buildClaudeEnvironment } from '../../executor/env-resolver';
|
||||||
|
|
||||||
|
describe('applyClaudeAutoCompactWindow', () => {
|
||||||
|
it('injects the catalog window for the effective model after suffix normalization', () => {
|
||||||
|
const env = applyClaudeAutoCompactWindow(
|
||||||
|
{
|
||||||
|
ANTHROPIC_MODEL: 'gpt-5.6-sol-xhigh[1m]',
|
||||||
|
},
|
||||||
|
'codex'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('372000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the selected provider when the same model family exists in multiple catalogs', () => {
|
||||||
|
const env = applyClaudeAutoCompactWindow(
|
||||||
|
{
|
||||||
|
ANTHROPIC_MODEL: 'claude-opus-4-6-thinking',
|
||||||
|
},
|
||||||
|
'agy'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('200000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not inject a value for unknown or custom models', () => {
|
||||||
|
const env = applyClaudeAutoCompactWindow(
|
||||||
|
{
|
||||||
|
ANTHROPIC_MODEL: 'custom-model-with-unknown-window',
|
||||||
|
},
|
||||||
|
'codex'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect('CLAUDE_CODE_AUTO_COMPACT_WINDOW' in env).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves an explicit user value', () => {
|
||||||
|
const env = applyClaudeAutoCompactWindow(
|
||||||
|
{
|
||||||
|
ANTHROPIC_MODEL: 'gpt-5.6-sol',
|
||||||
|
CLAUDE_CODE_AUTO_COMPACT_WINDOW: '123456',
|
||||||
|
},
|
||||||
|
'codex'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('123456');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('treats an explicit empty value as authoritative', () => {
|
||||||
|
const env = applyClaudeAutoCompactWindow(
|
||||||
|
{
|
||||||
|
ANTHROPIC_MODEL: 'gpt-5.6-sol',
|
||||||
|
CLAUDE_CODE_AUTO_COMPACT_WINDOW: '',
|
||||||
|
},
|
||||||
|
'codex'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not mistake a differently cased variable for the Claude Code key', () => {
|
||||||
|
const env = applyClaudeAutoCompactWindow(
|
||||||
|
{
|
||||||
|
ANTHROPIC_MODEL: 'gpt-5.6-sol',
|
||||||
|
claude_code_auto_compact_window: '123456',
|
||||||
|
},
|
||||||
|
'codex'
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(env.claude_code_auto_compact_window).toBe('123456');
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('372000');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Claude launch auto-compact integration', () => {
|
||||||
|
let tempHome: string;
|
||||||
|
let originalCcsHome: string | undefined;
|
||||||
|
let originalAutoCompactWindow: string | undefined;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
originalCcsHome = process.env.CCS_HOME;
|
||||||
|
originalAutoCompactWindow = process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
|
||||||
|
tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'ccs-auto-compact-'));
|
||||||
|
process.env.CCS_HOME = tempHome;
|
||||||
|
delete process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
|
||||||
|
invalidateConfigCache();
|
||||||
|
clearConfigCache();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
if (originalCcsHome === undefined) delete process.env.CCS_HOME;
|
||||||
|
else process.env.CCS_HOME = originalCcsHome;
|
||||||
|
|
||||||
|
if (originalAutoCompactWindow === undefined) {
|
||||||
|
delete process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
|
||||||
|
} else {
|
||||||
|
process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW = originalAutoCompactWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidateConfigCache();
|
||||||
|
clearConfigCache();
|
||||||
|
fs.rmSync(tempHome, { recursive: true, force: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('injects the catalog window for the provider default model on Claude launches', () => {
|
||||||
|
const env = buildClaudeEnvironment({
|
||||||
|
provider: 'codex',
|
||||||
|
useRemoteProxy: false,
|
||||||
|
localPort: 8317,
|
||||||
|
verbose: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(env.ANTHROPIC_MODEL).toBe('gpt-5.4(high)');
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('1050000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves an inherited user value on Claude launches', () => {
|
||||||
|
process.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW = '';
|
||||||
|
|
||||||
|
const env = buildClaudeEnvironment({
|
||||||
|
provider: 'codex',
|
||||||
|
useRemoteProxy: false,
|
||||||
|
localPort: 8317,
|
||||||
|
verbose: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses the composite default tier provider for model lookup', () => {
|
||||||
|
const env = buildClaudeEnvironment({
|
||||||
|
provider: 'codex',
|
||||||
|
useRemoteProxy: false,
|
||||||
|
localPort: 8317,
|
||||||
|
verbose: false,
|
||||||
|
isComposite: true,
|
||||||
|
compositeTiers: {
|
||||||
|
opus: { provider: 'agy', model: 'claude-opus-4-6-thinking' },
|
||||||
|
sonnet: { provider: 'codex', model: 'gpt-5.6-sol' },
|
||||||
|
haiku: { provider: 'kimi', model: 'kimi-k2' },
|
||||||
|
},
|
||||||
|
compositeDefaultTier: 'opus',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('200000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves an explicit empty value from a custom settings file', () => {
|
||||||
|
const settingsPath = path.join(tempHome, 'codex-custom.settings.json');
|
||||||
|
fs.writeFileSync(
|
||||||
|
settingsPath,
|
||||||
|
JSON.stringify({
|
||||||
|
env: {
|
||||||
|
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317',
|
||||||
|
ANTHROPIC_AUTH_TOKEN: 'test-token',
|
||||||
|
ANTHROPIC_MODEL: 'gpt-5.6-sol',
|
||||||
|
CLAUDE_CODE_AUTO_COMPACT_WINDOW: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const env = buildClaudeEnvironment({
|
||||||
|
provider: 'codex',
|
||||||
|
useRemoteProxy: false,
|
||||||
|
localPort: 8317,
|
||||||
|
customSettingsPath: settingsPath,
|
||||||
|
verbose: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe('');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -42,10 +42,7 @@ describe('cliproxy catalog --json output', () => {
|
|||||||
it('includes metadata fields when present on model entries', () => {
|
it('includes metadata fields when present on model entries', () => {
|
||||||
handleCatalogJson();
|
handleCatalogJson();
|
||||||
|
|
||||||
const parsed = JSON.parse(capturedOutput[0]) as Record<
|
const parsed = JSON.parse(capturedOutput[0]) as Record<string, Array<Record<string, unknown>>>;
|
||||||
string,
|
|
||||||
Array<Record<string, unknown>>
|
|
||||||
>;
|
|
||||||
const allModels = Object.values(parsed).flat();
|
const allModels = Object.values(parsed).flat();
|
||||||
|
|
||||||
// At least some models in the static catalog have tier set
|
// At least some models in the static catalog have tier set
|
||||||
@@ -56,15 +53,19 @@ describe('cliproxy catalog --json output', () => {
|
|||||||
for (const model of withTier) {
|
for (const model of withTier) {
|
||||||
expect(['free', 'pro', 'ultra']).toContain(model.tier);
|
expect(['free', 'pro', 'ultra']).toContain(model.tier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const withContextWindow = allModels.filter((m) => m.contextWindow !== undefined);
|
||||||
|
expect(withContextWindow.length).toBeGreaterThan(0);
|
||||||
|
for (const model of withContextWindow) {
|
||||||
|
expect(Number.isInteger(model.contextWindow)).toBe(true);
|
||||||
|
expect(model.contextWindow as number).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('omits undefined optional fields instead of including nulls', () => {
|
it('omits undefined optional fields instead of including nulls', () => {
|
||||||
handleCatalogJson();
|
handleCatalogJson();
|
||||||
|
|
||||||
const parsed = JSON.parse(capturedOutput[0]) as Record<
|
const parsed = JSON.parse(capturedOutput[0]) as Record<string, Array<Record<string, unknown>>>;
|
||||||
string,
|
|
||||||
Array<Record<string, unknown>>
|
|
||||||
>;
|
|
||||||
const allModels = Object.values(parsed).flat();
|
const allModels = Object.values(parsed).flat();
|
||||||
|
|
||||||
for (const model of allModels) {
|
for (const model of allModels) {
|
||||||
@@ -78,10 +79,7 @@ describe('cliproxy catalog --json output', () => {
|
|||||||
it('includes explicit false boolean values in output', () => {
|
it('includes explicit false boolean values in output', () => {
|
||||||
handleCatalogJson();
|
handleCatalogJson();
|
||||||
|
|
||||||
const parsed = JSON.parse(capturedOutput[0]) as Record<
|
const parsed = JSON.parse(capturedOutput[0]) as Record<string, Array<Record<string, unknown>>>;
|
||||||
string,
|
|
||||||
Array<Record<string, unknown>>
|
|
||||||
>;
|
|
||||||
const allModels = Object.values(parsed).flat();
|
const allModels = Object.values(parsed).flat();
|
||||||
|
|
||||||
// Static catalog has models with extendedContext: false
|
// Static catalog has models with extendedContext: false
|
||||||
@@ -92,10 +90,7 @@ describe('cliproxy catalog --json output', () => {
|
|||||||
it('includes thinking configuration when present on models', () => {
|
it('includes thinking configuration when present on models', () => {
|
||||||
handleCatalogJson();
|
handleCatalogJson();
|
||||||
|
|
||||||
const parsed = JSON.parse(capturedOutput[0]) as Record<
|
const parsed = JSON.parse(capturedOutput[0]) as Record<string, Array<Record<string, unknown>>>;
|
||||||
string,
|
|
||||||
Array<Record<string, unknown>>
|
|
||||||
>;
|
|
||||||
const allModels = Object.values(parsed).flat();
|
const allModels = Object.values(parsed).flat();
|
||||||
|
|
||||||
// Static catalog has thinking models (e.g. Claude Opus 4.6 Thinking)
|
// Static catalog has thinking models (e.g. Claude Opus 4.6 Thinking)
|
||||||
|
|||||||
Reference in new issue
Block a user