Skip to content

Commit ef1f07c

Browse files
Add basic integration tests for .deepnote file handling (GRN-4766)
- Created minimal .deepnote test fixture with init notebook and main notebook - Implemented integration tests for: - Loading .deepnote files and verifying metadata - Kernel startup for .deepnote files - Basic code block execution with output validation - Multiple code block execution - Cell output validation Tests follow existing patterns from executionService.vscode.test.ts Co-Authored-By: Filip Pyrek <PyrekFilip@gmail.com>
1 parent 9e00675 commit ef1f07c

File tree

2 files changed

+207
-0
lines changed

2 files changed

+207
-0
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
5+
import { assert } from 'chai';
6+
import * as path from '../../../platform/vscode-path/path';
7+
import { Uri, workspace } from 'vscode';
8+
import { IDisposable } from '../../../platform/common/types';
9+
import { captureScreenShot, IExtensionTestApi } from '../../common.node';
10+
import { EXTENSION_ROOT_DIR_FOR_TESTS, initialize } from '../../initialize.node';
11+
import {
12+
closeNotebooksAndCleanUpAfterTests,
13+
startJupyterServer,
14+
waitForExecutionCompletedSuccessfully,
15+
getCellOutputs,
16+
getDefaultKernelConnection
17+
} from './helper.node';
18+
import { IKernel, IKernelProvider, INotebookKernelExecution } from '../../../kernels/types';
19+
import { createKernelController, TestNotebookDocument } from './executionHelper';
20+
import { logger } from '../../../platform/logging';
21+
import { IDeepnoteNotebookManager } from '../../../notebooks/types';
22+
23+
/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */
24+
suite('Deepnote Integration Tests @kernelCore', function () {
25+
let api: IExtensionTestApi;
26+
const disposables: IDisposable[] = [];
27+
const deepnoteFilePath = Uri.file(
28+
path.join(EXTENSION_ROOT_DIR_FOR_TESTS, 'src', 'test', 'datascience', 'notebook', 'test.deepnote')
29+
);
30+
this.timeout(120_000);
31+
let notebook: TestNotebookDocument;
32+
let kernel: IKernel;
33+
let kernelExecution: INotebookKernelExecution;
34+
35+
suiteSetup(async function () {
36+
logger.info('Suite Setup VS Code Notebook - Deepnote Integration');
37+
this.timeout(120_000);
38+
try {
39+
api = await initialize();
40+
logger.debug('Before starting Jupyter');
41+
await startJupyterServer();
42+
logger.debug('After starting Jupyter');
43+
44+
notebook = new TestNotebookDocument(deepnoteFilePath);
45+
46+
const kernelProvider = api.serviceContainer.get<IKernelProvider>(IKernelProvider);
47+
logger.debug('Before creating kernel connection');
48+
const metadata = await getDefaultKernelConnection();
49+
logger.debug('After creating kernel connection');
50+
51+
const controller = createKernelController();
52+
kernel = kernelProvider.getOrCreate(notebook, { metadata, resourceUri: notebook.uri, controller });
53+
logger.debug('Before starting kernel');
54+
await kernel.start();
55+
logger.debug('After starting kernel');
56+
kernelExecution = kernelProvider.getKernelExecution(kernel);
57+
logger.info('Suite Setup (completed)');
58+
} catch (e) {
59+
logger.error('Suite Setup (failed) - Deepnote Integration', e);
60+
await captureScreenShot('deepnote-suite');
61+
throw e;
62+
}
63+
});
64+
65+
setup(function () {
66+
notebook.cells.length = 0;
67+
logger.info(`Start Test (completed) ${this.currentTest?.title}`);
68+
});
69+
70+
teardown(async function () {
71+
if (this.currentTest?.isFailed()) {
72+
await captureScreenShot(this);
73+
}
74+
logger.info(`Ended Test (completed) ${this.currentTest?.title}`);
75+
});
76+
77+
suiteTeardown(() => closeNotebooksAndCleanUpAfterTests(disposables));
78+
79+
test('Load .deepnote file', async function () {
80+
logger.debug('Test: Load .deepnote file - starting');
81+
82+
const notebookManager = api.serviceContainer.get<IDeepnoteNotebookManager>(IDeepnoteNotebookManager);
83+
84+
notebookManager.selectNotebookForProject('test-project-id', 'main-notebook-id');
85+
86+
const nbDocument = await workspace.openNotebookDocument(deepnoteFilePath);
87+
88+
logger.debug(`Opened notebook with type: ${nbDocument.notebookType}, cells: ${nbDocument.cellCount}`);
89+
90+
assert.equal(nbDocument.notebookType, 'deepnote', 'Notebook type should be deepnote');
91+
assert.isTrue(nbDocument.cellCount > 0, 'Notebook should have cells');
92+
93+
assert.equal(nbDocument.metadata?.deepnoteProjectId, 'test-project-id', 'Project ID should match');
94+
assert.equal(nbDocument.metadata?.deepnoteNotebookId, 'main-notebook-id', 'Notebook ID should match');
95+
96+
logger.debug('Test: Load .deepnote file - completed');
97+
});
98+
99+
test('Kernel starts for .deepnote file', async function () {
100+
logger.debug('Test: Kernel starts for .deepnote file - starting');
101+
102+
assert.isOk(kernel, 'Kernel should exist');
103+
assert.isOk(kernel.session, 'Kernel session should exist');
104+
105+
logger.debug('Test: Kernel starts for .deepnote file - completed');
106+
});
107+
108+
test('Execute code block', async function () {
109+
logger.debug('Test: Execute code block - starting');
110+
111+
const cell = await notebook.appendCodeCell('print("Hello World")');
112+
113+
await Promise.all([kernelExecution.executeCell(cell), waitForExecutionCompletedSuccessfully(cell)]);
114+
115+
assert.isAtLeast(cell.executionSummary?.executionOrder || 0, 1, 'Cell should have execution order');
116+
assert.isTrue(cell.executionSummary?.success, 'Cell execution should succeed');
117+
assert.isAtLeast(cell.outputs.length, 1, 'Cell should have output');
118+
119+
const output = getCellOutputs(cell);
120+
assert.include(output, 'Hello World', 'Output should contain "Hello World"');
121+
122+
logger.debug('Test: Execute code block - completed');
123+
});
124+
125+
test('Execute multiple code blocks', async function () {
126+
logger.debug('Test: Execute multiple code blocks - starting');
127+
128+
const cell1 = await notebook.appendCodeCell('x = 42');
129+
const cell2 = await notebook.appendCodeCell('print(f"The answer is {x}")');
130+
131+
await Promise.all([
132+
kernelExecution.executeCell(cell1),
133+
waitForExecutionCompletedSuccessfully(cell1),
134+
kernelExecution.executeCell(cell2),
135+
waitForExecutionCompletedSuccessfully(cell2)
136+
]);
137+
138+
assert.isAtLeast(cell1.executionSummary?.executionOrder || 0, 1, 'First cell should have execution order');
139+
assert.isTrue(cell1.executionSummary?.success, 'First cell execution should succeed');
140+
141+
assert.isAtLeast(cell2.executionSummary?.executionOrder || 0, 1, 'Second cell should have execution order');
142+
assert.isTrue(cell2.executionSummary?.success, 'Second cell execution should succeed');
143+
assert.isAtLeast(cell2.outputs.length, 1, 'Second cell should have output');
144+
145+
const output = getCellOutputs(cell2);
146+
assert.include(output, 'The answer is 42', 'Output should contain "The answer is 42"');
147+
148+
logger.debug('Test: Execute multiple code blocks - completed');
149+
});
150+
151+
test('Verify cell output validation', async function () {
152+
logger.debug('Test: Verify cell output validation - starting');
153+
154+
const cell = await notebook.appendCodeCell('for i in range(3):\n print(f"Line {i}")');
155+
156+
await Promise.all([kernelExecution.executeCell(cell), waitForExecutionCompletedSuccessfully(cell)]);
157+
158+
assert.isTrue(cell.executionSummary?.success, 'Cell execution should succeed');
159+
assert.isAtLeast(cell.outputs.length, 1, 'Cell should have output');
160+
161+
const output = getCellOutputs(cell);
162+
assert.include(output, 'Line 0', 'Output should contain "Line 0"');
163+
assert.include(output, 'Line 1', 'Output should contain "Line 1"');
164+
assert.include(output, 'Line 2', 'Output should contain "Line 2"');
165+
166+
logger.debug('Test: Verify cell output validation - completed');
167+
});
168+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
metadata:
2+
createdAt: '2025-01-01T00:00:00.000Z'
3+
modifiedAt: '2025-01-01T00:00:00.000Z'
4+
project:
5+
id: test-project-id
6+
name: Test Project
7+
initNotebookId: init-notebook-id
8+
notebooks:
9+
- id: init-notebook-id
10+
name: Init Notebook
11+
blocks:
12+
- id: init-block-1
13+
type: code
14+
content: |
15+
# This is the init notebook
16+
import sys
17+
print("Init notebook executed")
18+
sortingKey: '0001'
19+
- id: main-notebook-id
20+
name: Main Notebook
21+
blocks:
22+
- id: block-1
23+
type: code
24+
content: |
25+
print("Hello World")
26+
sortingKey: '0001'
27+
- id: block-2
28+
type: code
29+
content: |
30+
x = 42
31+
print(f"The answer is {x}")
32+
sortingKey: '0002'
33+
- id: block-3
34+
type: markdown
35+
content: |
36+
# Test Markdown
37+
This is a test markdown block.
38+
sortingKey: '0003'
39+
version: '1.0'

0 commit comments

Comments
 (0)