test(cliproxy): cover Claude long-context parity flows

- add regression coverage for runtime, api create flags, and help text

- add provider-editor tests for multi-key [1m] state and preset preservation

Refs #789
This commit is contained in:
Tam Nhu Tran committed 2026-03-26 16:36:14 -04:00
1 parent c05189b4b1
commit 5e11bba514
5 files changed
+305 -2

No files matched your search

@@ -136,6 +136,22 @@ describe('applyExtendedContextConfig', () => {
expect(env.ANTHROPIC_MODEL).toBe('claude-opus-4-5-20251101[1m]');
});
it('applies explicit Claude [1m] per tier and leaves unsupported Haiku plain', () => {
const env: NodeJS.ProcessEnv = {
ANTHROPIC_MODEL: 'claude-haiku-4-5-20251001',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-5-20251101',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-5-20250929',
ANTHROPIC_DEFAULT_HAIKU_MODEL: 'claude-haiku-4-5-20251001',
};
applyExtendedContextConfig(env, 'claude', true);
expect(env.ANTHROPIC_MODEL).toBe('claude-haiku-4-5-20251001');
expect(env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe('claude-opus-4-5-20251101[1m]');
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('claude-sonnet-4-5-20250929[1m]');
expect(env.ANTHROPIC_DEFAULT_HAIKU_MODEL).toBe('claude-haiku-4-5-20251001');
});
it('strips existing suffixes before catalog lookup', () => {
const env: NodeJS.ProcessEnv = {
ANTHROPIC_MODEL: 'gemini-2.5-pro(high)',
@@ -169,6 +185,20 @@ describe('applyExtendedContextConfig', () => {
expect(env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe('claude-opus-4-6-thinking');
});
it('strips stale Claude [1m] from unsupported mappings while preserving compatible ones', () => {
const env: NodeJS.ProcessEnv = {
ANTHROPIC_MODEL: 'claude-haiku-4-5-20251001[1m]',
ANTHROPIC_DEFAULT_OPUS_MODEL: 'claude-opus-4-5-20251101[1m]',
ANTHROPIC_DEFAULT_SONNET_MODEL: 'claude-sonnet-4-5-20250929[1m]',
};
applyExtendedContextConfig(env, 'claude', true);
expect(env.ANTHROPIC_MODEL).toBe('claude-haiku-4-5-20251001');
expect(env.ANTHROPIC_DEFAULT_OPUS_MODEL).toBe('claude-opus-4-5-20251101[1m]');
expect(env.ANTHROPIC_DEFAULT_SONNET_MODEL).toBe('claude-sonnet-4-5-20250929[1m]');
});
it('strips [1m] suffix when --no-1m is explicit even if model has it', () => {
const env: NodeJS.ProcessEnv = {
ANTHROPIC_MODEL: 'gemini-2.5-pro[1m]',
@@ -1,7 +1,10 @@
import { describe, expect, test } from 'bun:test';
import {
applyClaudeExtendedContextPreference,
collectUnexpectedApiArgs,
hasClaudeModelMapping,
hasExplicitClaudeExtendedContext,
parseApiCommandArgs,
} from '../../../src/commands/api-command/shared';
@@ -132,6 +135,27 @@ describe('api-command arg parser', () => {
expect(parsed.model).toBe('-preview');
expect(parsed.errors).toEqual([]);
});
test('parses --1m for explicit Claude long context', () => {
const parsed = parseApiCommandArgs(['my-api', '--1m']);
expect(parsed.extendedContext).toBe(true);
expect(parsed.errors).toEqual([]);
});
test('parses --no-1m for explicit standard-context preference', () => {
const parsed = parseApiCommandArgs(['my-api', '--no-1m']);
expect(parsed.extendedContext).toBe(false);
expect(parsed.errors).toEqual([]);
});
test('rejects conflicting --1m and --no-1m flags', () => {
const parsed = parseApiCommandArgs(['my-api', '--1m', '--no-1m']);
expect(parsed.extendedContext).toBeUndefined();
expect(parsed.errors).toEqual(['Cannot combine --1m and --no-1m']);
});
});
describe('collectUnexpectedApiArgs', () => {
@@ -155,3 +179,55 @@ describe('collectUnexpectedApiArgs', () => {
expect(parsed.errors).toEqual(['Unknown option: --bogus', 'Unexpected arguments: value']);
});
});
describe('Claude long-context mapping helpers', () => {
test('detects Claude mappings and explicit [1m] state across all tiers', () => {
const models = {
default: 'gpt-5.4',
opus: 'claude-opus-4-6[1m]',
sonnet: 'claude-sonnet-4-6',
haiku: 'claude-haiku-4-5-20251001',
};
expect(hasClaudeModelMapping(models)).toBe(true);
expect(hasExplicitClaudeExtendedContext(models)).toBe(true);
});
test('applies [1m] only to compatible Claude mappings and leaves Haiku plain', () => {
const models = applyClaudeExtendedContextPreference(
{
default: 'claude-sonnet-4-6',
opus: 'claude-opus-4-6',
sonnet: 'claude-sonnet-4-6',
haiku: 'claude-haiku-4-5-20251001',
},
true
);
expect(models).toEqual({
default: 'claude-sonnet-4-6[1m]',
opus: 'claude-opus-4-6[1m]',
sonnet: 'claude-sonnet-4-6[1m]',
haiku: 'claude-haiku-4-5-20251001',
});
});
test('strips [1m] from Claude mappings when standard context is requested', () => {
const models = applyClaudeExtendedContextPreference(
{
default: 'claude-sonnet-4-6[1m]',
opus: 'claude-opus-4-6[1m]',
sonnet: 'claude-sonnet-4-6[1m]',
haiku: 'claude-haiku-4-5-20251001',
},
false
);
expect(models).toEqual({
default: 'claude-sonnet-4-6',
opus: 'claude-opus-4-6',
sonnet: 'claude-sonnet-4-6',
haiku: 'claude-haiku-4-5-20251001',
});
});
});
@@ -1,5 +1,6 @@
import { afterEach, describe, expect, test } from 'bun:test';
import { showApiCommandHelp } from '../../../src/commands/api-command/help';
import { handleHelpCommand } from '../../../src/commands/help-command';
function stripAnsi(input: string): string {
@@ -103,4 +104,41 @@ describe('help command parity', () => {
true
);
});
test('root help explains Claude [1m] as an explicit CCS suffix with upstream limits', async () => {
const lines: string[] = [];
console.log = (...args: unknown[]) => {
lines.push(args.map((arg) => String(arg)).join(' '));
};
await handleHelpCommand();
const rendered = stripAnsi(lines.join('\n'));
expect(rendered.includes('Claude models: plain by default, opt-in with --1m or saved [1m]')).toBe(
true
);
expect(rendered.includes('CCS only controls the saved [1m] suffix.')).toBe(true);
expect(rendered.includes('return 429 extra-usage errors for long-context requests')).toBe(
true
);
});
test('api help documents create-time Claude [1m] flags and entitlement warning', async () => {
const lines: string[] = [];
console.log = (...args: unknown[]) => {
lines.push(args.map((arg) => String(arg)).join(' '));
};
await showApiCommandHelp();
const rendered = stripAnsi(lines.join('\n'));
expect(rendered.includes('--1m / --no-1m')).toBe(true);
expect(rendered.includes('ccs api create --preset anthropic --1m')).toBe(true);
expect(rendered.includes('Plain Claude model IDs stay on standard context by default.')).toBe(
true
);
expect(rendered.includes('some accounts can still return 429 for long-context requests')).toBe(
true
);
});
});