Create ExtensionManager class which manages all high level extension tasks (#11667)

This commit is contained in:
Jacob MacDonald
2025-10-23 11:39:36 -07:00
committed by GitHub
parent 3a501196f0
commit c4c0c0d182
31 changed files with 1450 additions and 1568 deletions
@@ -4,21 +4,22 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { vi } from 'vitest';
import { vi, type MockedFunction } from 'vitest';
import * as fs from 'node:fs';
import * as os from 'node:os';
import * as path from 'node:path';
import {
EXTENSIONS_CONFIG_FILENAME,
INSTALL_METADATA_FILENAME,
loadExtension,
} from '../extension.js';
import { checkForAllExtensionUpdates, updateExtension } from './update.js';
import { GEMINI_DIR } from '@google/gemini-cli-core';
import { isWorkspaceTrusted } from '../trustedFolders.js';
import { ExtensionUpdateState } from '../../ui/state/extensions.js';
import { createExtension } from '../../test-utils/createExtension.js';
import { ExtensionEnablementManager } from './extensionEnablement.js';
import {
EXTENSIONS_CONFIG_FILENAME,
INSTALL_METADATA_FILENAME,
} from './variables.js';
import { ExtensionManager } from '../extension-manager.js';
import { loadSettings } from '../settings.js';
import type { ExtensionSetting } from './extensionSettings.js';
const mockGit = {
clone: vi.fn(),
@@ -74,6 +75,11 @@ describe('update tests', () => {
let tempHomeDir: string;
let tempWorkspaceDir: string;
let userExtensionsDir: string;
let extensionManager: ExtensionManager;
let mockRequestConsent: MockedFunction<(consent: string) => Promise<boolean>>;
let mockPromptForSettings: MockedFunction<
(setting: ExtensionSetting) => Promise<string>
>;
beforeEach(() => {
tempHomeDir = fs.mkdtempSync(
@@ -93,6 +99,16 @@ describe('update tests', () => {
});
vi.spyOn(process, 'cwd').mockReturnValue(tempWorkspaceDir);
Object.values(mockGit).forEach((fn) => fn.mockReset());
mockRequestConsent = vi.fn();
mockRequestConsent.mockResolvedValue(true);
mockPromptForSettings = vi.fn();
mockPromptForSettings.mockResolvedValue('');
extensionManager = new ExtensionManager({
workspaceDir: tempWorkspaceDir,
requestConsent: mockRequestConsent,
requestSetting: mockPromptForSettings,
loadedSettings: loadSettings(tempWorkspaceDir),
});
});
afterEach(() => {
@@ -127,17 +143,10 @@ describe('update tests', () => {
);
});
mockGit.getRemotes.mockResolvedValue([{ name: 'origin' }]);
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir: targetExtDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(targetExtDir)!;
const updateInfo = await updateExtension(
extension,
extensionEnablementManager,
tempHomeDir,
async (_) => true,
extensionManager,
ExtensionUpdateState.UPDATE_AVAILABLE,
() => {},
);
@@ -181,17 +190,10 @@ describe('update tests', () => {
mockGit.getRemotes.mockResolvedValue([{ name: 'origin' }]);
const dispatch = vi.fn();
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(extensionDir)!;
await updateExtension(
extension,
extensionEnablementManager,
tempHomeDir,
async (_) => true,
extensionManager,
ExtensionUpdateState.UPDATE_AVAILABLE,
dispatch,
);
@@ -228,18 +230,11 @@ describe('update tests', () => {
mockGit.getRemotes.mockResolvedValue([{ name: 'origin' }]);
const dispatch = vi.fn();
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(extensionDir)!;
await expect(
updateExtension(
extension,
extensionEnablementManager,
tempHomeDir,
async (_) => true,
extensionManager,
ExtensionUpdateState.UPDATE_AVAILABLE,
dispatch,
),
@@ -273,12 +268,7 @@ describe('update tests', () => {
type: 'git',
},
});
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(extensionDir)!;
mockGit.getRemotes.mockResolvedValue([
{ name: 'origin', refs: { fetch: 'https://some.git/repo' } },
@@ -289,9 +279,8 @@ describe('update tests', () => {
const dispatch = vi.fn();
await checkForAllExtensionUpdates(
[extension],
extensionEnablementManager,
extensionManager,
dispatch,
tempWorkspaceDir,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
@@ -312,12 +301,7 @@ describe('update tests', () => {
type: 'git',
},
});
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(extensionDir)!;
mockGit.getRemotes.mockResolvedValue([
{ name: 'origin', refs: { fetch: 'https://some.git/repo' } },
@@ -328,9 +312,8 @@ describe('update tests', () => {
const dispatch = vi.fn();
await checkForAllExtensionUpdates(
[extension],
extensionEnablementManager,
extensionManager,
dispatch,
tempWorkspaceDir,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
@@ -355,18 +338,12 @@ describe('update tests', () => {
version: '1.0.0',
installMetadata: { source: sourceExtensionDir, type: 'local' },
});
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir: installedExtensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(installedExtensionDir)!;
const dispatch = vi.fn();
await checkForAllExtensionUpdates(
[extension],
extensionEnablementManager,
extensionManager,
dispatch,
tempWorkspaceDir,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
@@ -391,18 +368,12 @@ describe('update tests', () => {
version: '1.0.0',
installMetadata: { source: sourceExtensionDir, type: 'local' },
});
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir: installedExtensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(installedExtensionDir)!;
const dispatch = vi.fn();
await checkForAllExtensionUpdates(
[extension],
extensionEnablementManager,
extensionManager,
dispatch,
tempWorkspaceDir,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',
@@ -423,21 +394,15 @@ describe('update tests', () => {
type: 'git',
},
});
const extensionEnablementManager = new ExtensionEnablementManager();
const extension = loadExtension({
extensionDir,
workspaceDir: tempWorkspaceDir,
extensionEnablementManager,
})!;
const extension = extensionManager.loadExtension(extensionDir)!;
mockGit.getRemotes.mockRejectedValue(new Error('Git error'));
const dispatch = vi.fn();
await checkForAllExtensionUpdates(
[extension],
extensionEnablementManager,
extensionManager,
dispatch,
tempWorkspaceDir,
);
expect(dispatch).toHaveBeenCalledWith({
type: 'SET_STATE',