@phnq/message
Version:
Asynchronous, incremental messaging client and server
265 lines (264 loc) • 14 kB
JavaScript
;
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);
}));
});
});