diff --git a/.github/instructions/generic.instructions.md b/.github/instructions/generic.instructions.md index 9285662f2..8bbadc2e6 100644 --- a/.github/instructions/generic.instructions.md +++ b/.github/instructions/generic.instructions.md @@ -14,6 +14,11 @@ Provide project context and coding guidelines that AI should follow when generat - Use the extension’s logging utilities (`traceLog`, `traceVerbose`) for internal logs. - Do not use `console.log` or `console.warn` for logging. +## Telemetry + +- Always pass environment-manager and package-manager IDs through `getTelemetryManagerId()` from `src/common/telemetry/helpers.ts` when adding them to telemetry properties, including properties named `manager` or `managerId`. +- Hash IDs only at the telemetry boundary. Preserve exact manager IDs for registration, routing, settings, environment identity, logs, and user-facing output. + ## Settings Precedence - Always consider VS Code settings precedence: diff --git a/.github/prompts/add-telemetry.prompt.md b/.github/prompts/add-telemetry.prompt.md index 18c643d80..f32360ccc 100644 --- a/.github/prompts/add-telemetry.prompt.md +++ b/.github/prompts/add-telemetry.prompt.md @@ -8,6 +8,7 @@ When adding telemetry: - If the user wants to record when an action is started (such as a command invocation), place the telemetry call at the start of the handler or function. - If the user wants to record successful completions or outcomes, place the telemetry call at the end of the action, after the operation has succeeded (and optionally, record errors or failures as well). +- Always pass environment-manager and package-manager IDs through `getTelemetryManagerId()` from `src/common/telemetry/helpers.ts` before assigning them to telemetry properties such as `manager` or `managerId`. Do not hash IDs used for registration, routing, settings, environment identity, logs, or user-facing output. Instructions to add a new telemetry event: @@ -15,7 +16,9 @@ Instructions to add a new telemetry event: 2. Add a corresponding entry to the `IEventNamePropertyMapping` interface in the same file, including a GDPR comment and the expected properties. 3. In the relevant code location, call `sendTelemetryEvent` with the new event name and required properties. Example: ```typescript - sendTelemetryEvent(EventNames.YOUR_EVENT_NAME, undefined, { property: value }); + sendTelemetryEvent(EventNames.YOUR_EVENT_NAME, undefined, { + managerId: getTelemetryManagerId(manager.id), + }); ``` 4. If the event is triggered by a command, ensure the call is placed at the start of the command handler. diff --git a/src/common/constants.ts b/src/common/constants.ts index f4008f3d9..86091ff0c 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -3,19 +3,26 @@ import * as path from 'path'; export const ENVS_EXTENSION_ID = 'ms-python.vscode-python-envs'; export const PYTHON_EXTENSION_ID = 'ms-python.python'; export const PYLANCE_EXTENSION_ID = 'ms-python.vscode-pylance'; + +// Manager IDs export const CONDA_MANAGER_ID = `${PYTHON_EXTENSION_ID}:conda`; export const INLINE_SCRIPT_MANAGER_ID = `${PYTHON_EXTENSION_ID}:inline-script`; export const INLINE_SCRIPT_ENVS_KEY = `${ENVS_EXTENSION_ID}:inline-script:SCRIPT_ENVIRONMENTS`; export const PYENV_MANAGER_ID = `${PYTHON_EXTENSION_ID}:pyenv`; -export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter'; -export const EXTENSION_ROOT_DIR = path.dirname(__dirname); -export const ISSUES_URL = 'https://github.com/microsoft/vscode-python-environments/issues'; +export const PIPENV_MANAGER_ID = `${PYTHON_EXTENSION_ID}:pipenv`; +export const POETRY_MANAGER_ID = `${PYTHON_EXTENSION_ID}:poetry`; +export const PIXI_MANAGER_ID = `${PYTHON_EXTENSION_ID}:pixi`; +export const HATCH_MANAGER_ID = `${PYTHON_EXTENSION_ID}:hatch`; export const DEFAULT_PACKAGE_MANAGER_ID = 'ms-python.python:pip'; export const DEFAULT_ENV_MANAGER_ID = 'ms-python.python:venv'; export const VENV_MANAGER_ID = 'ms-python.python:venv'; export const SYSTEM_MANAGER_ID = 'ms-python.python:system'; +export const JUPYTER_EXTENSION_ID = 'ms-toolsai.jupyter'; +export const EXTENSION_ROOT_DIR = path.dirname(__dirname); +export const ISSUES_URL = 'https://github.com/microsoft/vscode-python-environments/issues'; + export const KNOWN_FILES = [ 'requirements.txt', 'requirements.in', diff --git a/src/common/telemetry/constants.ts b/src/common/telemetry/constants.ts index a983a1cf7..bb543f1c4 100644 --- a/src/common/telemetry/constants.ts +++ b/src/common/telemetry/constants.ts @@ -339,6 +339,7 @@ export interface IEventNamePropertyMapping { "isLegacy": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "eduardovil" } } */ + /** Manager IDs are reported as full SHA-256 digests; missing IDs are reported as "undefined". */ [EventNames.MANAGER_CAPABILITY_QUERY]: { managerId: string; managerKind: 'environment' | 'package'; diff --git a/src/common/telemetry/helpers.ts b/src/common/telemetry/helpers.ts index d3db790d8..b745c3df7 100644 --- a/src/common/telemetry/helpers.ts +++ b/src/common/telemetry/helpers.ts @@ -1,3 +1,4 @@ +import { createHash } from 'crypto'; import { getDefaultEnvManagerSetting, getDefaultPkgManagerSetting } from '../../features/settings/settingHelpers'; import type { EnvironmentManagers } from '../../features/envManagers'; import type { PythonProjectManager } from '../../features/projectManager'; @@ -8,6 +9,15 @@ import { getWorkspaceFolders } from '../workspace.apis'; import { EventNames } from './constants'; import { sendTelemetryEvent } from './sender'; +/** + * Returns a deterministic SHA-256 manager ID for telemetry, or the "undefined" sentinel. + * @param managerId Manager ID to hash. + * @returns The full lowercase hexadecimal SHA-256 digest, or "undefined" when no ID is available. + */ +export function getTelemetryManagerId(managerId: string | undefined): string { + return managerId === undefined ? 'undefined' : createHash('sha256').update(managerId, 'utf8').digest('hex'); +} + /** * Extracts the base tool name from a manager ID. * Example: 'ms-python.python:venv' -> 'venv' @@ -35,11 +45,15 @@ export function sendManagerSelectionTelemetry(pm: PythonProjectManager) { }); ems.forEach((em) => { - sendTelemetryEvent(EventNames.ENVIRONMENT_MANAGER_SELECTED, undefined, { managerId: em }); + sendTelemetryEvent(EventNames.ENVIRONMENT_MANAGER_SELECTED, undefined, { + managerId: getTelemetryManagerId(em), + }); }); ps.forEach((pkg) => { - sendTelemetryEvent(EventNames.PACKAGE_MANAGER_SELECTED, undefined, { managerId: pkg }); + sendTelemetryEvent(EventNames.PACKAGE_MANAGER_SELECTED, undefined, { + managerId: getTelemetryManagerId(pkg), + }); }); } diff --git a/src/common/utils/pythonPath.ts b/src/common/utils/pythonPath.ts index bd5093c3e..000260414 100644 --- a/src/common/utils/pythonPath.ts +++ b/src/common/utils/pythonPath.ts @@ -1,19 +1,29 @@ import { CancellationToken, Progress, Uri } from 'vscode'; import { PythonEnvironment } from '../../api'; import type { InternalEnvironmentManager } from '../../managers/common/registeredManagers'; -import { PYTHON_EXTENSION_ID } from '../constants'; +import { + CONDA_MANAGER_ID, + HATCH_MANAGER_ID, + PIPENV_MANAGER_ID, + PIXI_MANAGER_ID, + POETRY_MANAGER_ID, + PYENV_MANAGER_ID, + PYTHON_EXTENSION_ID, + SYSTEM_MANAGER_ID, + VENV_MANAGER_ID, +} from '../constants'; import { traceVerbose, traceWarn } from '../logging'; const priorityOrder = [ - `${PYTHON_EXTENSION_ID}:pyenv`, - `${PYTHON_EXTENSION_ID}:pixi`, - `${PYTHON_EXTENSION_ID}:conda`, - `${PYTHON_EXTENSION_ID}:pipenv`, - `${PYTHON_EXTENSION_ID}:poetry`, + PYENV_MANAGER_ID, + PIXI_MANAGER_ID, + CONDA_MANAGER_ID, + PIPENV_MANAGER_ID, + POETRY_MANAGER_ID, `${PYTHON_EXTENSION_ID}:activestate`, - `${PYTHON_EXTENSION_ID}:hatch`, - `${PYTHON_EXTENSION_ID}:venv`, - `${PYTHON_EXTENSION_ID}:system`, + HATCH_MANAGER_ID, + VENV_MANAGER_ID, + SYSTEM_MANAGER_ID, ]; function sortManagersByPriority(managers: InternalEnvironmentManager[]): InternalEnvironmentManager[] { const systemId = priorityOrder[priorityOrder.length - 1]; diff --git a/src/extension.ts b/src/extension.ts index 21c0755ac..8f18a7295 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -21,6 +21,7 @@ import { EventNames } from './common/telemetry/constants'; import { classifyError } from './common/telemetry/errorClassifier'; import { logDiscoverySummary, + getTelemetryManagerId, sendEnvironmentToolUsageTelemetry, sendManagerSelectionTelemetry, sendProjectStructureTelemetry, @@ -297,7 +298,7 @@ export async function activate(context: ExtensionContext): Promise { sendTelemetryEvent(EventNames.MANAGER_CAPABILITY_QUERY, undefined, { - managerId: manager?.id ?? 'none', + managerId: getTelemetryManagerId(manager?.id), managerKind: query.kind, managerVersion, capability: isKnownManagerCapability(capability) ? capability : 'unknown', diff --git a/src/features/common/managerReady.ts b/src/features/common/managerReady.ts index f9424641f..6cc2cb0e7 100644 --- a/src/features/common/managerReady.ts +++ b/src/features/common/managerReady.ts @@ -3,6 +3,7 @@ import { getExtension } from '../../common/extension.apis'; import { WorkbenchStrings } from '../../common/localize'; import { traceError, traceInfo, traceWarn } from '../../common/logging'; import { EventNames } from '../../common/telemetry/constants'; +import { getTelemetryManagerId } from '../../common/telemetry/helpers'; import { sendTelemetryEvent } from '../../common/telemetry/sender'; import { createDeferred, Deferred } from '../../common/utils/deferred'; import { showErrorMessage } from '../../common/window.apis'; @@ -52,7 +53,7 @@ export function withManagerTimeout( `To prevent this, check your "python-envs.defaultEnvManager" and "python-envs.pythonProjects" settings.`, ); sendTelemetryEvent(EventNames.MANAGER_READY_TIMEOUT, undefined, { - managerId, + managerId: getTelemetryManagerId(managerId), managerKind: kind, }); deferred.resolve(); diff --git a/src/features/creators/creationHelpers.ts b/src/features/creators/creationHelpers.ts index c17357557..571ac0920 100644 --- a/src/features/creators/creationHelpers.ts +++ b/src/features/creators/creationHelpers.ts @@ -6,6 +6,7 @@ import { traceError, traceVerbose } from '../../common/logging'; import { showQuickPickWithButtons } from '../../common/window.apis'; import type { EnvironmentManagers } from '../envManagers'; import type { InternalEnvironmentManager } from '../../managers/common/registeredManagers'; +import { VENV_MANAGER_ID } from '../../common/constants'; /** * Prompts the user to choose whether to create a new virtual environment (venv) for a project, with a clearer return and early exit. @@ -74,7 +75,7 @@ export async function promptForCopilotInstructions(): Promise m.id === 'ms-python.python:venv', + (m) => m.id === VENV_MANAGER_ID, ); const destinationUri = Uri.parse(destFolder); if (envManager?.supportsQuickCreate) { diff --git a/src/features/creators/newPackageProject.ts b/src/features/creators/newPackageProject.ts index 4e98a191f..1599505eb 100644 --- a/src/features/creators/newPackageProject.ts +++ b/src/features/creators/newPackageProject.ts @@ -2,7 +2,7 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import { commands, l10n, MarkdownString, QuickInputButtons, Uri, window, workspace } from 'vscode'; import { PythonEnvironment, PythonProject, PythonProjectCreator, PythonProjectCreatorOptions } from '../../api'; -import { NEW_PROJECT_TEMPLATES_FOLDER } from '../../common/constants'; +import { NEW_PROJECT_TEMPLATES_FOLDER, VENV_MANAGER_ID } from '../../common/constants'; import { traceError } from '../../common/logging'; import { showInputBoxWithButtons } from '../../common/window.apis'; import type { EnvironmentManagers } from '../envManagers'; @@ -134,7 +134,7 @@ export class NewPackageProject implements PythonProjectCreator { } else { // get venv manager or any manager that supports quick creating environments const venvManager = this.envManagers.managers.find( - (m) => m.id === 'ms-python.python:venv' || m.supportsQuickCreate, + (m) => m.id === VENV_MANAGER_ID || m.supportsQuickCreate, ); if (venvManager) { createdEnv = await venvManager.create(Uri.file(projectDestinationFolder), { diff --git a/src/features/envManagers.ts b/src/features/envManagers.ts index a1253f0f8..fa9fa83d3 100644 --- a/src/features/envManagers.ts +++ b/src/features/envManagers.ts @@ -26,6 +26,7 @@ import { import { traceError, traceVerbose } from '../common/logging'; import { StopWatch } from '../common/stopWatch'; import { EventNames } from '../common/telemetry/constants'; +import { getTelemetryManagerId } from '../common/telemetry/helpers'; import { sendTelemetryEvent } from '../common/telemetry/sender'; import { getCallingExtension } from '../common/utils/frameUtils'; import { normalizePath } from '../common/utils/pathUtils'; @@ -307,7 +308,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers { if (!managerId.toLowerCase().startsWith('undefined_publisher.')) { sendTelemetryEvent(EventNames.ENVIRONMENT_MANAGER_REGISTERED, registrationStopWatch.elapsedTime, { - managerId, + managerId: getTelemetryManagerId(managerId), }); } @@ -336,7 +337,7 @@ export class PythonEnvironmentManagers implements EnvironmentManagers { if (!managerId.toLowerCase().startsWith('undefined_publisher.')) { sendTelemetryEvent(EventNames.PACKAGE_MANAGER_REGISTERED, undefined, { - managerId, + managerId: getTelemetryManagerId(managerId), }); } diff --git a/src/features/interpreterSelection.ts b/src/features/interpreterSelection.ts index a0ed228fb..6614217ef 100644 --- a/src/features/interpreterSelection.ts +++ b/src/features/interpreterSelection.ts @@ -9,6 +9,7 @@ import { SYSTEM_MANAGER_ID, VENV_MANAGER_ID } from '../common/constants'; import { traceError, traceInfo, traceVerbose, traceWarn } from '../common/logging'; import { StopWatch } from '../common/stopWatch'; import { EventNames } from '../common/telemetry/constants'; +import { getTelemetryManagerId } from '../common/telemetry/helpers'; import { sendTelemetryEvent } from '../common/telemetry/sender'; import { resolveVariables } from '../common/utils/internalVariables'; import { normalizePath } from '../common/utils/pathUtils'; @@ -318,7 +319,7 @@ export async function applyInitialEnvironmentSelection( sendTelemetryEvent(EventNames.ENV_SELECTION_RESULT, scopeStopWatch.elapsedTime, { scope: 'workspace', prioritySource: result.source, - managerId: result.manager.id, + managerId: getTelemetryManagerId(result.manager.id), resolutionPath: result.environment ? 'envPreResolved' : 'managerDiscovery', hasPersistedSelection: env !== undefined, }); @@ -357,7 +358,7 @@ export async function applyInitialEnvironmentSelection( sendTelemetryEvent(EventNames.ENV_SELECTION_RESULT, globalStopWatch.elapsedTime, { scope: 'global', prioritySource: result.source, - managerId: result.manager.id, + managerId: getTelemetryManagerId(result.manager.id), resolutionPath: result.environment ? 'envPreResolved' : 'managerDiscovery', hasPersistedSelection: env !== undefined, }); diff --git a/src/managers/builtin/inlineScript/envManager.ts b/src/managers/builtin/inlineScript/envManager.ts index ab168e752..ba3d46f39 100644 --- a/src/managers/builtin/inlineScript/envManager.ts +++ b/src/managers/builtin/inlineScript/envManager.ts @@ -35,12 +35,7 @@ import { SetEnvironmentScope, } from '../../../api'; import { Capabilities, EnvironmentManagerCapability, supportedCapability, EnvironmentCapability } from '../../../capabilities'; -import { - CONDA_MANAGER_ID, - INLINE_SCRIPT_MANAGER_ID, - PYENV_MANAGER_ID, - SYSTEM_MANAGER_ID, -} from '../../../common/constants'; +import { CONDA_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID, PYENV_MANAGER_ID, SYSTEM_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../../common/constants'; import { getErrorMessage } from '../../../common/errors/utils'; import { computeCacheKey, normalizeDependency } from '../../../common/inlineScript/cacheKey'; import { InlineScriptEnvironmentModifiedError } from '../../../common/inlineScript/errors'; @@ -362,7 +357,7 @@ export class InlineScriptEnvManager implements EnvironmentManager, Disposable { public readonly name = 'inline-script'; public readonly displayName = l10n.t('Inline scripts'); - public readonly preferredPackageManagerId = 'ms-python.python:pip'; + public readonly preferredPackageManagerId = DEFAULT_PACKAGE_MANAGER_ID; public readonly description: string | undefined = undefined; public readonly tooltip: string | MarkdownString = new MarkdownString( l10n.t('Environments built from PEP 723 inline script metadata.'), diff --git a/src/managers/builtin/sysPythonManager.ts b/src/managers/builtin/sysPythonManager.ts index 2803674b3..33d580d7f 100644 --- a/src/managers/builtin/sysPythonManager.ts +++ b/src/managers/builtin/sysPythonManager.ts @@ -35,6 +35,7 @@ import { } from './cache'; import { getDefaultGlobalPython, refreshPythons, resolveSystemPythonEnvironmentPath } from './utils'; import { installPythonWithUv, promptInstallPythonViaUv, selectPythonVersionToInstall } from './uvPythonInstaller'; +import { DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; export class SysPythonManager implements EnvironmentManager { readonly capabilities: Capabilities = { @@ -73,7 +74,7 @@ export class SysPythonManager implements EnvironmentManager { ) { this.name = 'system'; this.displayName = 'Global'; - this.preferredPackageManagerId = 'ms-python.python:pip'; + this.preferredPackageManagerId = DEFAULT_PACKAGE_MANAGER_ID; this.description = undefined; this.tooltip = new MarkdownString(SysManagerStrings.sysManagerDescription, true); this.iconPath = new ThemeIcon('globe'); diff --git a/src/managers/builtin/venvManager.ts b/src/managers/builtin/venvManager.ts index c05669d14..632aba07a 100644 --- a/src/managers/builtin/venvManager.ts +++ b/src/managers/builtin/venvManager.ts @@ -31,7 +31,7 @@ import { } from '../../api'; import { Capabilities, EnvironmentManagerCapability, supportedCapability, EnvironmentCapability } from '../../capabilities'; import { executeCommand } from '../../common/command.api'; -import { PYTHON_EXTENSION_ID } from '../../common/constants'; +import { DEFAULT_PACKAGE_MANAGER_ID, VENV_MANAGER_ID } from '../../common/constants'; import { VenvManagerStrings } from '../../common/localize'; import { traceError, traceWarn } from '../../common/logging'; import { createDeferred, Deferred } from '../../common/utils/deferred'; @@ -111,7 +111,7 @@ export class VenvManager implements EnvironmentManager { // https://github.com/microsoft/vscode-python-environments/issues/167 this.description = undefined; this.tooltip = new MarkdownString(VenvManagerStrings.venvManagerDescription, true); - this.preferredPackageManagerId = 'ms-python.python:pip'; + this.preferredPackageManagerId = DEFAULT_PACKAGE_MANAGER_ID; this.iconPath = new ThemeIcon('python'); } @@ -675,7 +675,7 @@ export class VenvManager implements EnvironmentManager { this.baseManager, ); if (resolved) { - if (resolved.envId.managerId === `${PYTHON_EXTENSION_ID}:venv`) { + if (resolved.envId.managerId === VENV_MANAGER_ID) { // We should only return the resolved env if it is a venv. // Fall through an return undefined if it is not a venv this.addEnvironment(resolved, true); diff --git a/src/managers/common/registeredManagers.ts b/src/managers/common/registeredManagers.ts index 23d6470e9..12e96ef5d 100644 --- a/src/managers/common/registeredManagers.ts +++ b/src/managers/common/registeredManagers.ts @@ -15,6 +15,7 @@ import { traceWarn } from '../../common/logging'; import { StopWatch } from '../../common/stopWatch'; import { EventNames } from '../../common/telemetry/constants'; import { classifyError, isTimeoutErrorType } from '../../common/telemetry/errorClassifier'; +import { getTelemetryManagerId } from '../../common/telemetry/helpers'; import { sendTelemetryEvent } from '../../common/telemetry/sender'; import { EnvironmentToolSupport, @@ -143,7 +144,7 @@ export class InternalEnvironmentManager implements EnvironmentManager { const envs = await this.manager.getEnvironments('all').catch(() => []); const duration = sw.elapsedTime; sendTelemetryEvent(EventNames.ENVIRONMENT_DISCOVERY, duration, { - managerId: this.id, + managerId: getTelemetryManagerId(this.id), result: 'success', envCount: envs.length, }); @@ -162,7 +163,7 @@ export class InternalEnvironmentManager implements EnvironmentManager { EventNames.ENVIRONMENT_DISCOVERY, duration, { - managerId: this.id, + managerId: getTelemetryManagerId(this.id), result: errorType === 'canceled' || isTimeoutErrorType(errorType) ? 'timeout' : 'error', errorType, }, @@ -323,21 +324,21 @@ export class InternalPackageManager implements PackageManager { try { await this.manager.manage(environment, options); sendTelemetryEvent(EventNames.PACKAGE_MANAGEMENT, stopWatch.elapsedTime, { - managerId: this.id, + managerId: getTelemetryManagerId(this.id), result: 'success', triggerSource, }); } catch (error) { if (error instanceof CancellationError) { sendTelemetryEvent(EventNames.PACKAGE_MANAGEMENT, stopWatch.elapsedTime, { - managerId: this.id, + managerId: getTelemetryManagerId(this.id), result: 'cancelled', triggerSource, }); throw error; } sendTelemetryEvent(EventNames.PACKAGE_MANAGEMENT, stopWatch.elapsedTime, { - managerId: this.id, + managerId: getTelemetryManagerId(this.id), result: 'error', errorType: error instanceof Error ? error.name : 'unknown', triggerSource, diff --git a/src/managers/conda/condaEnvManager.ts b/src/managers/conda/condaEnvManager.ts index 413034d9e..401772b50 100644 --- a/src/managers/conda/condaEnvManager.ts +++ b/src/managers/conda/condaEnvManager.ts @@ -70,6 +70,7 @@ import { setCondaForWorkspace, setCondaForWorkspaces, } from './condaUtils'; +import { CONDA_MANAGER_ID } from '../../common/constants'; export class CondaEnvManager implements EnvironmentManager, Disposable { readonly capabilities: Capabilities = { @@ -101,7 +102,7 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { ) { this.name = 'conda'; this.displayName = 'Conda'; - this.preferredPackageManagerId = 'ms-python.python:conda'; + this.preferredPackageManagerId = CONDA_MANAGER_ID; this.tooltip = new MarkdownString(CondaStrings.condaManager, true); } @@ -250,7 +251,7 @@ export class CondaEnvManager implements EnvironmentManager, Disposable { result = 'tool_not_found'; traceInfo('Conda not found, conda features will be inactive.'); if (this.projectManager) { - await notifyMissingManagerIfDefault('ms-python.python:conda', this.projectManager, this.api); + await notifyMissingManagerIfDefault(CONDA_MANAGER_ID, this.projectManager, this.api); } } else if (condaTool) { // Conda was found — populate sourcing information for activation/shell support. diff --git a/src/managers/pipenv/pipenvManager.ts b/src/managers/pipenv/pipenvManager.ts index abbe6b240..c2ddd2844 100644 --- a/src/managers/pipenv/pipenvManager.ts +++ b/src/managers/pipenv/pipenvManager.ts @@ -16,7 +16,13 @@ import { ResolveEnvironmentContext, SetEnvironmentScope, } from '../../api'; -import { Capabilities, EnvironmentManagerCapability, supportedCapability, EnvironmentCapability } from '../../capabilities'; +import { + Capabilities, + EnvironmentCapability, + EnvironmentManagerCapability, + supportedCapability, +} from '../../capabilities'; +import { PIPENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; import { PipenvStrings } from '../../common/localize'; import { traceError, traceInfo } from '../../common/logging'; import { StopWatch } from '../../common/stopWatch'; @@ -28,14 +34,14 @@ import { normalizePath } from '../../common/utils/pathUtils'; import { withProgress } from '../../common/window.apis'; import { getConfiguration } from '../../common/workspace.apis'; import type { PythonProjectManager } from '../../features/projectManager'; -import { getProjectFsPathForScope, tryFastPathGet } from '../common/fastPath'; -import { NativePythonEnvironmentKind, NativePythonFinder } from '../common/nativePythonFinder'; -import { notifyMissingManagerIfDefault } from '../common/utils'; import { EnvironmentToolSupport, pythonToolSupport, resolveToolProjectEnvironment, } from '../../internal/pythonToolSupport'; +import { getProjectFsPathForScope, tryFastPathGet } from '../common/fastPath'; +import { NativePythonEnvironmentKind, NativePythonFinder } from '../common/nativePythonFinder'; +import { notifyMissingManagerIfDefault } from '../common/utils'; import { clearPipenvCache, getPipenv, @@ -91,7 +97,7 @@ export class PipenvManager implements EnvironmentManager, Disposable { ) { this.name = 'pipenv'; this.displayName = 'Pipenv'; - this.preferredPackageManagerId = 'ms-python.python:pip'; + this.preferredPackageManagerId = DEFAULT_PACKAGE_MANAGER_ID; this.tooltip = new MarkdownString(PipenvStrings.pipenvManager, true); } @@ -161,7 +167,7 @@ export class PipenvManager implements EnvironmentManager, Disposable { if (toolSource === 'none') { result = 'tool_not_found'; if (this.projectManager) { - await notifyMissingManagerIfDefault('ms-python.python:pipenv', this.projectManager, this.api); + await notifyMissingManagerIfDefault(PIPENV_MANAGER_ID, this.projectManager, this.api); } } } catch (ex) { @@ -369,18 +375,20 @@ export class PipenvManager implements EnvironmentManager, Disposable { } async get(scope: GetEnvironmentScope, toolExecution = false): Promise { - const fastResult = toolExecution ? undefined : await tryFastPathGet({ - initialized: this._initialized, - setInitialized: (deferred) => { - this._initialized = deferred; - }, - scope, - label: 'pipenv', - getProjectFsPath: (s) => getProjectFsPathForScope(this.api, s), - getPersistedPath: (fsPath) => getPipenvForWorkspace(fsPath), - resolve: (p) => resolvePipenvPath(p, this.nativeFinder, this.api, this), - startBackgroundInit: () => this.initializeDiscovery(), - }); + const fastResult = toolExecution + ? undefined + : await tryFastPathGet({ + initialized: this._initialized, + setInitialized: (deferred) => { + this._initialized = deferred; + }, + scope, + label: 'pipenv', + getProjectFsPath: (s) => getProjectFsPathForScope(this.api, s), + getPersistedPath: (fsPath) => getPipenvForWorkspace(fsPath), + resolve: (p) => resolvePipenvPath(p, this.nativeFinder, this.api, this), + startBackgroundInit: () => this.initializeDiscovery(), + }); if (fastResult) { return fastResult.env; } diff --git a/src/managers/poetry/poetryManager.ts b/src/managers/poetry/poetryManager.ts index 8de448b3e..1e0b7d881 100644 --- a/src/managers/poetry/poetryManager.ts +++ b/src/managers/poetry/poetryManager.ts @@ -48,6 +48,7 @@ import { setPoetryForWorkspaces, } from './poetryUtils'; import { PoetryVersionCommand } from './commands/version'; +import { POETRY_MANAGER_ID } from '../../common/constants'; export class PoetryManager implements EnvironmentManager, Disposable { readonly capabilities: Capabilities = { @@ -82,7 +83,7 @@ export class PoetryManager implements EnvironmentManager, Disposable { ) { this.name = 'poetry'; this.displayName = 'Poetry'; - this.preferredPackageManagerId = 'ms-python.python:poetry'; + this.preferredPackageManagerId = POETRY_MANAGER_ID; this.tooltip = new MarkdownString(PoetryStrings.poetryManager, true); } @@ -157,7 +158,7 @@ export class PoetryManager implements EnvironmentManager, Disposable { if (toolSource === 'none') { result = 'tool_not_found'; if (this.projectManager) { - await notifyMissingManagerIfDefault('ms-python.python:poetry', this.projectManager, this.api); + await notifyMissingManagerIfDefault(POETRY_MANAGER_ID, this.projectManager, this.api); } } } catch (ex) { diff --git a/src/managers/pyenv/pyenvManager.ts b/src/managers/pyenv/pyenvManager.ts index 1042c9b70..7c0fbc162 100644 --- a/src/managers/pyenv/pyenvManager.ts +++ b/src/managers/pyenv/pyenvManager.ts @@ -18,7 +18,7 @@ import { SetEnvironmentScope, } from '../../api'; import { Capabilities, EnvironmentManagerCapability, supportedCapability, EnvironmentCapability } from '../../capabilities'; -import { PYENV_MANAGER_ID } from '../../common/constants'; +import { PYENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; import { PyenvStrings } from '../../common/localize'; import { traceError, traceInfo } from '../../common/logging'; import { StopWatch } from '../../common/stopWatch'; @@ -80,7 +80,7 @@ export class PyEnvManager implements EnvironmentManager, Disposable { ) { this.name = 'pyenv'; this.displayName = 'PyEnv'; - this.preferredPackageManagerId = 'ms-python.python:pip'; + this.preferredPackageManagerId = DEFAULT_PACKAGE_MANAGER_ID; this.tooltip = new MarkdownString(PyenvStrings.pyenvManager, true); } diff --git a/src/test/common/inlineScript/cacheLayout.unit.test.ts b/src/test/common/inlineScript/cacheLayout.unit.test.ts index 5bb3c5de2..a7bdfbb5c 100644 --- a/src/test/common/inlineScript/cacheLayout.unit.test.ts +++ b/src/test/common/inlineScript/cacheLayout.unit.test.ts @@ -36,6 +36,7 @@ import * as logging from '../../../common/logging'; import { createDeferred } from '../../../common/utils/deferred'; import * as platformUtils from '../../../common/utils/platformUtils'; import { getVenvPythonPath } from '../../../common/utils/virtualEnvironment'; +import { INLINE_SCRIPT_MANAGER_ID, SYSTEM_MANAGER_ID } from '../../../common/constants'; // `import * as` would copy the module, leaving the spy unable to observe the real rename that // cacheLayout calls. @@ -857,7 +858,7 @@ suite('inlineScriptCacheLayout', () => { const pythonPath = getVenvPythonPath(envDir.fsPath); await fs.outputFile(pythonPath, ''); environment = { - envId: { id: 'inline-env', managerId: 'ms-python.python:inline-script' }, + envId: { id: 'inline-env', managerId: INLINE_SCRIPT_MANAGER_ID }, name: 'Python 3.12.4', displayName: 'Python 3.12.4', displayPath: pythonPath, @@ -879,7 +880,7 @@ suite('inlineScriptCacheLayout', () => { test('does not claim environments owned by another manager', async () => { const otherManager = { ...environment, - envId: { ...environment.envId, managerId: 'ms-python.python:system' }, + envId: { ...environment.envId, managerId: SYSTEM_MANAGER_ID }, }; assert.strictEqual(await inspectOwnedCacheEntry(otherManager, cacheRoot, envDir), 'uncertain'); }); diff --git a/src/test/common/inlineScript/interpreter.unit.test.ts b/src/test/common/inlineScript/interpreter.unit.test.ts index 37039d59b..cd74ad0e5 100644 --- a/src/test/common/inlineScript/interpreter.unit.test.ts +++ b/src/test/common/inlineScript/interpreter.unit.test.ts @@ -7,10 +7,11 @@ import { Uri } from 'vscode'; import { PythonEnvironment } from '../../../api'; import { extractLowerBoundVersion, pickCompatibleInterpreter } from '../../../common/inlineScript/interpreter'; import * as logging from '../../../common/logging'; +import { SYSTEM_MANAGER_ID } from '../../../common/constants'; function makeEnv(version: string, name = `Python ${version}`, error?: string): PythonEnvironment { return { - envId: { id: name, managerId: 'ms-python.python:system' }, + envId: { id: name, managerId: SYSTEM_MANAGER_ID }, name, displayName: name, displayPath: `/usr/bin/${name}`, diff --git a/src/test/common/pythonPath.unit.test.ts b/src/test/common/pythonPath.unit.test.ts index 9e5202b5b..6a1843ec3 100644 --- a/src/test/common/pythonPath.unit.test.ts +++ b/src/test/common/pythonPath.unit.test.ts @@ -8,6 +8,7 @@ import { PythonEnvironment } from '../../api'; import { handlePythonPath } from '../../common/utils/pythonPath'; import * as logging from '../../common/logging'; import type { InternalEnvironmentManager } from '../../managers/common/registeredManagers'; +import { VENV_MANAGER_ID, CONDA_MANAGER_ID, SYSTEM_MANAGER_ID } from '../../common/constants'; function createMockManager( id: string, @@ -43,8 +44,8 @@ suite('handlePythonPath', () => { test('tool resolution explicitly skips unsupported managers without invoking their public resolver', async () => { const unsupported = createMockManager('other.extension:manager', 'Unsupported'); - const expected = createMockEnv('ms-python.python:venv'); - const supported = createMockManager('ms-python.python:venv', 'Venv'); + const expected = createMockEnv(VENV_MANAGER_ID); + const supported = createMockManager(VENV_MANAGER_ID, 'Venv'); const resolve = sinon.stub().resolves(expected); Object.defineProperty(supported, 'tools', { value: { resolve } }); const logged = sinon.stub(logging, 'traceVerbose'); @@ -57,8 +58,8 @@ suite('handlePythonPath', () => { }); test('returns undefined when no managers can resolve the path', async () => { - const manager1 = createMockManager('ms-python.python:venv', 'Venv'); - const manager2 = createMockManager('ms-python.python:conda', 'Conda'); + const manager1 = createMockManager(VENV_MANAGER_ID, 'Venv'); + const manager2 = createMockManager(CONDA_MANAGER_ID, 'Conda'); const result = await handlePythonPath(testUri, [manager1, manager2], []); @@ -66,9 +67,9 @@ suite('handlePythonPath', () => { }); test('returns environment from project manager that resolves first', async () => { - const mockEnv = createMockEnv('ms-python.python:venv'); - const projectManager = createMockManager('ms-python.python:venv', 'Venv', mockEnv); - const globalManager = createMockManager('ms-python.python:conda', 'Conda'); + const mockEnv = createMockEnv(VENV_MANAGER_ID); + const projectManager = createMockManager(VENV_MANAGER_ID, 'Venv', mockEnv); + const globalManager = createMockManager(CONDA_MANAGER_ID, 'Conda'); const result = await handlePythonPath(testUri, [globalManager], [projectManager]); @@ -78,9 +79,9 @@ suite('handlePythonPath', () => { }); test('falls back to global managers when project managers cannot resolve', async () => { - const mockEnv = createMockEnv('ms-python.python:conda'); - const projectManager = createMockManager('ms-python.python:venv', 'Venv'); - const globalManager = createMockManager('ms-python.python:conda', 'Conda', mockEnv); + const mockEnv = createMockEnv(CONDA_MANAGER_ID); + const projectManager = createMockManager(VENV_MANAGER_ID, 'Venv'); + const globalManager = createMockManager(CONDA_MANAGER_ID, 'Conda', mockEnv); const result = await handlePythonPath(testUri, [globalManager], [projectManager]); @@ -88,8 +89,8 @@ suite('handlePythonPath', () => { }); test('does not re-check managers already checked as project managers', async () => { - const projectManager = createMockManager('ms-python.python:venv', 'Venv'); - const globalManager = createMockManager('ms-python.python:venv', 'Venv'); + const projectManager = createMockManager(VENV_MANAGER_ID, 'Venv'); + const globalManager = createMockManager(VENV_MANAGER_ID, 'Venv'); const result = await handlePythonPath(testUri, [globalManager], [projectManager]); @@ -100,7 +101,7 @@ suite('handlePythonPath', () => { }); test('returns undefined and does not throw for unresolvable paths', async () => { - const manager = createMockManager('ms-python.python:system', 'System'); + const manager = createMockManager(SYSTEM_MANAGER_ID, 'System'); const result = await handlePythonPath(Uri.file('/usr/bin/node'), [manager], []); @@ -111,7 +112,7 @@ suite('handlePythonPath', () => { const cts = new CancellationTokenSource(); cts.cancel(); - const manager = createMockManager('ms-python.python:venv', 'Venv'); + const manager = createMockManager(VENV_MANAGER_ID, 'Venv'); const result = await handlePythonPath(testUri, [], [manager], undefined, cts.token); @@ -123,7 +124,7 @@ suite('handlePythonPath', () => { const cts = new CancellationTokenSource(); cts.cancel(); - const manager = createMockManager('ms-python.python:venv', 'Venv'); + const manager = createMockManager(VENV_MANAGER_ID, 'Venv'); const result = await handlePythonPath(testUri, [manager], [], undefined, cts.token); @@ -133,7 +134,7 @@ suite('handlePythonPath', () => { test('reports progress for project managers', async () => { const reporter = { report: sinon.stub() }; - const projectManager = createMockManager('ms-python.python:venv', 'Venv'); + const projectManager = createMockManager(VENV_MANAGER_ID, 'Venv'); await handlePythonPath(testUri, [], [projectManager], reporter); @@ -143,8 +144,8 @@ suite('handlePythonPath', () => { test('reports progress for global managers', async () => { const reporter = { report: sinon.stub() }; - const manager1 = createMockManager('ms-python.python:venv', 'Venv'); - const manager2 = createMockManager('ms-python.python:conda', 'Conda'); + const manager1 = createMockManager(VENV_MANAGER_ID, 'Venv'); + const manager2 = createMockManager(CONDA_MANAGER_ID, 'Conda'); await handlePythonPath(testUri, [manager1, manager2], [], reporter); @@ -156,8 +157,8 @@ suite('handlePythonPath', () => { test('sorts managers by priority order', async () => { // Neither resolves, so both get called — lets us verify call order - const systemManager = createMockManager('ms-python.python:system', 'System'); - const condaManager = createMockManager('ms-python.python:conda', 'Conda'); + const systemManager = createMockManager(SYSTEM_MANAGER_ID, 'System'); + const condaManager = createMockManager(CONDA_MANAGER_ID, 'Conda'); // Pass system first in array, but conda should be tried first (higher priority) await handlePythonPath(testUri, [systemManager, condaManager], []); @@ -166,10 +167,10 @@ suite('handlePythonPath', () => { }); test('returns first resolving manager and stops checking', async () => { - const venvEnv = createMockEnv('ms-python.python:venv'); - const condaEnv = createMockEnv('ms-python.python:conda'); - const venvManager = createMockManager('ms-python.python:venv', 'Venv', venvEnv); - const condaManager = createMockManager('ms-python.python:conda', 'Conda', condaEnv); + const venvEnv = createMockEnv(VENV_MANAGER_ID); + const condaEnv = createMockEnv(CONDA_MANAGER_ID); + const venvManager = createMockManager(VENV_MANAGER_ID, 'Venv', venvEnv); + const condaManager = createMockManager(CONDA_MANAGER_ID, 'Conda', condaEnv); // Conda is higher priority, so it resolves first const result = await handlePythonPath(testUri, [venvManager, condaManager], []); diff --git a/src/test/common/telemetry/helpers.unit.test.ts b/src/test/common/telemetry/helpers.unit.test.ts index 1a36a2224..c35edb42d 100644 --- a/src/test/common/telemetry/helpers.unit.test.ts +++ b/src/test/common/telemetry/helpers.unit.test.ts @@ -2,14 +2,31 @@ import assert from 'node:assert'; import * as sinon from 'sinon'; import { Uri } from 'vscode'; import { PythonEnvironment, PythonProject } from '../../../api'; -import { sendProjectStructureTelemetry } from '../../../common/telemetry/helpers'; import { EventNames } from '../../../common/telemetry/constants'; +import { getTelemetryManagerId, sendProjectStructureTelemetry } from '../../../common/telemetry/helpers'; import * as sender from '../../../common/telemetry/sender'; import * as workspaceApis from '../../../common/workspace.apis'; import type { EnvironmentManagers } from '../../../features/envManagers'; import type { PythonProjectManager } from '../../../features/projectManager'; suite('Telemetry Helpers', () => { + suite('getTelemetryManagerId', () => { + test('returns the full deterministic SHA-256 digest for every manager ID', () => { + assert.strictEqual( + getTelemetryManagerId('ms-python.python:venv'), + 'c3ac61f81b7983a1b2d9f68d1758093845e42254531824ce6e008aab743284de', + ); + assert.strictEqual( + getTelemetryManagerId('third-party:manager'), + '02c9232e62766f581e69792f5853a77b9bae634b9f4f1c428bc93c6df5b96a5c', + ); + }); + + test('returns undefined sentinel only when the manager ID is missing', () => { + assert.strictEqual(getTelemetryManagerId(undefined), 'undefined'); + }); + }); + suite('sendProjectStructureTelemetry', () => { let sendTelemetryEventStub: sinon.SinonStub; let getWorkspaceFoldersStub: sinon.SinonStub; diff --git a/src/test/extensionApi.capabilities.unit.test.ts b/src/test/extensionApi.capabilities.unit.test.ts index d0d6ddb4b..f363f9cf1 100644 --- a/src/test/extensionApi.capabilities.unit.test.ts +++ b/src/test/extensionApi.capabilities.unit.test.ts @@ -16,6 +16,7 @@ import { EnvironmentCapability, PackageCapability } from '../capabilities'; import type { CapabilityContext, Support } from '../capabilities'; import * as logging from '../common/logging'; import { EventNames } from '../common/telemetry/constants'; +import { getTelemetryManagerId } from '../common/telemetry/helpers'; import * as telemetry from '../common/telemetry/sender'; import { allSupportedEnvironmentCapabilities, allSupportedPackageCapabilities } from './capabilityFixtures'; import * as extensionApis from '../common/extension.apis'; @@ -148,7 +149,7 @@ suite('PythonEnvironmentApiImpl - capability queries', () => { await clock.tickAsync(0); assert.ok(envProbe.calledOnceWithExactly(context)); assert.ok(sendTelemetry.calledWithExactly(EventNames.MANAGER_CAPABILITY_QUERY, undefined, { - managerId: ownerId, managerKind: 'environment', managerVersion: '2.0.0', + managerId: getTelemetryManagerId(ownerId), managerKind: 'environment', managerVersion: '2.0.0', capability: EnvironmentCapability.List, outcome: 'supported', isLegacy: false, })); @@ -238,7 +239,7 @@ suite('PythonEnvironmentApiImpl - capability queries', () => { const support = await api.getEnvironmentManagerCapability(ownerId, EnvironmentCapability.List); assert.ok(!support.supported && support.reason.includes(ownerId)); assert.ok(sendTelemetry.calledWithExactly(EventNames.MANAGER_CAPABILITY_QUERY, undefined, { - managerId: 'none', managerKind: 'environment', managerVersion: 'unknown', + managerId: 'undefined', managerKind: 'environment', managerVersion: 'unknown', capability: EnvironmentCapability.List, outcome: 'unavailable', isLegacy: false, })); @@ -283,7 +284,7 @@ suite('PythonEnvironmentApiImpl - capability queries', () => { assert.ok(pkgProbe.notCalled); assert.ok(configuredPackage.calledWithExactly(projectManager, project.uri)); assert.ok(sendTelemetry.calledWithExactly(EventNames.MANAGER_CAPABILITY_QUERY, undefined, { - managerId: `${extensionId}:configured`, managerKind: 'package', managerVersion: '2.1.0', + managerId: getTelemetryManagerId(`${extensionId}:configured`), managerKind: 'package', managerVersion: '2.1.0', capability: PackageCapability.List, outcome: 'unsupported', isLegacy: false, })); diff --git a/src/test/extensionApi.unit.test.ts b/src/test/extensionApi.unit.test.ts index f47ee1896..09975234b 100644 --- a/src/test/extensionApi.unit.test.ts +++ b/src/test/extensionApi.unit.test.ts @@ -9,6 +9,7 @@ import * as managerReady from '../features/common/managerReady'; import { PythonEnvironmentApiImpl } from '../extensionApi'; import type { PythonProjectManager } from '../features/projectManager'; import type { InternalPackageManager } from '../managers/common/registeredManagers'; +import { VENV_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID } from '../common/constants'; suite('PythonEnvironmentApiImpl - onDidChangePythonProjects', () => { test('fires event with correct added and removed projects', () => { @@ -81,7 +82,7 @@ suite('PythonEnvironmentApiImpl - getEnvironment timeout fallback', () => { test('returns the last-known environment while a slower lookup continues in the background', async () => { const scope = Uri.file('/workspace/script.py'); const lastKnown: PythonEnvironment = { - envId: { id: 'default', managerId: 'ms-python.python:venv' }, + envId: { id: 'default', managerId: VENV_MANAGER_ID }, name: 'default', displayName: 'default', displayPath: '/env/default', @@ -107,7 +108,7 @@ suite('PythonEnvironmentApiImpl - getEnvironment timeout fallback', () => { }), ), getLastKnownEnvironment: sinon.stub().withArgs(scope).returns(lastKnown), - getEnvironmentManager: sinon.stub().returns({ id: 'ms-python.python:venv' }), + getEnvironmentManager: sinon.stub().returns({ id: VENV_MANAGER_ID }), } as unknown as ApiArgs[0]; const mockProjectCreators = {} as unknown as ApiArgs[2]; const mockTerminalManager = {} as unknown as ApiArgs[3]; @@ -151,7 +152,7 @@ suite('PythonEnvironmentApiImpl - getEnvironment timeout fallback', () => { }), ), getLastKnownEnvironment: sinon.stub().returns(lastKnown), - getEnvironmentManager: sinon.stub().returns({ id: 'ms-python.python:inline-script' }), + getEnvironmentManager: sinon.stub().returns({ id: INLINE_SCRIPT_MANAGER_ID }), } as unknown as ApiArgs[0]; const api = new PythonEnvironmentApiImpl( diff --git a/src/test/features/common/managerReady.unit.test.ts b/src/test/features/common/managerReady.unit.test.ts index 3b6da542d..f52824022 100644 --- a/src/test/features/common/managerReady.unit.test.ts +++ b/src/test/features/common/managerReady.unit.test.ts @@ -4,6 +4,7 @@ import { Disposable, EventEmitter } from 'vscode'; import * as extensionApis from '../../../common/extension.apis'; import * as logging from '../../../common/logging'; import { EventNames } from '../../../common/telemetry/constants'; +import { getTelemetryManagerId } from '../../../common/telemetry/helpers'; import * as telemetrySender from '../../../common/telemetry/sender'; import { createDeferred } from '../../../common/utils/deferred'; import * as windowApis from '../../../common/window.apis'; @@ -26,6 +27,7 @@ import type { InternalEnvironmentManager, InternalPackageManager, } from '../../../managers/common/registeredManagers'; +import { VENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../../common/constants'; suite('withManagerTimeout', () => { let clock: sinon.SinonFakeTimers; @@ -65,7 +67,7 @@ suite('withManagerTimeout', () => { assert.ok(sendTelemetryStub.calledOnce, 'sendTelemetryEvent should be called once'); const [eventName, , properties] = sendTelemetryStub.firstCall.args; assert.strictEqual(eventName, EventNames.MANAGER_READY_TIMEOUT); - assert.strictEqual(properties.managerId, 'test-ext:venv'); + assert.strictEqual(properties.managerId, getTelemetryManagerId('test-ext:venv')); assert.strictEqual(properties.managerKind, 'environment'); }); @@ -125,7 +127,7 @@ suite('withManagerTimeout', () => { await promise; const [, , properties] = sendTelemetryStub.firstCall.args; - assert.strictEqual(properties.managerId, 'test-ext:pip'); + assert.strictEqual(properties.managerId, getTelemetryManagerId('test-ext:pip')); assert.strictEqual(properties.managerKind, 'package'); }); }); @@ -155,8 +157,8 @@ suite('ManagerReady - race condition handling', () => { id: 'ms-python.python', isActive: true, } as unknown as ReturnType); - sinon.stub(settingHelpers, 'getDefaultEnvManagerSetting').returns('ms-python.python:venv'); - sinon.stub(settingHelpers, 'getDefaultPkgManagerSetting').returns('ms-python.python:pip'); + sinon.stub(settingHelpers, 'getDefaultEnvManagerSetting').returns(VENV_MANAGER_ID); + sinon.stub(settingHelpers, 'getDefaultPkgManagerSetting').returns(DEFAULT_PACKAGE_MANAGER_ID); const mockEm = { onDidChangeEnvironmentManager: envManagerEmitter.event, @@ -180,7 +182,7 @@ suite('ManagerReady - race condition handling', () => { }); test('no install prompt when manager registers before timeout', async () => { - const waitPromise = waitForEnvManagerId(['ms-python.python:venv']); + const waitPromise = waitForEnvManagerId([VENV_MANAGER_ID]); // Flush microtasks so the internal await _deferred.promise completes // and the timeout/deferred is set up await clock.tickAsync(0); @@ -188,7 +190,7 @@ suite('ManagerReady - race condition handling', () => { // Manager registers before timeout envManagerEmitter.fire({ kind: 'registered', - manager: { id: 'ms-python.python:venv' } as unknown as InternalEnvironmentManager, + manager: { id: VENV_MANAGER_ID } as unknown as InternalEnvironmentManager, }); await clock.tickAsync(0); @@ -204,7 +206,7 @@ suite('ManagerReady - race condition handling', () => { test('no install prompt on timeout when extension is installed but manager never registered', async () => { // Extension IS installed (getExtension returns it), but manager never fires registration event - const waitPromise = waitForEnvManagerId(['ms-python.python:venv']); + const waitPromise = waitForEnvManagerId([VENV_MANAGER_ID]); // Flush microtasks so internal await completes and timeout is armed await clock.tickAsync(0); @@ -229,7 +231,7 @@ suite('ManagerReady - race condition handling', () => { const getExtensionStub = extensionApis.getExtension as sinon.SinonStub; getExtensionStub.returns(undefined); // Extension not installed - const waitPromise = waitForEnvManagerId(['ms-python.python:venv']); + const waitPromise = waitForEnvManagerId([VENV_MANAGER_ID]); // Flush microtasks so internal await completes and timeout is armed await clock.tickAsync(0); @@ -246,13 +248,13 @@ suite('ManagerReady - race condition handling', () => { test('manager registered before wait resolves immediately without prompt', async () => { envManagerEmitter.fire({ kind: 'registered', - manager: { id: 'ms-python.python:venv' } as unknown as InternalEnvironmentManager, + manager: { id: VENV_MANAGER_ID } as unknown as InternalEnvironmentManager, }); await clock.tickAsync(0); // Wait should resolve immediately since the manager already registered - const waitPromise = waitForEnvManagerId(['ms-python.python:venv']); + const waitPromise = waitForEnvManagerId([VENV_MANAGER_ID]); await clock.tickAsync(0); await waitPromise; @@ -261,11 +263,11 @@ suite('ManagerReady - race condition handling', () => { }); test('pkg manager wait resolves when registration event fires', async () => { - const waitPromise = waitForPkgManagerId(['ms-python.python:pip']); + const waitPromise = waitForPkgManagerId([DEFAULT_PACKAGE_MANAGER_ID]); pkgManagerEmitter.fire({ kind: 'registered', - manager: { id: 'ms-python.python:pip' } as unknown as InternalPackageManager, + manager: { id: DEFAULT_PACKAGE_MANAGER_ID } as unknown as InternalPackageManager, }); await clock.tickAsync(0); diff --git a/src/test/features/envCommands.inlineSelectionErrors.unit.test.ts b/src/test/features/envCommands.inlineSelectionErrors.unit.test.ts index 0c90e0356..c95225eca 100644 --- a/src/test/features/envCommands.inlineSelectionErrors.unit.test.ts +++ b/src/test/features/envCommands.inlineSelectionErrors.unit.test.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import { QuickInputButtons, Uri } from 'vscode'; import { EnvironmentManager, PythonEnvironment, PythonProject } from '../../api'; -import { INLINE_SCRIPT_MANAGER_ID } from '../../common/constants'; +import { INLINE_SCRIPT_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, VENV_MANAGER_ID } from '../../common/constants'; import { InlineScriptEnvironmentModifiedError, InlineScriptPackagesNotManagedError, @@ -68,7 +68,7 @@ suite('Inline environment selection error feedback', () => { } else if (kind === 'environment') { const provider: EnvironmentManager = { name: 'inline-script', - preferredPackageManagerId: 'ms-python.python:pip', + preferredPackageManagerId: DEFAULT_PACKAGE_MANAGER_ID, get: async () => environment, set: async () => undefined, getEnvironments: async () => [environment], @@ -152,7 +152,7 @@ suite('Inline environment package command guard', () => { test('still resolves package commands for ordinary environments', async () => { const environment = createMockPythonEnvironment({ envPath: path.join(process.cwd(), 'venv-package-env'), - managerId: 'ms-python.python:venv', + managerId: VENV_MANAGER_ID, }); const options = await getPackageCommandOptions(uri, createManagers(environment), projects); diff --git a/src/test/features/envCommands.unit.test.ts b/src/test/features/envCommands.unit.test.ts index 1fe9e997d..f269eb62c 100644 --- a/src/test/features/envCommands.unit.test.ts +++ b/src/test/features/envCommands.unit.test.ts @@ -5,7 +5,7 @@ import * as typeMoq from 'typemoq'; import { CancellationTokenSource, Memento, Terminal, Uri } from 'vscode'; import { PythonEnvironment, PythonEnvironmentApi, PythonProject } from '../../api'; import * as commandApi from '../../common/command.api'; -import { INLINE_SCRIPT_ENVS_KEY, INLINE_SCRIPT_MANAGER_ID } from '../../common/constants'; +import { INLINE_SCRIPT_ENVS_KEY, INLINE_SCRIPT_MANAGER_ID, VENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; import { Interpreter } from '../../common/localize'; import * as persistentState from '../../common/persistentState'; import * as managerApi from '../../common/pickers/managers'; @@ -44,7 +44,7 @@ import { setupNonThenable } from '../mocks/helper'; import { createMockPythonEnvironment } from '../mocks/pythonEnvironment'; suite('Environment removal command ownership', () => { - for (const managerId of [INLINE_SCRIPT_MANAGER_ID, 'ms-python.python:venv']) { + for (const managerId of [INLINE_SCRIPT_MANAGER_ID, VENV_MANAGER_ID]) { test(`routes a ${managerId} project environment without changing ordinary removal`, async () => { const project: PythonProject = { name: 'script', @@ -57,7 +57,7 @@ suite('Environment removal command ownership', () => { const remove = sinon.stub().resolves(); const owner = new InternalEnvironmentManager(managerId, { name: 'test', - preferredPackageManagerId: 'ms-python.python:pip', + preferredPackageManagerId: DEFAULT_PACKAGE_MANAGER_ID, get: async () => environment, set: async () => undefined, getEnvironments: async () => [environment], diff --git a/src/test/features/envManagers.lastKnown.unit.test.ts b/src/test/features/envManagers.lastKnown.unit.test.ts index 8fb2115d8..8d95cdd72 100644 --- a/src/test/features/envManagers.lastKnown.unit.test.ts +++ b/src/test/features/envManagers.lastKnown.unit.test.ts @@ -30,6 +30,7 @@ import * as settingHelpers from '../../features/settings/settingHelpers'; import type { PythonProjectManager } from '../../features/projectManager'; import type { InternalPackageManager } from '../../managers/common/registeredManagers'; import { setupNonThenable } from '../mocks/helper'; +import { DEFAULT_PACKAGE_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID } from '../../common/constants'; suite('PythonEnvironmentManagers getLastKnownEnvironment', () => { let envManagers: PythonEnvironmentManagers; @@ -102,7 +103,7 @@ suite('PythonEnvironmentManagers getLastKnownEnvironment', () => { const manager = { name, displayName: 'Test Env Manager', - preferredPackageManagerId: 'ms-python.python:pip', + preferredPackageManagerId: DEFAULT_PACKAGE_MANAGER_ID, onDidChangeEnvironment: onDidChangeEnvironment.event, onDidChangeEnvironments: onDidChangeEnvironments.event, get: getImpl, @@ -120,7 +121,7 @@ suite('PythonEnvironmentManagers getLastKnownEnvironment', () => { return id; } - function stubPackageManager(id = 'ms-python.python:pip'): void { + function stubPackageManager(id = DEFAULT_PACKAGE_MANAGER_ID): void { const packageManager = typeMoq.Mock.ofType(); setupNonThenable(packageManager); packageManager.setup((manager) => manager.id).returns(() => id); @@ -183,7 +184,7 @@ suite('PythonEnvironmentManagers getLastKnownEnvironment', () => { await recovered; assert.ok(events.some((event) => event.uri === scope && event.new === environment)); - assert.strictEqual(envManagers.getEnvironmentManager(scope)?.id, 'ms-python.python:inline-script'); + assert.strictEqual(envManagers.getEnvironmentManager(scope)?.id, INLINE_SCRIPT_MANAGER_ID); }); test('returns the active environment after it has been resolved', async () => { @@ -686,7 +687,7 @@ suite('PythonEnvironmentManagers getLastKnownEnvironment', () => { sinon.stub(settingHelpers, 'getExactPythonProjectSetting').returns({ path: 'script.py', envManager: selectedId, - packageManager: 'ms-python.python:pip', + packageManager: DEFAULT_PACKAGE_MANAGER_ID, _inlineScriptRegistration: { kind: 'created' }, }); markInlineScript(script); diff --git a/src/test/features/envManagers.unit.test.ts b/src/test/features/envManagers.unit.test.ts index 3879a2fce..b93f2ef72 100644 --- a/src/test/features/envManagers.unit.test.ts +++ b/src/test/features/envManagers.unit.test.ts @@ -12,6 +12,7 @@ import * as frameUtils from '../../common/utils/frameUtils'; import * as workspaceApis from '../../common/workspace.apis'; import { PythonEnvironmentManagers } from '../../features/envManagers'; import type { PythonProjectManager } from '../../features/projectManager'; +import { SYSTEM_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, CONDA_MANAGER_ID } from '../../common/constants'; suite('PythonEnvironmentManagers - getEnvironment', () => { let sandbox: sinon.SinonSandbox; @@ -19,7 +20,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { let mockProjectManager: sinon.SinonStubbedInstance; const env311: PythonEnvironment = { - envId: { id: 'system-311', managerId: 'ms-python.python:system' }, + envId: { id: 'system-311', managerId: SYSTEM_MANAGER_ID }, name: 'Python 3.11', displayName: 'Python 3.11.15', version: '3.11.15', @@ -30,7 +31,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { }; const env314: PythonEnvironment = { - envId: { id: 'system-314', managerId: 'ms-python.python:system' }, + envId: { id: 'system-314', managerId: SYSTEM_MANAGER_ID }, name: 'Python 3.14', displayName: 'Python 3.14.4', version: '3.14.4', @@ -50,7 +51,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { sandbox.stub(workspaceApis, 'getConfiguration').returns({ get: (key: string, defaultValue?: unknown) => { if (key === 'defaultEnvManager') { - return 'ms-python.python:system'; + return SYSTEM_MANAGER_ID; } if (key === 'pythonProjects') { return []; @@ -85,7 +86,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { const fakeManager = { name: managerId.split(':')[1], displayName: managerId, - preferredPackageManagerId: 'ms-python.python:pip', + preferredPackageManagerId: DEFAULT_PACKAGE_MANAGER_ID, getVersion, get: getStub, set: sandbox.stub().resolves(), @@ -101,7 +102,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { test('should NOT update cache when manager.get() returns a different env than what was set', async () => { // Register a system manager whose get() returns env314 (the "latest") const getStub = sandbox.stub().resolves(env314); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); // Simulate that initial selection set env311 via setEnvironment await envManagers.setEnvironment(undefined, env311, false); @@ -128,7 +129,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { test('should NOT fire change events on read', async () => { const getStub = sandbox.stub().resolves(env311); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); const changeEvents: any[] = []; envManagers.onDidChangeActiveEnvironment((e) => changeEvents.push(e)); @@ -144,7 +145,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { test('should still return the correct env from manager.get()', async () => { const getStub = sandbox.stub().resolves(env311); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); const result = await envManagers.getEnvironment(undefined); assert.strictEqual(result?.envId.id, 'system-311'); @@ -154,18 +155,18 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { test('should delegate getVersion to the registered manager', async () => { const version = parsePep440Version('1.2.3'); const getVersion = sandbox.stub().resolves(version); - registerFakeManager('ms-python.python:system', sandbox.stub().resolves(env311), getVersion); + registerFakeManager(SYSTEM_MANAGER_ID, sandbox.stub().resolves(env311), getVersion); - const result = await envManagers.getEnvironmentManager('ms-python.python:system')?.getVersion(); + const result = await envManagers.getEnvironmentManager(SYSTEM_MANAGER_ID)?.getVersion(); assert.deepStrictEqual(result, version); assert.ok(getVersion.calledOnce, 'Should delegate to manager.getVersion()'); }); test('should return undefined when the registered manager does not provide getVersion', async () => { - registerFakeManager('ms-python.python:system', sandbox.stub().resolves(env311)); + registerFakeManager(SYSTEM_MANAGER_ID, sandbox.stub().resolves(env311)); - const result = await envManagers.getEnvironmentManager('ms-python.python:system')?.getVersion(); + const result = await envManagers.getEnvironmentManager(SYSTEM_MANAGER_ID)?.getVersion(); assert.strictEqual(result, undefined); }); @@ -182,7 +183,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { // in the map, find nothing, check the cache (empty), and return undefined. // This exercises the fallback path in getEnvironmentManager beyond the size === 0 guard. const getStub = sandbox.stub().resolves(env311); - registerFakeManager('ms-python.python:conda', getStub); + registerFakeManager(CONDA_MANAGER_ID, getStub); const result = await envManagers.getEnvironment(Uri.file('/some/unrelated/path')); assert.strictEqual( @@ -194,7 +195,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { test('setEnvironment should still fire change events and update cache', async () => { const getStub = sandbox.stub().resolves(env311); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); const changeEvents: any[] = []; envManagers.onDidChangeActiveEnvironment((e) => changeEvents.push(e)); @@ -214,7 +215,7 @@ suite('PythonEnvironmentManagers - getEnvironment', () => { // 3. The selection should NOT flip to env314 const getStub = sandbox.stub().resolves(env314); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); // Step 1: Initial selection picks env311 await envManagers.setEnvironment(undefined, env311, false); @@ -246,7 +247,7 @@ suite('PythonEnvironmentManagers - refreshEnvironment', () => { let mockProjectManager: sinon.SinonStubbedInstance; const env311: PythonEnvironment = { - envId: { id: 'system-311', managerId: 'ms-python.python:system' }, + envId: { id: 'system-311', managerId: SYSTEM_MANAGER_ID }, name: 'Python 3.11', displayName: 'Python 3.11.15', version: '3.11.15', @@ -257,7 +258,7 @@ suite('PythonEnvironmentManagers - refreshEnvironment', () => { }; const env314: PythonEnvironment = { - envId: { id: 'system-314', managerId: 'ms-python.python:system' }, + envId: { id: 'system-314', managerId: SYSTEM_MANAGER_ID }, name: 'Python 3.14', displayName: 'Python 3.14.4', version: '3.14.4', @@ -273,7 +274,7 @@ suite('PythonEnvironmentManagers - refreshEnvironment', () => { sandbox.stub(workspaceApis, 'getConfiguration').returns({ get: (key: string, defaultValue?: unknown) => { if (key === 'defaultEnvManager') { - return 'ms-python.python:system'; + return SYSTEM_MANAGER_ID; } if (key === 'pythonProjects') { return []; @@ -301,7 +302,7 @@ suite('PythonEnvironmentManagers - refreshEnvironment', () => { const fakeManager = { name: managerId.split(':')[1], displayName: managerId, - preferredPackageManagerId: 'ms-python.python:pip', + preferredPackageManagerId: DEFAULT_PACKAGE_MANAGER_ID, get: getStub, set: sandbox.stub().resolves(), resolve: sandbox.stub().resolves(undefined), @@ -315,7 +316,7 @@ suite('PythonEnvironmentManagers - refreshEnvironment', () => { test('should fire change event when manager reports a new environment', async () => { const getStub = sandbox.stub().resolves(env314); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); // Set initial env await envManagers.setEnvironment(undefined, env311, false); @@ -340,7 +341,7 @@ suite('PythonEnvironmentManagers - refreshEnvironment', () => { test('should NOT fire change event when manager reports same environment', async () => { const getStub = sandbox.stub().resolves(env311); - registerFakeManager('ms-python.python:system', getStub); + registerFakeManager(SYSTEM_MANAGER_ID, getStub); // Set initial env to env311 await envManagers.setEnvironment(undefined, env311, false); @@ -384,7 +385,7 @@ suite('PythonEnvironmentManagers - clearCache', () => { { name, displayName: name, - preferredPackageManagerId: 'ms-python.python:pip', + preferredPackageManagerId: DEFAULT_PACKAGE_MANAGER_ID, get: sandbox.stub().resolves(undefined), set: sandbox.stub().resolves(), resolve: sandbox.stub().resolves(undefined), diff --git a/src/test/features/interpreterSelection.unit.test.ts b/src/test/features/interpreterSelection.unit.test.ts index 0fd7f6940..5300fe257 100644 --- a/src/test/features/interpreterSelection.unit.test.ts +++ b/src/test/features/interpreterSelection.unit.test.ts @@ -23,6 +23,7 @@ import type { EnvironmentManagers } from '../../features/envManagers'; import type { PythonProjectManager } from '../../features/projectManager'; import type { InternalEnvironmentManager } from '../../managers/common/registeredManagers'; import { NativePythonFinder } from '../../managers/common/nativePythonFinder'; +import { VENV_MANAGER_ID, SYSTEM_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, CONDA_MANAGER_ID } from '../../common/constants'; /** * Creates a mock WorkspaceConfiguration for testing. @@ -49,7 +50,7 @@ suite('Interpreter Selection - Priority Chain', () => { const testUri = Uri.file('/test/workspace'); const mockVenvEnv: PythonEnvironment = { - envId: { id: 'venv-env-1', managerId: 'ms-python.python:venv' }, + envId: { id: 'venv-env-1', managerId: VENV_MANAGER_ID }, name: 'Test Venv', displayName: 'Test Venv', version: '3.11.0', @@ -59,7 +60,7 @@ suite('Interpreter Selection - Priority Chain', () => { execInfo: { run: { executable: '/test/workspace/.venv/bin/python' } }, }; const mockSystemEnv: PythonEnvironment = { - envId: { id: 'system-env-1', managerId: 'ms-python.python:system' }, + envId: { id: 'system-env-1', managerId: SYSTEM_MANAGER_ID }, name: 'System Python', displayName: 'System Python 3.11', version: '3.11.0', @@ -74,7 +75,7 @@ suite('Interpreter Selection - Priority Chain', () => { // Create mock managers mockVenvManager = { - id: 'ms-python.python:venv', + id: VENV_MANAGER_ID, name: 'venv', displayName: 'Venv', get: sandbox.stub(), @@ -82,7 +83,7 @@ suite('Interpreter Selection - Priority Chain', () => { } as unknown as sinon.SinonStubbedInstance; mockSystemManager = { - id: 'ms-python.python:system', + id: SYSTEM_MANAGER_ID, name: 'system', displayName: 'System', get: sandbox.stub(), @@ -110,10 +111,10 @@ suite('Interpreter Selection - Priority Chain', () => { // Default: getEnvironmentManager returns the manager for a given ID mockEnvManagers.getEnvironmentManager.callsFake((scope: unknown) => { const id = typeof scope === 'string' ? scope : undefined; - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } return undefined; @@ -131,8 +132,8 @@ suite('Interpreter Selection - Priority Chain', () => { createMockConfig([ { path: '.', - envManager: 'ms-python.python:venv', - packageManager: 'ms-python.python:pip', + envManager: VENV_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ]) as WorkspaceConfiguration, ); @@ -151,7 +152,7 @@ suite('Interpreter Selection - Priority Chain', () => { ); assert.strictEqual(result.source, 'pythonProjects'); - assert.strictEqual(result.manager.id, 'ms-python.python:venv'); + assert.strictEqual(result.manager.id, VENV_MANAGER_ID); }); }); @@ -161,7 +162,7 @@ suite('Interpreter Selection - Priority Chain', () => { sandbox.stub(workspaceApis, 'getConfiguration').returns(createMockConfig([]) as WorkspaceConfiguration); sandbox.stub(helpers, 'getUserConfiguredSetting').callsFake((section: string, key: string) => { if (section === 'python-envs' && key === 'defaultEnvManager') { - return 'ms-python.python:venv'; + return VENV_MANAGER_ID; } return undefined; }); @@ -175,7 +176,7 @@ suite('Interpreter Selection - Priority Chain', () => { ); assert.strictEqual(result.source, 'defaultEnvManager'); - assert.strictEqual(result.manager.id, 'ms-python.python:venv'); + assert.strictEqual(result.manager.id, VENV_MANAGER_ID); }); test('should skip to Priority 3 when defaultEnvManager is not user-configured (only fallback)', async () => { @@ -657,7 +658,7 @@ suite('Interpreter Selection - Priority Chain', () => { // API resolves the homebrew path to a homebrew environment const homebrewEnv: PythonEnvironment = { - envId: { id: 'homebrew-env', managerId: 'ms-python.python:system' }, + envId: { id: 'homebrew-env', managerId: SYSTEM_MANAGER_ID }, name: 'Homebrew Python', displayName: 'Python 3.14.2 (homebrew)', version: '3.14.2', @@ -714,7 +715,7 @@ suite('Interpreter Selection - Priority Chain', () => { ); assert.strictEqual(result.source, 'autoDiscovery'); - assert.strictEqual(result.manager.id, 'ms-python.python:venv'); + assert.strictEqual(result.manager.id, VENV_MANAGER_ID); assert.strictEqual(result.environment, mockVenvEnv); }); @@ -732,7 +733,7 @@ suite('Interpreter Selection - Priority Chain', () => { ); assert.strictEqual(result.source, 'autoDiscovery'); - assert.strictEqual(result.manager.id, 'ms-python.python:system'); + assert.strictEqual(result.manager.id, SYSTEM_MANAGER_ID); }); test('should throw error when no managers are available', async () => { @@ -816,7 +817,7 @@ suite('Interpreter Selection - Priority Chain', () => { sandbox.stub(helpers, 'getUserConfiguredSetting').returns(undefined); const mockCondaManager = { - id: 'ms-python.python:conda', + id: CONDA_MANAGER_ID, name: 'conda', displayName: 'Conda', get: sandbox.stub().resolves(undefined), @@ -839,7 +840,7 @@ suite('Interpreter Selection - Priority Chain', () => { ); assert.strictEqual(result.source, 'autoDiscovery'); - assert.strictEqual(result.manager.id, 'ms-python.python:conda'); + assert.strictEqual(result.manager.id, CONDA_MANAGER_ID); }); }); }); @@ -855,7 +856,7 @@ suite('Interpreter Selection - applyInitialEnvironmentSelection', () => { const testUri = Uri.file('/test/workspace'); const mockVenvEnv: PythonEnvironment = { - envId: { id: 'venv-env-1', managerId: 'ms-python.python:venv' }, + envId: { id: 'venv-env-1', managerId: VENV_MANAGER_ID }, name: 'Test Venv', displayName: 'Test Venv', version: '3.11.0', @@ -870,7 +871,7 @@ suite('Interpreter Selection - applyInitialEnvironmentSelection', () => { resetSettingWarnings(); mockVenvManager = { - id: 'ms-python.python:venv', + id: VENV_MANAGER_ID, name: 'venv', displayName: 'Venv', get: sandbox.stub().resolves(mockVenvEnv), @@ -878,7 +879,7 @@ suite('Interpreter Selection - applyInitialEnvironmentSelection', () => { } as unknown as sinon.SinonStubbedInstance; mockSystemManager = { - id: 'ms-python.python:system', + id: SYSTEM_MANAGER_ID, name: 'system', displayName: 'System', get: sandbox.stub(), @@ -906,10 +907,10 @@ suite('Interpreter Selection - applyInitialEnvironmentSelection', () => { mockEnvManagers.getEnvironmentManager.callsFake((scope: unknown) => { const id = typeof scope === 'string' ? scope : undefined; - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } return undefined; @@ -1003,7 +1004,7 @@ suite('Interpreter Selection - applyInitialEnvironmentSelection', () => { prefix: path.join(testUri.fsPath, 'python-embedded'), }); const mockResolvedEnv: PythonEnvironment = { - envId: { id: 'embedded-env', managerId: 'ms-python.python:system' }, + envId: { id: 'embedded-env', managerId: SYSTEM_MANAGER_ID }, name: 'Embedded Python', displayName: 'Python 3.12.10', version: '3.12.10', @@ -1316,10 +1317,10 @@ suite('Interpreter Selection - applyInitialEnvironmentSelection', () => { if (id === hatchManagerId && hatchRegistered) { return mockHatchManager; } - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } return undefined; @@ -1384,7 +1385,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { let mockSystemManager: sinon.SinonStubbedInstance; const mockSystemEnv: PythonEnvironment = { - envId: { id: 'system-env-1', managerId: 'ms-python.python:system' }, + envId: { id: 'system-env-1', managerId: SYSTEM_MANAGER_ID }, name: 'System Python', displayName: 'System Python 3.11', version: '3.11.0', @@ -1398,7 +1399,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { sandbox = sinon.createSandbox(); mockVenvManager = { - id: 'ms-python.python:venv', + id: VENV_MANAGER_ID, name: 'venv', displayName: 'Venv', get: sandbox.stub(), @@ -1406,7 +1407,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { } as unknown as sinon.SinonStubbedInstance; mockSystemManager = { - id: 'ms-python.python:system', + id: SYSTEM_MANAGER_ID, name: 'system', displayName: 'System', get: sandbox.stub(), @@ -1430,10 +1431,10 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { mockEnvManagers.getEnvironmentManager.callsFake((scope: unknown) => { const id = typeof scope === 'string' ? scope : undefined; - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } if (id === undefined) { @@ -1450,7 +1451,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { test('should use user-configured defaultEnvManager for global scope', async () => { sandbox.stub(helpers, 'getUserConfiguredSetting').callsFake((section: string, key: string) => { if (section === 'python-envs' && key === 'defaultEnvManager') { - return 'ms-python.python:venv'; + return VENV_MANAGER_ID; } return undefined; }); @@ -1462,7 +1463,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { ); assert.strictEqual(result.source, 'defaultEnvManager'); - assert.strictEqual(result.manager.id, 'ms-python.python:venv'); + assert.strictEqual(result.manager.id, VENV_MANAGER_ID); }); test('should use defaultInterpreterPath for global scope when configured', async () => { @@ -1547,7 +1548,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { // API resolves the homebrew path to a homebrew environment const homebrewEnv: PythonEnvironment = { - envId: { id: 'homebrew-env', managerId: 'ms-python.python:system' }, + envId: { id: 'homebrew-env', managerId: SYSTEM_MANAGER_ID }, name: 'Homebrew Python', displayName: 'Python 3.14.2 (homebrew)', version: '3.14.2', @@ -1591,7 +1592,7 @@ suite('Interpreter Selection - resolveGlobalEnvironmentByPriority', () => { ); assert.strictEqual(result.source, 'autoDiscovery'); - assert.strictEqual(result.manager.id, 'ms-python.python:system'); + assert.strictEqual(result.manager.id, SYSTEM_MANAGER_ID); }); test('should silently skip ${workspaceFolder} in defaultInterpreterPath for global scope (issue #1316)', async () => { @@ -1633,7 +1634,7 @@ suite('Interpreter Selection - registerInterpreterSettingsChangeListener', () => const testUri = Uri.file('/test/workspace'); const mockVenvEnv: PythonEnvironment = { - envId: { id: 'venv-env-1', managerId: 'ms-python.python:venv' }, + envId: { id: 'venv-env-1', managerId: VENV_MANAGER_ID }, name: 'Test Venv', displayName: 'Test Venv', version: '3.11.0', @@ -1647,7 +1648,7 @@ suite('Interpreter Selection - registerInterpreterSettingsChangeListener', () => sandbox = sinon.createSandbox(); mockVenvManager = { - id: 'ms-python.python:venv', + id: VENV_MANAGER_ID, name: 'venv', displayName: 'Venv', get: sandbox.stub().resolves(mockVenvEnv), @@ -1655,7 +1656,7 @@ suite('Interpreter Selection - registerInterpreterSettingsChangeListener', () => } as unknown as sinon.SinonStubbedInstance; mockSystemManager = { - id: 'ms-python.python:system', + id: SYSTEM_MANAGER_ID, name: 'system', displayName: 'System', get: sandbox.stub(), @@ -1683,10 +1684,10 @@ suite('Interpreter Selection - registerInterpreterSettingsChangeListener', () => mockEnvManagers.getEnvironmentManager.callsFake((scope: unknown) => { const id = typeof scope === 'string' ? scope : undefined; - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } return undefined; @@ -1778,7 +1779,7 @@ suite('Interpreter Selection - registerInterpreterSettingsChangeListener', () => }); mockApi.resolveEnvironment.resolves({ ...mockVenvEnv, - envId: { id: 'environment-b', managerId: 'ms-python.python:system' }, + envId: { id: 'environment-b', managerId: SYSTEM_MANAGER_ID }, execInfo: { run: { executable: correctedInterpreter } }, }); @@ -1954,7 +1955,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { const testUri = Uri.file('/test/workspace'); const mockVenvEnv: PythonEnvironment = { - envId: { id: 'venv-env-1', managerId: 'ms-python.python:venv' }, + envId: { id: 'venv-env-1', managerId: VENV_MANAGER_ID }, name: 'Test Venv', displayName: 'Test Venv', version: '3.11.0', @@ -1968,7 +1969,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { sandbox = sinon.createSandbox(); mockVenvManager = { - id: 'ms-python.python:venv', + id: VENV_MANAGER_ID, name: 'venv', displayName: 'Venv', get: sandbox.stub().resolves(mockVenvEnv), @@ -1976,7 +1977,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { } as unknown as sinon.SinonStubbedInstance; mockSystemManager = { - id: 'ms-python.python:system', + id: SYSTEM_MANAGER_ID, name: 'system', displayName: 'System', get: sandbox.stub(), @@ -1984,7 +1985,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { } as unknown as sinon.SinonStubbedInstance; mockCondaManager = { - id: 'ms-python.python:conda', + id: CONDA_MANAGER_ID, name: 'conda', displayName: 'Conda', get: sandbox.stub(), @@ -2012,13 +2013,13 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { mockEnvManagers.getEnvironmentManager.callsFake((scope: unknown) => { const id = typeof scope === 'string' ? scope : undefined; - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } - if (id === 'ms-python.python:conda') { + if (id === CONDA_MANAGER_ID) { return mockCondaManager; } return undefined; @@ -2035,7 +2036,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { sandbox.stub(workspaceApis, 'getConfiguration').returns(createMockConfig([]) as WorkspaceConfiguration); sandbox.stub(helpers, 'getUserConfiguredSetting').callsFake((section: string, key: string) => { if (section === 'python-envs' && key === 'defaultEnvManager') { - return 'ms-python.python:conda'; // User wants conda + return CONDA_MANAGER_ID; // User wants conda } return undefined; }); @@ -2051,7 +2052,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { // The result should use the user's configured manager (conda), not the cached one assert.strictEqual(result.source, 'defaultEnvManager'); - assert.strictEqual(result.manager.id, 'ms-python.python:conda'); + assert.strictEqual(result.manager.id, CONDA_MANAGER_ID); }); test('should use pythonProjects manager even when defaultEnvManager is set', async () => { @@ -2061,8 +2062,8 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { createMockConfig([ { path: '.', - envManager: 'ms-python.python:venv', // Project says venv - packageManager: 'ms-python.python:pip', + envManager: VENV_MANAGER_ID, // Project says venv + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ]) as WorkspaceConfiguration, ); @@ -2073,7 +2074,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { sandbox.stub(helpers, 'getUserConfiguredSetting').callsFake((section: string, key: string) => { if (section === 'python-envs' && key === 'defaultEnvManager') { - return 'ms-python.python:conda'; // User's default is conda + return CONDA_MANAGER_ID; // User's default is conda } return undefined; }); @@ -2088,7 +2089,7 @@ suite('Interpreter Selection - Settings over Cache Priority', () => { // pythonProjects[] (venv) should take priority over defaultEnvManager (conda) assert.strictEqual(result.source, 'pythonProjects'); - assert.strictEqual(result.manager.id, 'ms-python.python:venv'); + assert.strictEqual(result.manager.id, VENV_MANAGER_ID); }); test('should fall back to auto-discovery when no user settings configured', async () => { @@ -2122,7 +2123,7 @@ suite('Interpreter Selection - Multi-Root Workspace', () => { const folder2Uri = Uri.file('/workspace/folder2'); const mockVenvEnv1: PythonEnvironment = { - envId: { id: 'venv-env-folder1', managerId: 'ms-python.python:venv' }, + envId: { id: 'venv-env-folder1', managerId: VENV_MANAGER_ID }, name: 'Folder1 Venv', displayName: 'Folder1 Venv 3.11', version: '3.11.0', @@ -2133,7 +2134,7 @@ suite('Interpreter Selection - Multi-Root Workspace', () => { }; const mockVenvEnv2: PythonEnvironment = { - envId: { id: 'venv-env-folder2', managerId: 'ms-python.python:venv' }, + envId: { id: 'venv-env-folder2', managerId: VENV_MANAGER_ID }, name: 'Folder2 Venv', displayName: 'Folder2 Venv 3.12', version: '3.12.0', @@ -2147,7 +2148,7 @@ suite('Interpreter Selection - Multi-Root Workspace', () => { sandbox = sinon.createSandbox(); mockVenvManager = { - id: 'ms-python.python:venv', + id: VENV_MANAGER_ID, name: 'venv', displayName: 'Venv', get: sandbox.stub(), @@ -2155,7 +2156,7 @@ suite('Interpreter Selection - Multi-Root Workspace', () => { } as unknown as sinon.SinonStubbedInstance; mockSystemManager = { - id: 'ms-python.python:system', + id: SYSTEM_MANAGER_ID, name: 'system', displayName: 'System', get: sandbox.stub(), @@ -2183,10 +2184,10 @@ suite('Interpreter Selection - Multi-Root Workspace', () => { mockEnvManagers.getEnvironmentManager.callsFake((scope: unknown) => { const id = typeof scope === 'string' ? scope : undefined; - if (id === 'ms-python.python:venv') { + if (id === VENV_MANAGER_ID) { return mockVenvManager; } - if (id === 'ms-python.python:system') { + if (id === SYSTEM_MANAGER_ID) { return mockSystemManager; } return undefined; @@ -2301,11 +2302,11 @@ suite('Interpreter Selection - Multi-Root Workspace', () => { sandbox.stub(workspaceApis, 'getConfiguration').callsFake((_section?: string, scope?: unknown) => { const scopeUri = scope as Uri | undefined; if (scopeUri?.fsPath === folder1Uri.fsPath) { - return createMockConfig([{ path: '.', envManager: 'ms-python.python:venv' }]) as WorkspaceConfiguration; + return createMockConfig([{ path: '.', envManager: VENV_MANAGER_ID }]) as WorkspaceConfiguration; } if (scopeUri?.fsPath === folder2Uri.fsPath) { return createMockConfig([ - { path: '.', envManager: 'ms-python.python:system' }, + { path: '.', envManager: SYSTEM_MANAGER_ID }, ]) as WorkspaceConfiguration; } return createMockConfig([]) as WorkspaceConfiguration; diff --git a/src/test/features/projectManager.fileEvents.unit.test.ts b/src/test/features/projectManager.fileEvents.unit.test.ts index 5a3131d41..8ff6620ad 100644 --- a/src/test/features/projectManager.fileEvents.unit.test.ts +++ b/src/test/features/projectManager.fileEvents.unit.test.ts @@ -7,6 +7,7 @@ import { PythonProjectManagerImpl } from '../../features/projectManager'; import * as settingHelpers from '../../features/settings/settingHelpers'; import { PythonProjectsImpl } from '../../features/projectManager'; import { MockWorkspaceConfiguration } from '../mocks/mockWorkspaceConfig'; +import { VENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, PYENV_MANAGER_ID, CONDA_MANAGER_ID } from '../../common/constants'; /** * Returns a platform-appropriate workspace path for testing. @@ -76,10 +77,10 @@ suite('Project Manager File Event Handling', () => { return [] as unknown as T; } if (key === 'defaultEnvManager') { - return 'ms-python.python:venv' as T; + return VENV_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:pip' as T; + return DEFAULT_PACKAGE_MANAGER_ID as T; } return defaultValue; }; @@ -311,8 +312,8 @@ suite('updatePythonProjectSettingPath', () => { return [ { path: 'old-project', - envManager: 'ms-python.python:venv', - packageManager: 'ms-python.python:pip', + envManager: VENV_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ] as unknown as T; } @@ -352,8 +353,8 @@ suite('updatePythonProjectSettingPath', () => { return [ { path: 'other-project', - envManager: 'ms-python.python:venv', - packageManager: 'ms-python.python:pip', + envManager: VENV_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ] as unknown as T; } @@ -390,8 +391,8 @@ suite('updatePythonProjectSettingPath', () => { return [ { path: 'pyenv-project', - envManager: 'ms-python.python:pyenv', - packageManager: 'ms-python.python:conda', + envManager: PYENV_MANAGER_ID, + packageManager: CONDA_MANAGER_ID, }, ] as unknown as T; } @@ -417,12 +418,12 @@ suite('updatePythonProjectSettingPath', () => { assert.strictEqual(updatedProjects[0].path, 'pyenv-project-renamed', 'Path should be updated'); assert.strictEqual( updatedProjects[0].envManager, - 'ms-python.python:pyenv', + PYENV_MANAGER_ID, 'envManager should be preserved (not reset to default)', ); assert.strictEqual( updatedProjects[0].packageManager, - 'ms-python.python:conda', + CONDA_MANAGER_ID, 'packageManager should be preserved (not reset to default)', ); }); diff --git a/src/test/features/projectManager.initialize.unit.test.ts b/src/test/features/projectManager.initialize.unit.test.ts index 0f65addd7..c75a559bc 100644 --- a/src/test/features/projectManager.initialize.unit.test.ts +++ b/src/test/features/projectManager.initialize.unit.test.ts @@ -9,6 +9,7 @@ import { PythonProjectManagerImpl } from '../../features/projectManager'; import * as settingHelpers from '../../features/settings/settingHelpers'; import type { PythonProjectSettings } from '../../features/projectManager'; import { MockWorkspaceConfiguration } from '../mocks/mockWorkspaceConfig'; +import { VENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, CONDA_MANAGER_ID, POETRY_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID } from '../../common/constants'; /** * Returns a platform-appropriate workspace path for testing. @@ -124,17 +125,17 @@ suite('Project Manager Initialization - Settings Preservation', () => { if (key === 'pythonProjects') { // These are existing project settings that should NOT be overwritten return [ - { path: 'alice', envManager: 'ms-python.python:venv', packageManager: 'ms-python.python:pip' }, - { path: 'alice/bob', envManager: 'ms-python.python:venv', packageManager: 'ms-python.python:pip' }, - { path: 'ada', envManager: 'ms-python.python:venv', packageManager: 'ms-python.python:pip' }, + { path: 'alice', envManager: VENV_MANAGER_ID, packageManager: DEFAULT_PACKAGE_MANAGER_ID }, + { path: 'alice/bob', envManager: VENV_MANAGER_ID, packageManager: DEFAULT_PACKAGE_MANAGER_ID }, + { path: 'ada', envManager: VENV_MANAGER_ID, packageManager: DEFAULT_PACKAGE_MANAGER_ID }, ] as unknown as T; } if (key === 'defaultEnvManager') { // User changed this to conda - return 'ms-python.python:conda' as T; + return CONDA_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:conda' as T; + return CONDA_MANAGER_ID as T; } return defaultValue; }; @@ -266,15 +267,15 @@ suite('Project Manager Initialization - Settings Preservation', () => { test('changing defaultEnvManager externally should NOT rewrite all project settings', async () => { // Start with venv as default - let currentDefaultEnvManager = 'ms-python.python:venv'; + let currentDefaultEnvManager = VENV_MANAGER_ID; const mockConfig = new MockWorkspaceConfiguration(); (mockConfig as any).get = (key: string, defaultValue?: T): T | undefined => { if (key === 'pythonProjects') { return [ { path: 'project-a', - envManager: 'ms-python.python:poetry', - packageManager: 'ms-python.python:pip', + envManager: POETRY_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ] as unknown as T; } @@ -294,7 +295,7 @@ suite('Project Manager Initialization - Settings Preservation', () => { addPythonProjectSettingStub.resetHistory(); // Simulate user changes defaultEnvManager to conda in settings.json - currentDefaultEnvManager = 'ms-python.python:conda'; + currentDefaultEnvManager = CONDA_MANAGER_ID; configChangeEmitter.fire({ affectsConfiguration: (section: string) => section === 'python-envs.defaultEnvManager', }); @@ -311,16 +312,16 @@ suite('Project Manager Initialization - Settings Preservation', () => { let workspaceValueProjects: PythonProjectSettings[] = [ { path: 'script.py', - envManager: 'ms-python.python:inline-script', - packageManager: 'ms-python.python:pip', + envManager: INLINE_SCRIPT_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, workspace: workspaceFolder.name, }, ]; let workspaceFolderProjects: PythonProjectSettings[] = [ { path: 'keep.py', - envManager: 'ms-python.python:venv', - packageManager: 'ms-python.python:pip', + envManager: VENV_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ]; const mockConfig = new MockWorkspaceConfiguration(); @@ -329,10 +330,10 @@ suite('Project Manager Initialization - Settings Preservation', () => { return [...workspaceValueProjects, ...workspaceFolderProjects] as unknown as T; } if (key === 'defaultEnvManager') { - return 'ms-python.python:venv' as T; + return VENV_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:pip' as T; + return DEFAULT_PACKAGE_MANAGER_ID as T; } return defaultValue; }; @@ -392,14 +393,14 @@ suite('Project Manager Initialization - Settings Preservation', () => { let sharedWorkspaceProjects: PythonProjectSettings[] = [ { path: 'first', - envManager: 'ms-python.python:inline-script', - packageManager: 'ms-python.python:pip', + envManager: INLINE_SCRIPT_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, workspace: workspaceFolder.name, }, { path: 'second', - envManager: 'ms-python.python:inline-script', - packageManager: 'ms-python.python:pip', + envManager: INLINE_SCRIPT_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, workspace: secondWorkspaceFolder.name, }, ]; @@ -410,10 +411,10 @@ suite('Project Manager Initialization - Settings Preservation', () => { return sharedWorkspaceProjects as unknown as T; } if (key === 'defaultEnvManager') { - return 'ms-python.python:venv' as T; + return VENV_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:pip' as T; + return DEFAULT_PACKAGE_MANAGER_ID as T; } return defaultValue; }; @@ -470,7 +471,7 @@ suite('Project Manager Initialization - Settings Preservation', () => { const mockConfig = new MockWorkspaceConfiguration(); (mockConfig as any).get = (key: string, defaultValue?: T): T | undefined => { if (key === 'pythonProjects') {return [] as unknown as T;} - if (key === 'defaultEnvManager') {return 'ms-python.python:venv' as T;} + if (key === 'defaultEnvManager') {return VENV_MANAGER_ID as T;} return defaultValue; }; mockConfig.update = () => Promise.resolve(); @@ -507,7 +508,7 @@ suite('Project Manager Initialization - Settings Preservation', () => { const mockConfig = new MockWorkspaceConfiguration(); (mockConfig as any).get = (key: string, defaultValue?: T): T | undefined => { if (key === 'pythonProjects') {return [] as unknown as T;} - if (key === 'defaultEnvManager') {return 'ms-python.python:venv' as T;} + if (key === 'defaultEnvManager') {return VENV_MANAGER_ID as T;} return defaultValue; }; mockConfig.update = () => Promise.resolve(); @@ -650,10 +651,10 @@ suite('Project Manager Initialization - Settings Preservation', () => { return [] as unknown as T; } if (key === 'defaultEnvManager') { - return 'ms-python.python:venv' as T; + return VENV_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:pip' as T; + return DEFAULT_PACKAGE_MANAGER_ID as T; } return defaultValue; }; @@ -683,8 +684,8 @@ suite('Project Manager Initialization - Settings Preservation', () => { const pythonProjects: PythonProjectSettings[] = [ { path: 'existing-project', - envManager: 'ms-python.python:poetry', - packageManager: 'ms-python.python:pip', + envManager: POETRY_MANAGER_ID, + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ]; @@ -694,10 +695,10 @@ suite('Project Manager Initialization - Settings Preservation', () => { return pythonProjects as unknown as T; } if (key === 'defaultEnvManager') { - return 'ms-python.python:venv' as T; + return VENV_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:pip' as T; + return DEFAULT_PACKAGE_MANAGER_ID as T; } return defaultValue; }; @@ -797,16 +798,16 @@ suite('Project-Specific Settings Preservation on Reload', () => { (mockConfig as any).get = (key: string, defaultValue?: T): T | undefined => { if (key === 'pythonProjects') { return [ - { path: 'alice/bob', envManager: 'ms-python.python:venv', packageManager: 'ms-python.python:pip' }, - { path: 'ada', envManager: 'ms-python.python:venv', packageManager: 'ms-python.python:pip' }, - { path: 'alice', envManager: 'ms-python.python:venv', packageManager: 'ms-python.python:pip' }, + { path: 'alice/bob', envManager: VENV_MANAGER_ID, packageManager: DEFAULT_PACKAGE_MANAGER_ID }, + { path: 'ada', envManager: VENV_MANAGER_ID, packageManager: DEFAULT_PACKAGE_MANAGER_ID }, + { path: 'alice', envManager: VENV_MANAGER_ID, packageManager: DEFAULT_PACKAGE_MANAGER_ID }, ] as unknown as T; } if (key === 'defaultEnvManager') { - return 'ms-python.python:conda' as T; + return CONDA_MANAGER_ID as T; } if (key === 'defaultPackageManager') { - return 'ms-python.python:conda' as T; + return CONDA_MANAGER_ID as T; } return defaultValue; }; diff --git a/src/test/features/settings/settingHelpers.unit.test.ts b/src/test/features/settings/settingHelpers.unit.test.ts index dc0897efa..12bb155f7 100644 --- a/src/test/features/settings/settingHelpers.unit.test.ts +++ b/src/test/features/settings/settingHelpers.unit.test.ts @@ -28,6 +28,7 @@ import { } from '../../../features/projectManager'; import type { PythonProjectManager } from '../../../features/projectManager'; import { MockWorkspaceConfiguration } from '../../mocks/mockWorkspaceConfig'; +import { VENV_MANAGER_ID as VENV_MANAGER_ID_CONSTANT, DEFAULT_PACKAGE_MANAGER_ID, SYSTEM_MANAGER_ID as SYSTEM_MANAGER_ID_CONSTANT, CONDA_MANAGER_ID as CONDA_MANAGER_ID_CONSTANT, INLINE_SCRIPT_MANAGER_ID } from '../../../common/constants'; /** * Returns a platform-appropriate workspace path for testing. @@ -38,8 +39,8 @@ function getTestWorkspacePath(): string { } suite('Setting Helpers - Optional Project Managers', () => { - const VENV_MANAGER_ID = 'ms-python.python:venv'; - const PIP_MANAGER_ID = 'ms-python.python:pip'; + const VENV_MANAGER_ID = VENV_MANAGER_ID_CONSTANT; + const PIP_MANAGER_ID = DEFAULT_PACKAGE_MANAGER_ID; const workspacePath = getTestWorkspacePath(); const workspaceUri = Uri.file(workspacePath); const projectUri = Uri.joinPath(workspaceUri, 'backend'); @@ -101,10 +102,10 @@ suite('Setting Helpers - Optional Project Managers', () => { * and are logged explicitly as ignored global edits. */ suite('Setting Helpers - Settings Write Behavior', () => { - const SYSTEM_MANAGER_ID = 'ms-python.python:system'; - const VENV_MANAGER_ID = 'ms-python.python:venv'; - const PIP_MANAGER_ID = 'ms-python.python:pip'; - const CONDA_MANAGER_ID = 'ms-python.python:conda'; + const SYSTEM_MANAGER_ID = SYSTEM_MANAGER_ID_CONSTANT; + const VENV_MANAGER_ID = VENV_MANAGER_ID_CONSTANT; + const PIP_MANAGER_ID = DEFAULT_PACKAGE_MANAGER_ID; + const CONDA_MANAGER_ID = CONDA_MANAGER_ID_CONSTANT; let updateCalls: Array<{ key: string; value: unknown; target: boolean | ConfigurationTarget | undefined }>; @@ -282,8 +283,8 @@ suite('Setting Helpers - Settings Write Behavior', () => { * Instead, we should use defaultEnvManager/defaultPackageManager settings. */ suite('Setting Helpers - Empty Path Bug Fix', () => { - const VENV_MANAGER_ID = 'ms-python.python:venv'; - const PIP_MANAGER_ID = 'ms-python.python:pip'; + const VENV_MANAGER_ID = VENV_MANAGER_ID_CONSTANT; + const PIP_MANAGER_ID = DEFAULT_PACKAGE_MANAGER_ID; const workspacePath = getTestWorkspacePath(); const workspaceUri = Uri.file(workspacePath); @@ -505,9 +506,9 @@ suite('Setting Helpers - Empty Path Bug Fix', () => { * When there's an existing entry with "path": "", it should be fixed or removed. */ suite('Setting Helpers - Empty Path Migration', () => { - const VENV_MANAGER_ID = 'ms-python.python:venv'; - const PIP_MANAGER_ID = 'ms-python.python:pip'; - const CONDA_MANAGER_ID = 'ms-python.python:conda'; + const VENV_MANAGER_ID = VENV_MANAGER_ID_CONSTANT; + const PIP_MANAGER_ID = DEFAULT_PACKAGE_MANAGER_ID; + const CONDA_MANAGER_ID = CONDA_MANAGER_ID_CONSTANT; const workspacePath = getTestWorkspacePath(); const workspaceUri = Uri.file(workspacePath); @@ -719,9 +720,9 @@ suite('Setting Helpers - Empty Path Migration', () => { }); suite('Setting Helpers - Project Removal', () => { - const INLINE_MANAGER_ID = 'ms-python.python:inline-script'; - const VENV_MANAGER_ID = 'ms-python.python:venv'; - const PIP_MANAGER_ID = 'ms-python.python:pip'; + const INLINE_MANAGER_ID = INLINE_SCRIPT_MANAGER_ID; + const VENV_MANAGER_ID = VENV_MANAGER_ID_CONSTANT; + const PIP_MANAGER_ID = DEFAULT_PACKAGE_MANAGER_ID; const firstWorkspacePath = getTestWorkspacePath(); const firstWorkspaceUri = Uri.file(firstWorkspacePath); const firstWorkspace: WorkspaceFolder = { @@ -1066,7 +1067,7 @@ suite('Setting Helpers - Project Removal', () => { await setAllManagerSettings([ { project, - envManager: 'ms-python.python:conda', + envManager: CONDA_MANAGER_ID_CONSTANT, packageManager: PIP_MANAGER_ID, }, ]); @@ -1075,7 +1076,7 @@ suite('Setting Helpers - Project Removal', () => { firstSetting, { path: 'script.py', - envManager: 'ms-python.python:conda', + envManager: CONDA_MANAGER_ID_CONSTANT, packageManager: PIP_MANAGER_ID, workspace: secondWorkspace.name, }, @@ -1097,14 +1098,14 @@ suite('Setting Helpers - Project Removal', () => { await setEnvironmentManager([ { project, - envManager: 'ms-python.python:conda', + envManager: CONDA_MANAGER_ID_CONSTANT, }, ]); assert.deepStrictEqual(getWorkspaceValue(), [ { path: 'script.py', - envManager: 'ms-python.python:conda', + envManager: CONDA_MANAGER_ID_CONSTANT, packageManager: PIP_MANAGER_ID, workspace: undefined, }, @@ -1475,8 +1476,8 @@ suite('Setting Helpers - Project Removal', () => { }); suite('Setting Helpers - migrateGlobalDefaultEnvManagerSetting', () => { - const SYSTEM_MANAGER_ID = 'ms-python.python:system'; - const VENV_MANAGER_ID = 'ms-python.python:venv'; + const SYSTEM_MANAGER_ID = SYSTEM_MANAGER_ID_CONSTANT; + const VENV_MANAGER_ID = VENV_MANAGER_ID_CONSTANT; const MIGRATION_FLAG_KEY = 'globalSettingsMigration.systemEnvManagerRemoved'; const TELEMETRY_EVENT = 'MIGRATION.SYSTEM_ENV_MANAGER'; diff --git a/src/test/features/terminal/shellStartupActivationVariablesManager.unit.test.ts b/src/test/features/terminal/shellStartupActivationVariablesManager.unit.test.ts index d5bfe9d3a..b0854dc10 100644 --- a/src/test/features/terminal/shellStartupActivationVariablesManager.unit.test.ts +++ b/src/test/features/terminal/shellStartupActivationVariablesManager.unit.test.ts @@ -8,6 +8,7 @@ import * as workspaceApis from '../../../common/workspace.apis'; import { ShellStartupActivationVariablesManagerImpl } from '../../../features/terminal/shellStartupActivationVariablesManager'; import { ShellEnvsProvider } from '../../../features/terminal/shells/startupProvider'; import * as terminalUtils from '../../../features/terminal/utils'; +import { VENV_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID } from '../../../common/constants'; function makeEnvironment(id: string, managerId: string): PythonEnvironment { return { @@ -47,8 +48,8 @@ class RecordingEnvsProvider implements ShellEnvsProvider { suite('ShellStartupActivationVariablesManager', () => { const folderUri = Uri.file('/workspace'); const workspaceFolder = { uri: folderUri, name: 'workspace', index: 0 } as WorkspaceFolder; - const folderEnvironment = makeEnvironment('folder-venv', 'ms-python.python:venv'); - const scriptEnvironment = makeEnvironment('script-env', 'ms-python.python:inline-script'); + const folderEnvironment = makeEnvironment('folder-venv', VENV_MANAGER_ID); + const scriptEnvironment = makeEnvironment('script-env', INLINE_SCRIPT_MANAGER_ID); let provider: RecordingEnvsProvider; let scopedCollection: object; @@ -113,7 +114,7 @@ suite('ShellStartupActivationVariablesManager', () => { test('writes the folder environment resolved at folder scope, not the event payload', async () => { await changeListener!({ uri: folderUri, - new: makeEnvironment('stale-payload', 'ms-python.python:venv'), + new: makeEnvironment('stale-payload', VENV_MANAGER_ID), old: undefined, }); @@ -137,7 +138,7 @@ suite('ShellStartupActivationVariablesManager', () => { }); test('refreshes global startup variables after a global selection changes', async () => { - const newEnvironment = makeEnvironment('global-venv-b', 'ms-python.python:venv'); + const newEnvironment = makeEnvironment('global-venv-b', VENV_MANAGER_ID); await changeListener!({ uri: undefined, new: folderEnvironment, old: undefined }); await changeListener!({ uri: undefined, new: newEnvironment, old: folderEnvironment }); @@ -147,7 +148,7 @@ suite('ShellStartupActivationVariablesManager', () => { }); test('uses the global change payload while the API still returns the previous selection', async () => { - const newEnvironment = makeEnvironment('global-venv-b', 'ms-python.python:venv'); + const newEnvironment = makeEnvironment('global-venv-b', VENV_MANAGER_ID); getEnvironmentStub.resolves(folderEnvironment); await changeListener!({ uri: undefined, new: newEnvironment, old: folderEnvironment }); @@ -157,7 +158,7 @@ suite('ShellStartupActivationVariablesManager', () => { }); test('does not let pending global initialization overwrite a newer selection', async () => { - const newEnvironment = makeEnvironment('global-venv-b', 'ms-python.python:venv'); + const newEnvironment = makeEnvironment('global-venv-b', VENV_MANAGER_ID); const pendingInitialization = createDeferred(); getEnvironmentStub.returns(pendingInitialization.promise); diff --git a/src/test/features/terminal/utils.unit.test.ts b/src/test/features/terminal/utils.unit.test.ts index e999c0a5b..5e00f4439 100644 --- a/src/test/features/terminal/utils.unit.test.ts +++ b/src/test/features/terminal/utils.unit.test.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as sinon from 'sinon'; import { ExtensionTerminalOptions, Terminal, TerminalOptions, Uri } from 'vscode'; import { PythonEnvironment, PythonEnvironmentApi, PythonProject } from '../../../api'; -import { VENV_MANAGER_ID } from '../../../common/constants'; +import { VENV_MANAGER_ID, CONDA_MANAGER_ID } from '../../../common/constants'; import * as windowApis from '../../../common/window.apis'; import * as workspaceApis from '../../../common/workspace.apis'; import * as shellDetector from '../../../features/common/shellDetector'; @@ -52,7 +52,7 @@ suite('Terminal Utils - getEnvironmentForTerminal', () => { return createMockPythonEnvironment({ name: id, id, - managerId: 'ms-python.python:conda', + managerId: CONDA_MANAGER_ID, envPath: path.resolve('external-environments', id, 'python'), }); } diff --git a/src/test/features/views/treeViewItems.unit.test.ts b/src/test/features/views/treeViewItems.unit.test.ts index 8f221060a..6836f354e 100644 --- a/src/test/features/views/treeViewItems.unit.test.ts +++ b/src/test/features/views/treeViewItems.unit.test.ts @@ -15,6 +15,7 @@ import { import { PythonEnvironmentImpl } from '../../../managers/common/models'; import { InternalEnvironmentManager } from '../../../managers/common/registeredManagers'; import type { InternalPackageManager } from '../../../managers/common/registeredManagers'; +import { VENV_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID, CONDA_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../../common/constants'; /** * Helper to create a mock PythonEnvironmentImpl with minimal required fields. @@ -84,13 +85,13 @@ suite('Test TreeView Items', () => { suite('EnvManagerTreeItem', () => { test('Sets id to manager id for tree item identification', () => { // Arrange - const manager = createMockManager({ id: 'ms-python.python:venv' }); + const manager = createMockManager({ id: VENV_MANAGER_ID }); // Act const item = new EnvManagerTreeItem(manager); // Assert - assert.strictEqual(item.treeItem.id, 'ms-python.python:venv'); + assert.strictEqual(item.treeItem.id, VENV_MANAGER_ID); }); test('Context value excludes create when manager does not support it', () => { @@ -207,7 +208,7 @@ suite('Test TreeView Items', () => { // Arrange const env = createMockEnvironment({ environmentPath: '/home/user/.cache/script-envs-v1/abc123/bin/python', - managerId: 'ms-python.python:inline-script', + managerId: INLINE_SCRIPT_MANAGER_ID, hasActivation: true, }); @@ -222,7 +223,7 @@ suite('Test TreeView Items', () => { // Arrange const env = createMockEnvironment({ environmentPath: '/home/user/.cache/script-envs-v1/abc123/bin/python', - managerId: 'ms-python.python:inline-script', + managerId: INLINE_SCRIPT_MANAGER_ID, }); const parent = new PythonEnvTreeItem(env, managerWithRemove); const pkg = { name: 'requests', displayName: 'requests', version: '2.32.0' } as Package; @@ -324,7 +325,7 @@ suite('Test TreeView Items', () => { let parentManager: EnvManagerTreeItem; setup(() => { - parentManager = new EnvManagerTreeItem(createMockManager({ id: 'ms-python.python:conda' })); + parentManager = new EnvManagerTreeItem(createMockManager({ id: CONDA_MANAGER_ID })); }); test('Sets id combining manager id and group name for tree item identification', () => { @@ -332,7 +333,7 @@ suite('Test TreeView Items', () => { const item = new PythonGroupEnvTreeItem(parentManager, 'base'); // Assert - assert.strictEqual(item.treeItem.id, 'ms-python.python:conda:base'); + assert.strictEqual(item.treeItem.id, `${CONDA_MANAGER_ID}:base`); }); test('Sets id correctly when group is EnvironmentGroupInfo object', () => { @@ -343,7 +344,7 @@ suite('Test TreeView Items', () => { const item = new PythonGroupEnvTreeItem(parentManager, groupInfo); // Assert - assert.strictEqual(item.treeItem.id, 'ms-python.python:conda:dev-envs'); + assert.strictEqual(item.treeItem.id, `${CONDA_MANAGER_ID}:dev-envs`); }); test('Uses string group as label', () => { @@ -370,7 +371,7 @@ suite('Test TreeView Items', () => { const item = new PythonGroupEnvTreeItem(parentManager, 'test-group'); // Assert - assert.strictEqual(item.treeItem.contextValue, 'pythonEnvGroup;ms-python.python:conda:test-group;'); + assert.strictEqual(item.treeItem.contextValue, `pythonEnvGroup;${CONDA_MANAGER_ID}:test-group;`); }); }); @@ -553,7 +554,7 @@ suite('Test TreeView Items', () => { tooltip: options.tooltip, iconPath: options.iconPath, isTransitive: options.isTransitive, - pkgId: { id: options.name ?? 'requests', managerId: 'ms-python.python:pip' }, + pkgId: { id: options.name ?? 'requests', managerId: DEFAULT_PACKAGE_MANAGER_ID }, } as Package; } diff --git a/src/test/integration/packageManager.integration.test.ts b/src/test/integration/packageManager.integration.test.ts index 7ec7d5058..03df2697a 100644 --- a/src/test/integration/packageManager.integration.test.ts +++ b/src/test/integration/packageManager.integration.test.ts @@ -8,7 +8,7 @@ import { PythonEnvironmentApi, isPackageVersionLookupNotSupportedError, } from '../../api'; -import { CONDA_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; +import { CONDA_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, POETRY_MANAGER_ID } from '../../common/constants'; import { ENVS_EXTENSION_ID } from '../constants'; import { waitForCondition } from '../testUtils'; import { @@ -50,7 +50,7 @@ const profiles: PackageManagerProfile[] = [ ]; const deferredPackageManagers: Readonly> = { - 'ms-python.python:poetry': 'Poetry lifecycle coverage requires a controlled Poetry installation.', + [POETRY_MANAGER_ID]: 'Poetry lifecycle coverage requires a controlled Poetry installation.', }; const deferredProfiles = { diff --git a/src/test/internal/publicApiCompatibility.unit.test.ts b/src/test/internal/publicApiCompatibility.unit.test.ts index 6bbc4f4ab..29b1ba739 100644 --- a/src/test/internal/publicApiCompatibility.unit.test.ts +++ b/src/test/internal/publicApiCompatibility.unit.test.ts @@ -28,6 +28,7 @@ import { PoetryManager } from '../../managers/poetry/poetryManager'; import { PoetryPackageManager } from '../../managers/poetry/poetryPackageManager'; import * as poetryUtils from '../../managers/poetry/poetryUtils'; import { createMockLogOutputChannel } from '../mocks/helper'; +import { VENV_MANAGER_ID } from '../../common/constants'; interface SpawnRecord { executable: string; @@ -54,7 +55,7 @@ suite('Public package API execution policy', () => { await fs.ensureDir(prefix); const executable = path.join(prefix, process.platform === 'win32' ? 'python.exe' : 'python'); environment = { - envId: { id: 'test', managerId: 'ms-python.python:venv' }, + envId: { id: 'test', managerId: VENV_MANAGER_ID }, name: 'test', displayName: 'test', displayPath: executable, diff --git a/src/test/internal/pythonToolManagers.unit.test.ts b/src/test/internal/pythonToolManagers.unit.test.ts index a7ab83b67..3ac9e1496 100644 --- a/src/test/internal/pythonToolManagers.unit.test.ts +++ b/src/test/internal/pythonToolManagers.unit.test.ts @@ -42,6 +42,7 @@ import { PoetryManager } from '../../managers/poetry/poetryManager'; import { PoetryPackageManager } from '../../managers/poetry/poetryPackageManager'; import * as poetryUtils from '../../managers/poetry/poetryUtils'; import { createMockLogOutputChannel } from '../mocks/helper'; +import { VENV_MANAGER_ID } from '../../common/constants'; suite('Python tool manager capabilities', () => { let source: CancellationTokenSource; @@ -58,7 +59,7 @@ suite('Python tool manager capabilities', () => { scope = Uri.file(path.join(process.cwd(), 'project with spaces')); const executable = path.join(scope.fsPath, '.venv', 'python.exe'); environment = { - envId: { id: 'test', managerId: 'ms-python.python:venv' }, + envId: { id: 'test', managerId: VENV_MANAGER_ID }, name: 'test', displayName: 'test', displayPath: executable, diff --git a/src/test/internal/pythonTools.startup.unit.test.ts b/src/test/internal/pythonTools.startup.unit.test.ts index fe2153d70..d872419fb 100644 --- a/src/test/internal/pythonTools.startup.unit.test.ts +++ b/src/test/internal/pythonTools.startup.unit.test.ts @@ -15,7 +15,7 @@ import { WorkspaceConfiguration, } from 'vscode'; import { PackageManager, PythonEnvironment } from '../../api'; -import { PYTHON_EXTENSION_ID, SYSTEM_MANAGER_ID, VENV_MANAGER_ID } from '../../common/constants'; +import { PYTHON_EXTENSION_ID, SYSTEM_MANAGER_ID, VENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; import * as persistentState from '../../common/persistentState'; import { createDeferred, Deferred } from '../../common/utils/deferred'; import * as frameUtils from '../../common/utils/frameUtils'; @@ -159,7 +159,7 @@ suite('Python tools stateless startup selection', () => { sinon.stub(workspaceApis, 'getConfiguration').callsFake((section) => { const defaults = new Map([ ['python-envs.defaultEnvManager', VENV_MANAGER_ID], - ['python-envs.defaultPackageManager', 'ms-python.python:pip'], + ['python-envs.defaultPackageManager', DEFAULT_PACKAGE_MANAGER_ID], ['python-envs.pythonProjects', []], ]); const configuration: WorkspaceConfiguration = { @@ -307,7 +307,7 @@ suite('Python tools stateless startup selection', () => { { path: 'New Service', envManager: VENV_MANAGER_ID, - packageManager: 'ms-python.python:pip', + packageManager: DEFAULT_PACKAGE_MANAGER_ID, }, ]); assert.strictEqual(workspaceSettings.get('python-envs.defaultEnvManager'), undefined); diff --git a/src/test/internal/pythonTools.unit.test.ts b/src/test/internal/pythonTools.unit.test.ts index 0293f2fb4..34ae494f7 100644 --- a/src/test/internal/pythonTools.unit.test.ts +++ b/src/test/internal/pythonTools.unit.test.ts @@ -15,14 +15,7 @@ import { WorkspaceConfiguration, } from 'vscode'; import { EnvironmentManager, Package, PackageManagementOptions, PythonEnvironment, PythonProject } from '../../api'; -import { - CONDA_MANAGER_ID, - INLINE_SCRIPT_MANAGER_ID, - PYENV_MANAGER_ID, - PYTHON_EXTENSION_ID, - SYSTEM_MANAGER_ID, - VENV_MANAGER_ID, -} from '../../common/constants'; +import { CONDA_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID, PIPENV_MANAGER_ID, POETRY_MANAGER_ID, PYENV_MANAGER_ID, PYTHON_EXTENSION_ID, SYSTEM_MANAGER_ID, VENV_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID } from '../../common/constants'; import { createDeferred } from '../../common/utils/deferred'; import * as frameUtils from '../../common/utils/frameUtils'; import { isSameOrParentPath, normalizePath } from '../../common/utils/pathUtils'; @@ -41,9 +34,6 @@ import { ToolPackageManager, } from '../../internal/pythonToolSupport'; -const POETRY_MANAGER_ID = `${PYTHON_EXTENSION_ID}:poetry`; -const PIPENV_MANAGER_ID = `${PYTHON_EXTENSION_ID}:pipenv`; - suite('Internal Python tools', () => { let temp: string; let root: Uri; @@ -92,7 +82,7 @@ suite('Internal Python tools', () => { function makeManager( name: string, fallback?: PythonEnvironment, - preferredPackageManagerId = `${PYTHON_EXTENSION_ID}:pip`, + preferredPackageManagerId = DEFAULT_PACKAGE_MANAGER_ID, ): ToolEnvironmentManager { const selections = new Map(); const manager: ToolEnvironmentManager = { @@ -101,7 +91,7 @@ suite('Internal Python tools', () => { get: sinon .stub() .callsFake(async (scope?: Uri) => - scope ? selections.get(normalizePath(scope.fsPath)) ?? fallback : fallback, + scope ? (selections.get(normalizePath(scope.fsPath)) ?? fallback) : fallback, ), set: sinon.stub().callsFake(async (scope: Uri | Uri[] | undefined, env?: PythonEnvironment) => { for (const uri of Array.isArray(scope) ? scope : scope ? [scope] : []) { @@ -183,15 +173,15 @@ suite('Internal Python tools', () => { (key === 'defaultEnvManager' ? VENV_MANAGER_ID : key === 'pythonProjects' - ? projectSettings - : key === 'defaultPackageManager' - ? `${PYTHON_EXTENSION_ID}:pip` - : fallback)) as T; + ? projectSettings + : key === 'defaultPackageManager' + ? DEFAULT_PACKAGE_MANAGER_ID + : fallback)) as T; }, inspect: (key: string) => ({ key, globalValue: explicitSettings.get(`${section}.${key}`) }), has: () => true, update: async () => {}, - } as WorkspaceConfiguration), + }) as WorkspaceConfiguration, ); sinon.stub(settings, 'setAllManagerSettings').callsFake(async (edits) => { for (const edit of edits) { @@ -546,11 +536,7 @@ suite('Internal Python tools', () => { getPackages: sinon.stub().throws(new Error('Public package cache must not be called')), refresh: sinon.stub().resolves(), [pythonToolSupport]: { - getPackages: async ( - _environment: PythonEnvironment, - _token: CancellationToken, - scope: Uri, - ) => { + getPackages: async (_environment: PythonEnvironment, _token: CancellationToken, scope: Uri) => { calls.push({ operation: 'get', project: project.uri.fsPath, scope: scope.fsPath }); return [{ name: `${project.name}-package`, version: '1.0' }]; }, @@ -571,10 +557,7 @@ suite('Internal Python tools', () => { }; managers.registerPackageManager(poetryPackages, { extensionId: PYTHON_EXTENSION_ID }); - const firstQuery = await api.getEnvironment( - { resourcePath: root.fsPath, includePackages: true }, - source.token, - ); + const firstQuery = await api.getEnvironment({ resourcePath: root.fsPath, includePackages: true }, source.token); const firstInstall = await api.installPackages( { resourcePath: root.fsPath, packages: ['first-package'] }, source.token, diff --git a/src/test/managers/builtin/inlineScript/envManager.unit.test.ts b/src/test/managers/builtin/inlineScript/envManager.unit.test.ts index 5ac2a5268..2192bacaf 100644 --- a/src/test/managers/builtin/inlineScript/envManager.unit.test.ts +++ b/src/test/managers/builtin/inlineScript/envManager.unit.test.ts @@ -28,7 +28,7 @@ import * as cacheLayout from '../../../../common/inlineScript/cacheLayout'; import * as metadataReader from '../../../../common/inlineScript/metadata'; import { InlineScriptRoutingRegistry } from '../../../../common/inlineScript/routingRegistry'; import * as lockfileApis from '../../../../common/lockfile.apis'; -import { INLINE_SCRIPT_ENVS_KEY } from '../../../../common/constants'; +import { INLINE_SCRIPT_ENVS_KEY, SYSTEM_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID, DEFAULT_PACKAGE_MANAGER_ID, PIPENV_MANAGER_ID, CONDA_MANAGER_ID, PYENV_MANAGER_ID, VENV_MANAGER_ID } from '../../../../common/constants'; import { EventNames } from '../../../../common/telemetry/constants'; import * as telemetrySender from '../../../../common/telemetry/sender'; import { isWindows } from '../../../../common/utils/platformUtils'; @@ -158,7 +158,7 @@ suite('InlineScriptEnvManager', () => { globalStorageUri = Uri.file(path.join(tempRoot, 'global-storage')); baseExecutable = path.join(tempRoot, 'base-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(baseExecutable, ''); - baseEnvironment = makeEnvironment('ms-python.python:system', '3.12.4', baseExecutable); + baseEnvironment = makeEnvironment(SYSTEM_MANAGER_ID, '3.12.4', baseExecutable); apiGetEnvironmentsStub = sinon.stub().resolves([baseEnvironment]); apiRefreshEnvironmentsStub = sinon.stub().resolves(); @@ -254,7 +254,7 @@ suite('InlineScriptEnvManager', () => { : path.join(envDir, 'lib', 'python3.12', 'site-packages'), ); const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, selectedBase.version, getVenvPythonPath(envDir), envDir, @@ -353,8 +353,8 @@ suite('InlineScriptEnvManager', () => { lastUsedAt: NOW.toISOString(), }, Uri.file(location)); const environment = { - ...makeEnvironment('ms-python.python:inline-script', '3.12.4', executable, location), - envId: { managerId: 'ms-python.python:inline-script', id: envId }, + ...makeEnvironment(INLINE_SCRIPT_MANAGER_ID, '3.12.4', executable, location), + envId: { managerId: INLINE_SCRIPT_MANAGER_ID, id: envId }, }; environmentsByExecutablePath.set(normalizePath(executable), environment); return environment; @@ -635,7 +635,7 @@ suite('InlineScriptEnvManager', () => { test('retains inline-script manager presentation metadata', () => { assert.strictEqual(manager.name, 'inline-script'); assert.ok(manager.displayName); - assert.strictEqual(manager.preferredPackageManagerId, 'ms-python.python:pip'); + assert.strictEqual(manager.preferredPackageManagerId, DEFAULT_PACKAGE_MANAGER_ID); assert.ok(manager.iconPath); assert.ok(manager.tooltip); }); @@ -675,7 +675,7 @@ suite('InlineScriptEnvManager', () => { suite('base interpreter selection', () => { test('excludes derived managers even when they report newer global environments', async () => { - const pipenv = makeEnvironment('ms-python.python:pipenv', '3.14.0', baseExecutable); + const pipenv = makeEnvironment(PIPENV_MANAGER_ID, '3.14.0', baseExecutable); apiGetEnvironmentsStub.resolves([pipenv, baseEnvironment]); await manager.create(scriptUri()); @@ -684,7 +684,7 @@ suite('InlineScriptEnvManager', () => { }); test('does not reapply release-only matching after strict PEP 440 filtering', async () => { - const finalRelease = makeEnvironment('ms-python.python:system', '3.15.0', baseExecutable); + const finalRelease = makeEnvironment(SYSTEM_MANAGER_ID, '3.15.0', baseExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '!=3.15.0rc2' }); apiGetEnvironmentsStub.resolves([finalRelease]); @@ -699,7 +699,7 @@ suite('InlineScriptEnvManager', () => { // (e.g. "3.14.3.final.0"), which is not valid PEP 440. This must still satisfy // requires-python (matchesInstallConstraint) and pass the post-create // release-equality check (areEqualPythonReleases) instead of being discarded. - const versionInfoBase = makeEnvironment('ms-python.python:system', '3.14.3.final.0', baseExecutable); + const versionInfoBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.14.3.final.0', baseExecutable); apiGetEnvironmentsStub.resolves([versionInfoBase]); assert.ok(await manager.create(scriptUri())); @@ -709,7 +709,7 @@ suite('InlineScriptEnvManager', () => { test('creates an environment with a sys.version_info version when requires-python is absent', async () => { readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: undefined }); - const versionInfoBase = makeEnvironment('ms-python.python:system', '3.14.3.final.0', baseExecutable); + const versionInfoBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.14.3.final.0', baseExecutable); apiGetEnvironmentsStub.resolves([versionInfoBase]); assert.ok(await manager.create(scriptUri())); @@ -719,14 +719,14 @@ suite('InlineScriptEnvManager', () => { test('excludes named conda environments even when they are newer than conda base', async () => { const condaNamed = makeEnvironment( - 'ms-python.python:conda', + CONDA_MANAGER_ID, '3.14.0', baseExecutable, undefined, 'project-env', ); const condaBase = makeEnvironment( - 'ms-python.python:conda', + CONDA_MANAGER_ID, '3.11.9', baseExecutable, undefined, @@ -741,7 +741,7 @@ suite('InlineScriptEnvManager', () => { test('falls back when the newest compatible interpreter cannot be canonicalized', async () => { const missingExecutable = path.join(tempRoot, 'missing', 'python'); - const newest = makeEnvironment('ms-python.python:system', '3.13.0', missingExecutable); + const newest = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.0', missingExecutable); apiGetEnvironmentsStub.resolves([baseEnvironment, newest]); await manager.create(scriptUri()); @@ -755,7 +755,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(pyenvVenvExecutable, ''); await fs.outputFile(path.join(pyenvVenvRoot, 'pyvenv.cfg'), 'home = base'); const pyenvVenv = makeEnvironment( - 'ms-python.python:pyenv', + PYENV_MANAGER_ID, '3.13.0', pyenvVenvExecutable, pyenvVenvRoot, @@ -780,8 +780,8 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(emptyPrefixExecutable, ''); await fs.outputFile(relativePrefixExecutable, ''); apiGetEnvironmentsStub.resolves([ - makeEnvironment('ms-python.python:system', '3.14.0', emptyPrefixExecutable, ''), - makeEnvironment('ms-python.python:system', '3.13.0', relativePrefixExecutable, 'relative-prefix'), + makeEnvironment(SYSTEM_MANAGER_ID, '3.14.0', emptyPrefixExecutable, ''), + makeEnvironment(SYSTEM_MANAGER_ID, '3.13.0', relativePrefixExecutable, 'relative-prefix'), baseEnvironment, ]); @@ -797,7 +797,7 @@ suite('InlineScriptEnvManager', () => { test('installs the requirement lower bound, refreshes, and uses the discovered base interpreter', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.2', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.2', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -818,7 +818,7 @@ suite('InlineScriptEnvManager', () => { test('asks uv for the latest Python when requires-python is absent', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.14.0', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.14.0', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: undefined }); apiGetEnvironmentsStub.onFirstCall().resolves([]); apiGetEnvironmentsStub.onSecondCall().resolves([]); @@ -862,7 +862,7 @@ suite('InlineScriptEnvManager', () => { test('directly resolves the installed interpreter when environment refresh fails', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.2', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.2', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); apiGetEnvironmentsStub.resolves([baseEnvironment]); promptInstallPythonViaUvStub.resolves({ kind: 'installed', pythonPath: uvExecutable }); @@ -885,7 +885,7 @@ suite('InlineScriptEnvManager', () => { test('directly resolves the installed interpreter when post-install discovery fails', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.2', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.2', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -908,7 +908,7 @@ suite('InlineScriptEnvManager', () => { test('uses strict PEP 440 matching for a directly resolved final release', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.15.0', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.15.0', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '!=3.15.0rc2' }); apiGetEnvironmentsStub.resolves([]); getAvailablePythonVersionsStub.resolves([makeUvPythonVersion('3.15.0')]); @@ -927,7 +927,7 @@ suite('InlineScriptEnvManager', () => { test('selects an available uv release that satisfies exclusion clauses', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.3', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.3', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13.2,!=3.13.2', @@ -978,7 +978,7 @@ suite('InlineScriptEnvManager', () => { test('uses an explicit patch release when a minor selector could exceed the constraint', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.0', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.0', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13,<=3.13' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1000,7 +1000,7 @@ suite('InlineScriptEnvManager', () => { test('uses an advertised release for a bounded range instead of fabricating patch zero', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.11.14', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.11.14', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.11,<3.12' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1020,7 +1020,7 @@ suite('InlineScriptEnvManager', () => { test('resolves a short exact requirement to an advertised concrete release', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.0', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.0', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '==3.13' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1059,7 +1059,7 @@ suite('InlineScriptEnvManager', () => { test('uses an exact requirement without needing an existing uv catalog', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '==3.13.1' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1079,7 +1079,7 @@ suite('InlineScriptEnvManager', () => { test('does not select a uv prerelease unless requires-python permits it', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.14.2', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.14.2', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.14,<3.16' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1101,7 +1101,7 @@ suite('InlineScriptEnvManager', () => { test('installs an explicitly permitted prerelease lower bound', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.15.0a1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.15.0a1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.15.0a1,<3.16' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1119,7 +1119,7 @@ suite('InlineScriptEnvManager', () => { test('normalizes a PEP 440 prerelease alias before installation', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.15.0rc1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.15.0rc1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '==3.15.0c1' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1137,7 +1137,7 @@ suite('InlineScriptEnvManager', () => { test('does not prompt when requires-python has no safe lower bound', async () => { readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '<3.13' }); apiGetEnvironmentsStub.resolves([ - makeEnvironment('ms-python.python:system', '3.13.0', baseExecutable), + makeEnvironment(SYSTEM_MANAGER_ID, '3.13.0', baseExecutable), ]); assert.strictEqual(await manager.create(scriptUri()), undefined); @@ -1189,7 +1189,7 @@ suite('InlineScriptEnvManager', () => { const uri = scriptUri(); const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); let installed = false; apiGetEnvironmentsStub.callsFake(async () => (installed ? [uvBase] : [baseEnvironment])); @@ -1252,7 +1252,7 @@ suite('InlineScriptEnvManager', () => { test('coalesces simultaneous fallback requests for the same Python version', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); let isInstalled = false; @@ -1305,7 +1305,7 @@ suite('InlineScriptEnvManager', () => { test('reuses a compatible installation for a queued request with a different lower bound', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.1', uvExecutable); readMetadataStub.callsFake(async (uri: Uri) => ({ ...VALID_METADATA, requiresPython: uri.fsPath.endsWith('compatible.py') ? '~=3.13.0' : '>=3.13', @@ -1357,7 +1357,7 @@ suite('InlineScriptEnvManager', () => { test('reuses a directly resolved installation when discovery remains stale', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); apiGetEnvironmentsStub.resolves([baseEnvironment]); resolveSystemPythonStub.resolves(uvBase); @@ -1393,7 +1393,7 @@ suite('InlineScriptEnvManager', () => { test('reuses a directly resolved installation when later discovery throws', async () => { const uvExecutable = path.join(tempRoot, 'uv-python', isWindows() ? 'python.exe' : 'python'); await fs.outputFile(uvExecutable, ''); - const uvBase = makeEnvironment('ms-python.python:system', '3.13.1', uvExecutable); + const uvBase = makeEnvironment(SYSTEM_MANAGER_ID, '3.13.1', uvExecutable); readMetadataStub.resolves({ ...VALID_METADATA, requiresPython: '>=3.13' }); apiGetEnvironmentsStub.onFirstCall().resolves([baseEnvironment]); apiGetEnvironmentsStub.onSecondCall().resolves([baseEnvironment]); @@ -1495,7 +1495,7 @@ suite('InlineScriptEnvManager', () => { lastUsedAt: '2030-01-01T00:00:00.000Z', } satisfies cacheLayout.InlineScriptEnvMeta; const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, baseEnvironment.version, executable, directory.fsPath, @@ -1560,7 +1560,7 @@ suite('InlineScriptEnvManager', () => { await gate; return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -1624,7 +1624,7 @@ suite('InlineScriptEnvManager', () => { const target = args[6] as string; await fs.outputFile(venvPythonPath(target), ''); const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -1745,7 +1745,7 @@ suite('InlineScriptEnvManager', () => { const target = args[6] as string; await fs.outputFile(venvPythonPath(target), ''); const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -1851,7 +1851,7 @@ suite('InlineScriptEnvManager', () => { const target = args[6] as string; await fs.outputFile(venvPythonPath(target), ''); const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -1994,7 +1994,7 @@ suite('InlineScriptEnvManager', () => { const target = args[6] as string; await fs.outputFile(venvPythonPath(target), ''); const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -2074,7 +2074,7 @@ suite('InlineScriptEnvManager', () => { lastUsedAt: '2026-07-20T00:00:00.000Z', }; const cached = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2111,7 +2111,7 @@ suite('InlineScriptEnvManager', () => { sourceMetadataIdentityHashes: [cacheLayout.hashSourceMetadataIdentity('{"requiresPython":">=3.12","dependencies":["rich"]}')], }); const cached = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2150,7 +2150,7 @@ suite('InlineScriptEnvManager', () => { sourceMetadataIdentityHashes: hashes, }); const cached = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2184,7 +2184,7 @@ suite('InlineScriptEnvManager', () => { lastUsedAt: '2026-07-20T00:00:00.000Z', }); const cached = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2258,7 +2258,7 @@ suite('InlineScriptEnvManager', () => { }); resolveVenvStub.resolves( makeEnvironment( - 'ms-python.python:system', + SYSTEM_MANAGER_ID, '3.11.9', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2283,7 +2283,7 @@ suite('InlineScriptEnvManager', () => { }); resolveVenvStub.resolves( makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, 'Unknown', getVenvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2306,7 +2306,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(envDir().fsPath), ''); resolveVenvStub.resolves( makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.10.0', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2327,7 +2327,7 @@ suite('InlineScriptEnvManager', () => { }); resolveVenvStub.resolves( makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.11.9', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -2762,7 +2762,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(target), ''); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -2789,7 +2789,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(path.join(target, '.meta.json.backup-0123456789ab'), '{}'); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -2837,7 +2837,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(target), ''); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -2868,7 +2868,7 @@ suite('InlineScriptEnvManager', () => { await fs.ensureDir(target); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -2908,7 +2908,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(target), ''); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.11.9', venvPythonPath(target), target, @@ -2930,7 +2930,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(otherPython, ''); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', otherPython, otherRoot, @@ -3216,7 +3216,7 @@ suite('InlineScriptEnvManager', () => { } await fs.outputFile(physicalExecutable, ''); const environment = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', physicalExecutable, physicalEnvDir, @@ -3625,7 +3625,7 @@ suite('InlineScriptEnvManager', () => { lastUsedAt: NOW.toISOString(), }); const cached = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -3780,7 +3780,7 @@ suite('InlineScriptEnvManager', () => { await gate; return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -3858,7 +3858,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(target), ''); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -3908,7 +3908,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(target), ''); return { environment: makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', venvPythonPath(target), target, @@ -3950,7 +3950,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(venvPythonPath(envDir().fsPath), ''); resolveVenvStub.resolves( makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.10.0', venvPythonPath(envDir().fsPath), envDir().fsPath, @@ -4392,7 +4392,7 @@ suite('InlineScriptEnvManager', () => { await manager.set(uri, environment!); resolveVenvStub.resolves({ ...environment!, - envId: { ...environment!.envId, managerId: 'ms-python.python:system' }, + envId: { ...environment!.envId, managerId: SYSTEM_MANAGER_ID }, }); openDocumentsStub.returns([]); routingRegistry.setMetadata(uri, VALID_METADATA); @@ -6280,7 +6280,7 @@ suite('InlineScriptEnvManager', () => { persistedAssociations = { [normalizePath(uri.fsPath)]: environment.environmentPath.fsPath }; resolveVenvStub.resolves({ ...environment, - envId: { ...environment.envId, managerId: 'ms-python.python:system' }, + envId: { ...environment.envId, managerId: SYSTEM_MANAGER_ID }, }); assert.strictEqual(await manager.get(uri), undefined); @@ -6299,7 +6299,7 @@ suite('InlineScriptEnvManager', () => { await fs.outputFile(outsideExecutable, ''); await fs.ensureDir(cacheLayout.getScriptEnvCacheRoot(globalStorageUri).fsPath); const unowned = makeEnvironment( - 'ms-python.python:inline-script', + INLINE_SCRIPT_MANAGER_ID, '3.12.4', outsideExecutable, outsideDir, @@ -6444,7 +6444,7 @@ suite('InlineScriptEnvManager', () => { await manager.set(uri, environment); const stale = { ...environment, - envId: { ...environment.envId, managerId: 'ms-python.python:system' }, + envId: { ...environment.envId, managerId: SYSTEM_MANAGER_ID }, }; resolvePending!(stale); @@ -7130,7 +7130,7 @@ suite('InlineScriptEnvManager', () => { const environment = await createOwnedEnvironment(); const candidate = { ...environment }; if (invalidTarget === 'foreign-manager') { - candidate.envId = { ...environment.envId, managerId: 'ms-python.python:venv' }; + candidate.envId = { ...environment.envId, managerId: VENV_MANAGER_ID }; } else if (invalidTarget === 'outside-cache') { candidate.sysPrefix = path.join(tempRoot, 'outside', path.basename(environment.sysPrefix)); candidate.environmentPath = Uri.file(getVenvPythonPath(candidate.sysPrefix)); diff --git a/src/test/managers/builtin/pipPackageManager.unit.test.ts b/src/test/managers/builtin/pipPackageManager.unit.test.ts index 549fd9a26..ff2acc6ba 100644 --- a/src/test/managers/builtin/pipPackageManager.unit.test.ts +++ b/src/test/managers/builtin/pipPackageManager.unit.test.ts @@ -21,6 +21,7 @@ import { VenvManager } from '../../../managers/builtin/venvManager'; import * as packageChanges from '../../../managers/common/packageChanges'; import { createMockLogOutputChannel } from '../../mocks/helper'; import { createMockPythonEnvironment } from '../../mocks/pythonEnvironment'; +import { VENV_MANAGER_ID } from '../../../common/constants'; suite('PipPackageManager', () => { teardown(() => { @@ -240,7 +241,7 @@ suite('PipPackageManager', () => { const environment = createMockPythonEnvironment({ envPath: path.join(process.cwd(), 'python2'), version: '2.7.18', - managerId: 'ms-python.python:venv', + managerId: VENV_MANAGER_ID, }); const manager = new PipPackageManager({} as PythonEnvironmentApi, {} as LogOutputChannel, {} as VenvManager); @@ -276,7 +277,7 @@ suite('PipPackageManager', () => { sinon.stub(packageChanges, 'updatePackagesAndNotify').resolves([]); const environment = createMockPythonEnvironment({ envPath: path.join(process.cwd(), '.venv'), - managerId: 'ms-python.python:venv', + managerId: VENV_MANAGER_ID, }); const manager = new PipPackageManager( {} as PythonEnvironmentApi, diff --git a/src/test/managers/builtin/sysPythonManager.savedSelection.unit.test.ts b/src/test/managers/builtin/sysPythonManager.savedSelection.unit.test.ts index 8409cb002..af161d0e1 100644 --- a/src/test/managers/builtin/sysPythonManager.savedSelection.unit.test.ts +++ b/src/test/managers/builtin/sysPythonManager.savedSelection.unit.test.ts @@ -12,6 +12,7 @@ import { SysPythonManager } from '../../../managers/builtin/sysPythonManager'; import * as sysUtils from '../../../managers/builtin/utils'; import * as uvPythonInstaller from '../../../managers/builtin/uvPythonInstaller'; import { NativePythonFinder } from '../../../managers/common/nativePythonFinder'; +import { SYSTEM_MANAGER_ID } from '../../../common/constants'; suite('SysPythonManager saved selections', () => { const projectUri = Uri.file(path.resolve('project')); @@ -21,7 +22,7 @@ suite('SysPythonManager saved selections', () => { function makeEnv(executable: string): PythonEnvironment { return { - envId: { id: `system:${executable}`, managerId: 'ms-python.python:system' }, + envId: { id: `system:${executable}`, managerId: SYSTEM_MANAGER_ID }, name: 'Python 3.13.15 (uv)', displayName: 'Python 3.13.15 (uv)', displayPath: executable, diff --git a/src/test/managers/builtin/venvManager.resolve.unit.test.ts b/src/test/managers/builtin/venvManager.resolve.unit.test.ts index bac0f1b91..6fe133a75 100644 --- a/src/test/managers/builtin/venvManager.resolve.unit.test.ts +++ b/src/test/managers/builtin/venvManager.resolve.unit.test.ts @@ -14,7 +14,7 @@ import { PythonEnvironmentApi, PythonProject, } from '../../../api'; -import { VENV_MANAGER_ID } from '../../../common/constants'; +import { VENV_MANAGER_ID, SYSTEM_MANAGER_ID } from '../../../common/constants'; import * as windowApis from '../../../common/window.apis'; import { VenvManager } from '../../../managers/builtin/venvManager'; import * as venvUtils from '../../../managers/builtin/venvUtils'; @@ -119,7 +119,7 @@ suite('VenvManager - manual path resolution', () => { ? undefined : createMockPythonEnvironment({ envPath: selected.environmentPath.fsPath, - managerId: 'ms-python.python:system', + managerId: SYSTEM_MANAGER_ID, }), ); const onChanged = sandbox.spy(); diff --git a/src/test/managers/builtin/venvUtils.createWithProgress.unit.test.ts b/src/test/managers/builtin/venvUtils.createWithProgress.unit.test.ts index c665d140b..274e4b480 100644 --- a/src/test/managers/builtin/venvUtils.createWithProgress.unit.test.ts +++ b/src/test/managers/builtin/venvUtils.createWithProgress.unit.test.ts @@ -17,6 +17,7 @@ import * as pipUtils from '../../../managers/builtin/pipUtils'; import { PythonToolError, PythonToolOperation } from '../../../internal/pythonToolSupport'; import { NativePythonEnvironmentKind, NativePythonFinder } from '../../../managers/common/nativePythonFinder'; import * as managerUtils from '../../../managers/common/utils'; +import { SYSTEM_MANAGER_ID, INLINE_SCRIPT_MANAGER_ID } from '../../../common/constants'; suite('createWithProgress uv tracking', () => { let addUvEnvironmentStub: sinon.SinonStub; @@ -35,7 +36,7 @@ suite('createWithProgress uv tracking', () => { await fs.outputFile(pythonPath, ''); baseEnvironment = { - envId: { id: 'base', managerId: 'ms-python.python:system' }, + envId: { id: 'base', managerId: SYSTEM_MANAGER_ID }, name: 'base', displayName: 'base', displayPath: pythonPath, @@ -46,7 +47,7 @@ suite('createWithProgress uv tracking', () => { }; const createdEnvironment = { ...baseEnvironment, - envId: { id: 'created', managerId: 'ms-python.python:inline-script' }, + envId: { id: 'created', managerId: INLINE_SCRIPT_MANAGER_ID }, }; api = { createPythonEnvironmentItem: sinon.stub().returns(createdEnvironment), @@ -266,7 +267,7 @@ suite('getBaseInterpreterForVenv', () => { function makeBase(executable: string, sysPrefix: string): PythonEnvironment { return { - envId: { id: 'base', managerId: 'ms-python.python:system' }, + envId: { id: 'base', managerId: SYSTEM_MANAGER_ID }, name: 'base', displayName: 'base', displayPath: executable, diff --git a/src/test/managers/conda/condaEnvManager.initialize.unit.test.ts b/src/test/managers/conda/condaEnvManager.initialize.unit.test.ts index 40329deec..d9776c278 100644 --- a/src/test/managers/conda/condaEnvManager.initialize.unit.test.ts +++ b/src/test/managers/conda/condaEnvManager.initialize.unit.test.ts @@ -14,6 +14,7 @@ import { CondaEnvManager } from '../../../managers/conda/condaEnvManager'; import * as condaSourcingUtils from '../../../managers/conda/condaSourcingUtils'; import * as condaUtils from '../../../managers/conda/condaUtils'; import { makeMockCondaEnvironment as makeEnv } from '../../mocks/pythonEnvironment'; +import { CONDA_MANAGER_ID } from '../../../common/constants'; /** * Tests for the lazy-registration flow on CondaEnvManager.initialize(). @@ -188,7 +189,7 @@ suite('CondaEnvManager.initialize - lazy registration flow', () => { assert.strictEqual(mgr.sourcingInformation, undefined); // missing-default notification was shown assert.strictEqual(notifyMissingStub.callCount, 1); - assert.strictEqual(notifyMissingStub.firstCall.args[0], 'ms-python.python:conda'); + assert.strictEqual(notifyMissingStub.firstCall.args[0], CONDA_MANAGER_ID); assert.strictEqual(notifyMissingStub.firstCall.args[1], projectManager); const props = getLazyInitTelemetry(); diff --git a/src/test/managers/conda/condaUtils.pythonVersions.unit.test.ts b/src/test/managers/conda/condaUtils.pythonVersions.unit.test.ts index b659983ee..91f9d13d5 100644 --- a/src/test/managers/conda/condaUtils.pythonVersions.unit.test.ts +++ b/src/test/managers/conda/condaUtils.pythonVersions.unit.test.ts @@ -5,10 +5,11 @@ import assert from 'node:assert'; import { Uri } from 'vscode'; import { PythonEnvironment } from '../../../api'; import { getPythonVersionsForCreation } from '../../../managers/conda/condaUtils'; +import { SYSTEM_MANAGER_ID } from '../../../common/constants'; function makeEnvironment(version: string): PythonEnvironment { return { - envId: { id: version, managerId: 'ms-python.python:system' }, + envId: { id: version, managerId: SYSTEM_MANAGER_ID }, name: version, displayName: version, displayPath: version, diff --git a/src/test/managers/fastPath.get.unit.test.ts b/src/test/managers/fastPath.get.unit.test.ts index 62a48a88b..f55a745ef 100644 --- a/src/test/managers/fastPath.get.unit.test.ts +++ b/src/test/managers/fastPath.get.unit.test.ts @@ -27,6 +27,7 @@ import { PipenvManager } from '../../managers/pipenv/pipenvManager'; import * as pipenvUtils from '../../managers/pipenv/pipenvUtils'; import { PyEnvManager } from '../../managers/pyenv/pyenvManager'; import * as pyenvUtils from '../../managers/pyenv/pyenvUtils'; +import { VENV_MANAGER_ID, CONDA_MANAGER_ID, SYSTEM_MANAGER_ID, PYENV_MANAGER_ID, PIPENV_MANAGER_ID } from '../../common/constants'; interface ManagerUnderTest { get(scope: GetEnvironmentScope): Promise; @@ -108,7 +109,7 @@ function createManagerCases(): ManagerCase[] { return [ { name: 'VenvManager', - managerId: 'ms-python.python:venv', + managerId: VENV_MANAGER_ID, persistedPath: path.resolve('test-workspace', '.venv'), createContext: (sandbox: sinon.SinonSandbox) => { const getPersistedStub = sandbox.stub(venvUtils, 'getVenvForWorkspace'); @@ -124,7 +125,7 @@ function createManagerCases(): ManagerCase[] { }, { name: 'CondaEnvManager', - managerId: 'ms-python.python:conda', + managerId: CONDA_MANAGER_ID, persistedPath: path.resolve('test', 'conda', 'envs', 'myenv'), createContext: (sandbox: sinon.SinonSandbox) => { const getPersistedStub = sandbox.stub(condaUtils, 'getCondaForWorkspace'); @@ -136,7 +137,7 @@ function createManagerCases(): ManagerCase[] { }, { name: 'SysPythonManager', - managerId: 'ms-python.python:system', + managerId: SYSTEM_MANAGER_ID, persistedPath: path.resolve('test', 'bin', 'python3'), supportsGlobalScope: true, createContext: (sandbox: sinon.SinonSandbox) => { @@ -150,7 +151,7 @@ function createManagerCases(): ManagerCase[] { }, { name: 'PyEnvManager', - managerId: 'ms-python.python:pyenv', + managerId: PYENV_MANAGER_ID, persistedPath: path.resolve('test', '.pyenv', 'versions', '3.11.0', 'bin', 'python'), createContext: (sandbox: sinon.SinonSandbox) => { const getPersistedStub = sandbox.stub(pyenvUtils, 'getPyenvForWorkspace'); @@ -162,7 +163,7 @@ function createManagerCases(): ManagerCase[] { }, { name: 'PipenvManager', - managerId: 'ms-python.python:pipenv', + managerId: PIPENV_MANAGER_ID, persistedPath: path.resolve('test', '.local', 'share', 'virtualenvs', 'project-abc123', 'bin', 'python'), createContext: (sandbox: sinon.SinonSandbox) => { const getPersistedStub = sandbox.stub(pipenvUtils, 'getPipenvForWorkspace'); @@ -303,7 +304,7 @@ suite('Manager get() fast path', () => { suite('VenvManager specific', () => { test('fast path: background init failure resets _initialized for retry', async () => { const persistedPath = path.resolve('test-workspace', '.venv'); - const mockEnv = createMockEnv('ms-python.python:venv', persistedPath); + const mockEnv = createMockEnv(VENV_MANAGER_ID, persistedPath); const getVenvStub = sandbox.stub(venvUtils, 'getVenvForWorkspace').resolves(persistedPath); const resolveVenvStub = sandbox.stub(venvUtils, 'resolveVenvPythonEnvironmentPath').resolves(mockEnv); @@ -333,7 +334,7 @@ suite('Manager get() fast path', () => { test('fast path: uses scope.fsPath when getPythonProject returns undefined', async () => { const persistedPath = path.resolve('test-workspace', '.venv'); - const mockEnv = createMockEnv('ms-python.python:venv', persistedPath); + const mockEnv = createMockEnv(VENV_MANAGER_ID, persistedPath); const getVenvStub = sandbox.stub(venvUtils, 'getVenvForWorkspace').resolves(persistedPath); const resolveVenvStub = sandbox.stub(venvUtils, 'resolveVenvPythonEnvironmentPath').resolves(mockEnv); diff --git a/src/test/managers/pipenv/pipenvUtils.getPipenv.unit.test.ts b/src/test/managers/pipenv/pipenvUtils.getPipenv.unit.test.ts index 9346e1cb3..53a4e7082 100644 --- a/src/test/managers/pipenv/pipenvUtils.getPipenv.unit.test.ts +++ b/src/test/managers/pipenv/pipenvUtils.getPipenv.unit.test.ts @@ -4,12 +4,7 @@ import * as childProcessApis from '../../../common/childProcess.apis'; import * as logging from '../../../common/logging'; import * as persistentState from '../../../common/persistentState'; import * as settingHelpers from '../../../features/settings/settingHelpers'; -import { - clearPipenvCache, - getPipenv, - getPipenvVersion, - PIPENV_PATH_KEY, -} from '../../../managers/pipenv/pipenvUtils'; +import { clearPipenvCache, getPipenv, getPipenvVersion, PIPENV_PATH_KEY } from '../../../managers/pipenv/pipenvUtils'; /** * Tests for getPipenv prioritization. diff --git a/src/test/managers/poetry/poetryPackageManager.unit.test.ts b/src/test/managers/poetry/poetryPackageManager.unit.test.ts index a22062cfb..5f7afd0c5 100644 --- a/src/test/managers/poetry/poetryPackageManager.unit.test.ts +++ b/src/test/managers/poetry/poetryPackageManager.unit.test.ts @@ -15,11 +15,12 @@ import { PoetryPackageManager } from '../../../managers/poetry/poetryPackageMana import { PoetryManager } from '../../../managers/poetry/poetryManager'; import * as poetryUtils from '../../../managers/poetry/poetryUtils'; import { createMockPythonEnvironment } from '../../mocks/pythonEnvironment'; +import { POETRY_MANAGER_ID } from '../../../common/constants'; suite('PoetryPackageManager', () => { const environment = createMockPythonEnvironment({ envPath: path.join(process.cwd(), '.venv'), - managerId: 'ms-python.python:poetry', + managerId: POETRY_MANAGER_ID, }); let logError: sinon.SinonStub; let manager: PoetryPackageManager; diff --git a/src/test/mocks/pythonEnvironment.ts b/src/test/mocks/pythonEnvironment.ts index 548a1d9be..5cbe90f40 100644 --- a/src/test/mocks/pythonEnvironment.ts +++ b/src/test/mocks/pythonEnvironment.ts @@ -4,6 +4,7 @@ import { Uri } from 'vscode'; import { PythonEnvironment } from '../../api'; import { PythonEnvironmentImpl } from '../../managers/common/models'; +import { CONDA_MANAGER_ID } from '../../common/constants'; /** * Options for {@link createMockPythonEnvironment}. @@ -42,7 +43,7 @@ export function createMockPythonEnvironment(options: MockPythonEnvironmentOption envPath, sysPrefix = envPath, version = '3.12.0', - managerId = 'ms-python.python:conda', + managerId = CONDA_MANAGER_ID, id = `${name}-test`, description, displayName = `${name} (${version})`,