reliable-zeromq
Version:
A collection of reliable zeromq messaging constructs
267 lines (233 loc) • 9.02 kB
text/typescript
/* tslint:disable: no-string-literal */
import type { TestInterface } from "ava";
import anyTest, { ExecutionContext } from "ava";
import * as sinon from "sinon";
import { ImportMock } from "ts-mock-imports";
import { Delay } from "../../Src/Utils/Delay";
import JSONBigInt from "../../Src/Utils/JSONBigInt";
import { EPublishMessage, TPublishMessage, ZMQPublisher } from "../../Src/ZMQPublisher";
import { ERequestResponse, TRequestResponse, TSuccessfulRequest, ZMQRequest } from "../../Src/ZMQRequest";
import { ZMQResponse } from "../../Src/ZMQResponse";
import { TSubscriptionEndpoints, ZMQSubscriber } from "../../Src/ZMQSubscriber/ZMQSubscriber";
import { DUMMY_ENDPOINTS } from "../Helpers/DummyEndpoints.data";
type TTestContext =
{
ResponderEndpoint: string;
TestData: any[];
};
const test: TestInterface<TTestContext> = anyTest as TestInterface<TTestContext> ;
test.before((t: ExecutionContext<TTestContext>): void =>
{
// Unnecessary
});
test.beforeEach((t: ExecutionContext<TTestContext>): void =>
{
t.context = {
ResponderEndpoint: "tcp://127.0.0.1:3241",
TestData: [
{
a: 100n,
b: 20n, // JSONBigInt will parse "20n" to 20n, known issue
c: 0.5,
d: [
5n,
"myFunc()",
],
},
],
};
});
test.afterEach((t: ExecutionContext<TTestContext>): void =>
{
sinon.restore();
ImportMock.restore();
});
test.serial("ZMQRequest: Start, Send, Receive, Close", async(t: ExecutionContext<TTestContext>): Promise<void> =>
{
const lExpected: { code: string; data: any } =
{
code: "success",
data: undefined!,
};
const lResponse: ZMQResponse = new ZMQResponse(t.context.ResponderEndpoint, async(aMsg: string): Promise<string> =>
{
let lResult: string;
try
{
lResult = JSONBigInt.Parse(aMsg);
}
catch (e)
{
lResult = aMsg as string;
}
return JSONBigInt.Stringify({
code: "success",
data: lResult,
});
});
const lRequester: ZMQRequest = new ZMQRequest(t.context.ResponderEndpoint);
const lPromiseResult: TRequestResponse = await lRequester.Send(JSONBigInt.Stringify(t.context.TestData));
lExpected.data = t.context.TestData;
if (lPromiseResult.ResponseType === ERequestResponse.SUCCESS)
{
t.deepEqual(JSONBigInt.Parse(lPromiseResult.Response), lExpected);
}
else
{
t.fail(JSONBigInt.Stringify(
{
msg: "Request failed",
lPromiseResult,
},
));
}
const lNotThrowResult: TRequestResponse = await lRequester.Send("this should not throw");
lExpected.data = "this should not throw";
if (lNotThrowResult.ResponseType === ERequestResponse.SUCCESS)
{
t.deepEqual(JSONBigInt.Parse(lNotThrowResult.Response), lExpected);
}
else
{
t.fail(JSONBigInt.Stringify({msg: "Request failed", lNotThrowResult }));
}
lRequester.Close();
lResponse.Close();
});
test.serial("ZMQResponse: Start, Receive, Close", async(t: ExecutionContext<TTestContext>): Promise<void> =>
{
let lResponder = async(aMsg: string): Promise<string> => "world";
const lResponderRouter = (aMsg: string): Promise<string> =>
{
return lResponder(aMsg); // Necessary so we can update lResponder throughout
};
t.context.ResponderEndpoint = "tcp://127.0.0.1:4276";
const lResponse: ZMQResponse = new ZMQResponse(t.context.ResponderEndpoint, lResponderRouter);
const lRequester: ZMQRequest = new ZMQRequest(t.context.ResponderEndpoint);
const lFirstResponse: TRequestResponse = await lRequester.Send("hello");
t.is((lFirstResponse as TSuccessfulRequest).Response, "world");
t.is(lResponse["mCachedRequests"].size, 1);
lResponder = async(aMsg: string): Promise<string> => aMsg + " response";
const lSecondResponse: TRequestResponse = await lRequester.Send("hello");
t.is((lSecondResponse as TSuccessfulRequest).Response, "hello response");
t.is(lResponse["mCachedRequests"].size, 2);
lResponse.Close();
lRequester.Close();
});
test.serial("ZMQPublisher & ZMQSubscriber", async(t: ExecutionContext<TTestContext>): Promise<void> =>
{
const lStatusUpdatePublisher: ZMQPublisher = new ZMQPublisher(
{
PublisherAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.RequestAddress,
},
);
const lWeatherUpdatePublisher: ZMQPublisher = new ZMQPublisher(
{
PublisherAddress: DUMMY_ENDPOINTS.WEATHER_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.WEATHER_UPDATES.RequestAddress,
},
);
const lSubscriber: ZMQSubscriber = new ZMQSubscriber();
await lStatusUpdatePublisher.Open();
await lWeatherUpdatePublisher.Open();
type TTestDataResult =
{
[index: string]: {
Publisher: ZMQPublisher;
Topics: {
[index: string]: {
data: string[];
result: string[];
};
};
};
};
const lTestDataResult: TTestDataResult =
{
[DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress]: {
Publisher: lStatusUpdatePublisher,
Topics: {},
},
[DUMMY_ENDPOINTS.WEATHER_UPDATES.PublisherAddress]: {
Publisher: lWeatherUpdatePublisher,
Topics: {},
},
};
lTestDataResult[DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress].Topics["TopicA"]
= { data: ["myTestMessage"], result: [] };
lTestDataResult[DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress].Topics["TopicB"]
= { data: ["myTestMessage"], result: [] };
lTestDataResult[DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress].Topics["TopicC"]
= { data: ["myTestMessage"], result: [] };
lTestDataResult[DUMMY_ENDPOINTS.WEATHER_UPDATES.PublisherAddress].Topics["Sydney"]
= { data: ["sunny"], result: [] };
lTestDataResult[DUMMY_ENDPOINTS.WEATHER_UPDATES.PublisherAddress].Topics["Newcastle"]
= { data: ["cloudy"], result: [] };
const lSaveResult = (aEndpoint: string, aTopic: string, aNonce: number, aMessage: string): void =>
{
lTestDataResult[aEndpoint].Topics[aTopic].result[aNonce] = aMessage;
};
const lSubscribe = (aEndpoint: TSubscriptionEndpoints, aTopic: string): void =>
{
lSubscriber.Subscribe(aEndpoint, aTopic, (aMsg: string): void =>
{
lTestDataResult[aEndpoint.PublisherAddress].Publisher["mMessageCaches"].get(aTopic)!.forEach(
(aValue: TPublishMessage, aKey: number): void =>
{
if (aValue[EPublishMessage.Message] === aMsg)
{
lSaveResult(aEndpoint.PublisherAddress, aTopic, aKey, aMsg);
}
});
});
};
lSubscribe({
PublisherAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.RequestAddress,
}, "TopicA");
lSubscribe({
PublisherAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.RequestAddress,
}, "TopicB");
lSubscribe({
PublisherAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.STATUS_UPDATES.RequestAddress,
}, "TopicC");
lSubscribe({
PublisherAddress: DUMMY_ENDPOINTS.WEATHER_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.WEATHER_UPDATES.RequestAddress,
}, "Newcastle");
lSubscribe({
PublisherAddress: DUMMY_ENDPOINTS.WEATHER_UPDATES.PublisherAddress,
RequestAddress: DUMMY_ENDPOINTS.WEATHER_UPDATES.RequestAddress,
}, "Sydney");
for (const aEndpoint in lTestDataResult)
{
const lPublisher: ZMQPublisher = lTestDataResult[aEndpoint].Publisher;
for (const aTopic in lTestDataResult[aEndpoint].Topics)
{
for (const aData of lTestDataResult[aEndpoint].Topics[aTopic].data)
{
lPublisher.Publish(aTopic, aData);
}
}
}
await Delay(50);
for (const aEndpoint in lTestDataResult)
{
for (const aTopic in lTestDataResult[aEndpoint].Topics)
{
const lTestData: string[] = lTestDataResult[aEndpoint].Topics[aTopic].data;
const lTestResult: string[] = lTestDataResult[aEndpoint].Topics[aTopic].result;
for (let i: number = 0; i < lTestData.length; ++i)
{
t.is(lTestData[i], lTestResult[i]);
}
}
}
lSubscriber.Close();
lStatusUpdatePublisher.Close();
lWeatherUpdatePublisher.Close();
});
test.todo("Multiple Subscribers");