@phnq/message
Version:
Asynchronous, incremental messaging client and server
343 lines (342 loc) • 17.9 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 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);
// }
// });
// });
});