CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
auth.test.ts417 linesDownload Raw Back to config
1/**2 * @license3 * Copyright 2025 Qwen Team4 * SPDX-License-Identifier: Apache-2.05 */6 7import { AuthType } from '@qwen-code/qwen-code-core';8import { vi } from 'vitest';9import { validateAuthMethod } from './auth.js';10import * as settings from './settings.js';11 12vi.mock('./settings.js', () => ({13  loadEnvironment: vi.fn(),14  loadSettings: vi.fn().mockReturnValue({15    merged: {},16  }),17}));18 19describe('validateAuthMethod', () => {20  beforeEach(() => {21    vi.resetModules();22    // Reset mock to default23    vi.mocked(settings.loadSettings).mockReturnValue({24      merged: {},25    } as ReturnType<typeof settings.loadSettings>);26  });27 28  afterEach(() => {29    vi.unstubAllEnvs();30    delete process.env['OPENAI_API_KEY'];31    delete process.env['CUSTOM_API_KEY'];32    delete process.env['GEMINI_API_KEY'];33    delete process.env['GEMINI_API_KEY_ALTERED'];34    delete process.env['ANTHROPIC_API_KEY'];35    delete process.env['ANTHROPIC_BASE_URL'];36    delete process.env['GOOGLE_API_KEY'];37    delete process.env['IDEALAB_KEY'];38    delete process.env['TOKEN_PLAN_KEY'];39  });40 41  it('should return null for USE_OPENAI with default env key', () => {42    process.env['OPENAI_API_KEY'] = 'fake-key';43    expect(validateAuthMethod(AuthType.USE_OPENAI)).toBeNull();44  });45 46  it('should return an error message for USE_OPENAI if no API key is available', () => {47    expect(validateAuthMethod(AuthType.USE_OPENAI)).toBe(48      "Missing API key for OpenAI-compatible auth. Set settings.security.auth.apiKey, or set the 'OPENAI_API_KEY' environment variable.",49    );50  });51 52  it('should return null for USE_OPENAI with custom envKey from modelProviders', () => {53    vi.mocked(settings.loadSettings).mockReturnValue({54      merged: {55        model: { name: 'custom-model' },56        modelProviders: {57          openai: [{ id: 'custom-model', envKey: 'CUSTOM_API_KEY' }],58        },59      },60    } as unknown as ReturnType<typeof settings.loadSettings>);61    process.env['CUSTOM_API_KEY'] = 'custom-key';62 63    expect(validateAuthMethod(AuthType.USE_OPENAI)).toBeNull();64  });65 66  it('should return null for USE_OPENAI with custom envKey stored in settings.env', () => {67    vi.mocked(settings.loadSettings).mockReturnValue({68      merged: {69        env: { CUSTOM_API_KEY: 'settings-env-key' },70        model: { name: 'custom-model' },71        modelProviders: {72          openai: [{ id: 'custom-model', envKey: 'CUSTOM_API_KEY' }],73        },74      },75    } as unknown as ReturnType<typeof settings.loadSettings>);76 77    expect(validateAuthMethod(AuthType.USE_OPENAI)).toBeNull();78  });79 80  it('uses providerProtocol mappings to find custom provider env keys', () => {81    vi.mocked(settings.loadSettings).mockReturnValue({82      merged: {83        model: { name: 'qwen3' },84        modelProviders: {85          idealab: [{ id: 'qwen3', envKey: 'IDEALAB_KEY' }],86        },87        providerProtocol: { idealab: 'openai' },88      },89    } as unknown as ReturnType<typeof settings.loadSettings>);90    process.env['IDEALAB_KEY'] = 'idealab-key';91 92    expect(validateAuthMethod(AuthType.USE_OPENAI)).toBeNull();93  });94 95  it('disambiguates by settings.model.baseUrl when providers share a model id', () => {96    // Two providers with the same id; the persisted baseUrl selects the second.97    // Only the second provider's env key is set, so validation passes only if98    // the lookup honors baseUrl rather than matching the first id entry.99    vi.mocked(settings.loadSettings).mockReturnValue({100      merged: {101        model: {102          name: 'qwen3.7-max',103          baseUrl: 'https://idealab.example.com/v1',104        },105        modelProviders: {106          openai: [107            {108              id: 'qwen3.7-max',109              baseUrl: 'https://token-plan.example.com/v1',110              envKey: 'TOKEN_PLAN_KEY',111            },112            {113              id: 'qwen3.7-max',114              baseUrl: 'https://idealab.example.com/v1',115              envKey: 'IDEALAB_KEY',116            },117          ],118        },119      },120    } as unknown as ReturnType<typeof settings.loadSettings>);121    process.env['IDEALAB_KEY'] = 'idealab-key';122 123    expect(validateAuthMethod(AuthType.USE_OPENAI)).toBeNull();124  });125 126  it('reports the selected provider env key when providers share a model id', () => {127    vi.mocked(settings.loadSettings).mockReturnValue({128      merged: {129        model: {130          name: 'qwen3.7-max',131          baseUrl: 'https://idealab.example.com/v1',132        },133        modelProviders: {134          openai: [135            {136              id: 'qwen3.7-max',137              baseUrl: 'https://token-plan.example.com/v1',138              envKey: 'TOKEN_PLAN_KEY',139            },140            {141              id: 'qwen3.7-max',142              baseUrl: 'https://idealab.example.com/v1',143              envKey: 'IDEALAB_KEY',144            },145          ],146        },147      },148    } as unknown as ReturnType<typeof settings.loadSettings>);149 150    // No env keys set → error must name the selected (IdeaLab) provider's key.151    const result = validateAuthMethod(AuthType.USE_OPENAI);152    expect(result).toContain('IDEALAB_KEY');153    expect(result).not.toContain('TOKEN_PLAN_KEY');154  });155 156  it('should return error with custom envKey hint when modelProviders envKey is set but env var is missing', () => {157    vi.mocked(settings.loadSettings).mockReturnValue({158      merged: {159        model: { name: 'custom-model' },160        modelProviders: {161          openai: [{ id: 'custom-model', envKey: 'CUSTOM_API_KEY' }],162        },163      },164    } as unknown as ReturnType<typeof settings.loadSettings>);165 166    const result = validateAuthMethod(AuthType.USE_OPENAI);167    expect(result).toContain('CUSTOM_API_KEY');168  });169 170  it('should return null for USE_GEMINI with custom envKey', () => {171    vi.mocked(settings.loadSettings).mockReturnValue({172      merged: {173        model: { name: 'gemini-1.5-flash' },174        modelProviders: {175          gemini: [176            { id: 'gemini-1.5-flash', envKey: 'GEMINI_API_KEY_ALTERED' },177          ],178        },179      },180    } as unknown as ReturnType<typeof settings.loadSettings>);181    process.env['GEMINI_API_KEY_ALTERED'] = 'altered-key';182 183    expect(validateAuthMethod(AuthType.USE_GEMINI)).toBeNull();184  });185 186  it('should return error with custom envKey for USE_GEMINI when env var is missing', () => {187    vi.mocked(settings.loadSettings).mockReturnValue({188      merged: {189        model: { name: 'gemini-1.5-flash' },190        modelProviders: {191          gemini: [192            { id: 'gemini-1.5-flash', envKey: 'GEMINI_API_KEY_ALTERED' },193          ],194        },195      },196    } as unknown as ReturnType<typeof settings.loadSettings>);197 198    const result = validateAuthMethod(AuthType.USE_GEMINI);199    expect(result).toContain('GEMINI_API_KEY_ALTERED');200  });201 202  it('should return an error for QWEN_OAUTH (free tier discontinued)', () => {203    const result = validateAuthMethod(AuthType.QWEN_OAUTH);204    expect(result).toContain('discontinued on 2026-04-15');205  });206 207  it('should return an error message for an invalid auth method', () => {208    expect(validateAuthMethod('invalid-method')).toBe(209      'Invalid auth method selected.',210    );211  });212 213  it('should return null for USE_ANTHROPIC with custom envKey and baseUrl', () => {214    vi.mocked(settings.loadSettings).mockReturnValue({215      merged: {216        model: { name: 'claude-3' },217        modelProviders: {218          anthropic: [219            {220              id: 'claude-3',221              envKey: 'CUSTOM_ANTHROPIC_KEY',222              baseUrl: 'https://api.anthropic.com',223            },224          ],225        },226      },227    } as unknown as ReturnType<typeof settings.loadSettings>);228    process.env['CUSTOM_ANTHROPIC_KEY'] = 'custom-anthropic-key';229 230    expect(validateAuthMethod(AuthType.USE_ANTHROPIC)).toBeNull();231  });232 233  it('should return error for USE_ANTHROPIC when baseUrl is missing', () => {234    vi.mocked(settings.loadSettings).mockReturnValue({235      merged: {236        model: { name: 'claude-3' },237        modelProviders: {238          anthropic: [{ id: 'claude-3', envKey: 'CUSTOM_ANTHROPIC_KEY' }],239        },240      },241    } as unknown as ReturnType<typeof settings.loadSettings>);242    process.env['CUSTOM_ANTHROPIC_KEY'] = 'custom-key';243 244    const result = validateAuthMethod(AuthType.USE_ANTHROPIC);245    expect(result).toContain('modelProviders[].baseUrl');246  });247 248  it('should return null for USE_VERTEX_AI with custom envKey', () => {249    vi.mocked(settings.loadSettings).mockReturnValue({250      merged: {251        model: { name: 'vertex-model' },252        modelProviders: {253          'vertex-ai': [254            { id: 'vertex-model', envKey: 'GOOGLE_API_KEY_VERTEX' },255          ],256        },257      },258    } as unknown as ReturnType<typeof settings.loadSettings>);259    process.env['GOOGLE_API_KEY_VERTEX'] = 'vertex-key';260 261    expect(validateAuthMethod(AuthType.USE_VERTEX_AI)).toBeNull();262  });263 264  it('should use config.getModelsConfig().getModel() when Config is provided', () => {265    // Settings has a different model266    vi.mocked(settings.loadSettings).mockReturnValue({267      merged: {268        model: { name: 'settings-model' },269        modelProviders: {270          openai: [271            { id: 'settings-model', envKey: 'SETTINGS_API_KEY' },272            { id: 'cli-model', envKey: 'CLI_API_KEY' },273          ],274        },275      },276    } as unknown as ReturnType<typeof settings.loadSettings>);277 278    // Mock Config object that returns a different model (e.g., from CLI args)279    const mockConfig = {280      getModelsConfig: vi.fn().mockReturnValue({281        getModel: vi.fn().mockReturnValue('cli-model'),282        getGenerationConfig: vi.fn().mockReturnValue({}),283      }),284    } as unknown as import('@qwen-code/qwen-code-core').Config;285 286    // Set the env key for the CLI model, not the settings model287    process.env['CLI_API_KEY'] = 'cli-key';288 289    // Should use 'cli-model' from config.getModelsConfig().getModel(), not 'settings-model'290    const result = validateAuthMethod(AuthType.USE_OPENAI, mockConfig);291    expect(result).toBeNull();292    expect(mockConfig.getModelsConfig).toHaveBeenCalled();293  });294 295  it('should fail validation when Config provides different model without matching env key', () => {296    // Clean up any existing env keys first297    delete process.env['CLI_API_KEY'];298    delete process.env['SETTINGS_API_KEY'];299    delete process.env['OPENAI_API_KEY'];300 301    vi.mocked(settings.loadSettings).mockReturnValue({302      merged: {303        model: { name: 'settings-model' },304        modelProviders: {305          openai: [306            { id: 'settings-model', envKey: 'SETTINGS_API_KEY' },307            { id: 'cli-model', envKey: 'CLI_API_KEY' },308          ],309        },310      },311    } as unknown as ReturnType<typeof settings.loadSettings>);312 313    const mockConfig = {314      getModelsConfig: vi.fn().mockReturnValue({315        getModel: vi.fn().mockReturnValue('cli-model'),316        getGenerationConfig: vi.fn().mockReturnValue({}),317      }),318    } as unknown as import('@qwen-code/qwen-code-core').Config;319 320    // Don't set CLI_API_KEY - validation should fail321    const result = validateAuthMethod(AuthType.USE_OPENAI, mockConfig);322    expect(result).not.toBeNull();323    expect(result).toContain('CLI_API_KEY');324  });325 326  // Regression test for #3171: validation must accept the API key resolved327  // into generationConfig.apiKey (e.g. from --openai-api-key) instead of328  // requiring an OPENAI_API_KEY env var.329  it('should accept API key resolved into generationConfig from CLI flag', () => {330    delete process.env['OPENAI_API_KEY'];331    vi.mocked(settings.loadSettings).mockReturnValue({332      merged: {},333    } as unknown as ReturnType<typeof settings.loadSettings>);334 335    const mockConfig = {336      getModelsConfig: vi.fn().mockReturnValue({337        getModel: vi.fn().mockReturnValue('gpt-4'),338        getGenerationConfig: vi339          .fn()340          .mockReturnValue({ apiKey: 'cli-provided-key' }),341      }),342    } as unknown as import('@qwen-code/qwen-code-core').Config;343 344    const result = validateAuthMethod(AuthType.USE_OPENAI, mockConfig);345    expect(result).toBeNull();346  });347 348  // Regression test for #3171: when a modelProvider has a custom envKey but349  // the user passes --openai-api-key on the CLI, the resolver picks the CLI350  // value. Validation should match the resolver and accept it instead of351  // demanding the env var.352  it('should accept CLI-resolved key even when modelProvider declares a custom envKey', () => {353    delete process.env['CUSTOM_API_KEY'];354    vi.mocked(settings.loadSettings).mockReturnValue({355      merged: {356        model: { name: 'custom-model' },357        modelProviders: {358          openai: [{ id: 'custom-model', envKey: 'CUSTOM_API_KEY' }],359        },360      },361    } as unknown as ReturnType<typeof settings.loadSettings>);362 363    const mockConfig = {364      getModelsConfig: vi.fn().mockReturnValue({365        getModel: vi.fn().mockReturnValue('custom-model'),366        getGenerationConfig: vi367          .fn()368          .mockReturnValue({ apiKey: 'cli-provided-key' }),369      }),370    } as unknown as import('@qwen-code/qwen-code-core').Config;371 372    const result = validateAuthMethod(AuthType.USE_OPENAI, mockConfig);373    expect(result).toBeNull();374  });375 376  it('should accept runtime-resolved settings key when modelProvider declares a custom envKey', () => {377    delete process.env['CUSTOM_API_KEY'];378    vi.mocked(settings.loadSettings).mockReturnValue({379      merged: {380        security: { auth: { apiKey: 'settings-fallback-key' } },381        model: { name: 'custom-model' },382        modelProviders: {383          openai: [{ id: 'custom-model', envKey: 'CUSTOM_API_KEY' }],384        },385      },386    } as unknown as ReturnType<typeof settings.loadSettings>);387 388    const mockConfig = {389      getModelsConfig: vi.fn().mockReturnValue({390        getModel: vi.fn().mockReturnValue('custom-model'),391        getGenerationConfig: vi392          .fn()393          .mockReturnValue({ apiKey: 'settings-fallback-key' }),394      }),395    } as unknown as import('@qwen-code/qwen-code-core').Config;396 397    const result = validateAuthMethod(AuthType.USE_OPENAI, mockConfig);398    expect(result).toBeNull();399  });400 401  it('should keep no-config validation strict for missing custom envKey', () => {402    delete process.env['CUSTOM_API_KEY'];403    vi.mocked(settings.loadSettings).mockReturnValue({404      merged: {405        security: { auth: { apiKey: 'settings-fallback-key' } },406        model: { name: 'custom-model' },407        modelProviders: {408          openai: [{ id: 'custom-model', envKey: 'CUSTOM_API_KEY' }],409        },410      },411    } as unknown as ReturnType<typeof settings.loadSettings>);412 413    const result = validateAuthMethod(AuthType.USE_OPENAI);414    expect(result).toContain('CUSTOM_API_KEY');415  });416});417 
basant307/AI_Governance_Project · CoolFace