-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add Project Wizard related Python extension tests (#4540)
### Description - Addresses: #3433 #### 🆕 Test Changes - `extensions/positron-python/src/test/positron/manager.unit.test.ts` - added a test to validate a metadata fragment - updated an existing test to check the result of - `extensions/positron-python/src/test/positron/createEnvApi.unit.test.ts` - create new tests for the `createEnvironmentAndRegister` api - `extensions/positron-python/src/test/pythonEnvironments/creation/provider/condaCreationProvider.unit.test.ts` - added a test to create a conda env with the code path used by the project wizard - `extensions/positron-python/src/test/pythonEnvironments/creation/provider/venvCreationProvider.unit.test.ts` - added a test to create a venv with the code path used by the project wizard #### Code Changes - moved the Positron createEnvironment command implementations to separate functions in `extensions/positron-python/src/client/positron/createEnvApi.ts` to make them more easily testable (without having to call the commands) - `extensions/positron-python/src/client/pythonEnvironments/creation/proposed.createEnvApis.ts` - added/fixed types for createEnvironment options and return type ### QA Notes - python extension unit tests should pass - project wizard smoke tests in particular should continue to pass --------- Signed-off-by: sharon <[email protected]> Co-authored-by: Wasim Lorgat <[email protected]>
- Loading branch information
1 parent
3bacb87
commit a88819e
Showing
7 changed files
with
371 additions
and
23 deletions.
There are no files selected for viewing
72 changes: 72 additions & 0 deletions
72
extensions/positron-python/src/client/positron/createEnvApi.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (C) 2024 Posit Software, PBC. All rights reserved. | ||
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
// eslint-disable-next-line import/no-unresolved | ||
import * as positron from 'positron'; | ||
import { CreateEnvironmentOptionsInternal } from '../pythonEnvironments/creation/types'; | ||
import { | ||
CreateEnvironmentOptions, | ||
CreateEnvironmentProvider, | ||
CreateEnvironmentResult, | ||
} from '../pythonEnvironments/creation/proposed.createEnvApis'; | ||
import { handleCreateEnvironmentCommand } from '../pythonEnvironments/creation/createEnvironment'; | ||
import { IPythonRuntimeManager } from './manager'; | ||
|
||
/** | ||
* A simplified version of an environment provider that can be used in the Positron Project Wizard | ||
*/ | ||
interface WizardEnvironmentProviders { | ||
id: string; | ||
name: string; | ||
description: string; | ||
} | ||
|
||
/** | ||
* Result of creating a Python environment and registering it with the language runtime manager. | ||
*/ | ||
type CreateEnvironmentAndRegisterResult = CreateEnvironmentResult & { metadata?: positron.LanguageRuntimeMetadata }; | ||
|
||
/** | ||
* Get the list of providers that can be used in the Positron Project Wizard | ||
* @param providers The available environment creation providers | ||
* @returns A list of providers that can be used in the Positron Project Wizard | ||
*/ | ||
export async function getCreateEnvironmentProviders( | ||
providers: readonly CreateEnvironmentProvider[], | ||
): Promise<WizardEnvironmentProviders[]> { | ||
const providersForWizard = providers.map((provider) => ({ | ||
id: provider.id, | ||
name: provider.name, | ||
description: provider.description, | ||
})); | ||
return providersForWizard; | ||
} | ||
|
||
/** | ||
* Create an environment and register it with the Python runtime manager | ||
* @param providers The available environment creation providers | ||
* @param pythonRuntimeManager The manager for the Python runtimes | ||
* @param options Options for creating the environment | ||
* @returns The result of creating the environment and registering it, including the metadata for the environment | ||
*/ | ||
export async function createEnvironmentAndRegister( | ||
providers: readonly CreateEnvironmentProvider[], | ||
pythonRuntimeManager: IPythonRuntimeManager, | ||
options: CreateEnvironmentOptions & CreateEnvironmentOptionsInternal, | ||
): Promise<CreateEnvironmentAndRegisterResult | undefined> { | ||
if (!options.providerId || (!options.interpreterPath && !options.condaPythonVersion)) { | ||
return { | ||
error: new Error( | ||
'Missing required options for creating an environment. Please specify a provider ID and a Python interpreter path or a Conda Python version.', | ||
), | ||
}; | ||
} | ||
const result = await handleCreateEnvironmentCommand(providers, options); | ||
if (result?.path) { | ||
const metadata = await pythonRuntimeManager.registerLanguageRuntimeFromPath(result.path); | ||
return { ...result, metadata }; | ||
} | ||
return result; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
extensions/positron-python/src/test/positron/createEnvApi.unit.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
/* eslint-disable @typescript-eslint/no-explicit-any */ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (C) 2023-2024 Posit Software, PBC. All rights reserved. | ||
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import * as chaiAsPromised from 'chai-as-promised'; | ||
import * as sinon from 'sinon'; | ||
import * as typemoq from 'typemoq'; | ||
import { assert, use as chaiUse } from 'chai'; | ||
import { Uri } from 'vscode'; | ||
// eslint-disable-next-line import/no-unresolved | ||
import * as positron from 'positron'; | ||
import * as path from 'path'; | ||
import { EXTENSION_ROOT_DIR_FOR_TESTS } from '../constants'; | ||
import * as commandApis from '../../client/common/vscodeApis/commandApis'; | ||
import * as createEnvironmentApis from '../../client/pythonEnvironments/creation/createEnvironment'; | ||
import { IDisposableRegistry, IInterpreterPathService, IPathUtils } from '../../client/common/types'; | ||
import { registerCreateEnvironmentFeatures } from '../../client/pythonEnvironments/creation/createEnvApi'; | ||
import { | ||
CreateEnvironmentOptions, | ||
CreateEnvironmentProvider, | ||
} from '../../client/pythonEnvironments/creation/proposed.createEnvApis'; | ||
import { CreateEnvironmentOptionsInternal } from '../../client/pythonEnvironments/creation/types'; | ||
import { IPythonRuntimeManager } from '../../client/positron/manager'; | ||
import { IInterpreterQuickPick } from '../../client/interpreter/configuration/types'; | ||
import { createEnvironmentAndRegister } from '../../client/positron/createEnvApi'; | ||
|
||
chaiUse(chaiAsPromised); | ||
|
||
suite('Positron Create Environment APIs', () => { | ||
let registerCommandStub: sinon.SinonStub; | ||
let handleCreateEnvironmentCommandStub: sinon.SinonStub; | ||
|
||
const disposables: IDisposableRegistry = []; | ||
const mockProvider = typemoq.Mock.ofType<CreateEnvironmentProvider>(); | ||
const mockProviders = [mockProvider.object]; | ||
|
||
let pythonRuntimeManager: typemoq.IMock<IPythonRuntimeManager>; | ||
let pathUtils: typemoq.IMock<IPathUtils>; | ||
let interpreterQuickPick: typemoq.IMock<IInterpreterQuickPick>; | ||
let interpreterPathService: typemoq.IMock<IInterpreterPathService>; | ||
|
||
// Test workspace | ||
const workspace1 = { | ||
uri: Uri.file(path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'testMultiRootWkspc', 'workspace1')), | ||
name: 'workspace1', | ||
index: 0, | ||
}; | ||
|
||
// Environment options | ||
const envOptions: CreateEnvironmentOptions & CreateEnvironmentOptionsInternal = { | ||
providerId: 'envProvider-id', | ||
interpreterPath: '/path/to/venv/python', | ||
workspaceFolder: workspace1, | ||
}; | ||
const envOptionsWithInfo = { | ||
withInterpreterPath: { ...envOptions }, | ||
withCondaPythonVersion: { ...envOptions, interpreterPath: undefined, condaPythonVersion: '3.12' }, | ||
}; | ||
const envOptionsMissingInfo = { | ||
noProviderId: { ...envOptions, providerId: undefined }, | ||
noPythonSpecified: { ...envOptions, interpreterPath: undefined, condaPythonVersion: undefined }, | ||
}; | ||
|
||
setup(() => { | ||
registerCommandStub = sinon.stub(commandApis, 'registerCommand'); | ||
handleCreateEnvironmentCommandStub = sinon.stub(createEnvironmentApis, 'handleCreateEnvironmentCommand'); | ||
|
||
pythonRuntimeManager = typemoq.Mock.ofType<IPythonRuntimeManager>(); | ||
pathUtils = typemoq.Mock.ofType<IPathUtils>(); | ||
interpreterQuickPick = typemoq.Mock.ofType<IInterpreterQuickPick>(); | ||
interpreterPathService = typemoq.Mock.ofType<IInterpreterPathService>(); | ||
|
||
registerCommandStub.callsFake((_command: string, _callback: (...args: any[]) => any) => ({ | ||
dispose: () => { | ||
// Do nothing | ||
}, | ||
})); | ||
pathUtils.setup((p) => p.getDisplayName(typemoq.It.isAny())).returns(() => 'test'); | ||
|
||
registerCreateEnvironmentFeatures( | ||
disposables, | ||
interpreterQuickPick.object, | ||
interpreterPathService.object, | ||
pathUtils.object, | ||
pythonRuntimeManager.object, | ||
); | ||
}); | ||
|
||
teardown(() => { | ||
disposables.forEach((d) => d.dispose()); | ||
sinon.restore(); | ||
}); | ||
|
||
Object.entries(envOptionsWithInfo).forEach(([optionsName, options]) => { | ||
test(`Environment creation succeeds when required options specified: ${optionsName}`, async () => { | ||
const resultPath = '/path/to/created/env'; | ||
pythonRuntimeManager | ||
.setup((p) => p.registerLanguageRuntimeFromPath(resultPath)) | ||
.returns(() => Promise.resolve(typemoq.Mock.ofType<positron.LanguageRuntimeMetadata>().object)) | ||
.verifiable(typemoq.Times.once()); | ||
handleCreateEnvironmentCommandStub.returns(Promise.resolve({ path: resultPath })); | ||
|
||
const result = await createEnvironmentAndRegister(mockProviders, pythonRuntimeManager.object, options); | ||
|
||
assert.isDefined(result); | ||
assert.isDefined(result?.path); | ||
assert.isDefined(result?.metadata); | ||
assert.isUndefined(result?.error); | ||
assert.isTrue(handleCreateEnvironmentCommandStub.calledOnce); | ||
pythonRuntimeManager.verifyAll(); | ||
}); | ||
}); | ||
|
||
Object.entries(envOptionsMissingInfo).forEach(([optionsName, options]) => { | ||
test(`Environment creation fails when options are missing: ${optionsName} `, async () => { | ||
pythonRuntimeManager | ||
.setup((p) => p.registerLanguageRuntimeFromPath(typemoq.It.isAny())) | ||
.returns(() => Promise.resolve(typemoq.Mock.ofType<positron.LanguageRuntimeMetadata>().object)) | ||
.verifiable(typemoq.Times.never()); | ||
|
||
const result = await createEnvironmentAndRegister(mockProviders, pythonRuntimeManager.object, options); | ||
|
||
assert.isDefined(result); | ||
assert.isUndefined(result?.path); | ||
assert.isUndefined(result?.metadata); | ||
assert.isDefined(result?.error); | ||
assert.isTrue(handleCreateEnvironmentCommandStub.notCalled); | ||
pythonRuntimeManager.verifyAll(); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.