@@ -266,4 +266,157 @@ suite('DeepnoteEnvironmentManager', () => {
266266 // Should not throw
267267 } ) ;
268268 } ) ;
269+
270+ suite ( 'environment migration' , ( ) => {
271+ test ( 'should migrate hash-based venv paths to UUID-based paths' , async ( ) => {
272+ const oldHashBasedConfig = {
273+ id : 'abcd1234-5678-90ab-cdef-123456789012' ,
274+ name : 'Old Hash Config' ,
275+ pythonInterpreter : testInterpreter ,
276+ venvPath : Uri . file ( '/global/storage/deepnote-venvs/venv_7626587d-1.0.0' ) ,
277+ createdAt : new Date ( ) ,
278+ lastUsedAt : new Date ( )
279+ } ;
280+
281+ when ( mockStorage . loadEnvironments ( ) ) . thenResolve ( [ oldHashBasedConfig ] ) ;
282+ when ( mockStorage . saveEnvironments ( anything ( ) ) ) . thenResolve ( ) ;
283+ when ( mockContext . globalStorageUri ) . thenReturn ( Uri . file ( '/global/storage' ) ) ;
284+
285+ manager . activate ( ) ;
286+ await manager . waitForInitialization ( ) ;
287+
288+ const configs = manager . listEnvironments ( ) ;
289+ assert . strictEqual ( configs . length , 1 ) ;
290+
291+ // Should have migrated to UUID-based path
292+ assert . strictEqual (
293+ configs [ 0 ] . venvPath . fsPath ,
294+ '/global/storage/deepnote-venvs/abcd1234-5678-90ab-cdef-123456789012'
295+ ) ;
296+
297+ // Should clear toolkit version to force reinstallation
298+ assert . isUndefined ( configs [ 0 ] . toolkitVersion ) ;
299+
300+ // Should have saved the migration
301+ verify ( mockStorage . saveEnvironments ( anything ( ) ) ) . once ( ) ;
302+ } ) ;
303+
304+ test ( 'should migrate VS Code storage paths to Cursor storage paths' , async ( ) => {
305+ const vsCodeConfig = {
306+ id : 'cursor-env-id' ,
307+ name : 'VS Code Environment' ,
308+ pythonInterpreter : testInterpreter ,
309+ venvPath : Uri . file (
310+ '/Library/Application Support/Code/User/globalStorage/deepnote.vscode-deepnote/deepnote-venvs/cursor-env-id'
311+ ) ,
312+ createdAt : new Date ( ) ,
313+ lastUsedAt : new Date ( ) ,
314+ toolkitVersion : '1.0.0'
315+ } ;
316+
317+ when ( mockStorage . loadEnvironments ( ) ) . thenResolve ( [ vsCodeConfig ] ) ;
318+ when ( mockStorage . saveEnvironments ( anything ( ) ) ) . thenResolve ( ) ;
319+ when ( mockContext . globalStorageUri ) . thenReturn (
320+ Uri . file ( '/Library/Application Support/Cursor/User/globalStorage/deepnote.vscode-deepnote' )
321+ ) ;
322+
323+ manager . activate ( ) ;
324+ await manager . waitForInitialization ( ) ;
325+
326+ const configs = manager . listEnvironments ( ) ;
327+ assert . strictEqual ( configs . length , 1 ) ;
328+
329+ // Should have migrated to Cursor storage
330+ assert . match ( configs [ 0 ] . venvPath . fsPath , / C u r s o r .* d e e p n o t e - v e n v s \/ c u r s o r - e n v - i d $ / ) ;
331+
332+ // Should clear toolkit version to force reinstallation
333+ assert . isUndefined ( configs [ 0 ] . toolkitVersion ) ;
334+
335+ verify ( mockStorage . saveEnvironments ( anything ( ) ) ) . once ( ) ;
336+ } ) ;
337+
338+ test ( 'should not migrate environments with correct UUID paths in correct storage' , async ( ) => {
339+ const testDate = new Date ( ) ;
340+ const correctConfig = {
341+ id : '12345678-1234-1234-1234-123456789abc' ,
342+ name : 'Correct Config' ,
343+ pythonInterpreter : testInterpreter ,
344+ venvPath : Uri . file ( '/global/storage/deepnote-venvs/12345678-1234-1234-1234-123456789abc' ) ,
345+ createdAt : testDate ,
346+ lastUsedAt : testDate ,
347+ toolkitVersion : '1.0.0' ,
348+ packages : [ ]
349+ } ;
350+
351+ when ( mockStorage . loadEnvironments ( ) ) . thenResolve ( [ correctConfig ] ) ;
352+ when ( mockStorage . saveEnvironments ( anything ( ) ) ) . thenResolve ( ) ;
353+ when ( mockContext . globalStorageUri ) . thenReturn ( Uri . file ( '/global/storage' ) ) ;
354+
355+ manager . activate ( ) ;
356+ await manager . waitForInitialization ( ) ;
357+
358+ const configs = manager . listEnvironments ( ) ;
359+ assert . strictEqual ( configs . length , 1 ) ;
360+
361+ // Path should remain unchanged
362+ assert . strictEqual (
363+ configs [ 0 ] . venvPath . fsPath ,
364+ '/global/storage/deepnote-venvs/12345678-1234-1234-1234-123456789abc'
365+ ) ;
366+
367+ // ID and name should be preserved
368+ assert . strictEqual ( configs [ 0 ] . id , '12345678-1234-1234-1234-123456789abc' ) ;
369+ assert . strictEqual ( configs [ 0 ] . name , 'Correct Config' ) ;
370+
371+ // Should NOT have saved (no migration needed)
372+ verify ( mockStorage . saveEnvironments ( anything ( ) ) ) . never ( ) ;
373+ } ) ;
374+
375+ test ( 'should migrate multiple environments at once' , async ( ) => {
376+ const configs = [
377+ {
378+ id : 'uuid1' ,
379+ name : 'Hash Config' ,
380+ pythonInterpreter : testInterpreter ,
381+ venvPath : Uri . file ( '/global/storage/deepnote-venvs/venv_abc123-1.0.0' ) ,
382+ createdAt : new Date ( ) ,
383+ lastUsedAt : new Date ( )
384+ } ,
385+ {
386+ id : 'uuid2' ,
387+ name : 'VS Code Config' ,
388+ pythonInterpreter : testInterpreter ,
389+ venvPath : Uri . file ( '/Code/globalStorage/deepnote-venvs/uuid2' ) ,
390+ createdAt : new Date ( ) ,
391+ lastUsedAt : new Date ( )
392+ } ,
393+ {
394+ id : 'uuid3' ,
395+ name : 'Correct Config' ,
396+ pythonInterpreter : testInterpreter ,
397+ venvPath : Uri . file ( '/global/storage/deepnote-venvs/uuid3' ) ,
398+ createdAt : new Date ( ) ,
399+ lastUsedAt : new Date ( )
400+ }
401+ ] ;
402+
403+ when ( mockStorage . loadEnvironments ( ) ) . thenResolve ( configs ) ;
404+ when ( mockStorage . saveEnvironments ( anything ( ) ) ) . thenResolve ( ) ;
405+ when ( mockContext . globalStorageUri ) . thenReturn ( Uri . file ( '/global/storage' ) ) ;
406+
407+ manager . activate ( ) ;
408+ await manager . waitForInitialization ( ) ;
409+
410+ const loaded = manager . listEnvironments ( ) ;
411+ assert . strictEqual ( loaded . length , 3 ) ;
412+
413+ // First two should be migrated
414+ assert . strictEqual ( loaded [ 0 ] . venvPath . fsPath , '/global/storage/deepnote-venvs/uuid1' ) ;
415+ assert . strictEqual ( loaded [ 1 ] . venvPath . fsPath , '/global/storage/deepnote-venvs/uuid2' ) ;
416+ // Third should remain unchanged
417+ assert . strictEqual ( loaded [ 2 ] . venvPath . fsPath , '/global/storage/deepnote-venvs/uuid3' ) ;
418+
419+ verify ( mockStorage . saveEnvironments ( anything ( ) ) ) . once ( ) ;
420+ } ) ;
421+ } ) ;
269422} ) ;
0 commit comments