2025-09-08 16:19:52 -04:00
|
|
|
/**
|
|
|
|
|
* @license
|
|
|
|
|
* Copyright 2025 Google LLC
|
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
describe,
|
|
|
|
|
it,
|
|
|
|
|
expect,
|
|
|
|
|
vi,
|
|
|
|
|
beforeEach,
|
|
|
|
|
type Mock,
|
|
|
|
|
type MockInstance,
|
|
|
|
|
afterEach,
|
|
|
|
|
} from 'vitest';
|
|
|
|
|
import { handleFallback } from './handler.js';
|
|
|
|
|
import type { Config } from '../config/config.js';
|
2025-11-26 12:36:42 -08:00
|
|
|
import type { ModelAvailabilityService } from '../availability/modelAvailabilityService.js';
|
2025-12-08 06:44:34 -08:00
|
|
|
import { createAvailabilityServiceMock } from '../availability/testUtils.js';
|
2025-09-08 16:19:52 -04:00
|
|
|
import { AuthType } from '../core/contentGenerator.js';
|
|
|
|
|
import {
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
DEFAULT_GEMINI_MODEL,
|
2025-12-17 09:43:21 -08:00
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
PREVIEW_GEMINI_FLASH_MODEL,
|
2025-11-18 12:01:16 -05:00
|
|
|
PREVIEW_GEMINI_MODEL,
|
2025-12-17 09:43:21 -08:00
|
|
|
PREVIEW_GEMINI_MODEL_AUTO,
|
2025-09-08 16:19:52 -04:00
|
|
|
} from '../config/models.js';
|
|
|
|
|
import type { FallbackModelHandler } from './types.js';
|
2025-11-26 12:36:42 -08:00
|
|
|
import { openBrowserSecurely } from '../utils/secure-browser-launcher.js';
|
|
|
|
|
import { debugLogger } from '../utils/debugLogger.js';
|
|
|
|
|
import * as policyHelpers from '../availability/policyHelpers.js';
|
|
|
|
|
import { createDefaultPolicy } from '../availability/policyCatalog.js';
|
2025-11-25 16:17:22 -05:00
|
|
|
import {
|
|
|
|
|
RetryableQuotaError,
|
|
|
|
|
TerminalQuotaError,
|
|
|
|
|
} from '../utils/googleQuotaErrors.js';
|
2025-09-08 16:19:52 -04:00
|
|
|
|
|
|
|
|
// Mock the telemetry logger and event class
|
|
|
|
|
vi.mock('../telemetry/index.js', () => ({
|
|
|
|
|
logFlashFallback: vi.fn(),
|
|
|
|
|
FlashFallbackEvent: class {},
|
|
|
|
|
}));
|
2025-11-26 12:36:42 -08:00
|
|
|
vi.mock('../utils/secure-browser-launcher.js', () => ({
|
|
|
|
|
openBrowserSecurely: vi.fn(),
|
|
|
|
|
}));
|
2025-09-08 16:19:52 -04:00
|
|
|
|
2025-12-08 06:44:34 -08:00
|
|
|
// Mock debugLogger to prevent console pollution and allow spying
|
|
|
|
|
vi.mock('../utils/debugLogger.js', () => ({
|
|
|
|
|
debugLogger: {
|
|
|
|
|
warn: vi.fn(),
|
|
|
|
|
error: vi.fn(),
|
|
|
|
|
log: vi.fn(),
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
|
2025-09-08 16:19:52 -04:00
|
|
|
const MOCK_PRO_MODEL = DEFAULT_GEMINI_MODEL;
|
|
|
|
|
const FALLBACK_MODEL = DEFAULT_GEMINI_FLASH_MODEL;
|
|
|
|
|
const AUTH_OAUTH = AuthType.LOGIN_WITH_GOOGLE;
|
|
|
|
|
const AUTH_API_KEY = AuthType.USE_GEMINI;
|
|
|
|
|
|
|
|
|
|
const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
|
|
|
|
({
|
|
|
|
|
fallbackHandler: undefined,
|
2025-11-18 12:01:16 -05:00
|
|
|
getFallbackModelHandler: vi.fn(),
|
2025-12-08 06:44:34 -08:00
|
|
|
setActiveModel: vi.fn(),
|
2026-01-20 01:25:15 -05:00
|
|
|
setModel: vi.fn(),
|
|
|
|
|
activateFallbackMode: vi.fn(),
|
2025-11-26 12:36:42 -08:00
|
|
|
getModelAvailabilityService: vi.fn(() =>
|
2025-12-08 06:44:34 -08:00
|
|
|
createAvailabilityServiceMock({
|
|
|
|
|
selectedModel: FALLBACK_MODEL,
|
|
|
|
|
skipped: [],
|
|
|
|
|
}),
|
2025-11-26 12:36:42 -08:00
|
|
|
),
|
2025-12-17 09:43:21 -08:00
|
|
|
getActiveModel: vi.fn(() => MOCK_PRO_MODEL),
|
2025-11-26 12:36:42 -08:00
|
|
|
getModel: vi.fn(() => MOCK_PRO_MODEL),
|
|
|
|
|
getUserTier: vi.fn(() => undefined),
|
2025-11-11 02:03:32 -08:00
|
|
|
isInteractive: vi.fn(() => false),
|
2025-09-08 16:19:52 -04:00
|
|
|
...overrides,
|
|
|
|
|
}) as unknown as Config;
|
|
|
|
|
|
|
|
|
|
describe('handleFallback', () => {
|
|
|
|
|
let mockConfig: Config;
|
|
|
|
|
let mockHandler: Mock<FallbackModelHandler>;
|
|
|
|
|
let consoleErrorSpy: MockInstance;
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
|
|
|
|
mockHandler = vi.fn();
|
|
|
|
|
// Default setup: OAuth user, Pro model failed, handler injected
|
|
|
|
|
mockConfig = createMockConfig({
|
|
|
|
|
fallbackModelHandler: mockHandler,
|
|
|
|
|
});
|
2025-12-08 06:44:34 -08:00
|
|
|
// Explicitly set the property to ensure it's present for legacy checks
|
|
|
|
|
mockConfig.fallbackModelHandler = mockHandler;
|
|
|
|
|
|
|
|
|
|
// We mocked debugLogger, so we don't need to spy on console.error for handler failures
|
|
|
|
|
// But tests might check console.error usage in legacy code if any?
|
|
|
|
|
// The handler uses console.error in legacyHandleFallback.
|
2025-09-08 16:19:52 -04:00
|
|
|
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
consoleErrorSpy.mockRestore();
|
|
|
|
|
});
|
|
|
|
|
|
2025-11-26 12:36:42 -08:00
|
|
|
describe('policy-driven flow', () => {
|
|
|
|
|
let policyConfig: Config;
|
|
|
|
|
let availability: ModelAvailabilityService;
|
|
|
|
|
let policyHandler: Mock<FallbackModelHandler>;
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
vi.clearAllMocks();
|
2025-12-08 06:44:34 -08:00
|
|
|
availability = createAvailabilityServiceMock({
|
2025-12-01 12:41:06 -08:00
|
|
|
selectedModel: DEFAULT_GEMINI_FLASH_MODEL,
|
2025-11-26 12:36:42 -08:00
|
|
|
skipped: [],
|
|
|
|
|
});
|
|
|
|
|
policyHandler = vi.fn().mockResolvedValue('retry_once');
|
|
|
|
|
policyConfig = createMockConfig();
|
2025-12-17 09:43:21 -08:00
|
|
|
|
|
|
|
|
// Ensure we test the availability path
|
|
|
|
|
vi.mocked(policyConfig.getModelAvailabilityService).mockReturnValue(
|
2025-11-26 12:36:42 -08:00
|
|
|
availability,
|
|
|
|
|
);
|
2025-12-17 09:43:21 -08:00
|
|
|
vi.mocked(policyConfig.getFallbackModelHandler).mockReturnValue(
|
2025-11-26 12:36:42 -08:00
|
|
|
policyHandler,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
it('should return null immediately if authType is not OAuth', async () => {
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_API_KEY,
|
|
|
|
|
);
|
|
|
|
|
expect(result).toBeNull();
|
|
|
|
|
expect(policyHandler).not.toHaveBeenCalled();
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-01 12:41:06 -08:00
|
|
|
it('uses availability selection with correct candidates when enabled', async () => {
|
2025-12-17 09:43:21 -08:00
|
|
|
// Direct mock manipulation since it's already a vi.fn()
|
|
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
2025-12-01 12:41:06 -08:00
|
|
|
|
|
|
|
|
await handleFallback(policyConfig, DEFAULT_GEMINI_MODEL, AUTH_OAUTH);
|
|
|
|
|
|
|
|
|
|
expect(availability.selectFirstAvailable).toHaveBeenCalledWith([
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
]);
|
2025-11-26 12:36:42 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('falls back to last resort when availability returns null', async () => {
|
2025-12-17 09:43:21 -08:00
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
2025-11-26 12:36:42 -08:00
|
|
|
availability.selectFirstAvailable = vi
|
|
|
|
|
.fn()
|
|
|
|
|
.mockReturnValue({ selectedModel: null, skipped: [] });
|
|
|
|
|
policyHandler.mockResolvedValue('retry_once');
|
|
|
|
|
|
|
|
|
|
await handleFallback(policyConfig, MOCK_PRO_MODEL, AUTH_OAUTH);
|
|
|
|
|
|
|
|
|
|
expect(policyHandler).toHaveBeenCalledWith(
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
undefined,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('executes silent policy action without invoking UI handler', async () => {
|
|
|
|
|
const proPolicy = createDefaultPolicy(MOCK_PRO_MODEL);
|
|
|
|
|
const flashPolicy = createDefaultPolicy(DEFAULT_GEMINI_FLASH_MODEL);
|
|
|
|
|
flashPolicy.actions = {
|
|
|
|
|
...flashPolicy.actions,
|
|
|
|
|
terminal: 'silent',
|
|
|
|
|
unknown: 'silent',
|
|
|
|
|
};
|
|
|
|
|
flashPolicy.isLastResort = true;
|
|
|
|
|
|
|
|
|
|
const silentChain = [proPolicy, flashPolicy];
|
|
|
|
|
const chainSpy = vi
|
|
|
|
|
.spyOn(policyHelpers, 'resolvePolicyChain')
|
|
|
|
|
.mockReturnValue(silentChain);
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
availability.selectFirstAvailable = vi.fn().mockReturnValue({
|
|
|
|
|
selectedModel: DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
skipped: [],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBe(true);
|
|
|
|
|
expect(policyConfig.getFallbackModelHandler).not.toHaveBeenCalled();
|
2026-01-20 01:25:15 -05:00
|
|
|
expect(policyConfig.activateFallbackMode).toHaveBeenCalledWith(
|
2025-12-08 06:44:34 -08:00
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
);
|
2025-11-26 12:36:42 -08:00
|
|
|
} finally {
|
|
|
|
|
chainSpy.mockRestore();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
it('does not wrap around to upgrade candidates if the current model was selected at the end (e.g. by router)', async () => {
|
2025-12-01 12:41:06 -08:00
|
|
|
// Last-resort failure (Flash) in [Preview, Pro, Flash] checks Preview then Pro (all upstream).
|
2025-12-17 09:43:21 -08:00
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
2025-12-01 12:41:06 -08:00
|
|
|
|
|
|
|
|
availability.selectFirstAvailable = vi.fn().mockReturnValue({
|
|
|
|
|
selectedModel: MOCK_PRO_MODEL,
|
|
|
|
|
skipped: [],
|
|
|
|
|
});
|
|
|
|
|
policyHandler.mockResolvedValue('retry_once');
|
|
|
|
|
|
|
|
|
|
await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
expect(availability.selectFirstAvailable).not.toHaveBeenCalled();
|
2025-12-01 12:41:06 -08:00
|
|
|
expect(policyHandler).toHaveBeenCalledWith(
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
2025-12-17 09:43:21 -08:00
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
2025-12-01 12:41:06 -08:00
|
|
|
undefined,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
it('successfully follows expected availability response for Preview Chain', async () => {
|
|
|
|
|
availability.selectFirstAvailable = vi.fn().mockReturnValue({
|
|
|
|
|
selectedModel: PREVIEW_GEMINI_FLASH_MODEL,
|
|
|
|
|
skipped: [],
|
|
|
|
|
});
|
|
|
|
|
policyHandler.mockResolvedValue('retry_once');
|
|
|
|
|
vi.mocked(policyConfig.getActiveModel).mockReturnValue(
|
|
|
|
|
PREVIEW_GEMINI_MODEL,
|
|
|
|
|
);
|
|
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
PREVIEW_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
PREVIEW_GEMINI_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBe(true);
|
|
|
|
|
expect(availability.selectFirstAvailable).toHaveBeenCalledWith([
|
|
|
|
|
PREVIEW_GEMINI_FLASH_MODEL,
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should launch upgrade flow and avoid fallback mode when handler returns "upgrade"', async () => {
|
|
|
|
|
policyHandler.mockResolvedValue('upgrade');
|
|
|
|
|
vi.mocked(openBrowserSecurely).mockResolvedValue(undefined);
|
|
|
|
|
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBe(false);
|
|
|
|
|
expect(openBrowserSecurely).toHaveBeenCalledWith(
|
|
|
|
|
'https://goo.gle/set-up-gemini-code-assist',
|
|
|
|
|
);
|
2026-01-20 01:25:15 -05:00
|
|
|
expect(policyConfig.activateFallbackMode).not.toHaveBeenCalled();
|
2025-12-17 09:43:21 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should catch errors from the handler, log an error, and return null', async () => {
|
|
|
|
|
const handlerError = new Error('UI interaction failed');
|
|
|
|
|
policyHandler.mockRejectedValue(handlerError);
|
|
|
|
|
|
2025-11-26 12:36:42 -08:00
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBeNull();
|
2025-12-17 09:43:21 -08:00
|
|
|
expect(debugLogger.error).toHaveBeenCalledWith(
|
2025-11-26 12:36:42 -08:00
|
|
|
'Fallback handler failed:',
|
2025-12-17 09:43:21 -08:00
|
|
|
handlerError,
|
2025-11-26 12:36:42 -08:00
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
it('should pass TerminalQuotaError (429) correctly to the handler', async () => {
|
|
|
|
|
const mockGoogleApiError = {
|
|
|
|
|
code: 429,
|
|
|
|
|
message: 'mock error',
|
|
|
|
|
details: [],
|
|
|
|
|
};
|
|
|
|
|
const terminalError = new TerminalQuotaError(
|
|
|
|
|
'Quota error',
|
|
|
|
|
mockGoogleApiError,
|
|
|
|
|
5,
|
|
|
|
|
);
|
|
|
|
|
policyHandler.mockResolvedValue('retry_always');
|
|
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
2025-11-26 12:36:42 -08:00
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
await handleFallback(
|
2025-11-26 12:36:42 -08:00
|
|
|
policyConfig,
|
2025-12-17 09:43:21 -08:00
|
|
|
MOCK_PRO_MODEL,
|
2025-11-26 12:36:42 -08:00
|
|
|
AUTH_OAUTH,
|
2025-12-17 09:43:21 -08:00
|
|
|
terminalError,
|
2025-11-26 12:36:42 -08:00
|
|
|
);
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
expect(policyHandler).toHaveBeenCalledWith(
|
|
|
|
|
MOCK_PRO_MODEL,
|
2025-11-26 12:36:42 -08:00
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
2025-12-17 09:43:21 -08:00
|
|
|
terminalError,
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should pass RetryableQuotaError correctly to the handler', async () => {
|
|
|
|
|
const mockGoogleApiError = {
|
|
|
|
|
code: 503,
|
|
|
|
|
message: 'mock error',
|
|
|
|
|
details: [],
|
|
|
|
|
};
|
|
|
|
|
const retryableError = new RetryableQuotaError(
|
|
|
|
|
'Service unavailable',
|
|
|
|
|
mockGoogleApiError,
|
|
|
|
|
1000,
|
|
|
|
|
);
|
|
|
|
|
policyHandler.mockResolvedValue('retry_once');
|
|
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
retryableError,
|
|
|
|
|
);
|
|
|
|
|
|
2025-11-26 12:36:42 -08:00
|
|
|
expect(policyHandler).toHaveBeenCalledWith(
|
2025-12-17 09:43:21 -08:00
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
retryableError,
|
2025-11-26 12:36:42 -08:00
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
it('Call the handler with fallback model same as the failed model when the failed model is the last-resort policy', async () => {
|
2025-12-01 12:41:06 -08:00
|
|
|
// Ensure short-circuit when wrapping to an unavailable upstream model.
|
|
|
|
|
availability.selectFirstAvailable = vi
|
|
|
|
|
.fn()
|
|
|
|
|
.mockReturnValue({ selectedModel: null, skipped: [] });
|
2025-12-17 09:43:21 -08:00
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
2025-12-01 12:41:06 -08:00
|
|
|
|
2025-11-26 12:36:42 -08:00
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-17 09:43:21 -08:00
|
|
|
policyHandler.mockResolvedValue('retry_once');
|
|
|
|
|
|
|
|
|
|
expect(result).not.toBeNull();
|
|
|
|
|
expect(policyHandler).toHaveBeenCalledWith(
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
DEFAULT_GEMINI_FLASH_MODEL,
|
|
|
|
|
undefined,
|
|
|
|
|
);
|
2025-11-26 12:36:42 -08:00
|
|
|
});
|
2025-12-08 06:44:34 -08:00
|
|
|
|
2026-01-20 01:25:15 -05:00
|
|
|
it('calls activateFallbackMode when handler returns "retry_always"', async () => {
|
2025-12-08 06:44:34 -08:00
|
|
|
policyHandler.mockResolvedValue('retry_always');
|
2025-12-17 09:43:21 -08:00
|
|
|
vi.mocked(policyConfig.getModel).mockReturnValue(
|
|
|
|
|
DEFAULT_GEMINI_MODEL_AUTO,
|
|
|
|
|
);
|
2025-12-08 06:44:34 -08:00
|
|
|
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBe(true);
|
2026-01-20 01:25:15 -05:00
|
|
|
expect(policyConfig.activateFallbackMode).toHaveBeenCalledWith(
|
|
|
|
|
FALLBACK_MODEL,
|
|
|
|
|
);
|
2025-12-08 06:44:34 -08:00
|
|
|
// TODO: add logging expect statement
|
|
|
|
|
});
|
|
|
|
|
|
2026-01-20 01:25:15 -05:00
|
|
|
it('does NOT call activateFallbackMode when handler returns "stop"', async () => {
|
2025-12-08 06:44:34 -08:00
|
|
|
policyHandler.mockResolvedValue('stop');
|
|
|
|
|
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBe(false);
|
2026-01-20 01:25:15 -05:00
|
|
|
expect(policyConfig.activateFallbackMode).not.toHaveBeenCalled();
|
2025-12-08 06:44:34 -08:00
|
|
|
// TODO: add logging expect statement
|
|
|
|
|
});
|
2025-12-17 09:43:21 -08:00
|
|
|
|
2026-01-20 01:25:15 -05:00
|
|
|
it('does NOT call activateFallbackMode when handler returns "retry_once"', async () => {
|
2025-12-17 09:43:21 -08:00
|
|
|
policyHandler.mockResolvedValue('retry_once');
|
|
|
|
|
|
|
|
|
|
const result = await handleFallback(
|
|
|
|
|
policyConfig,
|
|
|
|
|
MOCK_PRO_MODEL,
|
|
|
|
|
AUTH_OAUTH,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(result).toBe(true);
|
2026-01-20 01:25:15 -05:00
|
|
|
expect(policyConfig.activateFallbackMode).not.toHaveBeenCalled();
|
2025-12-17 09:43:21 -08:00
|
|
|
});
|
2025-11-26 12:36:42 -08:00
|
|
|
});
|
2025-09-08 16:19:52 -04:00
|
|
|
});
|