UNPKG

@phnq/message

Version:

Asynchronous, incremental messaging client and server

343 lines (342 loc) 17.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var g = generator.apply(thisArg, _arguments || []), i, q = []; return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } function fulfill(value) { resume("next", value); } function reject(value) { resume("throw", value); } function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } }; var __asyncValues = (this && this.__asyncValues) || function (o) { if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); var m = o[Symbol.asyncIterator], i; return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i); function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; } function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); } }; Object.defineProperty(exports, "__esModule", { value: true }); const errors_1 = require("../errors"); const MessageConnection_1 = require("../MessageConnection"); const MessageTransport_1 = require("../MessageTransport"); const DirectTransport_1 = require("../transports/DirectTransport"); const serverTransport = new DirectTransport_1.DirectTransport(); const serverConnection = new MessageConnection_1.MessageConnection(serverTransport); const clientConnection = new MessageConnection_1.MessageConnection(serverTransport.getConnectedTransport()); const wait = (millis = 0) => new Promise((resolve) => { setTimeout(resolve, millis); }); describe('MessageConnection', () => { describe('with DirectTransport', () => { describe('requests with multiple responses', () => { it('should handle multiple responses with an async iterator', () => __awaiter(void 0, void 0, void 0, function* () { var _a, e_1, _b, _c, _d, e_2, _e, _f; serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return (function () { return __asyncGenerator(this, arguments, function* () { expect(message).toBe('knock knock'); yield yield __await("who's"); yield yield __await('there'); yield yield __await('?'); }); })(); }); const resps1 = []; try { for (var _g = true, _h = __asyncValues(yield clientConnection.requestMulti('knock knock')), _j; _j = yield _h.next(), _a = _j.done, !_a;) { _c = _j.value; _g = false; try { const resp = _c; resps1.push(resp); } finally { _g = true; } } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (!_g && !_a && (_b = _h.return)) yield _b.call(_h); } finally { if (e_1) throw e_1.error; } } expect(resps1).toEqual(["who's", 'there', '?']); const resps2 = []; try { for (var _k = true, _l = __asyncValues(yield clientConnection.requestMulti('knock knock')), _m; _m = yield _l.next(), _d = _m.done, !_d;) { _f = _m.value; _k = false; try { const resp = _f; resps2.push(resp); } finally { _k = true; } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (!_k && !_d && (_e = _l.return)) yield _e.call(_l); } finally { if (e_2) throw e_2.error; } } expect(resps2).toEqual(["who's", 'there', '?']); })); it('should handle a single returned response with a single value', () => __awaiter(void 0, void 0, void 0, function* () { serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return `you said ${message}`; }); const resp = yield clientConnection.requestOne('hello'); expect(resp).toEqual('you said hello'); })); }); describe('requestOne', () => { it('should handle a single returned response', () => __awaiter(void 0, void 0, void 0, function* () { serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return `you said ${message}`; }); const resp = yield clientConnection.requestOne('hello'); expect(resp).toEqual('you said hello'); })); it('should return the first response if multiple are provided', () => __awaiter(void 0, void 0, void 0, function* () { serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return (function () { return __asyncGenerator(this, arguments, function* () { yield yield __await('hey'); yield yield __await('there'); yield yield __await(message); }); })(); }); const resp = yield clientConnection.requestOne('hello'); expect(resp).toEqual('hey'); })); }); describe('requestMulti', () => { it('should return an iterator when a single response is provided', () => __awaiter(void 0, void 0, void 0, function* () { var _a, e_3, _b, _c; serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return `you said ${message}`; }); const resp = yield clientConnection.requestMulti('hello'); expect(typeof resp).toBe('object'); expect(typeof resp[Symbol.asyncIterator]).toBe('function'); const resps = []; try { for (var _d = true, resp_1 = __asyncValues(resp), resp_1_1; resp_1_1 = yield resp_1.next(), _a = resp_1_1.done, !_a;) { _c = resp_1_1.value; _d = false; try { const r = _c; resps.push(r); } finally { _d = true; } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (!_d && !_a && (_b = resp_1.return)) yield _b.call(resp_1); } finally { if (e_3) throw e_3.error; } } expect(resps).toEqual(['you said hello']); })); it('should return an iterator when multiple responses are provided', () => __awaiter(void 0, void 0, void 0, function* () { var _e, e_4, _f, _g; serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return (function () { return __asyncGenerator(this, arguments, function* () { yield yield __await('hey'); yield yield __await('there'); yield yield __await(message); }); })(); }); const resp = yield clientConnection.requestMulti('hello'); expect(typeof resp).toBe('object'); expect(typeof resp[Symbol.asyncIterator]).toBe('function'); const resps = []; try { for (var _h = true, resp_2 = __asyncValues(resp), resp_2_1; resp_2_1 = yield resp_2.next(), _e = resp_2_1.done, !_e;) { _g = resp_2_1.value; _h = false; try { const r = _g; resps.push(r); } finally { _h = true; } } } catch (e_4_1) { e_4 = { error: e_4_1 }; } finally { try { if (!_h && !_e && (_f = resp_2.return)) yield _f.call(resp_2); } finally { if (e_4) throw e_4.error; } } expect(resps).toEqual(['hey', 'there', 'hello']); })); }); describe('one-way send (push)', () => { it('should handle pushes in both directions', () => __awaiter(void 0, void 0, void 0, function* () { const serverReceive = jest.fn(); const clientReceive = jest.fn(); serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { serverReceive(message); return ''; }); clientConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { clientReceive(message); return ''; }); yield Promise.all([clientConnection.send('one way'), serverConnection.send('or another')]); expect(serverReceive).toHaveBeenCalledWith('one way'); expect(clientReceive).toHaveBeenCalledWith('or another'); })); }); describe('handling errors', () => { it('should handle internal errors', () => __awaiter(void 0, void 0, void 0, function* () { serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { throw new Error(`Error: ${message}`); }); try { yield clientConnection.requestOne('hello'); fail('Should have thrown'); } catch (err) { expect(err).toBeInstanceOf(Error); expect(err.message).toEqual('Error: hello'); } })); it('should handle anomalies', () => __awaiter(void 0, void 0, void 0, function* () { serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { throw new errors_1.Anomaly(`Anomaly: ${message}`, { foo: 'bar' }); }); try { yield clientConnection.requestOne('hello'); fail('Should have thrown'); } catch (err) { expect(err).toBeInstanceOf(errors_1.Anomaly); expect(err.message).toEqual('Anomaly: hello'); expect(err.info).toEqual({ foo: 'bar' }); } })); }); }); describe('requests with timeouts', () => { it('should throw an error if the response times out', () => __awaiter(void 0, void 0, void 0, function* () { clientConnection.responseTimeout = 50; serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { yield wait(100); return `you said ${message}`; }); try { yield clientConnection.requestOne('hello'); fail('Should have thrown'); } catch (err) { expect(err).toBeInstanceOf(Error); } })); }); describe('Conversation Summaries', () => { const serverTrans = new DirectTransport_1.DirectTransport(); const serverConn = new MessageConnection_1.MessageConnection(serverTrans); const clientConn = new MessageConnection_1.MessageConnection(serverTrans.getConnectedTransport()); it('should yield expected summaries that agree client vs. server', () => __awaiter(void 0, void 0, void 0, function* () { var _a, e_5, _b, _c; let clientConvSummary; let serverConvSummary; serverConn.onConversation = (convSummary) => { serverConvSummary = convSummary; }; clientConn.onConversation = (convSummary) => { clientConvSummary = convSummary; }; serverConn.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return (function () { return __asyncGenerator(this, arguments, function* () { expect(message).toBe('knock knock'); yield yield __await("who's"); yield yield __await('there'); yield yield __await('?'); }); })(); }); const resps = []; try { for (var _d = true, _e = __asyncValues(yield clientConn.requestMulti('knock knock')), _f; _f = yield _e.next(), _a = _f.done, !_a;) { _c = _f.value; _d = false; try { const resp = _c; resps.push(resp); } finally { _d = true; } } } catch (e_5_1) { e_5 = { error: e_5_1 }; } finally { try { if (!_d && !_a && (_b = _e.return)) yield _b.call(_e); } finally { if (e_5) throw e_5.error; } } expect(serverConvSummary).toBeDefined(); expect(clientConvSummary).toBeDefined(); if (serverConvSummary && clientConvSummary) { expect(clientConvSummary.perspective).toBe(MessageConnection_1.ConversationPerspective.Requester); expect(serverConvSummary.perspective).toBe(MessageConnection_1.ConversationPerspective.Responder); expect(serverConvSummary.responses.map(({ message }) => message)).toEqual(clientConvSummary.responses.map(({ message }) => message)); expect(serverConvSummary.responses.length).toBe(4); expect(serverConvSummary.responses[0].message.t).toBe(MessageTransport_1.MessageType.Multi); expect(serverConvSummary.responses[1].message.t).toBe(MessageTransport_1.MessageType.Multi); expect(serverConvSummary.responses[2].message.t).toBe(MessageTransport_1.MessageType.Multi); expect(serverConvSummary.responses[3].message.t).toBe(MessageTransport_1.MessageType.End); } })); }); // describe('ping/pong', (): void => { // it('should return true for ping when connected', async (): Promise<void> => { // const serverTrans = new DirectTransport(); // const serverConn = new MessageConnection<string>(serverTrans); // const clientConn = new MessageConnection<string>(serverTrans.getConnectedTransport()); // expect(await serverConn.ping()).toBe(true); // expect(await clientConn.ping()).toBe(true); // }); // it('should throw for ping when not connected', async (): Promise<void> => { // const nonConn = new MessageConnection<string>(new DirectTransport()); // nonConn.responseTimeout = 50; // try { // await nonConn.ping(); // fail('Should have thrown'); // } catch (err) { // expect(err).toBeInstanceOf(Error); // } // }); // }); });