agent-ecosystem/test/renderer/components/team/TeamModelSelector.test.ts
2026-04-19 22:22:13 +03:00

158 lines
6.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
computeEffectiveTeamModel,
formatTeamModelSummary,
} from '@renderer/components/team/dialogs/TeamModelSelector';
import {
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON,
GPT_5_2_CODEX_UI_DISABLED_REASON,
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON,
getAvailableTeamProviderModels,
getTeamModelSelectionError,
getTeamModelUiDisabledReason,
normalizeTeamModelForUi,
} from '@renderer/utils/teamModelAvailability';
describe('formatTeamModelSummary', () => {
it('shows cross-provider Anthropic models as backend-routed instead of brand-mismatched', () => {
expect(formatTeamModelSummary('codex', 'claude-opus-4-6', 'medium')).toBe(
'Opus 4.6 · via Codex · Medium'
);
});
it('formats current Anthropic Opus model ids with the latest 4.7 label', () => {
expect(formatTeamModelSummary('anthropic', 'claude-opus-4-7', 'high')).toBe(
'Anthropic · Opus 4.7 · High'
);
expect(formatTeamModelSummary('codex', 'claude-opus-4-7', 'medium')).toBe(
'Opus 4.7 · via Codex · Medium'
);
});
it('keeps native Codex-family models branded normally', () => {
expect(formatTeamModelSummary('codex', 'gpt-5.4', 'medium')).toBe('5.4 · Medium');
});
it('marks the known disabled Codex models only for Codex team selection', () => {
expect(getTeamModelUiDisabledReason('codex', 'gpt-5.1-codex-mini')).toBe(
GPT_5_1_CODEX_MINI_UI_DISABLED_REASON
);
expect(getTeamModelUiDisabledReason('codex', 'gpt-5.2-codex')).toBe(
GPT_5_2_CODEX_UI_DISABLED_REASON
);
expect(getTeamModelUiDisabledReason('codex', 'gpt-5.3-codex-spark')).toBe(
GPT_5_3_CODEX_SPARK_UI_DISABLED_REASON
);
expect(getTeamModelUiDisabledReason('codex', 'gpt-5.4-mini')).toBeNull();
expect(getTeamModelUiDisabledReason('anthropic', 'gpt-5.1-codex-mini')).toBeNull();
});
it('keeps 5.1 Codex Max available on the native Codex path', () => {
const nativeCodexProviderStatus = {
providerId: 'codex' as const,
models: ['gpt-5.4', 'gpt-5.1-codex-max'],
authMethod: 'api_key' as const,
backend: {
kind: 'codex-native',
label: 'Codex native',
endpointLabel: 'codex exec --json',
},
modelVerificationState: 'verified' as const,
modelAvailability: [],
authenticated: true,
supported: true,
};
expect(
getTeamModelUiDisabledReason('codex', 'gpt-5.1-codex-max', nativeCodexProviderStatus)
).toBeNull();
expect(normalizeTeamModelForUi('codex', 'gpt-5.1-codex-max', nativeCodexProviderStatus)).toBe(
'gpt-5.1-codex-max'
);
expect(
getTeamModelSelectionError('codex', 'gpt-5.1-codex-max', nativeCodexProviderStatus)
).toBeNull();
expect(getTeamModelUiDisabledReason('codex', 'gpt-5.1-codex-max')).toBeNull();
});
it('normalizes disabled Codex model selections back to default', () => {
expect(normalizeTeamModelForUi('codex', 'gpt-5.1-codex-mini')).toBe('');
expect(normalizeTeamModelForUi('codex', 'gpt-5.2-codex')).toBe('');
expect(normalizeTeamModelForUi('codex', 'gpt-5.3-codex-spark')).toBe('');
expect(normalizeTeamModelForUi('codex', 'gpt-5.4-mini')).toBe('');
});
it('uses the runtime-reported Codex model list when provider status is available', () => {
const codexProviderStatus = {
providerId: 'codex' as const,
models: ['gpt-5.4', 'gpt-5.3-codex'],
authMethod: 'api_key' as const,
backend: {
kind: 'codex-native',
label: 'Codex native',
endpointLabel: 'codex exec --json',
},
modelVerificationState: 'verified' as const,
modelAvailability: [
{ modelId: 'gpt-5.4', status: 'available' as const, checkedAt: null },
{ modelId: 'gpt-5.3-codex', status: 'available' as const, checkedAt: null },
],
authenticated: true,
supported: true,
};
expect(getAvailableTeamProviderModels('codex', codexProviderStatus)).toEqual([
'gpt-5.4',
'gpt-5.3-codex',
]);
expect(normalizeTeamModelForUi('codex', 'gpt-5.2-codex', codexProviderStatus)).toBe('');
expect(normalizeTeamModelForUi('codex', 'gpt-5.4', codexProviderStatus)).toBe('gpt-5.4');
});
it('waits for the runtime model list before validating explicit Codex selections', () => {
expect(getTeamModelSelectionError('codex', 'gpt-5.4')).toContain('waiting for Codex runtime verification');
expect(getTeamModelSelectionError('codex', '')).toBeNull();
expect(getTeamModelSelectionError('anthropic', 'opus')).toBeNull();
expect(getTeamModelSelectionError('anthropic', 'claude-opus-4-7')).toBeNull();
});
});
describe('computeEffectiveTeamModel', () => {
it('appends [1m] for anthropic models', () => {
expect(computeEffectiveTeamModel('opus', false, 'anthropic')).toBe('opus[1m]');
expect(computeEffectiveTeamModel('sonnet', false, 'anthropic')).toBe('sonnet[1m]');
});
it('does not double-append [1m] when input already has it', () => {
expect(computeEffectiveTeamModel('opus[1m]', false, 'anthropic')).toBe('opus[1m]');
expect(computeEffectiveTeamModel('sonnet[1m]', false, 'anthropic')).toBe('sonnet[1m]');
expect(computeEffectiveTeamModel('opus[1m][1m]', false, 'anthropic')).toBe('opus[1m]');
});
it('defaults to opus[1m] when no model selected', () => {
expect(computeEffectiveTeamModel('', false, 'anthropic')).toBe('opus[1m]');
});
it('returns base model without [1m] when limitContext is true', () => {
expect(computeEffectiveTeamModel('opus', true, 'anthropic')).toBe('opus');
expect(computeEffectiveTeamModel('opus[1m]', true, 'anthropic')).toBe('opus');
expect(computeEffectiveTeamModel('opus[1m][1m]', true, 'anthropic')).toBe('opus');
expect(computeEffectiveTeamModel('', true, 'anthropic')).toBe('opus');
expect(computeEffectiveTeamModel('claude-opus-4-7[1m]', true, 'anthropic')).toBe(
'claude-opus-4-7'
);
});
it('returns haiku as-is', () => {
expect(computeEffectiveTeamModel('haiku', false, 'anthropic')).toBe('haiku');
expect(computeEffectiveTeamModel('claude-haiku-4-5-20251001', false, 'anthropic')).toBe(
'claude-haiku-4-5-20251001'
);
});
it('returns non-anthropic models as-is', () => {
expect(computeEffectiveTeamModel('gpt-5.4', false, 'codex')).toBe('gpt-5.4');
expect(computeEffectiveTeamModel('custom-model[1m]', false, 'codex')).toBe('custom-model[1m]');
});
});