reliable-zeromq
Version:
A collection of reliable zeromq messaging constructs
206 lines • 18.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const ava_1 = __importDefault(require("ava"));
const sinon = __importStar(require("sinon"));
const ts_mock_imports_1 = require("ts-mock-imports");
const zmq = __importStar(require("zeromq"));
const Config_1 = __importDefault(require("../../Src/Config"));
const JSONBigInt_1 = __importDefault(require("../../Src/Utils/JSONBigInt"));
const ZMQRequest_1 = require("../../Src/ZMQRequest");
const ZMQResponse_1 = require("../../Src/ZMQResponse");
const AsyncTools_1 = require("../Helpers/AsyncTools");
const test = ava_1.default;
test.beforeEach((t) => {
const lResolvers = [];
function FakeIterator() {
return {
async next() {
return new Promise((aResolve) => {
lResolvers.push(aResolve);
});
},
};
}
const lMockManager = ts_mock_imports_1.ImportMock.mockClass(zmq, "Dealer");
// @ts-ignore
const lAsyncIteratorMock = lMockManager.mock(Symbol.asyncIterator);
lAsyncIteratorMock.callsFake(FakeIterator);
t.context = {
ResponderEndpoint: "tcp://127.0.0.1:3000",
TestData: [
{
a: 100n,
b: 20n,
c: 0.5,
d: [
5n,
"myFunc()",
],
},
],
DealerMock: lMockManager,
SendToReceiver: (aIndex, aMessage) => {
lResolvers[aIndex]({ value: aMessage, done: false });
},
};
});
test.afterEach((t) => {
sinon.restore();
ts_mock_imports_1.ImportMock.restore();
});
test.serial("Start, Send, Receive, Close", async (t) => {
const lDealerStub = t.context.DealerMock;
const lRequester = new ZMQRequest_1.ZMQRequest(t.context.ResponderEndpoint);
t.is(lRequester.Endpoint, t.context.ResponderEndpoint);
lDealerStub.mock("connect", undefined);
const lSendMock = lDealerStub.mock("send", Promise.resolve());
const lRequestPromise = lRequester.Send(JSONBigInt_1.default.Stringify(t.context.TestData));
await AsyncTools_1.YieldToEventLoop();
let lCallCount = 0;
t.is(lSendMock.callCount, ++lCallCount);
t.deepEqual(lSendMock.getCall(lCallCount - 1).args[0], [
lRequester["mOurUniqueId"],
"0",
JSONBigInt_1.default.Stringify(t.context.TestData),
]);
const lResponse = [
"0",
"dummy message",
];
t.context.SendToReceiver(0, lResponse);
const lPromiseResult = await lRequestPromise;
t.deepEqual(lPromiseResult.Response, lResponse[1]);
lRequester.Close();
});
test.serial("Degraded Connection", async (t) => {
const clock = sinon.useFakeTimers();
const lDealerStub = t.context.DealerMock;
const lRequester = new ZMQRequest_1.ZMQRequest(t.context.ResponderEndpoint);
t.is(lRequester.Endpoint, t.context.ResponderEndpoint);
lDealerStub.mock("connect", undefined);
lDealerStub.mock("send", Promise.resolve());
const lRequestPromise = lRequester.Send(JSONBigInt_1.default.Stringify(t.context.TestData));
const lResponse = [
"0",
"dummy message",
];
// Send response with 500ms delay
await AsyncTools_1.YieldToEventLoop(); // Sinon timers don't seem super reliable at triggering timeouts
clock.tick(501);
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(0, lResponse);
const lPromiseResult = await lRequestPromise;
t.is(lPromiseResult.Response, lResponse[1]);
const lSecondRequest = lRequester.Send("MyTestDelayedResponse");
const lSecondResponse = [
"1",
"another response",
];
// Send 3 responses (2 duplicates) with 2200ms delay
await AsyncTools_1.YieldToEventLoop();
clock.tick(501);
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(1, lSecondResponse);
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(2, lSecondResponse);
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(3, lSecondResponse);
await AsyncTools_1.YieldToEventLoop();
t.is((await lSecondRequest).Response, lSecondResponse[1]);
lRequester.Close();
});
test.serial("Errors & Warns", async (t) => {
const clock = sinon.useFakeTimers();
const lDealerStub = t.context.DealerMock;
const lEndpoint = t.context.ResponderEndpoint;
const lHighWaterMarkWarnings = [];
const lRequester = new ZMQRequest_1.ZMQRequest(lEndpoint);
const lRequesterCustom = new ZMQRequest_1.ZMQRequest(lEndpoint, {
HighWaterMarkWarning: (aWarning) => {
lHighWaterMarkWarnings.push(aWarning);
},
});
lDealerStub.mock("send", Promise.resolve());
lDealerStub.mock("connect", undefined);
const lFirstResponsePromise = lRequester.Send(JSONBigInt_1.default.Stringify("hello"));
t.context.SendToReceiver(0, [JSONBigInt_1.default.Stringify(0), "world"]);
clock.tick(500);
t.is((await lFirstResponsePromise).Response, "world");
const lFailedRequest = lRequester.Send(JSONBigInt_1.default.Stringify("hello"));
await AsyncTools_1.YieldToEventLoop();
clock.tick(Config_1.default.MaximumLatency * 2 + 600); // 500ms represents the polling interval in ZMQRequest
await AsyncTools_1.YieldToEventLoop();
const lFailedResult = await lFailedRequest;
if (lFailedResult.ResponseType === ZMQRequest_1.ERequestResponse.TIMEOUT) {
t.is(lFailedResult.MessageNonce, 1);
t.is(lFailedResult.RequestBody[ZMQRequest_1.ERequestBody.Nonce], "1");
t.is(lFailedResult.RequestBody[ZMQRequest_1.ERequestBody.Message], "\"hello\"");
}
else {
t.fail("lFailedRequest should have resolved to TRequestTimeOut");
}
lDealerStub.mock("send", Promise.reject({
code: "EAGAIN",
}));
t.is(lHighWaterMarkWarnings.length, 0);
const lDefaultWarnPromise = lRequester.Send("THIS SHOULD BE SUPPRESSED");
const lCustomWarnPromise = lRequesterCustom.Send("THIS SHOULD PUSH TO ARRAY");
await AsyncTools_1.YieldToEventLoop();
t.is(lHighWaterMarkWarnings.length, 1);
t.deepEqual(lHighWaterMarkWarnings[0], {
Requester: lRequesterCustom["mOurUniqueId"],
Nonce: 0,
Message: "THIS SHOULD PUSH TO ARRAY",
});
lDealerStub.mock("send", Promise.resolve());
t.context.SendToReceiver(2, ["2", "default errors response"]);
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(1, ["0", "custom errors response"]);
await AsyncTools_1.YieldToEventLoop();
t.deepEqual(await lDefaultWarnPromise, {
ResponseType: ZMQRequest_1.ERequestResponse.SUCCESS,
Response: "default errors response",
});
t.deepEqual(await lCustomWarnPromise, {
ResponseType: ZMQRequest_1.ERequestResponse.SUCCESS,
Response: "custom errors response",
});
const lCacheError = lRequester.Send("THIS SHOULD TRIGGER A CACHE ERROR");
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(3, ["3", ZMQResponse_1.RESPONSE_CACHE_EXPIRED]);
await AsyncTools_1.YieldToEventLoop();
t.deepEqual(await lCacheError, {
ResponseType: ZMQRequest_1.ERequestResponse.CACHE_ERROR,
Endpoint: lEndpoint,
MessageNonce: 3,
});
lRequester.Close();
lRequesterCustom.Close();
await AsyncTools_1.YieldToEventLoop();
t.context.SendToReceiver(4, ["-10", "custom handler suppress"]);
t.context.SendToReceiver(5, ["-10", "default handler suppress"]);
await AsyncTools_1.YieldToEventLoop();
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiWk1RUmVxdWVzdC50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vVGVzdC9TcmMvWk1RUmVxdWVzdC50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLDhDQUFnRDtBQUNoRCw2Q0FBK0I7QUFDL0IscURBQTBEO0FBQzFELDRDQUE4QjtBQUM5Qiw4REFBc0M7QUFFdEMsNEVBQW9EO0FBQ3BELHFEQUF3SDtBQUN4SCx1REFBK0Q7QUFDL0Qsc0RBQXlEO0FBV3pELE1BQU0sSUFBSSxHQUFnQyxhQUFzQyxDQUFDO0FBRWpGLElBQUksQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFpQyxFQUFRLEVBQUU7SUFFeEQsTUFBTSxVQUFVLEdBQWtELEVBQUUsQ0FBQztJQUNyRSxTQUFTLFlBQVk7UUFFakIsT0FBTztZQUNILEtBQUssQ0FBQyxJQUFJO2dCQUVOLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxRQUFnRCxFQUFRLEVBQUU7b0JBRTFFLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBQzlCLENBQUMsQ0FBQyxDQUFDO1lBQ1AsQ0FBQztTQUNKLENBQUM7SUFDTixDQUFDO0lBRUQsTUFBTSxZQUFZLEdBQTRCLDRCQUFVLENBQUMsU0FBUyxDQUFhLEdBQUcsRUFBRSxRQUFRLENBQUMsQ0FBQztJQUM5RixhQUFhO0lBQ2IsTUFBTSxrQkFBa0IsR0FBb0IsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLENBQUM7SUFDcEYsa0JBQWtCLENBQUMsU0FBUyxDQUFDLFlBQVksQ0FBQyxDQUFDO0lBRTNDLENBQUMsQ0FBQyxPQUFPLEdBQUc7UUFDUixpQkFBaUIsRUFBRSxzQkFBc0I7UUFDekMsUUFBUSxFQUFFO1lBQ047Z0JBQ0ksQ0FBQyxFQUFFLElBQUk7Z0JBQ1AsQ0FBQyxFQUFFLEdBQUc7Z0JBQ04sQ0FBQyxFQUFFLEdBQUc7Z0JBQ04s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