| |
| |
| |
| |
| |
|
|
| import { describe, it, expect, beforeEach, vi } from 'vitest'; |
| import { Config } from './config.js'; |
| import { DEFAULT_GEMINI_MODEL, DEFAULT_GEMINI_FLASH_MODEL } from './models.js'; |
| import { logFlashFallback } from '../telemetry/loggers.js'; |
| import { FlashFallbackEvent } from '../telemetry/types.js'; |
|
|
| import fs from 'node:fs'; |
|
|
| vi.mock('node:fs'); |
| vi.mock('../telemetry/loggers.js', () => ({ |
| logFlashFallback: vi.fn(), |
| logRipgrepFallback: vi.fn(), |
| })); |
|
|
| describe('Flash Model Fallback Configuration', () => { |
| let config: Config; |
|
|
| beforeEach(() => { |
| vi.mocked(fs.existsSync).mockReturnValue(true); |
| vi.mocked(fs.statSync).mockReturnValue({ |
| isDirectory: () => true, |
| } as fs.Stats); |
| config = new Config({ |
| sessionId: 'test-session', |
| targetDir: '/test', |
| debugMode: false, |
| cwd: '/test', |
| model: DEFAULT_GEMINI_MODEL, |
| }); |
|
|
| |
| ( |
| config as unknown as { contentGeneratorConfig: unknown } |
| ).contentGeneratorConfig = { |
| model: DEFAULT_GEMINI_MODEL, |
| authType: 'oauth-personal', |
| }; |
| }); |
|
|
| describe('getModel', () => { |
| it('should return contentGeneratorConfig model if available', () => { |
| |
| config.setModel(DEFAULT_GEMINI_FLASH_MODEL); |
| expect(config.getModel()).toBe(DEFAULT_GEMINI_FLASH_MODEL); |
| }); |
|
|
| it('should fall back to initial model if contentGeneratorConfig is not available', () => { |
| |
| const newConfig = new Config({ |
| sessionId: 'test-session-2', |
| targetDir: '/test', |
| debugMode: false, |
| cwd: '/test', |
| model: 'custom-model', |
| }); |
|
|
| expect(newConfig.getModel()).toBe('custom-model'); |
| }); |
| }); |
|
|
| describe('activateFallbackMode', () => { |
| it('should set model to fallback and log event', () => { |
| config.activateFallbackMode(DEFAULT_GEMINI_FLASH_MODEL); |
| expect(config.getModel()).toBe(DEFAULT_GEMINI_FLASH_MODEL); |
| expect(logFlashFallback).toHaveBeenCalledWith( |
| config, |
| expect.any(FlashFallbackEvent), |
| ); |
| }); |
|
|
| it('should set fallback override when failedModel is provided and register runtime override', () => { |
| config.activateFallbackMode( |
| DEFAULT_GEMINI_FLASH_MODEL, |
| DEFAULT_GEMINI_MODEL, |
| ); |
| expect(config.getModel()).toBe(DEFAULT_GEMINI_FLASH_MODEL); |
| expect(config.getFallbackOverride(DEFAULT_GEMINI_MODEL)).toBe( |
| DEFAULT_GEMINI_FLASH_MODEL, |
| ); |
|
|
| |
| expect( |
| config |
| .getModelConfigService() |
| .getResolvedConfig({ model: DEFAULT_GEMINI_MODEL }).model, |
| ).toBe(DEFAULT_GEMINI_FLASH_MODEL); |
| }); |
|
|
| it('should flatten override chains when a model that was previously a target fails', () => { |
| |
| config.activateFallbackMode('model-B', 'model-A'); |
| expect(config.getFallbackOverride('model-A')).toBe('model-B'); |
| expect( |
| config.getModelConfigService().getResolvedConfig({ model: 'model-A' }) |
| .model, |
| ).toBe('model-B'); |
|
|
| |
| |
| config.activateFallbackMode('model-C', 'model-B'); |
|
|
| expect(config.getFallbackOverride('model-A')).toBe('model-C'); |
| expect(config.getFallbackOverride('model-B')).toBe('model-C'); |
|
|
| expect( |
| config.getModelConfigService().getResolvedConfig({ model: 'model-A' }) |
| .model, |
| ).toBe('model-C'); |
| expect( |
| config.getModelConfigService().getResolvedConfig({ model: 'model-B' }) |
| .model, |
| ).toBe('model-C'); |
| }); |
|
|
| it('should not reset availability service if model has not changed', () => { |
| const resetSpy = vi.spyOn(config.getModelAvailabilityService(), 'reset'); |
| const currentModel = config.getActiveModel(); |
|
|
| config.activateFallbackMode(currentModel); |
|
|
| expect(resetSpy).not.toHaveBeenCalled(); |
| }); |
| }); |
| }); |
|
|