Disallow unnecessary awaits. (#15172)

This commit is contained in:
Christian Gunderman
2025-12-16 21:28:18 -08:00
committed by GitHub
parent 3e9a0a7628
commit 7f2d33458a
38 changed files with 129 additions and 106 deletions

View File

@@ -228,7 +228,9 @@ describe('extensions disable command', () => {
_: [],
$0: '',
};
await (command.handler as unknown as (args: TestArgv) => void)(argv);
await (command.handler as unknown as (args: TestArgv) => Promise<void>)(
argv,
);
expect(mockExtensionManager).toHaveBeenCalledWith(
expect.objectContaining({
workspaceDir: '/test/dir',

View File

@@ -205,7 +205,9 @@ describe('extensions enable command', () => {
_: [],
$0: '',
};
await (command.handler as unknown as (args: TestArgv) => void)(argv);
await (command.handler as unknown as (args: TestArgv) => Promise<void>)(
argv,
);
expect(
mockExtensionManager.prototype.enableExtension,

View File

@@ -180,7 +180,9 @@ describe('extensions link command', () => {
_: [],
$0: '',
};
await (command.handler as unknown as (args: TestArgv) => void)(argv);
await (command.handler as unknown as (args: TestArgv) => Promise<void>)(
argv,
);
expect(
mockExtensionManager.prototype.installOrUpdateExtension,

View File

@@ -49,7 +49,7 @@ const setCommand: CommandModule<object, SetArgs> = {
if (!extension || !extensionManager) {
return;
}
const extensionConfig = extensionManager.loadExtensionConfig(
const extensionConfig = await extensionManager.loadExtensionConfig(
extension.path,
);
if (!extensionConfig) {
@@ -89,7 +89,7 @@ const listCommand: CommandModule<object, ListArgs> = {
if (!extension || !extensionManager) {
return;
}
const extensionConfig = extensionManager.loadExtensionConfig(
const extensionConfig = await extensionManager.loadExtensionConfig(
extension.path,
);
if (

View File

@@ -287,7 +287,9 @@ describe('extensions uninstall command', () => {
[key: string]: unknown;
}
const argv: TestArgv = { names: ['my-extension'], _: [], $0: '' };
await (command.handler as unknown as (args: TestArgv) => void)(argv);
await (command.handler as unknown as (args: TestArgv) => Promise<void>)(
argv,
);
expect(mockUninstallExtension).toHaveBeenCalledWith(
'my-extension',

View File

@@ -41,7 +41,7 @@ async function validateExtension(args: ValidateArgs) {
});
const absoluteInputPath = path.resolve(args.path);
const extensionConfig: ExtensionConfig =
extensionManager.loadExtensionConfig(absoluteInputPath);
await extensionManager.loadExtensionConfig(absoluteInputPath);
const warnings: string[] = [];
const errors: string[] = [];

View File

@@ -4,7 +4,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi, type Mock, type MockInstance } from 'vitest';
import {
describe,
it,
expect,
vi,
beforeEach,
type Mock,
type MockInstance,
} from 'vitest';
import yargs, { type Argv } from 'yargs';
import { addCommand } from './add.js';
import { loadSettings, SettingScope } from '../../config/settings.js';

View File

@@ -220,7 +220,7 @@ export class ExtensionManager extends ExtensionLoader {
}
try {
newExtensionConfig = this.loadExtensionConfig(localSourcePath);
newExtensionConfig = await this.loadExtensionConfig(localSourcePath);
if (isUpdate && installMetadata.autoUpdate) {
const oldSettings = new Set(
@@ -364,7 +364,7 @@ export class ExtensionManager extends ExtensionLoader {
// to get the name and version for logging.
if (!newExtensionConfig && localSourcePath) {
try {
newExtensionConfig = this.loadExtensionConfig(localSourcePath);
newExtensionConfig = await this.loadExtensionConfig(localSourcePath);
} catch {
// Ignore error, this is just for logging.
}
@@ -491,7 +491,7 @@ export class ExtensionManager extends ExtensionLoader {
}
try {
let config = this.loadExtensionConfig(effectiveExtensionPath);
let config = await this.loadExtensionConfig(effectiveExtensionPath);
if (
this.getExtensions().find((extension) => extension.name === config.name)
) {
@@ -571,13 +571,13 @@ export class ExtensionManager extends ExtensionLoader {
return this.maybeStopExtension(extension);
}
loadExtensionConfig(extensionDir: string): ExtensionConfig {
async loadExtensionConfig(extensionDir: string): Promise<ExtensionConfig> {
const configFilePath = path.join(extensionDir, EXTENSIONS_CONFIG_FILENAME);
if (!fs.existsSync(configFilePath)) {
throw new Error(`Configuration file not found at ${configFilePath}`);
}
try {
const configContent = fs.readFileSync(configFilePath, 'utf-8');
const configContent = await fs.promises.readFile(configFilePath, 'utf-8');
const rawConfig = JSON.parse(configContent) as ExtensionConfig;
if (!rawConfig.name || !rawConfig.version) {
throw new Error(

View File

@@ -1424,9 +1424,10 @@ This extension will run the following MCP servers:
],
});
const previousExtensionConfig = extensionManager.loadExtensionConfig(
path.join(userExtensionsDir, 'my-local-extension'),
);
const previousExtensionConfig =
await extensionManager.loadExtensionConfig(
path.join(userExtensionsDir, 'my-local-extension'),
);
mockPromptForSettings.mockResolvedValueOnce('new-setting-value');
// 3. Call installOrUpdateExtension to perform the update.
@@ -1481,9 +1482,10 @@ This extension will run the following MCP servers:
],
});
const previousExtensionConfig = extensionManager.loadExtensionConfig(
path.join(userExtensionsDir, 'my-auto-update-ext'),
);
const previousExtensionConfig =
await extensionManager.loadExtensionConfig(
path.join(userExtensionsDir, 'my-auto-update-ext'),
);
// 3. Attempt to update and assert it fails
await expect(
@@ -1967,7 +1969,7 @@ This extension will run the following MCP servers:
expect(activeExtensions).toHaveLength(0);
await extensionManager.enableExtension('ext1', SettingScope.User);
activeExtensions = await getActiveExtensions();
activeExtensions = getActiveExtensions();
expect(activeExtensions).toHaveLength(1);
expect(activeExtensions[0].name).toBe('ext1');
});
@@ -1984,7 +1986,7 @@ This extension will run the following MCP servers:
expect(activeExtensions).toHaveLength(0);
await extensionManager.enableExtension('ext1', SettingScope.Workspace);
activeExtensions = await getActiveExtensions();
activeExtensions = getActiveExtensions();
expect(activeExtensions).toHaveLength(1);
expect(activeExtensions[0].name).toBe('ext1');
});

View File

@@ -221,9 +221,11 @@ describe('github.ts', () => {
});
it('should return NOT_UPDATABLE for non-git/non-release extensions', async () => {
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue({
version: '1.0.0',
} as unknown as ExtensionConfig);
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue(
Promise.resolve({
version: '1.0.0',
} as unknown as ExtensionConfig),
);
const linkExt = {
installMetadata: { type: 'link' },

View File

@@ -175,7 +175,7 @@ export async function checkForExtensionUpdate(
if (installMetadata?.type === 'local') {
let latestConfig: ExtensionConfig | undefined;
try {
latestConfig = extensionManager.loadExtensionConfig(
latestConfig = await extensionManager.loadExtensionConfig(
installMetadata.source,
);
} catch (e) {

View File

@@ -145,10 +145,12 @@ describe('Extension Update Logic', () => {
});
it('should successfully update extension and set state to UPDATED_NEEDS_RESTART by default', async () => {
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue({
name: 'test-extension',
version: '1.0.0',
});
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue(
Promise.resolve({
name: 'test-extension',
version: '1.0.0',
}),
);
vi.mocked(
mockExtensionManager.installOrUpdateExtension,
).mockResolvedValue({
@@ -183,10 +185,12 @@ describe('Extension Update Logic', () => {
});
it('should set state to UPDATED if enableExtensionReloading is true', async () => {
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue({
name: 'test-extension',
version: '1.0.0',
});
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue(
Promise.resolve({
name: 'test-extension',
version: '1.0.0',
}),
);
vi.mocked(
mockExtensionManager.installOrUpdateExtension,
).mockResolvedValue({
@@ -212,10 +216,12 @@ describe('Extension Update Logic', () => {
});
it('should rollback and set state to ERROR if installation fails', async () => {
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue({
name: 'test-extension',
version: '1.0.0',
});
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue(
Promise.resolve({
name: 'test-extension',
version: '1.0.0',
}),
);
vi.mocked(
mockExtensionManager.installOrUpdateExtension,
).mockRejectedValue(new Error('Install failed'));
@@ -257,10 +263,12 @@ describe('Extension Update Logic', () => {
['ext3', { status: ExtensionUpdateState.UPDATE_AVAILABLE }],
]);
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue({
name: 'ext',
version: '1.0.0',
});
vi.mocked(mockExtensionManager.loadExtensionConfig).mockReturnValue(
Promise.resolve({
name: 'ext',
version: '1.0.0',
}),
);
vi.mocked(
mockExtensionManager.installOrUpdateExtension,
).mockResolvedValue({ ...mockExtension, version: '1.1.0' });

View File

@@ -59,7 +59,7 @@ export async function updateExtension(
const tempDir = await ExtensionStorage.createTmpDir();
try {
const previousExtensionConfig = extensionManager.loadExtensionConfig(
const previousExtensionConfig = await extensionManager.loadExtensionConfig(
extension.path,
);
let updatedExtension: GeminiCLIExtension;

View File

@@ -48,7 +48,7 @@ describe('chatCommand', () => {
beforeEach(() => {
mockGetHistory = vi.fn().mockReturnValue([]);
mockGetChat = vi.fn().mockResolvedValue({
mockGetChat = vi.fn().mockReturnValue({
getHistory: mockGetHistory,
});
mockSaveCheckpoint = vi.fn().mockResolvedValue(undefined);

View File

@@ -121,7 +121,7 @@ const saveCommand: SlashCommand = {
}
}
const chat = await config?.getGeminiClient()?.getChat();
const chat = config?.getGeminiClient()?.getChat();
if (!chat) {
return {
type: 'message',
@@ -332,7 +332,7 @@ const shareCommand: SlashCommand = {
};
}
const chat = await context.services.config?.getGeminiClient()?.getChat();
const chat = context.services.config?.getGeminiClient()?.getChat();
if (!chat) {
return {
type: 'message',

View File

@@ -15,7 +15,7 @@ export const copyCommand: SlashCommand = {
kind: CommandKind.BUILT_IN,
autoExecute: true,
action: async (context, _args): Promise<SlashCommandActionReturn | void> => {
const chat = await context.services.config?.getGeminiClient()?.getChat();
const chat = context.services.config?.getGeminiClient()?.getChat();
const history = chat?.getHistory();
// Get the last message from the AI (model role)

View File

@@ -85,7 +85,7 @@ describe('mcpCommand', () => {
}),
getMcpServers: vi.fn().mockReturnValue({}),
getBlockedMcpServers: vi.fn().mockReturnValue([]),
getPromptRegistry: vi.fn().mockResolvedValue({
getPromptRegistry: vi.fn().mockReturnValue({
getAllPrompts: vi.fn().mockReturnValue([]),
getPromptsByServer: vi.fn().mockReturnValue([]),
}),

View File

@@ -216,7 +216,7 @@ const listAction = async (
const allTools = toolRegistry.getAllTools();
const mcpTools = allTools.filter((tool) => tool instanceof DiscoveredMCPTool);
const promptRegistry = await config.getPromptRegistry();
const promptRegistry = config.getPromptRegistry();
const mcpPrompts = promptRegistry
.getAllPrompts()
.filter(

View File

@@ -85,7 +85,7 @@ export const memoryCommand: SlashCommand = {
);
try {
const config = await context.services.config;
const config = context.services.config;
if (config) {
const { memoryContent, fileCount } =
await refreshServerHierarchicalMemory(config);

View File

@@ -116,7 +116,7 @@ async function restoreAction(
} else if (action.type === 'load_history' && loadHistory) {
loadHistory(action.history);
if (action.clientHistory) {
await config?.getGeminiClient()?.setHistory(action.clientHistory);
config?.getGeminiClient()?.setHistory(action.clientHistory);
}
}
}

View File

@@ -89,7 +89,7 @@ describe('MultiFolderTrustDialog', () => {
const keypressCallback = mockedUseKeypress.mock.calls[0][0];
await act(async () => {
await keypressCallback({
keypressCallback({
name: 'escape',
ctrl: false,
meta: false,
@@ -117,7 +117,7 @@ describe('MultiFolderTrustDialog', () => {
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await act(async () => {
await onSelect(MultiFolderTrustChoice.NO);
onSelect(MultiFolderTrustChoice.NO);
});
expect(mockFinishAddingDirectories).toHaveBeenCalledWith(
@@ -145,7 +145,7 @@ describe('MultiFolderTrustDialog', () => {
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await act(async () => {
await onSelect(MultiFolderTrustChoice.YES);
onSelect(MultiFolderTrustChoice.YES);
});
expect(mockAddDirectory).toHaveBeenCalledWith('/path/to/folder1');
@@ -166,7 +166,7 @@ describe('MultiFolderTrustDialog', () => {
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await act(async () => {
await onSelect(MultiFolderTrustChoice.YES_AND_REMEMBER);
onSelect(MultiFolderTrustChoice.YES_AND_REMEMBER);
});
expect(mockAddDirectory).toHaveBeenCalledWith('/path/to/folder1');
@@ -192,7 +192,7 @@ describe('MultiFolderTrustDialog', () => {
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await act(async () => {
await onSelect(MultiFolderTrustChoice.NO);
onSelect(MultiFolderTrustChoice.NO);
});
expect(lastFrame()).toContain('Applying trust settings...');
@@ -210,7 +210,7 @@ describe('MultiFolderTrustDialog', () => {
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await act(async () => {
await onSelect(MultiFolderTrustChoice.YES);
onSelect(MultiFolderTrustChoice.YES);
});
expect(mockAddItem).toHaveBeenCalledWith(
@@ -243,7 +243,7 @@ describe('MultiFolderTrustDialog', () => {
const { onSelect } = mockedRadioButtonSelect.mock.calls[0][0];
await act(async () => {
await onSelect(MultiFolderTrustChoice.YES);
onSelect(MultiFolderTrustChoice.YES);
});
expect(mockAddDirectory).toHaveBeenCalledWith('/path/to/good');

View File

@@ -55,7 +55,7 @@ export const VimModeProvider = ({
if (newValue) {
setVimMode('NORMAL');
}
await settings.setValue(SettingScope.User, 'general.vimMode', newValue);
settings.setValue(SettingScope.User, 'general.vimMode', newValue);
return newValue;
}, [vimEnabled, settings]);

View File

@@ -1120,7 +1120,7 @@ describe('useSlashCommandProcessor', () => {
// We should not see a change until we fire an event.
await waitFor(() => expect(result.current.slashCommands).toEqual([]));
await act(() => {
act(() => {
appEvents.emit('extensionsStarting');
});
await waitFor(() =>

View File

@@ -140,9 +140,7 @@ describe('useFolderTrust', () => {
});
await act(async () => {
await result.current.handleFolderTrustSelect(
FolderTrustChoice.TRUST_FOLDER,
);
result.current.handleFolderTrustSelect(FolderTrustChoice.TRUST_FOLDER);
});
await waitFor(() => {

View File

@@ -136,7 +136,7 @@ describe('useQuotaAndFallback', () => {
mockGoogleApiError,
1000 * 60 * 5,
); // 5 minutes
await act(() => {
act(() => {
promise = handler('gemini-pro', 'gemini-flash', error);
});
@@ -155,7 +155,7 @@ describe('useQuotaAndFallback', () => {
expect(mockHistoryManager.addItem).not.toHaveBeenCalled();
// Simulate the user choosing to continue with the fallback model
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});
@@ -182,7 +182,7 @@ describe('useQuotaAndFallback', () => {
.calls[0][0] as FallbackModelHandler;
let promise1: Promise<FallbackIntent | null>;
await act(() => {
act(() => {
promise1 = handler(
'gemini-pro',
'gemini-flash',
@@ -206,7 +206,7 @@ describe('useQuotaAndFallback', () => {
expect(result2!).toBe('stop');
expect(result.current.proQuotaRequest).toBe(firstRequest);
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});
@@ -247,7 +247,7 @@ describe('useQuotaAndFallback', () => {
.calls[0][0] as FallbackModelHandler;
let promise: Promise<FallbackIntent | null>;
await act(() => {
act(() => {
promise = handler('model-A', 'model-B', error);
});
@@ -265,7 +265,7 @@ describe('useQuotaAndFallback', () => {
);
// Simulate the user choosing to continue with the fallback model
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});
@@ -303,7 +303,7 @@ describe('useQuotaAndFallback', () => {
let promise: Promise<FallbackIntent | null>;
const error = new ModelNotFoundError('model not found', 404);
await act(() => {
act(() => {
promise = handler('gemini-3-pro-preview', 'gemini-2.5-pro', error);
});
@@ -322,7 +322,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
);
// Simulate the user choosing to switch
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});
@@ -364,7 +364,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
const handler = setFallbackHandlerSpy.mock
.calls[0][0] as FallbackModelHandler;
let promise: Promise<FallbackIntent | null>;
await act(() => {
act(() => {
promise = handler(
'gemini-pro',
'gemini-flash',
@@ -372,7 +372,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
);
});
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_later');
});
@@ -395,7 +395,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
.calls[0][0] as FallbackModelHandler;
let promise: Promise<FallbackIntent | null>;
await act(() => {
act(() => {
promise = handler(
'gemini-pro',
'gemini-flash',
@@ -403,7 +403,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
);
});
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});
@@ -431,7 +431,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
const handler = setFallbackHandlerSpy.mock
.calls[0][0] as FallbackModelHandler;
let promise: Promise<FallbackIntent | null>;
await act(() => {
act(() => {
promise = handler(
PREVIEW_GEMINI_MODEL,
DEFAULT_GEMINI_MODEL,
@@ -439,7 +439,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
);
});
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});
@@ -466,7 +466,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
const handler = setFallbackHandlerSpy.mock
.calls[0][0] as FallbackModelHandler;
let promise: Promise<FallbackIntent | null>;
await act(() => {
act(() => {
promise = handler(
PREVIEW_GEMINI_MODEL,
DEFAULT_GEMINI_FLASH_MODEL,
@@ -474,7 +474,7 @@ To disable Gemini 3, disable "Preview features" in /settings.`,
);
});
await act(() => {
act(() => {
result.current.handleProQuotaChoice('retry_always');
});