Inital platform relayering (#6385)

* moving test files and inital refactoring

* relayer extension host code

* fix imports

* make insights work

* relayer dashboard

* relayer notebooks

* moveing more code around

* formatting

* accept angular as browser

* fix serializer

* add missing files

* remove declarations from extensions

* fix build errors

* more relayering

* change urls to relative to help code relayering

* remove layering to prep for merge

* fix hygiene errors

* fix hygiene errors

* fix tests
This commit is contained in:
Anthony Dresser
2019-07-18 17:29:17 -07:00
committed by GitHub
parent 45c13116de
commit c23738f935
576 changed files with 2090 additions and 2788 deletions

View File

@@ -0,0 +1,312 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import * as TypeMoq from 'typemoq';
import { nb } from 'azdata';
import * as objects from 'vs/base/common/objects';
import { CellTypes } from 'sql/workbench/parts/notebook/common/models/contracts';
import { ModelFactory } from 'sql/workbench/parts/notebook/node/models/modelFactory';
import { NotebookModelStub } from './common';
import { EmptyFuture } from 'sql/workbench/services/notebook/common/sessionManager';
import { ICellModel } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import { Deferred } from 'sql/base/common/promise';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
let instantiationService: IInstantiationService;
suite('Cell Model', function (): void {
let serviceCollection = new ServiceCollection();
instantiationService = new InstantiationService(serviceCollection, true);
let factory = new ModelFactory(instantiationService);
test('Should set default values if none defined', async function (): Promise<void> {
let cell = factory.createCell(undefined, undefined);
should(cell.cellType).equal(CellTypes.Code);
should(cell.source).equal('');
});
test('Should update values', async function (): Promise<void> {
let cell = factory.createCell(undefined, undefined);
cell.setOverrideLanguage('sql');
should(cell.language).equal('sql');
cell.source = 'abcd';
should(cell.source).equal('abcd');
});
test('Should match ICell values if defined', async function (): Promise<void> {
let output: nb.IStreamResult = {
output_type: 'stream',
text: 'Some output',
name: 'stdout'
};
let cellData: nb.ICellContents = {
cell_type: CellTypes.Markdown,
source: 'some *markdown*',
outputs: [output],
metadata: { language: 'python' },
execution_count: 1
};
let cell = factory.createCell(cellData, undefined);
should(cell.cellType).equal(cellData.cell_type);
should(cell.source).equal(cellData.source);
should(cell.outputs).have.length(1);
should(cell.outputs[0].output_type).equal('stream');
should((<nb.IStreamResult>cell.outputs[0]).text).equal('Some output');
});
test('Should set cell language to python if defined as python in languageInfo', async function (): Promise<void> {
let cellData: nb.ICellContents = {
cell_type: CellTypes.Code,
source: 'print(\'1\')',
metadata: { language: 'python' },
execution_count: 1
};
let notebookModel = new NotebookModelStub({
name: 'python',
version: '',
mimetype: ''
});
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
should(cell.language).equal('python');
});
test('Should set cell language to python if defined as pyspark in languageInfo', async function (): Promise<void> {
let cellData: nb.ICellContents = {
cell_type: CellTypes.Code,
source: 'print(\'1\')',
metadata: { language: 'python' },
execution_count: 1
};
let notebookModel = new NotebookModelStub({
name: 'pyspark',
version: '',
mimetype: ''
});
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
should(cell.language).equal('python');
});
test('Should keep cell language as python if cell has language override', async function (): Promise<void> {
let cellData: nb.ICellContents = {
cell_type: CellTypes.Code,
source: 'print(\'1\')',
metadata: { language: 'python' },
execution_count: 1
};
let notebookModel = new NotebookModelStub({
name: 'scala',
version: '',
mimetype: ''
});
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
should(cell.language).equal('python');
});
test('Should set cell language to python if no language defined', async function (): Promise<void> {
let cellData: nb.ICellContents = {
cell_type: CellTypes.Code,
source: 'print(\'1\')',
metadata: { language: 'python' },
execution_count: 1
};
let notebookModel = new NotebookModelStub({
name: '',
version: '',
mimetype: ''
});
let cell = factory.createCell(cellData, { notebook: notebookModel, isTrusted: false });
should(cell.language).equal('python');
});
suite('Model Future handling', function (): void {
let future: TypeMoq.Mock<EmptyFuture>;
let cell: ICellModel;
const stdInDefaultMessage: nb.IStdinMessage = {
channel: 'stdin',
type: 'stdin',
parent_header: undefined,
metadata: undefined,
header: <nb.IHeader>{
msg_type: 'stream'
},
content: {
prompt: 'Prompt',
password: false
}
};
setup(() => {
future = TypeMoq.Mock.ofType(EmptyFuture);
cell = factory.createCell({
cell_type: CellTypes.Code,
source: 'print "Hello"',
metadata: { language: 'python' },
execution_count: 1
}, {
notebook: new NotebookModelStub({
name: '',
version: '',
mimetype: 'x-scala'
}),
isTrusted: false
});
});
test('should send and handle incoming messages', async () => {
// Given a future
let onReply: nb.MessageHandler<nb.IShellMessage>;
let onIopub: nb.MessageHandler<nb.IIOPubMessage>;
future.setup(f => f.setReplyHandler(TypeMoq.It.isAny())).callback((handler) => onReply = handler);
future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler);
let outputs: ReadonlyArray<nb.ICellOutput> = undefined;
cell.onOutputsChanged((o => outputs = o.outputs));
// When I set it on the cell
cell.setFuture(future.object);
// Then I expect outputs to have been cleared
should(outputs).have.length(0);
should(onReply).not.be.undefined();
// ... And when I send an IoPub message
let message: nb.IIOPubMessage = {
channel: 'iopub',
type: 'iopub',
parent_header: undefined,
metadata: undefined,
header: <nb.IHeader>{
msg_type: 'stream'
},
content: {
text: 'Printed hello world'
}
};
onIopub.handle(message);
// Then I expect an output to be added
should(outputs).have.length(1);
should(outputs[0].output_type).equal('stream');
message = objects.deepClone(message);
message.header.msg_type = 'display_data';
onIopub.handle(message);
should(outputs[1].output_type).equal('display_data');
});
test('stdin should return void if no handler registered', async () => {
// Given stdIn does not have a request handler setup
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
// When I set it on the cell
cell.setFuture(future.object);
// Then I expect stdIn to have been hooked up
should(onStdIn).not.be.undefined();
// ... And when I send a stdIn request message
let result = onStdIn.handle(stdInDefaultMessage);
// Then I expect the promise to resolve
await result;
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.never());
});
test('stdin should wait on handler if handler registered', async () => {
// Given stdIn has a handler set up
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
let deferred = new Deferred<void>();
let stdInMessage: nb.IStdinMessage = undefined;
cell.setStdInHandler({
handle: (msg: nb.IStdinMessage) => {
stdInMessage = msg;
return deferred.promise;
}
});
// When I send a stdIn request message
cell.setFuture(future.object);
let result = onStdIn.handle(stdInDefaultMessage);
deferred.resolve();
// Then I expect promise to resolve since it should wait on upstream handling
await result;
// And I expect message to have been passed upstream and no message sent from the cell
should(stdInMessage).not.be.undefined();
should(stdInMessage.content.prompt).equal(stdInDefaultMessage.content.prompt);
should(stdInMessage.content.password).equal(stdInDefaultMessage.content.password);
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.never());
});
test('stdin should send default response if there is upstream error', async () => {
// Given stdIn has a handler set up
let onStdIn: nb.MessageHandler<nb.IStdinMessage>;
future.setup(f => f.setStdInHandler(TypeMoq.It.isAny())).callback((handler) => onStdIn = handler);
let deferred = new Deferred<void>();
let stdInMessage: nb.IStdinMessage = undefined;
cell.setStdInHandler({
handle: (msg: nb.IStdinMessage) => {
stdInMessage = msg;
return deferred.promise;
}
});
// When I send a stdIn request message
cell.setFuture(future.object);
let result = onStdIn.handle(stdInDefaultMessage);
deferred.reject('Something went wrong');
// Then I expect promise to resolve since it should wait on upstream handling
await result;
future.verify(f => f.sendInputReply(TypeMoq.It.isAny()), TypeMoq.Times.once());
});
test('should delete transient tag while handling incoming messages', async () => {
// Given a future
let onIopub: nb.MessageHandler<nb.IIOPubMessage>;
future.setup(f => f.setIOPubHandler(TypeMoq.It.isAny())).callback((handler) => onIopub = handler);
let outputs: ReadonlyArray<nb.ICellOutput> = undefined;
cell.onOutputsChanged((o => outputs = o.outputs));
//Set the future
cell.setFuture(future.object);
// ... And when I send an IoPub message
let message: nb.IIOPubMessage = {
channel: 'iopub',
type: 'iopub',
parent_header: undefined,
metadata: undefined,
header: <nb.IHeader>{
msg_type: 'display_data'
},
content: {
text: 'Printed hello world',
transient: 'transient data'
}
};
onIopub.handle(message);
//Output array's length should be 1
//'transient' tag should no longer exist in the output
should(outputs).have.length(1);
should(outputs[0]['transient']).be.undefined();
});
test('should dispose old future', async () => {
let oldFuture = TypeMoq.Mock.ofType(EmptyFuture);
cell.setFuture(oldFuture.object);
cell.setFuture(future.object);
oldFuture.verify(f => f.dispose(), TypeMoq.Times.once());
});
});
});

View File

@@ -0,0 +1,183 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import * as TypeMoq from 'typemoq';
import { nb } from 'azdata';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { URI } from 'vs/base/common/uri';
import { ClientSession } from 'sql/workbench/parts/notebook/node/models/clientSession';
import { SessionManager, EmptySession } from 'sql/workbench/services/notebook/common/sessionManager';
import { NotebookManagerStub, ServerManagerStub } from './common';
suite('Client Session', function (): void {
let path = URI.file('my/notebook.ipynb');
let notebookManager: NotebookManagerStub;
let serverManager: ServerManagerStub;
let mockSessionManager: TypeMoq.Mock<nb.SessionManager>;
let notificationService: TypeMoq.Mock<INotificationService>;
let session: ClientSession;
let remoteSession: ClientSession;
setup(() => {
serverManager = new ServerManagerStub();
mockSessionManager = TypeMoq.Mock.ofType(SessionManager);
notebookManager = new NotebookManagerStub();
notebookManager.serverManager = serverManager;
notebookManager.sessionManager = mockSessionManager.object;
notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
session = new ClientSession({
notebookManager: notebookManager,
notebookUri: path,
notificationService: notificationService.object,
kernelSpec: undefined
});
let serverlessNotebookManager = new NotebookManagerStub();
serverlessNotebookManager.sessionManager = mockSessionManager.object;
remoteSession = new ClientSession({
notebookManager: serverlessNotebookManager,
notebookUri: path,
notificationService: notificationService.object,
kernelSpec: undefined
});
});
test('Should set path, isReady and ready on construction', function (): void {
should(session.notebookUri).equal(path);
should(session.ready).not.be.undefined();
should(session.isReady).be.false();
should(session.status).equal('starting');
should(session.isInErrorState).be.false();
should(session.errorMessage).be.undefined();
});
test('Should call on serverManager startup if set', async function (): Promise<void> {
// Given I have a serverManager that starts successfully
serverManager.result = Promise.resolve();
should(session.isReady).be.false();
// When I kick off initialization
await session.initialize();
// Then I expect ready to be completed too
await session.ready;
should(serverManager.calledStart).be.true();
should(session.isReady).be.true();
});
test('Should go to error state if serverManager startup fails', async function (): Promise<void> {
// Given I have a serverManager that fails to start
serverManager.result = Promise.reject('error');
should(session.isInErrorState).be.false();
// When I initialize
await session.initialize();
// Then I expect ready to complete, but isInErrorState to be true
await session.ready;
should(session.isReady).be.true();
should(serverManager.calledStart).be.true();
should(session.isInErrorState).be.true();
should(session.errorMessage).equal('error');
});
test('Should be ready when session manager is ready', async function (): Promise<void> {
serverManager.result = new Promise((resolve) => {
serverManager.isStarted = true;
resolve();
});
mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
// When I call initialize
await session.initialize();
// Then
should(session.isReady).be.true();
should(session.isInErrorState).be.false();
await session.ready;
});
test('Should be in error state if server fails to start', async function (): Promise<void> {
serverManager.result = new Promise((resolve) => {
serverManager.isStarted = false;
resolve();
});
mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
// When I call initialize
await session.initialize();
// Then
await session.ready;
should(session.isReady).be.true();
should(session.isInErrorState).be.true();
});
test('Should go to error state if sessionManager fails', async function (): Promise<void> {
serverManager.isStarted = true;
mockSessionManager.setup(s => s.isReady).returns(() => false);
mockSessionManager.setup(s => s.ready).returns(() => Promise.reject('error'));
// When I call initialize
await session.initialize();
// Then
should(session.isReady).be.true();
should(session.isInErrorState).be.true();
should(session.errorMessage).equal('error');
});
// test('Should start session automatically if kernel preference requests it', async function (): Promise<void> {
// serverManager.isStarted = true;
// mockSessionManager.setup(s => s.ready).returns(() => Promise.resolve());
// let sessionMock = TypeMoq.Mock.ofType(EmptySession);
// let startOptions: nb.ISessionOptions = undefined;
// mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns((options) => {
// startOptions = options;
// return Promise.resolve(sessionMock.object);
// });
// // When I call initialize after defining kernel preferences
// session.kernelPreference = {
// shouldStart: true,
// name: 'python'
// };
// await session.initialize();
// // Then
// should(session.isReady).be.true();
// should(session.isInErrorState).be.false();
// should(startOptions.kernelName).equal('python');
// should(startOptions.path).equal(path.fsPath);
// });
// test('Should shutdown session even if no serverManager is set', async function (): Promise<void> {
// // Given a session against a remote server
// let expectedId = 'abc';
// mockSessionManager.setup(s => s.isReady).returns(() => true);
// mockSessionManager.setup(s => s.shutdown(TypeMoq.It.isAny())).returns(() => Promise.resolve());
// let sessionMock = TypeMoq.Mock.ofType(EmptySession);
// sessionMock.setup(s => s.id).returns(() => expectedId);
// mockSessionManager.setup(s => s.startNew(TypeMoq.It.isAny())).returns(() => Promise.resolve(sessionMock.object));
// remoteSession.kernelPreference = {
// shouldStart: true,
// name: 'python'
// };
// await remoteSession.initialize();
// // When I call shutdown
// await remoteSession.shutdown();
// // Then
// mockSessionManager.verify(s => s.shutdown(TypeMoq.It.isValue(expectedId)), TypeMoq.Times.once());
// });
});

View File

@@ -0,0 +1,134 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { nb, IConnectionProfile } from 'azdata';
import { Event, Emitter } from 'vs/base/common/event';
import { INotebookModel, ICellModel, IClientSession, IDefaultConnection, NotebookContentChange } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import { NotebookChangeType, CellType } from 'sql/workbench/parts/notebook/common/models/contracts';
import { INotebookManager } from 'sql/workbench/services/notebook/common/notebookService';
import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes';
import { IStandardKernelWithProvider } from 'sql/workbench/parts/notebook/node/models/notebookUtils';
export class NotebookModelStub implements INotebookModel {
constructor(private _languageInfo?: nb.ILanguageInfo) {
}
public trustedMode: boolean;
language: string;
standardKernels: IStandardKernelWithProvider[];
public get languageInfo(): nb.ILanguageInfo {
return this._languageInfo;
}
onCellChange(cell: ICellModel, change: NotebookChangeType): void {
// Default: do nothing
}
get cells(): ReadonlyArray<ICellModel> {
throw new Error('method not implemented.');
}
get activeCell(): ICellModel {
throw new Error('method not implemented.');
}
get clientSession(): IClientSession {
throw new Error('method not implemented.');
}
get notebookManagers(): INotebookManager[] {
throw new Error('method not implemented.');
}
get kernelChanged(): Event<nb.IKernelChangedArgs> {
throw new Error('method not implemented.');
}
get kernelsChanged(): Event<nb.IKernelSpec> {
throw new Error('method not implemented.');
}
get layoutChanged(): Event<void> {
throw new Error('method not implemented.');
}
get defaultKernel(): nb.IKernelSpec {
throw new Error('method not implemented.');
}
get contextsChanged(): Event<void> {
throw new Error('method not implemented.');
}
get contextsLoading(): Event<void> {
throw new Error('method not implemented.');
}
get contentChanged(): Event<NotebookContentChange> {
throw new Error('method not implemented.');
}
get specs(): nb.IAllKernels {
throw new Error('method not implemented.');
}
get contexts(): IDefaultConnection {
throw new Error('method not implemented.');
}
get providerId(): string {
throw new Error('method not implemented.');
}
get applicableConnectionProviderIds(): string[] {
throw new Error('method not implemented.');
}
getStandardKernelFromName(name: string): IStandardKernelWithProvider {
throw new Error('Method not implemented.');
}
changeKernel(displayName: string): void {
throw new Error('Method not implemented.');
}
changeContext(host: string, connection?: IConnectionProfile, hideErrorMessage?: boolean): Promise<void> {
throw new Error('Method not implemented.');
}
findCellIndex(cellModel: ICellModel): number {
throw new Error('Method not implemented.');
}
addCell(cellType: CellType, index?: number): void {
throw new Error('Method not implemented.');
}
deleteCell(cellModel: ICellModel): void {
throw new Error('Method not implemented.');
}
pushEditOperations(edits: ISingleNotebookEditOperation[]): void {
throw new Error('Method not implemented.');
}
getApplicableConnectionProviderIds(kernelName: string): string[] {
throw new Error('Method not implemented.');
}
get onValidConnectionSelected(): Event<boolean> {
throw new Error('method not implemented.');
}
get onProviderIdChange(): Event<string> {
throw new Error('method not impelemented.');
}
toJSON(): nb.INotebookContents {
throw new Error('Method not implemented.');
}
serializationStateChanged(changeType: NotebookChangeType): void {
throw new Error('Method not implemented.');
}
}
export class NotebookManagerStub implements INotebookManager {
providerId: string;
contentManager: nb.ContentManager;
sessionManager: nb.SessionManager;
serverManager: nb.ServerManager;
}
export class ServerManagerStub implements nb.ServerManager {
public onServerStartedEmitter = new Emitter<void>();
onServerStarted: Event<void> = this.onServerStartedEmitter.event;
isStarted: boolean = false;
calledStart: boolean = false;
calledEnd: boolean = false;
public result: Promise<void> = undefined;
startServer(): Promise<void> {
this.calledStart = true;
return this.result;
}
stopServer(): Promise<void> {
this.calledEnd = true;
return this.result;
}
}

View File

@@ -0,0 +1,110 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import { nb } from 'azdata';
import { URI } from 'vs/base/common/uri';
import * as tempWrite from 'temp-write';
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
import * as testUtils from '../../../../../../sqltest/utils/testUtils';
import { CellTypes } from 'sql/workbench/parts/notebook/common/models/contracts';
let expectedNotebookContent: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,
source: 'insert into t1 values (c1, c2)',
metadata: { language: 'python' },
execution_count: 1
}],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 2
};
let notebookContentString = JSON.stringify(expectedNotebookContent);
function verifyMatchesExpectedNotebook(notebook: nb.INotebookContents): void {
should(notebook.cells).have.length(1, 'Expected 1 cell');
should(notebook.cells[0].cell_type).equal(CellTypes.Code);
should(notebook.cells[0].source).equal(expectedNotebookContent.cells[0].source);
should(notebook.metadata.kernelspec.name).equal(expectedNotebookContent.metadata.kernelspec.name);
should(notebook.nbformat).equal(expectedNotebookContent.nbformat);
should(notebook.nbformat_minor).equal(expectedNotebookContent.nbformat_minor);
}
suite('Local Content Manager', function (): void {
let contentManager = new LocalContentManager();
test('Should return undefined if path is undefined', async function (): Promise<void> {
let content = await contentManager.getNotebookContents(undefined);
should(content).be.undefined();
// tslint:disable-next-line:no-null-keyword
content = await contentManager.getNotebookContents(null);
should(content).be.undefined();
});
test('Should throw if file does not exist', async function (): Promise<void> {
await testUtils.assertThrowsAsync(async () => await contentManager.getNotebookContents(URI.file('/path/doesnot/exist.ipynb')), undefined);
});
test('Should return notebook contents parsed as INotebook when valid notebook file parsed', async function (): Promise<void> {
// Given a file containing a valid notebook
let localFile = tempWrite.sync(notebookContentString, 'notebook.ipynb');
// when I read the content
let notebook = await contentManager.getNotebookContents(URI.file(localFile));
// then I expect notebook format to match
verifyMatchesExpectedNotebook(notebook);
});
test('Should ignore invalid content in the notebook file', async function (): Promise<void> {
// Given a file containing a notebook with some garbage properties
let invalidContent = notebookContentString + '\\nasddfdsafasdf';
let localFile = tempWrite.sync(invalidContent, 'notebook.ipynb');
// when I read the content
let notebook = await contentManager.getNotebookContents(URI.file(localFile));
// then I expect notebook format to still be valid
verifyMatchesExpectedNotebook(notebook);
});
test('Should inline mime data into a single string', async function (): Promise<void> {
let mimeNotebook: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,
source: 'insert into t1 values (c1, c2)',
metadata: { language: 'python' },
execution_count: 1,
outputs: [
<nb.IDisplayData>{
output_type: 'display_data',
data: {
'text/html': [
'<div>',
'</div>'
]
}
}
]
}],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 2
};
let mimeContentString = JSON.stringify(mimeNotebook);
// Given a file containing a valid notebook with multiline mime type
let localFile = tempWrite.sync(mimeContentString, 'notebook.ipynb');
// when I read the content
let notebook = await contentManager.getNotebookContents(URI.file(localFile));
// then I expect output to have been normalized into a single string
let displayOutput = <nb.IDisplayData>notebook.cells[0].outputs[0];
should(displayOutput.data['text/html']).equal('<div></div>');
});
});

View File

@@ -0,0 +1,304 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import * as TypeMoq from 'typemoq';
import { nb } from 'azdata';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { URI } from 'vs/base/common/uri';
import { LocalContentManager } from 'sql/workbench/services/notebook/node/localContentManager';
import { NotebookManagerStub } from './common';
import { NotebookModel } from 'sql/workbench/parts/notebook/node/models/notebookModel';
import { ModelFactory } from 'sql/workbench/parts/notebook/node/models/modelFactory';
import { IClientSession, ICellModel, INotebookModelOptions, NotebookContentChange } from 'sql/workbench/parts/notebook/node/models/modelInterfaces';
import { ClientSession } from 'sql/workbench/parts/notebook/node/models/clientSession';
import { CellTypes, NotebookChangeType } from 'sql/workbench/parts/notebook/common/models/contracts';
import { Deferred } from 'sql/base/common/promise';
import { ConnectionManagementService } from 'sql/platform/connection/common/connectionManagementService';
import { Memento } from 'vs/workbench/common/memento';
import { Emitter } from 'vs/base/common/event';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { TestStorageService } from 'vs/workbench/test/workbenchTestServices';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
import { NullLogService } from 'vs/platform/log/common/log';
let expectedNotebookContent: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,
source: 'insert into t1 values (c1, c2)',
metadata: { language: 'python' },
execution_count: 1
}, {
cell_type: CellTypes.Markdown,
source: 'I am *markdown*',
metadata: { language: 'python' },
execution_count: 1
}],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 5
};
let expectedNotebookContentOneCell: nb.INotebookContents = {
cells: [{
cell_type: CellTypes.Code,
source: 'insert into t1 values (c1, c2)',
metadata: { language: 'python' },
execution_count: 1
}],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 5
};
let defaultUri = URI.file('/some/path.ipynb');
let mockClientSession: TypeMoq.Mock<IClientSession>;
let sessionReady: Deferred<void>;
let mockModelFactory: TypeMoq.Mock<ModelFactory>;
let notificationService: TypeMoq.Mock<INotificationService>;
let capabilitiesService: TypeMoq.Mock<ICapabilitiesService>;
let instantiationService: IInstantiationService;
suite('notebook model', function (): void {
let notebookManagers = [new NotebookManagerStub()];
let memento: TypeMoq.Mock<Memento>;
let queryConnectionService: TypeMoq.Mock<ConnectionManagementService>;
let defaultModelOptions: INotebookModelOptions;
const logService = new NullLogService();
setup(() => {
sessionReady = new Deferred<void>();
notificationService = TypeMoq.Mock.ofType(TestNotificationService, TypeMoq.MockBehavior.Loose);
capabilitiesService = TypeMoq.Mock.ofType(TestCapabilitiesService);
memento = TypeMoq.Mock.ofType(Memento, TypeMoq.MockBehavior.Loose, '');
memento.setup(x => x.getMemento(TypeMoq.It.isAny())).returns(() => void 0);
queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined, new TestStorageService());
queryConnectionService.callBase = true;
let serviceCollection = new ServiceCollection();
instantiationService = new InstantiationService(serviceCollection, true);
defaultModelOptions = {
notebookUri: defaultUri,
factory: new ModelFactory(instantiationService),
notebookManagers,
contentManager: undefined,
notificationService: notificationService.object,
connectionService: queryConnectionService.object,
providerId: 'SQL',
cellMagicMapper: undefined,
defaultKernel: undefined,
layoutChanged: undefined,
capabilitiesService: capabilitiesService.object
};
mockClientSession = TypeMoq.Mock.ofType(ClientSession, undefined, defaultModelOptions);
mockClientSession.setup(c => c.initialize()).returns(() => {
return Promise.resolve();
});
mockClientSession.setup(c => c.ready).returns(() => sessionReady.promise);
mockModelFactory = TypeMoq.Mock.ofType(ModelFactory);
mockModelFactory.callBase = true;
mockModelFactory.setup(f => f.createClientSession(TypeMoq.It.isAny())).returns(() => {
return mockClientSession.object;
});
});
test('Should create no cells if model has no contents', async function (): Promise<void> {
// Given an empty notebook
let emptyNotebook: nb.INotebookContents = {
cells: [],
metadata: {
kernelspec: {
name: 'mssql',
language: 'sql'
}
},
nbformat: 4,
nbformat_minor: 5
};
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(emptyNotebook));
notebookManagers[0].contentManager = mockContentManager.object;
// When I initialize the model
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
await model.loadContents();
// Then I expect to have 0 code cell as the contents
should(model.cells).have.length(0);
// And Trust should be false by default
should(model.trustedMode).be.false();
});
test('Should use trusted state set in model load', async function (): Promise<void> {
// Given a notebook
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContent));
notebookManagers[0].contentManager = mockContentManager.object;
// When I initialize the model
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
await model.loadContents(true);
await model.requestModelLoad();
// Then Trust should be true
should(model.trustedMode).be.true();
});
// test('Should throw if model load fails', async function(): Promise<void> {
// // Given a call to get Contents fails
// let error = new Error('File not found');
// let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
// mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).throws(error);
// notebookManagers[0].contentManager = mockContentManager.object;
// // When I initalize the model
// // Then it should throw
// let model = new NotebookModel(defaultModelOptions);
// should(model.inErrorState).be.false();
// await testUtils.assertThrowsAsync(() => model.requestModelLoad(), error.message);
// should(model.inErrorState).be.true();
// });
// test('Should convert cell info to CellModels', async function(): Promise<void> {
// // Given a notebook with 2 cells
// let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
// mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContent));
// notebookManagers[0].contentManager = mockContentManager.object;
// // When I initalize the model
// let model = new NotebookModel(defaultModelOptions);
// await model.requestModelLoad();
// // Then I expect all cells to be in the model
// should(model.cells).have.length(2);
// should(model.cells[0].source).be.equal(expectedNotebookContent.cells[0].source);
// should(model.cells[1].source).be.equal(expectedNotebookContent.cells[1].source);
// });
// test('Should load contents but then go to error state if client session startup fails', async function(): Promise<void> {
// let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
// mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContentOneCell));
// notebookManagers[0].contentManager = mockContentManager.object;
// // Given I have a session that fails to start
// mockClientSession.setup(c => c.isInErrorState).returns(() => true);
// mockClientSession.setup(c => c.errorMessage).returns(() => 'Error');
// sessionReady.resolve();
// let sessionFired = false;
// let options: INotebookModelOptions = Object.assign({}, defaultModelOptions, <Partial<INotebookModelOptions>> {
// factory: mockModelFactory.object
// });
// let model = new NotebookModel(options);
// model.onClientSessionReady((session) => sessionFired = true);
// await model.requestModelLoad();
// model.startSession(notebookManagers[0]);
// // Then I expect load to succeed
// shouldHaveOneCell(model);
// should(model.clientSession).not.be.undefined();
// // but on server load completion I expect error state to be set
// // Note: do not expect serverLoad event to throw even if failed
// await model.sessionLoadFinished;
// should(model.inErrorState).be.true();
// should(sessionFired).be.false();
// });
test('Should not be in error state if client session initialization succeeds', async function (): Promise<void> {
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContentOneCell));
notebookManagers[0].contentManager = mockContentManager.object;
let kernelChangedEmitter: Emitter<nb.IKernelChangedArgs> = new Emitter<nb.IKernelChangedArgs>();
let statusChangedEmitter: Emitter<nb.ISession> = new Emitter<nb.ISession>();
mockClientSession.setup(c => c.isInErrorState).returns(() => false);
mockClientSession.setup(c => c.isReady).returns(() => true);
mockClientSession.setup(c => c.kernelChanged).returns(() => kernelChangedEmitter.event);
mockClientSession.setup(c => c.statusChanged).returns(() => statusChangedEmitter.event);
queryConnectionService.setup(c => c.getActiveConnections(TypeMoq.It.isAny())).returns(() => null);
sessionReady.resolve();
let actualSession: IClientSession = undefined;
let options: INotebookModelOptions = Object.assign({}, defaultModelOptions, <Partial<INotebookModelOptions>>{
factory: mockModelFactory.object
});
let model = new NotebookModel(options, undefined, logService, undefined, undefined);
model.onClientSessionReady((session) => actualSession = session);
await model.requestModelLoad();
await model.startSession(notebookManagers[0]);
// Then I expect load to succeed
should(model.clientSession).not.be.undefined();
// but on server load completion I expect error state to be set
// Note: do not expect serverLoad event to throw even if failed
let kernelChangedArg: nb.IKernelChangedArgs = undefined;
model.kernelChanged((kernel) => kernelChangedArg = kernel);
await model.sessionLoadFinished;
should(model.inErrorState).be.false();
should(actualSession).equal(mockClientSession.object);
should(model.clientSession).equal(mockClientSession.object);
});
test('Should sanitize kernel display name when IP is included', async function (): Promise<void> {
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
let displayName = 'PySpark (1.1.1.1)';
let sanitizedDisplayName = model.sanitizeDisplayName(displayName);
should(sanitizedDisplayName).equal('PySpark');
});
test('Should sanitize kernel display name properly when IP is not included', async function (): Promise<void> {
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
let displayName = 'PySpark';
let sanitizedDisplayName = model.sanitizeDisplayName(displayName);
should(sanitizedDisplayName).equal('PySpark');
});
test('Should notify on trust set', async function () {
// Given a notebook that's been loaded
let mockContentManager = TypeMoq.Mock.ofType(LocalContentManager);
mockContentManager.setup(c => c.getNotebookContents(TypeMoq.It.isAny())).returns(() => Promise.resolve(expectedNotebookContent));
notebookManagers[0].contentManager = mockContentManager.object;
let model = new NotebookModel(defaultModelOptions, undefined, logService, undefined, undefined);
await model.requestModelLoad();
let actualChanged: NotebookContentChange;
model.contentChanged((changed) => actualChanged = changed);
// When I change trusted state
model.trustedMode = true;
// Then content changed notification should be sent
should(model.trustedMode).be.true();
should(actualChanged).not.be.undefined();
should(actualChanged.changeType).equal(NotebookChangeType.TrustChanged);
});
function shouldHaveOneCell(model: NotebookModel): void {
should(model.cells).have.length(1);
verifyCellModel(model.cells[0], { cell_type: CellTypes.Code, source: 'insert into t1 values (c1, c2)', metadata: { language: 'python' }, execution_count: 1 });
}
function verifyCellModel(cellModel: ICellModel, expected: nb.ICellContents): void {
should(cellModel.cellType).equal(expected.cell_type);
should(cellModel.source).equal(expected.source);
}
});

View File

@@ -0,0 +1,87 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as should from 'should';
import { IConnectionProfile } from 'azdata';
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
import { formatServerNameWithDatabaseNameForAttachTo, getServerFromFormattedAttachToName, getDatabaseFromFormattedAttachToName } from 'sql/workbench/parts/notebook/node/models/notebookUtils';
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
suite('notebookUtils', function (): void {
let conn: IConnectionProfile = {
connectionName: '',
serverName: '',
databaseName: '',
userName: '',
password: '',
authenticationType: '',
savePassword: true,
groupFullName: '',
groupId: '',
providerName: mssqlProviderName,
saveProfile: true,
id: '',
options: {},
azureTenantId: undefined
};
test('Should format server and database name correctly for attach to', async function (): Promise<void> {
let capabilitiesService = new TestCapabilitiesService();
let connProfile = new ConnectionProfile(capabilitiesService, conn);
connProfile.serverName = 'serverName';
connProfile.databaseName = 'databaseName';
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
should(attachToNameFormatted).equal('serverName (databaseName)');
});
test('Should format server name correctly for attach to', async function (): Promise<void> {
let capabilitiesService = new TestCapabilitiesService();
let connProfile = new ConnectionProfile(capabilitiesService, conn);
connProfile.serverName = 'serverName';
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
should(attachToNameFormatted).equal('serverName');
});
test('Should format server name correctly for attach to when database is undefined', async function (): Promise<void> {
let capabilitiesService = new TestCapabilitiesService();
let connProfile = new ConnectionProfile(capabilitiesService, conn);
connProfile.serverName = 'serverName';
connProfile.databaseName = undefined;
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
should(attachToNameFormatted).equal('serverName');
});
test('Should format server name as empty string when server/database are undefined', async function (): Promise<void> {
let capabilitiesService = new TestCapabilitiesService();
let connProfile = new ConnectionProfile(capabilitiesService, conn);
connProfile.serverName = undefined;
connProfile.databaseName = undefined;
let attachToNameFormatted = formatServerNameWithDatabaseNameForAttachTo(connProfile);
should(attachToNameFormatted).equal('');
});
test('Should extract server name when no database specified', async function (): Promise<void> {
let serverName = getServerFromFormattedAttachToName('serverName');
let databaseName = getDatabaseFromFormattedAttachToName('serverName');
should(serverName).equal('serverName');
should(databaseName).equal('');
});
test('Should extract server and database name', async function (): Promise<void> {
let serverName = getServerFromFormattedAttachToName('serverName (databaseName)');
let databaseName = getDatabaseFromFormattedAttachToName('serverName (databaseName)');
should(serverName).equal('serverName');
should(databaseName).equal('databaseName');
});
test('Should extract server and database name with other parentheses', async function (): Promise<void> {
let serverName = getServerFromFormattedAttachToName('serv()erName (databaseName)');
let databaseName = getDatabaseFromFormattedAttachToName('serv()erName (databaseName)');
should(serverName).equal('serv()erName');
should(databaseName).equal('databaseName');
});
});