UNPKG

@phnq/message

Version:

Asynchronous, incremental messaging client and server

265 lines (264 loc) 14 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 NATSTransport_1 = require("../transports/NATSTransport"); const isCCI = process.env['CCI'] === '1'; const wait = (millis = 0) => new Promise(resolve => { setTimeout(resolve, millis); }); describe('NATSTransport', () => { let clientConnection; let serverConnection; let clientObjectConnection; let serverObjectConnection; let monitorTransport; beforeAll(() => __awaiter(void 0, void 0, void 0, function* () { const config = { servers: [`nats://localhost:${isCCI ? 4222 : 4225}`] }; const monitorUrl = `http://localhost:${isCCI ? 8222 : 8225}`; const signSalt = String(Date.now()); clientConnection = new MessageConnection_1.MessageConnection(yield NATSTransport_1.NATSTransport.create(Object.assign(Object.assign({}, config), { name: 'client' }), { publishSubject: 's1', subscriptions: ['s2'] }), { signSalt }); serverConnection = new MessageConnection_1.MessageConnection(yield NATSTransport_1.NATSTransport.create(Object.assign(Object.assign({}, config), { name: 'server' }), { publishSubject: 's2', subscriptions: ['s1'] }), { signSalt }); clientObjectConnection = new MessageConnection_1.MessageConnection(yield NATSTransport_1.NATSTransport.create(Object.assign(Object.assign({}, config), { name: 'objectclient' }), { publishSubject: 'so1', subscriptions: ['so2'] }), { signSalt }); serverObjectConnection = new MessageConnection_1.MessageConnection(yield NATSTransport_1.NATSTransport.create(Object.assign(Object.assign({}, config), { name: 'objectserver' }), { publishSubject: 'so2', subscriptions: ['so1'] }), { signSalt }); monitorTransport = yield NATSTransport_1.NATSTransport.create(Object.assign(Object.assign({}, config), { monitorUrl, name: 'monitor' }), { publishSubject: 's3', subscriptions: ['s3'] }); })); afterAll(() => { clientConnection.transport.close(); serverConnection.transport.close(); }); describe('connecting', () => { it('should throw an error if connection fails (maxConnectAttempts: 1)', () => __awaiter(void 0, void 0, void 0, function* () { try { yield NATSTransport_1.NATSTransport.create({ servers: ['nats://localhost:4224'], maxConnectAttempts: 1 }, { publishSubject: 'subject', subscriptions: [] }); fail('should have thrown'); } catch (err) { // do nothing } })); }); 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 an iterator', () => __awaiter(void 0, void 0, void 0, function* () { var _o, e_3, _p, _q; serverConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { return `you said ${message}`; }); const resps1 = []; try { for (var _r = true, _s = __asyncValues(yield clientConnection.requestMulti('hello')), _t; _t = yield _s.next(), _o = _t.done, !_o;) { _q = _t.value; _r = false; try { const resp = _q; resps1.push(resp); } finally { _r = true; } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (!_r && !_o && (_p = _s.return)) yield _p.call(_s); } finally { if (e_3) throw e_3.error; } } expect(resps1).toEqual(['you said hello']); })); }); describe('requests with a single response', () => { 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('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 undefined; }); clientConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { clientReceive(message); return undefined; }); yield Promise.all([clientConnection.send('one way'), serverConnection.send('or another')]); yield wait(100); 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('chunking', () => { it('should handle messages that are larger than the max payload size', () => __awaiter(void 0, void 0, void 0, function* () { const SIZE = 5000000; const buf = Buffer.alloc(SIZE, 'a'); let i = 0; while (i < SIZE) { buf[i] = 97 + Math.round(26 * Math.random()); i += Math.round(Math.random() * 1000); } const bigRandomString = buf.toString(); serverConnection.onReceive = () => __awaiter(void 0, void 0, void 0, function* () { return bigRandomString; }); const resp = yield clientConnection.requestOne('hello'); expect(resp).toEqual(bigRandomString); })); }); describe('monitoring', () => { it('should return the list of connections', () => __awaiter(void 0, void 0, void 0, function* () { const conns = yield monitorTransport.getConnections(); const names = new Set(conns.map(c => c.name)); expect(names.has('client')).toBeTruthy(); expect(names.has('server')).toBeTruthy(); expect(names.has('monitor')).toBeTruthy(); })); }); describe('serialization/unserialization', () => { it('should correctly pass a date object', () => __awaiter(void 0, void 0, void 0, function* () { const now = new Date(); serverObjectConnection.onReceive = (message) => __awaiter(void 0, void 0, void 0, function* () { const { date } = message; date.setSeconds(date.getSeconds() + 1); return { date }; }); const resp = yield clientObjectConnection.requestOne({ date: now }); const { date } = resp; expect(date.getTime()).toEqual(now.getTime() + 1000); })); }); });