123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- // Copyright (c) Jupyter Development Team.
- import 'jest';
- const it = require('jest-retries');
- import { PageConfig } from '@jupyterlab/coreutils';
- import { UUID } from '@lumino/coreutils';
- import { Signal } from '@lumino/signaling';
- import {
- KernelMessage,
- Session,
- SessionManager,
- KernelManager,
- KernelAPI
- } from '../../src';
- import {
- expectFailure,
- testEmission,
- JupyterServer
- } from '@jupyterlab/testutils';
- import { handleRequest, SessionTester, init } from '../utils';
- init();
- let kernelManager: KernelManager;
- let sessionManager: SessionManager;
- /**
- * Start a new session with a unique name.
- */
- async function startNew(): Promise<Session.ISessionConnection> {
- const session = await sessionManager.startNew({
- path: UUID.uuid4(),
- name: UUID.uuid4(),
- type: 'test'
- });
- return session;
- }
- const server = new JupyterServer();
- beforeAll(async () => {
- await server.start();
- kernelManager = new KernelManager();
- sessionManager = new SessionManager({ kernelManager });
- });
- afterAll(async () => {
- await server.shutdown();
- });
- describe('session', () => {
- let session: Session.ISessionConnection;
- let defaultSession: Session.ISessionConnection;
- beforeAll(async () => {
- jest.setTimeout(20000);
- defaultSession = await startNew();
- });
- afterEach(async () => {
- if (session && !session.isDisposed) {
- await session.shutdown();
- }
- });
- afterAll(async () => {
- await defaultSession.shutdown();
- });
- describe('Session.DefaultSession', () => {
- describe('#disposed', () => {
- it('should emit when the session is disposed', async () => {
- let called = false;
- session = await startNew();
- session.disposed.connect(() => {
- called = true;
- });
- await session.shutdown();
- session.dispose();
- expect(called).toBe(true);
- });
- });
- describe('#kernelChanged', () => {
- it('should emit when the kernel changes', async () => {
- let called: Session.ISessionConnection.IKernelChangedArgs | null = null;
- const object = {};
- await defaultSession.kernel?.requestKernelInfo();
- defaultSession.kernelChanged.connect((s, args) => {
- called = args;
- Signal.disconnectReceiver(object);
- }, object);
- const original = defaultSession.kernel!;
- // Create a new kernel with the same kernel name (same type)
- await defaultSession.changeKernel({ name: original.name });
- expect(original).not.toBe(defaultSession.kernel);
- expect(called).toEqual({
- name: 'kernel',
- oldValue: original,
- newValue: defaultSession.kernel
- });
- original.dispose();
- });
- });
- describe('#statusChanged', () => {
- it('should emit when the kernel status changes', async () => {
- let called = false;
- defaultSession.statusChanged.connect((s, status) => {
- if (status === 'busy') {
- called = true;
- }
- });
- await defaultSession.kernel!.requestKernelInfo();
- expect(called).toBe(true);
- });
- });
- describe('#iopubMessage', () => {
- it('should be emitted for an iopub message', async () => {
- let called = false;
- defaultSession.iopubMessage.connect((s, msg) => {
- if (msg.header.msg_type === 'status') {
- called = true;
- }
- });
- await defaultSession.kernel!.requestExecute({ code: 'a=1' }, true).done;
- expect(called).toBe(true);
- });
- });
- describe('#unhandledMessage', () => {
- it('should be emitted for an unhandled message', async () => {
- const tester = new SessionTester();
- const session = await tester.startSession();
- const msgId = UUID.uuid4();
- const emission = testEmission(session.unhandledMessage, {
- find: (k, msg) => msg.header.msg_id === msgId
- });
- const msg = KernelMessage.createMessage({
- msgType: 'kernel_info_request',
- channel: 'shell',
- session: tester.serverSessionId,
- msgId,
- content: {}
- });
- msg.parent_header = { session: session.kernel!.clientId };
- tester.send(msg);
- await emission;
- await tester.shutdown();
- tester.dispose();
- });
- });
- describe('#propertyChanged', () => {
- it('should be emitted when the session path changes', async () => {
- const newPath = UUID.uuid4();
- let called = false;
- const object = {};
- defaultSession.propertyChanged.connect((s, type) => {
- expect(defaultSession.path).toBe(newPath);
- expect(type).toBe('path');
- called = true;
- Signal.disconnectReceiver(object);
- }, object);
- await defaultSession.setPath(newPath);
- expect(called).toBe(true);
- });
- });
- describe('#id', () => {
- it('should be a string', () => {
- expect(typeof defaultSession.id).toBe('string');
- });
- });
- describe('#path', () => {
- it('should be a string', () => {
- expect(typeof defaultSession.path).toBe('string');
- });
- });
- describe('#name', () => {
- it('should be a string', () => {
- expect(typeof defaultSession.name).toBe('string');
- });
- });
- describe('#type', () => {
- it('should be a string', () => {
- expect(typeof defaultSession.name).toBe('string');
- });
- });
- describe('#model', () => {
- it('should be an IModel', () => {
- const model = defaultSession.model;
- expect(typeof model.id).toBe('string');
- expect(typeof model.path).toBe('string');
- expect(typeof model.kernel!.name).toBe('string');
- expect(typeof model.kernel!.id).toBe('string');
- });
- });
- describe('#kernel', () => {
- it('should be an IKernel object', () => {
- expect(typeof defaultSession.kernel!.id).toBe('string');
- });
- });
- describe('#serverSettings', () => {
- it('should be the serverSettings', () => {
- expect(defaultSession.serverSettings.baseUrl).toBe(
- PageConfig.getBaseUrl()
- );
- });
- });
- describe('#isDisposed', () => {
- it('should be true after we dispose of the session', async () => {
- const session = await startNew();
- expect(session.isDisposed).toBe(false);
- session.dispose();
- expect(session.isDisposed).toBe(true);
- });
- it('should be safe to call multiple times', async () => {
- const session = await startNew();
- expect(session.isDisposed).toBe(false);
- expect(session.isDisposed).toBe(false);
- session.dispose();
- expect(session.isDisposed).toBe(true);
- expect(session.isDisposed).toBe(true);
- });
- });
- describe('#dispose()', () => {
- it('should dispose of the resources held by the session', async () => {
- const session = await startNew();
- session.dispose();
- expect(session.isDisposed).toBe(true);
- });
- it('should be safe to call twice', async () => {
- const session = await startNew();
- session.dispose();
- expect(session.isDisposed).toBe(true);
- session.dispose();
- expect(session.isDisposed).toBe(true);
- });
- it('should be safe to call if the kernel is disposed', async () => {
- const session = await startNew();
- session.kernel!.dispose();
- session.dispose();
- expect(session.isDisposed).toBe(true);
- });
- });
- describe('#setPath()', () => {
- it('should set the path of the session', async () => {
- const newPath = UUID.uuid4();
- await defaultSession.setPath(newPath);
- expect(defaultSession.path).toBe(newPath);
- });
- it('should fail for improper response status', async () => {
- handleRequest(defaultSession, 201, {});
- await expectFailure(defaultSession.setPath(UUID.uuid4()));
- });
- it('should fail for error response status', async () => {
- handleRequest(defaultSession, 500, {});
- await expectFailure(defaultSession.setPath(UUID.uuid4()), '');
- });
- it('should fail for improper model', async () => {
- handleRequest(defaultSession, 200, {});
- await expectFailure(defaultSession.setPath(UUID.uuid4()));
- });
- it('should fail if the session is disposed', async () => {
- const session = sessionManager.connectTo({
- model: defaultSession.model
- });
- session.dispose();
- const promise = session.setPath(UUID.uuid4());
- await expectFailure(promise, 'Session is disposed');
- });
- });
- describe('#setType()', () => {
- it('should set the type of the session', async () => {
- const session = await startNew();
- const type = UUID.uuid4();
- await session.setType(type);
- expect(session.type).toBe(type);
- await session.shutdown();
- });
- it('should fail for improper response status', async () => {
- handleRequest(defaultSession, 201, {});
- await expectFailure(defaultSession.setType(UUID.uuid4()));
- });
- it('should fail for error response status', async () => {
- handleRequest(defaultSession, 500, {});
- await expectFailure(defaultSession.setType(UUID.uuid4()), '');
- });
- it('should fail for improper model', async () => {
- handleRequest(defaultSession, 200, {});
- await expectFailure(defaultSession.setType(UUID.uuid4()));
- });
- it('should fail if the session is disposed', async () => {
- const session = sessionManager.connectTo({
- model: defaultSession.model
- });
- session.dispose();
- const promise = session.setPath(UUID.uuid4());
- await expectFailure(promise, 'Session is disposed');
- });
- });
- describe('#setName()', () => {
- it('should set the name of the session', async () => {
- const name = UUID.uuid4();
- await defaultSession.setName(name);
- expect(defaultSession.name).toBe(name);
- });
- it('should fail for improper response status', async () => {
- handleRequest(defaultSession, 201, {});
- await expectFailure(defaultSession.setName(UUID.uuid4()));
- });
- it('should fail for error response status', async () => {
- handleRequest(defaultSession, 500, {});
- await expectFailure(defaultSession.setName(UUID.uuid4()), '');
- });
- it('should fail for improper model', async () => {
- handleRequest(defaultSession, 200, {});
- await expectFailure(defaultSession.setName(UUID.uuid4()));
- });
- it('should fail if the session is disposed', async () => {
- const session = sessionManager.connectTo({
- model: defaultSession.model
- });
- session.dispose();
- const promise = session.setPath(UUID.uuid4());
- await expectFailure(promise, 'Session is disposed');
- });
- });
- describe('#changeKernel()', () => {
- it('should create a new kernel with the new name', async () => {
- session = await startNew();
- const previous = session.kernel!;
- await previous.info;
- await session.changeKernel({ name: previous.name });
- expect(session.kernel!.name).toBe(previous.name);
- expect(session.kernel!.id).not.toBe(previous.id);
- expect(session.kernel).not.toBe(previous);
- previous.dispose();
- });
- it('should accept the id of the new kernel', async () => {
- session = await startNew();
- const previous = session.kernel!;
- await previous.info;
- const kernel = await KernelAPI.startNew();
- await session.changeKernel({ id: kernel.id });
- expect(session.kernel!.id).toBe(kernel.id);
- expect(session.kernel).not.toBe(previous);
- expect(session.kernel).not.toBe(kernel);
- previous.dispose();
- await KernelAPI.shutdownKernel(kernel.id);
- });
- it('should update the session path if it has changed', async () => {
- session = await startNew();
- const previous = session.kernel!;
- await previous.info;
- const path = UUID.uuid4() + '.ipynb';
- const model = { ...session.model, path };
- handleRequest(session, 200, model);
- await session.changeKernel({ name: previous.name });
- expect(session.kernel!.name).toBe(previous.name);
- expect(session.path).toBe(model.path);
- previous.dispose();
- });
- });
- describe('#shutdown()', () => {
- it('should shut down properly', async () => {
- session = await startNew();
- await session.shutdown();
- });
- it('should emit a disposed signal', async () => {
- let called = false;
- session = await startNew();
- session.disposed.connect(() => {
- called = true;
- });
- await session.shutdown();
- expect(called).toBe(true);
- });
- it('should fail for an incorrect response status', async () => {
- handleRequest(defaultSession, 200, {});
- await expectFailure(defaultSession.shutdown());
- });
- it('should handle a 404 status', async () => {
- session = await startNew();
- handleRequest(session, 404, {});
- await session.shutdown();
- });
- it('should handle a specific error status', async () => {
- handleRequest(defaultSession, 410, {});
- const promise = defaultSession.shutdown();
- try {
- await promise;
- throw Error('should not get here');
- } catch (err) {
- const text = 'The kernel was deleted but the session was not';
- expect(err.message).toEqual(text);
- }
- });
- it('should fail for an error response status', async () => {
- handleRequest(defaultSession, 500, {});
- await expectFailure(defaultSession.shutdown(), '');
- });
- it('should fail if the session is disposed', async () => {
- const session = sessionManager.connectTo({
- model: defaultSession.model
- });
- session.dispose();
- await expectFailure(session.shutdown(), 'Session is disposed');
- });
- });
- });
- });
|