Path: blob/main/src/vs/platform/extensionManagement/test/node/extensionDownloader.test.ts
5245 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { VSBuffer } from '../../../../base/common/buffer.js';7import { platform } from '../../../../base/common/platform.js';8import { arch } from '../../../../base/common/process.js';9import { joinPath } from '../../../../base/common/resources.js';10import { URI } from '../../../../base/common/uri.js';11import { generateUuid } from '../../../../base/common/uuid.js';12import { mock } from '../../../../base/test/common/mock.js';13import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';14import { INativeEnvironmentService } from '../../../environment/common/environment.js';15import { ExtensionSignatureVerificationCode, getTargetPlatform, IExtensionGalleryService, IGalleryExtension, IGalleryExtensionAssets, InstallOperation } from '../../common/extensionManagement.js';16import { getGalleryExtensionId } from '../../common/extensionManagementUtil.js';17import { ExtensionsDownloader } from '../../node/extensionDownloader.js';18import { IExtensionSignatureVerificationResult, IExtensionSignatureVerificationService } from '../../node/extensionSignatureVerificationService.js';19import { IFileService } from '../../../files/common/files.js';20import { FileService } from '../../../files/common/fileService.js';21import { InMemoryFileSystemProvider } from '../../../files/common/inMemoryFilesystemProvider.js';22import { TestInstantiationService } from '../../../instantiation/test/common/instantiationServiceMock.js';23import { ILogService, NullLogService } from '../../../log/common/log.js';24import { IUriIdentityService } from '../../../uriIdentity/common/uriIdentity.js';25import { UriIdentityService } from '../../../uriIdentity/common/uriIdentityService.js';26import { IStringDictionary } from '../../../../base/common/collections.js';2728const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' });2930class TestExtensionSignatureVerificationService extends mock<IExtensionSignatureVerificationService>() {3132constructor(33private readonly verificationResult: string | boolean) {34super();35}3637override async verify(): Promise<IExtensionSignatureVerificationResult | undefined> {38if (this.verificationResult === true) {39return {40code: ExtensionSignatureVerificationCode.Success41};42}43if (this.verificationResult === false) {44return undefined;45}46return {47code: this.verificationResult as ExtensionSignatureVerificationCode,48};49}50}5152class TestExtensionDownloader extends ExtensionsDownloader {53protected override async validate(): Promise<void> { }54}5556suite('ExtensionDownloader Tests', () => {5758const disposables = ensureNoDisposablesAreLeakedInTestSuite();59let instantiationService: TestInstantiationService;6061setup(() => {62instantiationService = disposables.add(new TestInstantiationService());6364const logService = new NullLogService();65const fileService = disposables.add(new FileService(logService));66const fileSystemProvider = disposables.add(new InMemoryFileSystemProvider());67disposables.add(fileService.registerProvider(ROOT.scheme, fileSystemProvider));6869instantiationService.stub(ILogService, logService);70instantiationService.stub(IFileService, fileService);71instantiationService.stub(ILogService, logService);72instantiationService.stub(IUriIdentityService, disposables.add(new UriIdentityService(fileService)));73instantiationService.stub(INativeEnvironmentService, { extensionsDownloadLocation: joinPath(ROOT, 'CachedExtensionVSIXs') });74instantiationService.stub(IExtensionGalleryService, {75async download(extension, location, operation) {76await fileService.writeFile(location, VSBuffer.fromString('extension vsix'));77},78async downloadSignatureArchive(extension, location) {79await fileService.writeFile(location, VSBuffer.fromString('extension signature'));80},81});82});8384test('download completes successfully if verification is disabled by options', async () => {85const testObject = aTestObject({ verificationResult: 'error' });8687const actual = await testObject.download(aGalleryExtension('a', { isSigned: true }), InstallOperation.Install, false);8889assert.strictEqual(actual.verificationStatus, undefined);90});9192test('download completes successfully if verification is disabled because the module is not loaded', async () => {93const testObject = aTestObject({ verificationResult: false });9495const actual = await testObject.download(aGalleryExtension('a', { isSigned: true }), InstallOperation.Install, true);9697assert.strictEqual(actual.verificationStatus, undefined);98});99100test('download completes successfully if verification fails to execute', async () => {101const errorCode = 'ENOENT';102const testObject = aTestObject({ verificationResult: errorCode });103104const actual = await testObject.download(aGalleryExtension('a', { isSigned: true }), InstallOperation.Install, true);105106assert.strictEqual(actual.verificationStatus, errorCode);107});108109test('download completes successfully if verification fails ', async () => {110const errorCode = 'IntegrityCheckFailed';111const testObject = aTestObject({ verificationResult: errorCode });112113const actual = await testObject.download(aGalleryExtension('a', { isSigned: true }), InstallOperation.Install, true);114115assert.strictEqual(actual.verificationStatus, errorCode);116});117118test('download completes successfully if verification succeeds', async () => {119const testObject = aTestObject({ verificationResult: true });120121const actual = await testObject.download(aGalleryExtension('a', { isSigned: true }), InstallOperation.Install, true);122123assert.strictEqual(actual.verificationStatus, ExtensionSignatureVerificationCode.Success);124});125126test('download completes successfully for unsigned extension', async () => {127const testObject = aTestObject({ verificationResult: true });128129const actual = await testObject.download(aGalleryExtension('a', { isSigned: false }), InstallOperation.Install, true);130131assert.strictEqual(actual.verificationStatus, ExtensionSignatureVerificationCode.NotSigned);132});133134test('download completes successfully for an unsigned extension even when signature verification throws error', async () => {135const testObject = aTestObject({ verificationResult: 'error' });136137const actual = await testObject.download(aGalleryExtension('a', { isSigned: false }), InstallOperation.Install, true);138139assert.strictEqual(actual.verificationStatus, ExtensionSignatureVerificationCode.NotSigned);140});141142function aTestObject(options: { verificationResult: boolean | string }): ExtensionsDownloader {143instantiationService.stub(IExtensionSignatureVerificationService, new TestExtensionSignatureVerificationService(options.verificationResult));144return disposables.add(instantiationService.createInstance(TestExtensionDownloader));145}146147function aGalleryExtension(name: string, properties: Partial<IGalleryExtension> = {}, galleryExtensionProperties: IStringDictionary<unknown> = {}, assets: Partial<IGalleryExtensionAssets> = {}): IGalleryExtension {148const targetPlatform = getTargetPlatform(platform, arch);149const galleryExtension = <IGalleryExtension>Object.create({ name, publisher: 'pub', version: '1.0.0', allTargetPlatforms: [targetPlatform], properties: {}, assets: {}, ...properties });150galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], targetPlatform, ...galleryExtensionProperties };151galleryExtension.assets = { ...galleryExtension.assets, ...assets };152galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() };153return <IGalleryExtension>galleryExtension;154}155});156157158