Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions packages/core/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4291,6 +4291,53 @@ describe('Model Persistence Bug Fix (#19864)', () => {
expect(config.getHasAccessToPreviewModel()).toBe(true);
});

it('should warn when a requested preview model is silently substituted due to lack of entitlement', async () => {
const mockContentConfig = {
authType: AuthType.LOGIN_WITH_GOOGLE,
} as Partial<ContentGeneratorConfig> as ContentGeneratorConfig;

const mockContentGenerator = {
generateContent: vi.fn(),
} as Partial<ContentGenerator> as ContentGenerator;

vi.mocked(createContentGeneratorConfig).mockResolvedValue(
mockContentConfig,
);
vi.mocked(createContentGenerator).mockResolvedValue(mockContentGenerator);
vi.mocked(getCodeAssistServer).mockReturnValue({
projectId: 'test-project',
retrieveUserQuota: vi.fn().mockResolvedValue({
buckets: [
{
modelId: 'gemini-2.5-pro',
remainingAmount: '10',
remainingFraction: 0.1,
},
],
}),
} as Partial<CodeAssistServer> as CodeAssistServer);
vi.mocked(getExperiments).mockResolvedValue({
experimentIds: [],
flags: {},
});

const config = new Config(baseParams);
const warnSpy = vi.spyOn(debugLogger, 'warn').mockImplementation(() => {});

expect(config.getModel()).toBe(PREVIEW_GEMINI_3_1_MODEL);

await config.refreshAuth(AuthType.LOGIN_WITH_GOOGLE);

// The account lacks preview entitlement, so the model is substituted.
expect(config.getHasAccessToPreviewModel()).toBe(false);
expect(config.getModel()).toBe(DEFAULT_GEMINI_MODEL_AUTO);
// The substitution must not happen silently.
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(PREVIEW_GEMINI_3_1_MODEL),
);
warnSpy.mockRestore();
});
Comment on lines +4335 to +4339

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The spy on debugLogger.warn is not restored at the end of the test. Since debugLogger is a shared module-level singleton, not restoring the spy can leak into other tests in the suite, potentially swallowing warnings or causing unexpected side effects. Please call warnSpy.mockRestore() at the end of the test to clean up the spy, matching the pattern used elsewhere in this file.

Suggested change
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(PREVIEW_GEMINI_3_1_MODEL),
);
});
expect(warnSpy).toHaveBeenCalledWith(
expect.stringContaining(PREVIEW_GEMINI_3_1_MODEL),
);
warnSpy.mockRestore();
});


it('should persist model when user selects it with persistMode=true', () => {
const onModelChange = vi.fn();
const config = new Config({
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1658,7 +1658,13 @@ export class Config implements McpContext, AgentLoopContext {
isPreviewModel(this.model, this) &&
this.hasAccessToPreviewModel === false
) {
const requestedModel = this.model;
this.setModel(DEFAULT_GEMINI_MODEL_AUTO);
debugLogger.warn(
`[Config] Requested model "${requestedModel}" is not available to ` +
`the current account and will be substituted with an automatically ` +
`selected model instead.`,
);
}

const adminControlsEnabled =
Expand Down