mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 23:42:03 +00:00
fix(cli): recognize OpenAI-compatible providers in qwen auth status (#3623)
* fix(cli): recognize OpenAI-compatible providers in `qwen auth status` Previously `qwen auth status` treated all `selectedType=openai` setups as Coding Plan, checking only `BAILIAN_CODING_PLAN_API_KEY`. Users who configured generic OpenAI-compatible providers (e.g. Xunfei, DeepSeek, Ollama) via `modelProviders.openai` saw a misleading "Alibaba Cloud Coding Plan (Incomplete)" even though their provider worked correctly. Split the USE_OPENAI branch into two paths: - Coding Plan: detected by `codingPlan.region` or `CODING_PLAN_ENV_KEY` - Generic OpenAI-compatible: checks API key from modelProviders envKey, OPENAI_API_KEY, or settings.security.auth.apiKey; displays provider info including model name and base URL. Closes #3612 Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): improve Coding Plan detection and align API key check semantics Address review feedback: 1. Detect Coding Plan via isCodingPlanConfig(baseUrl, envKey) on the active modelProviders entry instead of checking BAILIAN_CODING_PLAN_API_KEY env var presence. A stale env key from a previous setup no longer misclassifies a generic OpenAI-compatible provider as Coding Plan. 2. When modelProviders entry has an explicit envKey, only check that key without falling back to OPENAI_API_KEY or settings.security.auth.apiKey. This mirrors hasApiKeyForAuth() semantics in auth.ts, preventing status from reporting "configured" when the actual provider key is missing. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): fix TS2367 type error in displayRegion comparison `detectedCodingPlanRegion` is `CodingPlanRegion | false`, so `!== true` comparison is invalid. Simplify to truthiness check. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): refine model fallback and Coding Plan key detection - Only fall back to models[0] when model.name is unset; when set but not matching any provider entry, treat as unmanaged to avoid binding status to an unrelated provider's envKey/baseUrl. - Simplify hasCodingPlanKey to only check CODING_PLAN_ENV_KEY, not activeModelConfig.envKey, preventing a generic provider key from being mistaken as Coding Plan credentials when codingPlan.region is stale. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): prioritize active model config over stale codingPlan.region When activeModelConfig exists, trust isCodingPlanConfig() result over potentially stale codingPlan.region from a previous setup. This prevents a user who switched from Coding Plan to a generic provider from still seeing "Alibaba Cloud Coding Plan" in auth status. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> * fix(cli): avoid stale Coding Plan fallback in auth status Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com> --------- Co-authored-by: jinye.djy <jinye.djy@alibaba-inc.com> Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
1befabe586
commit
4ac9ec07c3
2 changed files with 475 additions and 30 deletions
|
|
@ -55,6 +55,8 @@ interface MergedSettingsWithCodingPlan {
|
|||
security?: {
|
||||
auth?: {
|
||||
selectedType?: string;
|
||||
apiKey?: string;
|
||||
baseUrl?: string;
|
||||
};
|
||||
};
|
||||
codingPlan?: CodingPlanSettings;
|
||||
|
|
@ -832,9 +834,15 @@ export async function showAuthStatus(): Promise<void> {
|
|||
const isActiveOpenRouter = activeConfig
|
||||
? isOpenRouterConfig(activeConfig)
|
||||
: false;
|
||||
const isActiveCodingPlan =
|
||||
activeConfig &&
|
||||
isCodingPlanConfig(activeConfig.baseUrl, activeConfig.envKey) !== false;
|
||||
const providerCodingPlanRegion = isCodingPlanConfig(
|
||||
activeConfig?.baseUrl,
|
||||
activeConfig?.envKey,
|
||||
);
|
||||
const detectedCodingPlanRegion = activeConfig
|
||||
? providerCodingPlanRegion
|
||||
: !modelName
|
||||
? codingPlanRegion
|
||||
: false;
|
||||
const isActiveStandard =
|
||||
activeConfig &&
|
||||
activeConfig.envKey === DASHSCOPE_STANDARD_API_KEY_ENV_KEY &&
|
||||
|
|
@ -849,11 +857,13 @@ export async function showAuthStatus(): Promise<void> {
|
|||
if (isActiveOpenRouter) {
|
||||
if (hasOpenRouterApiKey) {
|
||||
writeStdoutLine(t('✓ Authentication Method: OpenRouter'));
|
||||
|
||||
if (modelName) {
|
||||
writeStdoutLine(
|
||||
t(' Current Model: {{model}}', { model: modelName }),
|
||||
);
|
||||
}
|
||||
|
||||
writeStdoutLine(t(' Status: API key configured\n'));
|
||||
} else {
|
||||
writeStdoutLine(
|
||||
|
|
@ -864,7 +874,7 @@ export async function showAuthStatus(): Promise<void> {
|
|||
);
|
||||
writeStdoutLine(t(' Run `qwen auth openrouter` to re-configure.\n'));
|
||||
}
|
||||
} else if (isActiveCodingPlan) {
|
||||
} else if (detectedCodingPlanRegion) {
|
||||
const hasCodingPlanKey =
|
||||
!!process.env[CODING_PLAN_ENV_KEY] ||
|
||||
!!mergedSettings.env?.[CODING_PLAN_ENV_KEY];
|
||||
|
|
@ -874,9 +884,10 @@ export async function showAuthStatus(): Promise<void> {
|
|||
t('✓ Authentication Method: Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
|
||||
if (codingPlanRegion) {
|
||||
const displayRegion = codingPlanRegion || detectedCodingPlanRegion;
|
||||
if (displayRegion) {
|
||||
const regionDisplay =
|
||||
codingPlanRegion === CodingPlanRegion.CHINA
|
||||
displayRegion === CodingPlanRegion.CHINA
|
||||
? t('中国 (China) - 阿里云百炼')
|
||||
: t('Global - Alibaba Cloud');
|
||||
writeStdoutLine(
|
||||
|
|
@ -943,13 +954,21 @@ export async function showAuthStatus(): Promise<void> {
|
|||
writeStdoutLine(t(' Run `qwen auth api-key` to re-configure.\n'));
|
||||
}
|
||||
} else if (activeConfig) {
|
||||
const envKey = activeConfig.envKey;
|
||||
const hasKey =
|
||||
envKey && (!!process.env[envKey] || !!mergedSettings.env?.[envKey]);
|
||||
let hasApiKey: boolean;
|
||||
if (activeConfig.envKey) {
|
||||
hasApiKey =
|
||||
!!process.env[activeConfig.envKey] ||
|
||||
!!mergedSettings.env?.[activeConfig.envKey];
|
||||
} else {
|
||||
hasApiKey =
|
||||
!!process.env['OPENAI_API_KEY'] ||
|
||||
!!mergedSettings.env?.['OPENAI_API_KEY'] ||
|
||||
!!mergedSettings.security?.auth?.apiKey;
|
||||
}
|
||||
|
||||
if (hasKey || !envKey) {
|
||||
if (hasApiKey) {
|
||||
writeStdoutLine(
|
||||
t('✓ Authentication Method: OpenAI-compatible API Key'),
|
||||
t('✓ Authentication Method: OpenAI-compatible Provider'),
|
||||
);
|
||||
|
||||
if (modelName) {
|
||||
|
|
@ -958,34 +977,53 @@ export async function showAuthStatus(): Promise<void> {
|
|||
);
|
||||
}
|
||||
|
||||
writeStdoutLine(t(' Status: Configured\n'));
|
||||
const baseUrl =
|
||||
activeConfig.baseUrl || mergedSettings.security?.auth?.baseUrl;
|
||||
if (baseUrl) {
|
||||
writeStdoutLine(t(' Base URL: {{baseUrl}}', { baseUrl }));
|
||||
}
|
||||
|
||||
writeStdoutLine(t(' Status: API key configured\n'));
|
||||
} else {
|
||||
writeStdoutLine(
|
||||
t(
|
||||
'⚠️ Authentication Method: OpenAI-compatible API Key (Incomplete)',
|
||||
'⚠️ Authentication Method: OpenAI-compatible Provider (Incomplete)',
|
||||
),
|
||||
);
|
||||
writeStdoutLine(
|
||||
t(
|
||||
' Issue: API key {{envKey}} not found in environment or settings\n',
|
||||
{
|
||||
envKey,
|
||||
},
|
||||
),
|
||||
);
|
||||
writeStdoutLine(
|
||||
t(
|
||||
' Configure it in settings.json or set the environment variable.\n',
|
||||
),
|
||||
t(' Issue: API key not found in environment or settings\n'),
|
||||
);
|
||||
writeStdoutLine(t(' Run `qwen auth` to re-configure.\n'));
|
||||
}
|
||||
} else {
|
||||
const hasCodingPlanKey =
|
||||
!!process.env[CODING_PLAN_ENV_KEY] ||
|
||||
!!mergedSettings.env?.[CODING_PLAN_ENV_KEY];
|
||||
const hasCodingPlanMetadata = !!codingPlanRegion || !!codingPlanVersion;
|
||||
const hasGenericApiKey =
|
||||
!!process.env['OPENAI_API_KEY'] ||
|
||||
!!mergedSettings.env?.['OPENAI_API_KEY'] ||
|
||||
!!mergedSettings.security?.auth?.apiKey;
|
||||
const hasCodingPlanMetadata =
|
||||
!modelName && (!!codingPlanRegion || !!codingPlanVersion);
|
||||
|
||||
if (hasCodingPlanKey) {
|
||||
if (hasGenericApiKey) {
|
||||
writeStdoutLine(
|
||||
t('✓ Authentication Method: OpenAI-compatible Provider'),
|
||||
);
|
||||
|
||||
if (modelName) {
|
||||
writeStdoutLine(
|
||||
t(' Current Model: {{model}}', { model: modelName }),
|
||||
);
|
||||
}
|
||||
|
||||
const baseUrl = mergedSettings.security?.auth?.baseUrl;
|
||||
if (baseUrl) {
|
||||
writeStdoutLine(t(' Base URL: {{baseUrl}}', { baseUrl }));
|
||||
}
|
||||
|
||||
writeStdoutLine(t(' Status: API key configured\n'));
|
||||
} else if (hasCodingPlanKey) {
|
||||
writeStdoutLine(
|
||||
t('✓ Authentication Method: Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
|
|
@ -1030,13 +1068,11 @@ export async function showAuthStatus(): Promise<void> {
|
|||
} else {
|
||||
writeStdoutLine(
|
||||
t(
|
||||
'⚠️ Authentication Method: OpenAI-compatible API Key (Incomplete)',
|
||||
'⚠️ Authentication Method: OpenAI-compatible Provider (Incomplete)',
|
||||
),
|
||||
);
|
||||
writeStdoutLine(
|
||||
t(
|
||||
' Issue: No model provider configuration found for the selected model.\n',
|
||||
),
|
||||
t(' Issue: API key not found in environment or settings\n'),
|
||||
);
|
||||
writeStdoutLine(t(' Run `qwen auth` to re-configure.\n'));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,12 +27,14 @@ describe('showAuthStatus', () => {
|
|||
vi.clearAllMocks();
|
||||
vi.spyOn(process, 'exit').mockImplementation((() => undefined) as never);
|
||||
delete process.env[CODING_PLAN_ENV_KEY];
|
||||
delete process.env['OPENAI_API_KEY'];
|
||||
delete process.env['OPENROUTER_API_KEY'];
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
delete process.env[CODING_PLAN_ENV_KEY];
|
||||
delete process.env['OPENAI_API_KEY'];
|
||||
delete process.env['OPENROUTER_API_KEY'];
|
||||
});
|
||||
|
||||
|
|
@ -221,6 +223,78 @@ describe('showAuthStatus', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should show Coding Plan when detected via modelProviders entry (no codingPlan.region)', async () => {
|
||||
process.env[CODING_PLAN_ENV_KEY] = 'test-api-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'qwen3.5-plus',
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'qwen3.5-plus',
|
||||
envKey: 'BAILIAN_CODING_PLAN_API_KEY',
|
||||
baseUrl: 'https://coding.dashscope.aliyuncs.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('中国 (China)'),
|
||||
);
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should not fall back to stale Coding Plan metadata when model selection is unmatched', async () => {
|
||||
process.env['OPENAI_API_KEY'] = 'test-openai-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
codingPlan: {
|
||||
region: 'global',
|
||||
version: 'abc123def456',
|
||||
},
|
||||
model: {
|
||||
name: 'manual-provider-model',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should show Coding Plan region for china', async () => {
|
||||
process.env[CODING_PLAN_ENV_KEY] = 'test-api-key';
|
||||
|
||||
|
|
@ -339,4 +413,339 @@ describe('showAuthStatus', () => {
|
|||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(1);
|
||||
});
|
||||
|
||||
describe('OpenAI-compatible provider (no Coding Plan)', () => {
|
||||
afterEach(() => {
|
||||
delete process.env['OPENAI_API_KEY'];
|
||||
delete process.env['CUSTOM_API_KEY'];
|
||||
delete process.env['XUNFEI_API_KEY'];
|
||||
delete process.env[CODING_PLAN_ENV_KEY];
|
||||
});
|
||||
|
||||
it('should show OpenAI-compatible status with OPENAI_API_KEY', async () => {
|
||||
process.env['OPENAI_API_KEY'] = 'test-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'gpt-4o',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('gpt-4o'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should show OpenAI-compatible status with custom envKey from modelProviders', async () => {
|
||||
process.env['CUSTOM_API_KEY'] = 'test-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'custom-model',
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'custom-model',
|
||||
envKey: 'CUSTOM_API_KEY',
|
||||
baseUrl: 'https://custom-api.example.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('custom-model'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('https://custom-api.example.com/v1'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should show OpenAI-compatible status with settings.security.auth.apiKey', async () => {
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
apiKey: 'settings-api-key',
|
||||
baseUrl: 'https://my-provider.example.com/v1',
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'my-model',
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('https://my-provider.example.com/v1'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should show incomplete when no API key is found for OpenAI-compatible provider', async () => {
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider (Incomplete)'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key not found'),
|
||||
);
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should detect API key via default model when model.name is unset', async () => {
|
||||
process.env['CUSTOM_API_KEY'] = 'test-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'default-model',
|
||||
envKey: 'CUSTOM_API_KEY',
|
||||
baseUrl: 'https://default-api.example.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('https://default-api.example.com/v1'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should show Incomplete when explicit envKey is missing even if OPENAI_API_KEY is set', async () => {
|
||||
process.env['OPENAI_API_KEY'] = 'fallback-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'custom-model',
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'custom-model',
|
||||
envKey: 'CUSTOM_API_KEY',
|
||||
baseUrl: 'https://custom-api.example.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider (Incomplete)'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key not found'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not bind to unrelated provider entry when model.name does not match', async () => {
|
||||
process.env['OPENAI_API_KEY'] = 'test-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'manual-model-not-in-providers',
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'other-model',
|
||||
envKey: 'OTHER_API_KEY',
|
||||
baseUrl: 'https://other-api.example.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
// Should NOT show the unrelated provider's base URL
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('https://other-api.example.com/v1'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should show OpenAI-compatible when stale codingPlan.region exists but active model is generic', async () => {
|
||||
process.env['XUNFEI_API_KEY'] = 'active-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
codingPlan: {
|
||||
region: 'china',
|
||||
version: 'stale-version',
|
||||
},
|
||||
model: {
|
||||
name: 'spark-v4',
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'spark-v4',
|
||||
envKey: 'XUNFEI_API_KEY',
|
||||
baseUrl: 'https://spark-api-open.xf-yun.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it('should show OpenAI-compatible when stale Coding Plan key exists but active model is generic', async () => {
|
||||
process.env[CODING_PLAN_ENV_KEY] = 'stale-coding-plan-key';
|
||||
process.env['XUNFEI_API_KEY'] = 'active-key';
|
||||
|
||||
vi.mocked(loadSettings).mockReturnValue(
|
||||
createMockSettings({
|
||||
security: {
|
||||
auth: {
|
||||
selectedType: AuthType.USE_OPENAI,
|
||||
},
|
||||
},
|
||||
model: {
|
||||
name: 'spark-v4',
|
||||
},
|
||||
modelProviders: {
|
||||
openai: [
|
||||
{
|
||||
id: 'spark-v4',
|
||||
envKey: 'XUNFEI_API_KEY',
|
||||
baseUrl: 'https://spark-api-open.xf-yun.com/v1',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await showAuthStatus();
|
||||
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('OpenAI-compatible Provider'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('spark-v4'),
|
||||
);
|
||||
expect(writeStdoutLine).toHaveBeenCalledWith(
|
||||
expect.stringContaining('API key configured'),
|
||||
);
|
||||
expect(writeStdoutLine).not.toHaveBeenCalledWith(
|
||||
expect.stringContaining('Alibaba Cloud Coding Plan'),
|
||||
);
|
||||
expect(process.exit).toHaveBeenCalledWith(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue