66import { expect } from 'chai' ;
77import * as sinon from 'sinon' ;
88import { Uri , Disposable , Extension , extensions } from 'vscode' ;
9+ import * as path from 'path' ;
910import * as pythonApi from '../../../extension/common/python' ;
1011import * as utilities from '../../../extension/common/utilities' ;
1112import { buildPythonEnvironment } from './helpers' ;
1213
14+ // Platform-specific path constants using path.join so tests assert using native separators.
15+ // Leading root '/' preserved; on Windows this yields a leading backslash (e.g. '\\usr\\bin').
16+ const PYTHON_PATH = path . join ( '/' , 'usr' , 'bin' , 'python3' ) ;
17+ const PYTHON_PATH_39 = path . join ( '/' , 'usr' , 'bin' , 'python3.9' ) ;
18+ const PYTHON_PATH_WITH_SPACES = path . join ( '/' , 'path with spaces' , 'python3' ) ;
19+ const QUOTED_PYTHON_PATH = `"${ PYTHON_PATH_WITH_SPACES } "` ;
20+ const PYTHON_PATH_DIR = path . join ( '/' , 'usr' , 'bin' ) ;
21+ const PYTHON_LIB_PYTHON3_DIR = path . join ( '/' , 'usr' , 'lib' , 'python3' ) ;
22+ const WORKSPACE_FILE = path . join ( '/' , 'workspace' , 'file.py' ) ;
23+ const WORKSPACE_PYTHON_DIR = path . join ( '/' , 'workspace' , 'python' ) ;
24+ const INVALID_PATH = path . join ( '/' , 'invalid' , 'path' ) ;
25+ const MOCK_PATH = path . join ( '/' , 'mock' , 'path' ) ;
26+
1327suite ( 'Python API Tests- useEnvironmentsExtension:true' , ( ) => {
1428 let getExtensionStub : sinon . SinonStub ;
1529 let mockPythonExtension : Extension < any > ;
@@ -26,8 +40,8 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
2640 // Create mock Python extension
2741 mockPythonExtension = {
2842 id : 'ms-python.python' ,
29- extensionUri : Uri . file ( '/mock/path' ) ,
30- extensionPath : '/mock/path' ,
43+ extensionUri : Uri . file ( MOCK_PATH ) ,
44+ extensionPath : MOCK_PATH ,
3145 isActive : true ,
3246 packageJSON : { } ,
3347 exports : undefined ,
@@ -38,8 +52,8 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
3852 // Create mock Python Envs extension
3953 mockEnvsExtension = {
4054 id : 'ms-python.vscode-python-envs' ,
41- extensionUri : Uri . file ( '/mock/path' ) ,
42- extensionPath : '/mock/path' ,
55+ extensionUri : Uri . file ( MOCK_PATH ) ,
56+ extensionPath : MOCK_PATH ,
4357 isActive : true ,
4458 packageJSON : { } ,
4559 exports : undefined ,
@@ -72,7 +86,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
7286 test ( 'Should initialize python and set up event listeners' , async ( ) => {
7387 const disposables : Disposable [ ] = [ ] ;
7488 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
75- const mockPythonEnv = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
89+ const mockPythonEnv = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
7690 mockPythonEnvApi . getEnvironment . resolves ( mockPythonEnv ) ;
7791 mockPythonEnvApi . resolveEnvironment . resolves ( mockPythonEnv ) ;
7892 mockPythonEnvApi . onDidChangeEnvironments . returns ( {
@@ -100,7 +114,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
100114 const mockEventHandler = sinon . stub ( ) ;
101115
102116 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
103- const mockPythonEnv = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
117+ const mockPythonEnv = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
104118 mockPythonEnvApi . getEnvironment . resolves ( mockPythonEnv ) ;
105119 mockPythonEnvApi . resolveEnvironment . resolves ( mockPythonEnv ) ;
106120
@@ -115,9 +129,9 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
115129
116130 suite ( 'getSettingsPythonPath' , ( ) => {
117131 test ( 'Should return execution details from Python extension API' , async ( ) => {
118- const expectedPath = [ '/usr/bin/python3' ] ;
132+ const expectedPath = [ PYTHON_PATH ] ;
119133 // OLD API: Using getEnvironment() + resolveEnvironment() instead of settings.getExecutionDetails
120- const mockPythonEnv = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
134+ const mockPythonEnv = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
121135 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
122136 mockPythonEnvApi . getEnvironment . resolves ( mockPythonEnv ) ;
123137 mockPythonEnvApi . resolveEnvironment . resolves ( mockPythonEnv ) ;
@@ -128,10 +142,10 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
128142 } ) ;
129143
130144 test ( 'Should return execution details for specific resource' , async ( ) => {
131- const resource = Uri . file ( '/workspace/file.py' ) ;
132- const expectedPath = [ '/usr/bin/python3' ] ;
145+ const resource = Uri . file ( WORKSPACE_FILE ) ;
146+ const expectedPath = [ PYTHON_PATH ] ;
133147 // OLD API: Using getEnvironment() + resolveEnvironment() instead of settings.getExecutionDetails
134- const mockPythonEnv = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
148+ const mockPythonEnv = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
135149 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
136150 mockPythonEnvApi . getEnvironment . resolves ( mockPythonEnv ) ;
137151 mockPythonEnvApi . resolveEnvironment . resolves ( mockPythonEnv ) ;
@@ -157,7 +171,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
157171 suite ( 'getEnvironmentVariables' , ( ) => {
158172 test ( 'Should return environment variables from Python extension API' , async ( ) => {
159173 // eslint-disable-next-line @typescript-eslint/naming-convention
160- const expectedVars = { PATH : '/usr/bin' , PYTHONPATH : '/usr/lib/python3' } ;
174+ const expectedVars = { PATH : PYTHON_PATH_DIR , PYTHONPATH : PYTHON_LIB_PYTHON3_DIR } ;
161175 // OLD API: Using getEnvironmentVariables() instead of environments.getEnvironmentVariables
162176 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
163177 mockPythonEnvApi . getEnvironmentVariables . resolves ( expectedVars ) ;
@@ -170,9 +184,9 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
170184 } ) ;
171185
172186 test ( 'Should get environment variables for specific resource' , async ( ) => {
173- const resource = Uri . file ( '/workspace/file.py' ) ;
187+ const resource = Uri . file ( WORKSPACE_FILE ) ;
174188 // eslint-disable-next-line @typescript-eslint/naming-convention
175- const expectedVars = { PATH : '/usr/bin' } ;
189+ const expectedVars = { PATH : PYTHON_PATH_DIR } ;
176190 // OLD API: Using getEnvironmentVariables() instead of environments.getEnvironmentVariables
177191 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
178192 mockPythonEnvApi . getEnvironmentVariables . resolves ( expectedVars ) ;
@@ -186,7 +200,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
186200
187201 test ( 'Should handle undefined resource and return workspace environment variables' , async ( ) => {
188202 // eslint-disable-next-line @typescript-eslint/naming-convention
189- const expectedVars = { PATH : '/usr/bin' , PYTHONPATH : '/workspace/python' } ;
203+ const expectedVars = { PATH : PYTHON_PATH_DIR , PYTHONPATH : WORKSPACE_PYTHON_DIR } ;
190204 // OLD API: Using getEnvironmentVariables() instead of environments.getEnvironmentVariables
191205 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
192206 mockPythonEnvApi . getEnvironmentVariables . resolves ( expectedVars ) ;
@@ -201,7 +215,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
201215
202216 suite ( 'resolveEnvironment' , ( ) => {
203217 test ( 'Should resolve environment from path string' , async ( ) => {
204- const envPath = '/usr/bin/python3' ;
218+ const envPath = PYTHON_PATH ;
205219 // Use buildPythonEnvironment for realistic mock
206220 const expectedEnv = buildPythonEnvironment ( envPath , '3.9.0' ) ;
207221
@@ -218,7 +232,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
218232
219233 test ( 'Should resolve environment from Environment object' , async ( ) => {
220234 // Use buildPythonEnvironment for realistic mock
221- const expectedEnv = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
235+ const expectedEnv = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
222236
223237 // OLD API: Using resolveEnvironment() instead of environments.resolveEnvironment
224238 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
@@ -230,7 +244,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
230244 } ) ;
231245
232246 test ( 'Should return undefined for invalid environment' , async ( ) => {
233- const envPath = '/invalid/path' ;
247+ const envPath = INVALID_PATH ;
234248 // OLD API: Using resolveEnvironment() instead of environments.resolveEnvironment
235249 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
236250 mockPythonEnvApi . resolveEnvironment . resolves ( undefined ) ;
@@ -244,32 +258,32 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
244258 suite ( 'getActiveEnvironmentPath' , ( ) => {
245259 test ( 'Should return active environment path' , async ( ) => {
246260 // Match production shape: getEnvironment() returns a PythonEnvironment-like object
247- const envObj = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
261+ const envObj = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
248262 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
249263 mockPythonEnvApi . getEnvironment . returns ( envObj ) ;
250264
251265 const result = await pythonApi . getActiveEnvironmentPath ( ) ;
252266
253- expect ( ( result as any ) . environmentPath . fsPath ) . to . equal ( '/usr/bin/python3' ) ;
254- expect ( ( result as any ) . execInfo . run . executable ) . to . equal ( '/usr/bin/python3' ) ;
267+ expect ( ( result as any ) . environmentPath . fsPath ) . to . equal ( PYTHON_PATH ) ;
268+ expect ( ( result as any ) . execInfo . run . executable ) . to . equal ( PYTHON_PATH ) ;
255269 } ) ;
256270
257271 test ( 'Should return active environment path for specific resource' , async ( ) => {
258- const resource = Uri . file ( '/workspace/file.py' ) ;
259- const envObj = buildPythonEnvironment ( '/usr/bin/python3' , '3.9.0' ) ;
272+ const resource = Uri . file ( WORKSPACE_FILE ) ;
273+ const envObj = buildPythonEnvironment ( PYTHON_PATH , '3.9.0' ) ;
260274 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
261275 mockPythonEnvApi . getEnvironment . returns ( envObj ) ;
262276
263277 const result = await pythonApi . getActiveEnvironmentPath ( resource ) ;
264278
265- expect ( ( result as any ) . environmentPath . fsPath ) . to . equal ( '/usr/bin/python3' ) ;
279+ expect ( ( result as any ) . environmentPath . fsPath ) . to . equal ( PYTHON_PATH ) ;
266280 sinon . assert . calledWith ( mockPythonEnvApi . getEnvironment , resource ) ;
267281 } ) ;
268282 } ) ;
269283
270284 suite ( 'getInterpreterDetails' , ( ) => {
271285 test ( 'Should return interpreter details without resource' , async ( ) => {
272- const pythonPath = '/usr/bin/python3' ;
286+ const pythonPath = PYTHON_PATH ;
273287 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
274288
275289 // OLD API: Using getEnvironment() and resolveEnvironment() instead of environments.*
@@ -285,8 +299,8 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
285299 } ) ;
286300
287301 test ( 'Should return interpreter details with resource' , async ( ) => {
288- const resource = Uri . file ( '/workspace/file.py' ) ;
289- const pythonPath = '/usr/bin/python3' ;
302+ const resource = Uri . file ( WORKSPACE_FILE ) ;
303+ const pythonPath = PYTHON_PATH ;
290304 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
291305
292306 // OLD API: Using getEnvironment() and resolveEnvironment() instead of environments.*
@@ -303,7 +317,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
303317
304318 test ( 'Should not quote path with spaces' , async ( ) => {
305319 // this should be updated when we fix the quoting logic in getInterpreterDetails
306- const pythonPath = '/path with spaces/python3' ;
320+ const pythonPath = PYTHON_PATH_WITH_SPACES ;
307321 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
308322
309323 // OLD API: Using getEnvironment() and resolveEnvironment() instead of environments.*
@@ -317,7 +331,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
317331 } ) ;
318332
319333 test ( 'Should not double-quote already quoted path' , async ( ) => {
320- const quotedPython = Uri . file ( '"/path with spaces/python3"' ) ;
334+ const quotedPython = Uri . file ( QUOTED_PYTHON_PATH ) ;
321335 const quotedPythonPath = quotedPython . fsPath ;
322336 const mockEnv = buildPythonEnvironment ( quotedPythonPath , '3.9.0' ) ;
323337
@@ -334,7 +348,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
334348 test ( 'Should return undefined path when environment is not resolved' , async ( ) => {
335349 // OLD API: Using getEnvironment() and resolveEnvironment() instead of environments.*
336350 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
337- mockPythonEnvApi . getEnvironment . returns ( { environmentPath : Uri . file ( '/usr/bin/python3' ) } ) ;
351+ mockPythonEnvApi . getEnvironment . returns ( { environmentPath : Uri . file ( PYTHON_PATH ) } ) ;
338352 mockPythonEnvApi . resolveEnvironment . resolves ( undefined ) ;
339353
340354 const result = await pythonApi . getInterpreterDetails ( ) ;
@@ -353,7 +367,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
353367
354368 // OLD API: Using getEnvironment() and resolveEnvironment() instead of environments.*
355369 ( mockEnvsExtension as any ) . exports = mockPythonEnvApi ;
356- mockPythonEnvApi . getEnvironment . returns ( { environmentPath : Uri . file ( '/usr/bin/python3' ) } ) ;
370+ mockPythonEnvApi . getEnvironment . returns ( { environmentPath : Uri . file ( PYTHON_PATH ) } ) ;
357371 mockPythonEnvApi . resolveEnvironment . resolves ( mockEnv ) ;
358372
359373 const result = await pythonApi . getInterpreterDetails ( ) ;
@@ -367,7 +381,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
367381 const disposables : Disposable [ ] = [ ] ;
368382 let eventCallback : any ;
369383 const mockEventHandler = sinon . stub ( ) ;
370- const pythonPath = '/usr/bin/python3.9' ;
384+ const pythonPath = PYTHON_PATH_39 ;
371385 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
372386
373387 // OLD API: Using onDidChangeEnvironments instead of onDidChangeActiveEnvironmentPath
@@ -398,7 +412,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
398412 const disposables : Disposable [ ] = [ ] ;
399413 let eventCallback : any ;
400414 const mockEventHandler = sinon . stub ( ) ;
401- const pythonPath = '/usr/bin/python3.9' ;
415+ const pythonPath = PYTHON_PATH_39 ;
402416 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
403417
404418 // OLD API: Using onDidChangeEnvironments instead of onDidChangeActiveEnvironmentPath
@@ -427,7 +441,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
427441 const disposables : Disposable [ ] = [ ] ;
428442 let eventCallback : any ;
429443 const mockEventHandler = sinon . stub ( ) ;
430- const pythonPath = '/usr/bin/python3.9' ;
444+ const pythonPath = PYTHON_PATH_39 ;
431445 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
432446
433447 // OLD API: Using onDidChangeEnvironments instead of onDidChangeActiveEnvironmentPath
@@ -456,7 +470,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
456470 const disposables : Disposable [ ] = [ ] ;
457471 let eventCallback : any ;
458472 const mockEventHandler = sinon . stub ( ) ;
459- const pythonPath = '/usr/bin/python3.9' ;
473+ const pythonPath = PYTHON_PATH_39 ;
460474 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
461475
462476 // OLD API: Using onDidChangeEnvironments instead of onDidChangeActiveEnvironmentPath
@@ -485,7 +499,7 @@ suite('Python API Tests- useEnvironmentsExtension:true', () => {
485499 const disposables : Disposable [ ] = [ ] ;
486500 let eventCallback : any ;
487501 const mockEventHandler = sinon . stub ( ) ;
488- const pythonPath = '/usr/bin/python3.9' ;
502+ const pythonPath = PYTHON_PATH_39 ;
489503 const mockEnv = buildPythonEnvironment ( pythonPath , '3.9.0' ) ;
490504
491505 // OLD API: Using onDidChangeEnvironments instead of onDidChangeActiveEnvironmentPath
0 commit comments