11import { assert } from 'chai' ;
22import * as sinon from 'sinon' ;
3- import { anything , instance , mock , when } from 'ts-mockito' ;
3+ import { anything , instance , mock , verify , when } from 'ts-mockito' ;
44import { DeepnoteKernelAutoSelector } from './deepnoteKernelAutoSelector.node' ;
55import {
66 IDeepnoteEnvironmentManager ,
@@ -21,6 +21,7 @@ import { NotebookDocument, Uri, NotebookController, CancellationToken } from 'vs
2121import { DeepnoteEnvironment } from '../../kernels/deepnote/environments/deepnoteEnvironment' ;
2222import { PythonEnvironment } from '../../platform/pythonEnvironments/info' ;
2323import { computeRequirementsHash } from './deepnoteProjectUtils' ;
24+ import { mockedVSCodeNamespaces , resetVSCodeMocks } from '../../test/vscode-mock' ;
2425
2526suite ( 'DeepnoteKernelAutoSelector - rebuildController' , ( ) => {
2627 let selector : DeepnoteKernelAutoSelector ;
@@ -48,6 +49,7 @@ suite('DeepnoteKernelAutoSelector - rebuildController', () => {
4849 let sandbox : sinon . SinonSandbox ;
4950
5051 setup ( ( ) => {
52+ resetVSCodeMocks ( ) ;
5153 sandbox = sinon . createSandbox ( ) ;
5254
5355 // Create mocks for all dependencies
@@ -275,6 +277,169 @@ suite('DeepnoteKernelAutoSelector - rebuildController', () => {
275277 } ) ;
276278 } ) ;
277279
280+ suite ( 'pickEnvironment' , ( ) => {
281+ test ( 'should return selected environment when user picks one' , async ( ) => {
282+ // Arrange
283+ const notebookUri = Uri . parse ( 'file:///test/notebook.deepnote' ) ;
284+ const mockEnv1 = createMockEnvironment ( 'env-1' , 'Environment 1' ) ;
285+ const mockEnv2 = createMockEnvironment ( 'env-2' , 'Environment 2' ) ;
286+ const environments = [ mockEnv1 , mockEnv2 ] ;
287+
288+ // Mock environment manager
289+ when ( mockEnvironmentManager . waitForInitialization ( ) ) . thenResolve ( ) ;
290+ when ( mockEnvironmentManager . listEnvironments ( ) ) . thenReturn ( environments ) ;
291+
292+ // Mock window.showQuickPick to simulate user selecting the first environment
293+ when ( mockedVSCodeNamespaces . window . showQuickPick ( anything ( ) , anything ( ) ) ) . thenResolve ( {
294+ label : mockEnv1 . name ,
295+ description : mockEnv1 . pythonInterpreter . uri . fsPath ,
296+ environment : mockEnv1
297+ } as any ) ;
298+
299+ // Act
300+ const result = await selector . pickEnvironment ( notebookUri ) ;
301+
302+ // Assert
303+ assert . strictEqual ( result , mockEnv1 , 'Should return the selected environment' ) ;
304+ } ) ;
305+ } ) ;
306+
307+ suite ( 'onKernelStarted' , ( ) => {
308+ test ( 'should return early and not call initNotebookRunner for non-deepnote notebooks' , async ( ) => {
309+ // Arrange
310+ const mockKernel = mock < IKernel > ( ) ;
311+ const mockJupyterNotebook = mock < NotebookDocument > ( ) ;
312+
313+ when ( mockJupyterNotebook . notebookType ) . thenReturn ( 'jupyter-notebook' ) ;
314+ when ( mockKernel . notebook ) . thenReturn ( instance ( mockJupyterNotebook ) ) ;
315+
316+ // Mock initNotebookRunner to track if it gets called
317+ when ( mockInitNotebookRunner . runInitNotebookIfNeeded ( anything ( ) , anything ( ) , anything ( ) ) ) . thenResolve ( ) ;
318+
319+ // Act
320+ await selector . onKernelStarted ( instance ( mockKernel ) ) ;
321+
322+ // Assert - verify initNotebookRunner was never called
323+ verify ( mockInitNotebookRunner . runInitNotebookIfNeeded ( anything ( ) , anything ( ) , anything ( ) ) ) . never ( ) ;
324+ } ) ;
325+ } ) ;
326+
327+ suite ( 'ensureKernelSelected' , ( ) => {
328+ test ( 'should return false when no environment ID is assigned to the notebook' , async ( ) => {
329+ // Mock environment mapper to return null (no environment assigned)
330+ when ( mockNotebookEnvironmentMapper . getEnvironmentForNotebook ( anything ( ) ) ) . thenReturn ( undefined ) ;
331+
332+ // Stub ensureKernelSelectedWithConfiguration to track if it gets called
333+ const ensureKernelSelectedStub = sandbox . stub ( selector , 'ensureKernelSelectedWithConfiguration' ) . resolves ( ) ;
334+
335+ // Mock commands.executeCommand
336+ when ( mockedVSCodeNamespaces . commands . executeCommand ( anything ( ) , anything ( ) ) ) . thenResolve ( ) ;
337+
338+ // Act
339+ const result = await selector . ensureKernelSelected (
340+ mockNotebook ,
341+ mockProgress ,
342+ instance ( mockCancellationToken )
343+ ) ;
344+
345+ // Assert
346+ assert . strictEqual ( result , false , 'Should return false when no environment is assigned' ) ;
347+ assert . strictEqual (
348+ ensureKernelSelectedStub . called ,
349+ false ,
350+ 'ensureKernelSelectedWithConfiguration should not be called'
351+ ) ;
352+ verify ( mockNotebookEnvironmentMapper . getEnvironmentForNotebook ( anything ( ) ) ) . once ( ) ;
353+ } ) ;
354+
355+ test ( 'should return false and remove mapping when environment is not found' , async ( ) => {
356+ // Arrange
357+ const environmentId = 'missing-env-id' ;
358+
359+ // Mock environment mapper to return an ID
360+ when ( mockNotebookEnvironmentMapper . getEnvironmentForNotebook ( anything ( ) ) ) . thenReturn ( environmentId ) ;
361+
362+ // Mock environment manager to return null (environment not found)
363+ when ( mockEnvironmentManager . getEnvironment ( environmentId ) ) . thenReturn ( undefined ) ;
364+
365+ // Mock remove environment mapping
366+ when ( mockNotebookEnvironmentMapper . removeEnvironmentForNotebook ( anything ( ) ) ) . thenResolve ( ) ;
367+
368+ // Stub ensureKernelSelectedWithConfiguration to track if it gets called
369+ const ensureKernelSelectedStub = sandbox . stub ( selector , 'ensureKernelSelectedWithConfiguration' ) . resolves ( ) ;
370+
371+ // Mock commands.executeCommand
372+ when ( mockedVSCodeNamespaces . commands . executeCommand ( anything ( ) , anything ( ) ) ) . thenResolve ( ) ;
373+
374+ // Act
375+ const result = await selector . ensureKernelSelected (
376+ mockNotebook ,
377+ mockProgress ,
378+ instance ( mockCancellationToken )
379+ ) ;
380+
381+ // Assert
382+ assert . strictEqual ( result , false , 'Should return false when environment is not found' ) ;
383+ assert . strictEqual (
384+ ensureKernelSelectedStub . called ,
385+ false ,
386+ 'ensureKernelSelectedWithConfiguration should not be called'
387+ ) ;
388+ verify ( mockNotebookEnvironmentMapper . getEnvironmentForNotebook ( anything ( ) ) ) . once ( ) ;
389+ verify ( mockEnvironmentManager . getEnvironment ( environmentId ) ) . once ( ) ;
390+ verify ( mockNotebookEnvironmentMapper . removeEnvironmentForNotebook ( anything ( ) ) ) . once ( ) ;
391+ } ) ;
392+
393+ test ( 'should return true and call ensureKernelSelectedWithConfiguration when environment is found' , async ( ) => {
394+ // Arrange
395+ const baseFileUri = mockNotebook . uri . with ( { query : '' , fragment : '' } ) ;
396+ const notebookKey = mockNotebook . uri . toString ( ) ;
397+ const projectKey = baseFileUri . fsPath ;
398+ const environmentId = 'test-env-id' ;
399+ const mockEnvironment = createMockEnvironment ( environmentId , 'Test Environment' ) ;
400+
401+ // Mock environment mapper to return an ID
402+ when ( mockNotebookEnvironmentMapper . getEnvironmentForNotebook ( anything ( ) ) ) . thenReturn ( environmentId ) ;
403+
404+ // Mock environment manager to return the environment
405+ when ( mockEnvironmentManager . getEnvironment ( environmentId ) ) . thenReturn ( mockEnvironment ) ;
406+
407+ // Stub ensureKernelSelectedWithConfiguration to track calls
408+ const ensureKernelSelectedStub = sandbox . stub ( selector , 'ensureKernelSelectedWithConfiguration' ) . resolves ( ) ;
409+
410+ // Mock commands.executeCommand
411+ when ( mockedVSCodeNamespaces . commands . executeCommand ( anything ( ) , anything ( ) ) ) . thenResolve ( ) ;
412+
413+ // Act
414+ const result = await selector . ensureKernelSelected (
415+ mockNotebook ,
416+ mockProgress ,
417+ instance ( mockCancellationToken )
418+ ) ;
419+
420+ // Assert
421+ assert . strictEqual ( result , true , 'Should return true when environment is found' ) ;
422+ assert . strictEqual (
423+ ensureKernelSelectedStub . calledOnce ,
424+ true ,
425+ 'ensureKernelSelectedWithConfiguration should be called once'
426+ ) ;
427+
428+ // Verify it was called with correct arguments
429+ const callArgs = ensureKernelSelectedStub . firstCall . args ;
430+ assert . strictEqual ( callArgs [ 0 ] , mockNotebook , 'First arg should be notebook' ) ;
431+ assert . strictEqual ( callArgs [ 1 ] , mockEnvironment , 'Second arg should be environment' ) ;
432+ assert . strictEqual ( callArgs [ 2 ] . toString ( ) , baseFileUri . toString ( ) , 'Third arg should be baseFileUri' ) ;
433+ assert . strictEqual ( callArgs [ 3 ] , notebookKey , 'Fourth arg should be notebookKey' ) ;
434+ assert . strictEqual ( callArgs [ 4 ] , projectKey , 'Fifth arg should be projectKey' ) ;
435+ assert . strictEqual ( callArgs [ 5 ] , mockProgress , 'Sixth arg should be progress' ) ;
436+ assert . strictEqual ( callArgs [ 6 ] , instance ( mockCancellationToken ) , 'Seventh arg should be token' ) ;
437+
438+ verify ( mockNotebookEnvironmentMapper . getEnvironmentForNotebook ( anything ( ) ) ) . once ( ) ;
439+ verify ( mockEnvironmentManager . getEnvironment ( environmentId ) ) . once ( ) ;
440+ } ) ;
441+ } ) ;
442+
278443 // Priority 1 Tests - Critical for environment switching
279444 // UT-4: Configuration Refresh After startServer
280445 suite ( 'Priority 1: Configuration Refresh (UT-4)' , ( ) => {
0 commit comments