You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-desktop/test/session/unit/swarm_polling/SwarmPolling_test.js

222 lines
43 KiB
JavaScript

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
var import_chai = __toESM(require("chai"));
var sinon = __toESM(require("sinon"));
var import_mocha = require("mocha");
var import_chai_as_promised = __toESM(require("chai-as-promised"));
var import_test_utils = require("../../../test-utils");
var import_utils = require("../../../../session/utils");
var import_conversations = require("../../../../session/conversations");
var Data = __toESM(require("../../../../../ts/data/data"));
var import_snode_api = require("../../../../session/apis/snode_api");
var import_constants = require("../../../../session/constants");
var import_conversation = require("../../../../models/conversation");
var import_types = require("../../../../session/types");
var import_utils2 = require("../../../test-utils/utils");
import_chai.default.use(import_chai_as_promised.default);
import_chai.default.should();
const { expect } = import_chai.default;
(0, import_mocha.describe)("SwarmPolling", () => {
const sandbox = sinon.createSandbox();
const ourPubkey = import_test_utils.TestUtils.generateFakePubKey();
const ourNumber = ourPubkey.key;
let TEST_pollOnceForKeySpy;
let swarmPolling;
let clock;
beforeEach(async () => {
sandbox.stub(import_utils.UserUtils, "getOurPubKeyStrFromCache").returns(ourNumber);
sandbox.stub(Data, "getAllConversations").resolves(new import_conversation.ConversationCollection());
sandbox.stub(Data, "getItemById").resolves();
sandbox.stub(Data, "saveConversation").resolves();
sandbox.stub(Data, "getSwarmNodesForPubkey").resolves();
sandbox.stub(Data, "getLastHashBySnode").resolves();
sandbox.stub(import_snode_api.SnodePool, "getSwarmFor").resolves((0, import_utils2.generateFakeSnodes)(5));
sandbox.stub(import_snode_api.SNodeAPI, "retrieveNextMessages").resolves([]);
import_test_utils.TestUtils.stubWindow("inboxStore", void 0);
import_test_utils.TestUtils.stubWindow("getGlobalOnlineStatus", () => true);
import_test_utils.TestUtils.stubWindowLog();
const convoController = (0, import_conversations.getConversationController)();
await convoController.load();
(0, import_conversations.getConversationController)().getOrCreate(ourPubkey.key, import_conversation.ConversationTypeEnum.PRIVATE);
swarmPolling = (0, import_snode_api.getSwarmPollingInstance)();
swarmPolling.TEST_reset();
TEST_pollOnceForKeySpy = sandbox.spy(swarmPolling, "TEST_pollOnceForKey");
clock = sinon.useFakeTimers(Date.now());
});
afterEach(() => {
import_test_utils.TestUtils.restoreStubs();
sandbox.restore();
(0, import_conversations.getConversationController)().reset();
clock.restore();
});
(0, import_mocha.describe)("getPollingTimeout", () => {
it("returns INACTIVE for non existing convo", () => {
const fakeConvo = import_test_utils.TestUtils.generateFakePubKey();
expect(swarmPolling.TEST_getPollingTimeout(fakeConvo)).to.eq(import_constants.SWARM_POLLING_TIMEOUT.INACTIVE);
});
it("returns ACTIVE for convo with less than two days old activeAt", () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", Date.now() - 2 * 23 * 3600 * 1e3);
expect(swarmPolling.TEST_getPollingTimeout(import_types.PubKey.cast(convo.id))).to.eq(import_constants.SWARM_POLLING_TIMEOUT.ACTIVE);
});
it("returns INACTIVE for convo with undefined activeAt", () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", void 0);
expect(swarmPolling.TEST_getPollingTimeout(import_types.PubKey.cast(convo.id))).to.eq(import_constants.SWARM_POLLING_TIMEOUT.INACTIVE);
});
it("returns MEDIUM_ACTIVE for convo with activeAt of more than 2 days but less than a week old", () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", Date.now() - 1e3 * 3600 * 25 * 2);
expect(swarmPolling.TEST_getPollingTimeout(import_types.PubKey.cast(convo.id))).to.eq(import_constants.SWARM_POLLING_TIMEOUT.MEDIUM_ACTIVE);
convo.set("active_at", Date.now() - 1e3 * 3600 * 24 * 7 + 3600);
expect(swarmPolling.TEST_getPollingTimeout(import_types.PubKey.cast(convo.id))).to.eq(import_constants.SWARM_POLLING_TIMEOUT.MEDIUM_ACTIVE);
});
it("returns INACTIVE for convo with activeAt of more than a week", () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", Date.now() - 1e3 * 3600 * 24 * 8);
expect(swarmPolling.TEST_getPollingTimeout(import_types.PubKey.cast(convo.id))).to.eq(import_constants.SWARM_POLLING_TIMEOUT.INACTIVE);
});
});
(0, import_mocha.describe)("pollForAllKeys", () => {
it("does run for our pubkey even if activeAt is really old ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(ourNumber, import_conversation.ConversationTypeEnum.PRIVATE);
convo.set("active_at", Date.now() - 1e3 * 3600 * 25);
await swarmPolling.start(true);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(1);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
});
it("does run for our pubkey even if activeAt is recent ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(ourNumber, import_conversation.ConversationTypeEnum.PRIVATE);
convo.set("active_at", Date.now());
await swarmPolling.start(true);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(1);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
});
it("does run for group pubkey on start no matter the recent timestamp ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", Date.now());
const groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(2);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
});
it("does run for group pubkey on start no matter the old timestamp ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", 1);
const groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(2);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
});
it("does run for group pubkey on start but not another time if activeAt is old ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", 1);
const groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
await swarmPolling.TEST_pollForAllKeys();
expect(TEST_pollOnceForKeySpy.callCount).to.eq(3);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
expect(TEST_pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]);
});
it("does run twice if activeAt less than one hour ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", Date.now());
const groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
clock.tick(6e3);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(4);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
expect(TEST_pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.lastCall.args).to.deep.eq([groupConvoPubkey, true]);
});
it("does run twice if activeAt is inactive and we tick longer than 2 minutes", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
TEST_pollOnceForKeySpy.resetHistory();
convo.set("active_at", Date.now());
const groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
const timeToTick = 3 * 60 * 1e3;
swarmPolling.TEST_forcePolledTimestamp(groupConvoPubkey, Date.now() - timeToTick);
convo.set("active_at", Date.now() - 7 * 25 * 3600 * 1e3);
clock.tick(timeToTick);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(4);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
expect(TEST_pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]);
});
it("does run once only if group is inactive and we tick less than 2 minutes ", async () => {
const convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
TEST_pollOnceForKeySpy.resetHistory();
convo.set("active_at", Date.now());
const groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
convo.set("active_at", Date.now() - 7 * 24 * 3600 * 1e3 - 3600 * 1e3);
clock.tick(1 * 60 * 1e3);
expect(TEST_pollOnceForKeySpy.callCount).to.eq(3);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
expect(TEST_pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]);
});
(0, import_mocha.describe)("multiple runs", () => {
let convo;
let groupConvoPubkey;
beforeEach(async () => {
convo = (0, import_conversations.getConversationController)().getOrCreate(import_test_utils.TestUtils.generateFakePubKeyStr(), import_conversation.ConversationTypeEnum.GROUP);
convo.set("active_at", Date.now());
groupConvoPubkey = import_types.PubKey.cast(convo.id);
swarmPolling.addGroupId(groupConvoPubkey);
await swarmPolling.start(true);
});
it("does run twice if activeAt is less than 2 days", async () => {
TEST_pollOnceForKeySpy.resetHistory();
convo.set("active_at", Date.now() - 2 * 24 * 3600 * 1e3 - 3600 * 1e3);
const timeToTick = 6 * 1e3;
swarmPolling.TEST_forcePolledTimestamp(convo.id, timeToTick);
clock.tick(timeToTick);
await swarmPolling.TEST_pollForAllKeys();
expect(TEST_pollOnceForKeySpy.callCount).to.eq(4);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
expect(TEST_pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]);
});
it("does run twice if activeAt is more than 2 days old and we tick more than one minute ", async () => {
TEST_pollOnceForKeySpy.resetHistory();
convo.set("active_at", Date.now() - 2 * 25 * 3600 * 1e3);
const timeToTick = 65 * 1e3;
swarmPolling.TEST_forcePolledTimestamp(convo.id, timeToTick);
clock.tick(timeToTick);
await swarmPolling.TEST_pollForAllKeys();
expect(TEST_pollOnceForKeySpy.callCount).to.eq(4);
expect(TEST_pollOnceForKeySpy.firstCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.secondCall.args).to.deep.eq([groupConvoPubkey, true]);
expect(TEST_pollOnceForKeySpy.thirdCall.args).to.deep.eq([ourPubkey, false]);
expect(TEST_pollOnceForKeySpy.getCalls()[3].args).to.deep.eq([groupConvoPubkey, true]);
});
});
});
});
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vLi4vdHMvdGVzdC9zZXNzaW9uL3VuaXQvc3dhcm1fcG9sbGluZy9Td2FybVBvbGxpbmdfdGVzdC50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLy8gdHNsaW50OmRpc2FibGU6IG5vLWltcGxpY2l0LWRlcGVuZGVuY2llcyBtYXgtZnVuYy1ib2R5LWxlbmd0aCBuby11bnVzZWQtZXhwcmVzc2lvblxuXG5pbXBvcnQgY2hhaSBmcm9tICdjaGFpJztcbmltcG9ydCBTaW5vbiwgKiBhcyBzaW5vbiBmcm9tICdzaW5vbic7XG5pbXBvcnQgeyBkZXNjcmliZSB9IGZyb20gJ21vY2hhJztcblxuaW1wb3J0IGNoYWlBc1Byb21pc2VkIGZyb20gJ2NoYWktYXMtcHJvbWlzZWQnO1xuaW1wb3J0IHsgVGVzdFV0aWxzIH0gZnJvbSAnLi4vLi4vLi4vdGVzdC11dGlscyc7XG5pbXBvcnQgeyBVc2VyVXRpbHMgfSBmcm9tICcuLi8uLi8uLi8uLi9zZXNzaW9uL3V0aWxzJztcbmltcG9ydCB7IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIgfSBmcm9tICcuLi8uLi8uLi8uLi9zZXNzaW9uL2NvbnZlcnNhdGlvbnMnO1xuaW1wb3J0ICogYXMgRGF0YSBmcm9tICcuLi8uLi8uLi8uLi8uLi90cy9kYXRhL2RhdGEnO1xuaW1wb3J0IHsgZ2V0U3dhcm1Qb2xsaW5nSW5zdGFuY2UsIFNOb2RlQVBJLCBTbm9kZVBvb2wgfSBmcm9tICcuLi8uLi8uLi8uLi9zZXNzaW9uL2FwaXMvc25vZGVfYXBpJztcbmltcG9ydCB7IFN3YXJtUG9sbGluZyB9IGZyb20gJy4uLy4uLy4uLy4uL3Nlc3Npb24vYXBpcy9zbm9kZV9hcGkvc3dhcm1Qb2xsaW5nJztcbmltcG9ydCB7IFNXQVJNX1BPTExJTkdfVElNRU9VVCB9IGZyb20gJy4uLy4uLy4uLy4uL3Nlc3Npb24vY29uc3RhbnRzJztcbmltcG9ydCB7XG4gIENvbnZlcnNhdGlvbkNvbGxlY3Rpb24sXG4gIENvbnZlcnNhdGlvbk1vZGVsLFxuICBDb252ZXJzYXRpb25UeXBlRW51bSxcbn0gZnJvbSAnLi4vLi4vLi4vLi4vbW9kZWxzL2NvbnZlcnNhdGlvbic7XG5pbXBvcnQgeyBQdWJLZXkgfSBmcm9tICcuLi8uLi8uLi8uLi9zZXNzaW9uL3R5cGVzJztcbmltcG9ydCB7IGdlbmVyYXRlRmFrZVNub2RlcyB9IGZyb20gJy4uLy4uLy4uL3Rlc3QtdXRpbHMvdXRpbHMnO1xuLy8gdHNsaW50OmRpc2FibGU6IGNoYWktdmFndWUtZXJyb3JzXG5cbmNoYWkudXNlKGNoYWlBc1Byb21pc2VkIGFzIGFueSk7XG5jaGFpLnNob3VsZCgpO1xuXG5jb25zdCB7IGV4cGVjdCB9ID0gY2hhaTtcblxuLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiBtYXgtZnVuYy1ib2R5LWxlbmd0aFxuZGVzY3JpYmUoJ1N3YXJtUG9sbGluZycsICgpID0+IHtcbiAgLy8gSW5pdGlhbGl6ZSBuZXcgc3R1YmJlZCBjYWNoZVxuICBjb25zdCBzYW5kYm94ID0gc2lub24uY3JlYXRlU2FuZGJveCgpO1xuICBjb25zdCBvdXJQdWJrZXkgPSBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCk7XG4gIGNvbnN0IG91ck51bWJlciA9IG91clB1YmtleS5rZXk7XG5cbiAgLy8gdHNsaW50OmRpc2FibGUtbmV4dC1saW5lOiB2YXJpYWJsZS1uYW1lXG4gIGxldCBURVNUX3BvbGxPbmNlRm9yS2V5U3B5OiBTaW5vbi5TaW5vblNweTxhbnk+O1xuXG4gIGxldCBzd2FybVBvbGxpbmc6IFN3YXJtUG9sbGluZztcblxuICBsZXQgY2xvY2s6IFNpbm9uLlNpbm9uRmFrZVRpbWVycztcbiAgYmVmb3JlRWFjaChhc3luYyAoKSA9PiB7XG4gICAgLy8gVXRpbHMgU3R1YnNcbiAgICBzYW5kYm94LnN0dWIoVXNlclV0aWxzLCAnZ2V0T3VyUHViS2V5U3RyRnJvbUNhY2hlJykucmV0dXJucyhvdXJOdW1iZXIpO1xuXG4gICAgc2FuZGJveC5zdHViKERhdGEsICdnZXRBbGxDb252ZXJzYXRpb25zJykucmVzb2x2ZXMobmV3IENvbnZlcnNhdGlvbkNvbGxlY3Rpb24oKSk7XG4gICAgc2FuZGJveC5zdHViKERhdGEsICdnZXRJdGVtQnlJZCcpLnJlc29sdmVzKCk7XG4gICAgc2FuZGJveC5zdHViKERhdGEsICdzYXZlQ29udmVyc2F0aW9uJykucmVzb2x2ZXMoKTtcbiAgICBzYW5kYm94LnN0dWIoRGF0YSwgJ2dldFN3YXJtTm9kZXNGb3JQdWJrZXknKS5yZXNvbHZlcygpO1xuICAgIHNhbmRib3guc3R1YihEYXRhLCAnZ2V0TGFzdEhhc2hCeVNub2RlJykucmVzb2x2ZXMoKTtcblxuICAgIHNhbmRib3guc3R1YihTbm9kZVBvb2wsICdnZXRTd2FybUZvcicpLnJlc29sdmVzKGdlbmVyYXRlRmFrZVNub2Rlcyg1KSk7XG4gICAgc2FuZGJveC5zdHViKFNOb2RlQVBJLCAncmV0cmlldmVOZXh0TWVzc2FnZXMnKS5yZXNvbHZlcyhbXSk7XG4gICAgVGVzdFV0aWxzLnN0dWJXaW5kb3coJ2luYm94U3RvcmUnLCB1bmRlZmluZWQpO1xuICAgIFRlc3RVdGlscy5zdHViV2luZG93KCdnZXRHbG9iYWxPbmxpbmVTdGF0dXMnLCAoKSA9PiB0cnVlKTtcbiAgICBUZXN0VXRpbHMuc3R1YldpbmRvd0xvZygpO1xuXG4gICAgY29uc3QgY29udm9Db250cm9sbGVyID0gZ2V0Q29udmVyc2F0aW9uQ29udHJvbGxlcigpO1xuICAgIGF3YWl0IGNvbnZvQ29udHJvbGxlci5sb2FkKCk7XG4gICAgZ2V0Q29udmVyc2F0aW9uQ29udHJvbGxlcigpLmdldE9yQ3JlYXRlKG91clB1YmtleS5rZXksIENvbnZlcnNhdGlvblR5cGVFbnVtLlBSSVZBVEUpO1xuXG4gICAgc3dhcm1Qb2xsaW5nID0gZ2V0U3dhcm1Qb2xsaW5nSW5zdGFuY2UoKTtcbiAgICBzd2FybVBvbGxpbmcuVEVTVF9yZXNldCgpO1xuICAgIFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkgPSBzYW5kYm94LnNweShzd2FybVBvbGxpbmcsICdURVNUX3BvbGxPbmNlRm9yS2V5Jyk7XG5cbiAgICBjbG9jayA9IHNpbm9uLnVzZUZha2VUaW1lcnMoRGF0ZS5ub3coKSk7XG4gIH0pO1xuXG4gIGFmdGVyRWFjaCgoKSA9PiB7XG4gICAgVGVzdFV0aWxzLnJlc3RvcmVTdHVicygpO1xuICAgIHNhbmRib3gucmVzdG9yZSgpO1xuICAgIGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5yZXNldCgpO1xuICAgIGNsb2NrLnJlc3RvcmUoKTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ2dldFBvbGxpbmdUaW1lb3V0JywgKCkgPT4ge1xuICAgIGl0KCdyZXR1cm5zIElOQUNUSVZFIGZvciBub24gZXhpc3RpbmcgY29udm8nLCAoKSA9PiB7XG4gICAgICBjb25zdCBmYWtlQ29udm8gPSBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5KCk7XG5cbiAgICAgIGV4cGVjdChzd2FybVBvbGxpbmcuVEVTVF9nZXRQb2xsaW5nVGltZW91dChmYWtlQ29udm8pKS50by5lcShTV0FSTV9QT0xMSU5HX1RJTUVPVVQuSU5BQ1RJVkUpO1xuICAgIH0pO1xuXG4gICAgaXQoJ3JldHVybnMgQUNUSVZFIGZvciBjb252byB3aXRoIGxlc3MgdGhhbiB0d28gZGF5cyBvbGQgYWN0aXZlQXQnLCAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleVN0cigpLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5HUk9VUFxuICAgICAgKTtcbiAgICAgIGNvbnZvLnNldCgnYWN0aXZlX2F0JywgRGF0ZS5ub3coKSAtIDIgKiAyMyAqIDM2MDAgKiAxMDAwKTsgLy8gMjMgKiAyID0gNDYgaG91cnMgb2xkXG4gICAgICBleHBlY3Qoc3dhcm1Qb2xsaW5nLlRFU1RfZ2V0UG9sbGluZ1RpbWVvdXQoUHViS2V5LmNhc3QoY29udm8uaWQgYXMgc3RyaW5nKSkpLnRvLmVxKFxuICAgICAgICBTV0FSTV9QT0xMSU5HX1RJTUVPVVQuQUNUSVZFXG4gICAgICApO1xuICAgIH0pO1xuXG4gICAgaXQoJ3JldHVybnMgSU5BQ1RJVkUgZm9yIGNvbnZvIHdpdGggdW5kZWZpbmVkIGFjdGl2ZUF0JywgKCkgPT4ge1xuICAgICAgY29uc3QgY29udm8gPSBnZXRDb252ZXJzYXRpb25Db250cm9sbGVyKCkuZ2V0T3JDcmVhdGUoXG4gICAgICAgIFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXlTdHIoKSxcbiAgICAgICAgQ29udmVyc2F0aW9uVHlwZUVudW0uR1JPVVBcbiAgICAgICk7XG4gICAgICBjb252by5zZXQoJ2FjdGl2ZV9hdCcsIHVuZGVmaW5lZCk7XG4gICAgICBleHBlY3Qoc3dhcm1Qb2xsaW5nLlRFU1RfZ2V0UG9sbGluZ1RpbWVvdXQoUHViS2V5LmNhc3QoY29udm8uaWQgYXMgc3RyaW5nKSkpLnRvLmVxKFxuICAgICAgICBTV0FSTV9QT0xMSU5HX1RJTUVPVVQuSU5BQ1RJVkVcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgncmV0dXJucyBNRURJVU1fQUNUSVZFIGZvciBjb252byB3aXRoIGFjdGl2ZUF0IG9mIG1vcmUgdGhhbiAyIGRheXMgYnV0IGxlc3MgdGhhbiBhIHdlZWsgb2xkJywgKCkgPT4ge1xuICAgICAgY29uc3QgY29udm8gPSBnZXRDb252ZXJzYXRpb25Db250cm9sbGVyKCkuZ2V0T3JDcmVhdGUoXG4gICAgICAgIFRlc3RVdGlscy5nZW5lcmF0ZUZha2VQdWJLZXlTdHIoKSxcbiAgICAgICAgQ29udmVyc2F0aW9uVHlwZUVudW0uR1JPVVBcbiAgICAgICk7XG4gICAgICBjb252by5zZXQoJ2FjdGl2ZV9hdCcsIERhdGUubm93KCkgLSAxMDAwICogMzYwMCAqIDI1ICogMik7IC8vIDI1IGhvdXJzIHggMiA9IDUwIGhvdXJzIG9sZFxuICAgICAgZXhwZWN0KHN3YXJtUG9sbGluZy5URVNUX2dldFBvbGxpbmdUaW1lb3V0KFB1YktleS5jYXN0KGNvbnZvLmlkIGFzIHN0cmluZykpKS50by5lcShcbiAgICAgICAgU1dBUk1fUE9MTElOR19USU1FT1VULk1FRElVTV9BQ1RJVkVcbiAgICAgICk7XG5cbiAgICAgIGNvbnZvLnNldCgnYWN0aXZlX2F0JywgRGF0ZS5ub3coKSAtIDEwMDAgKiAzNjAwICogMjQgKiA3ICsgMzYwMCk7IC8vIGEgd2VlayBtaW51cyBhbiBob3VyIG9sZFxuICAgICAgZXhwZWN0KHN3YXJtUG9sbGluZy5URVNUX2dldFBvbGxpbmdUaW1lb3V0KFB1YktleS5jYXN0KGNvbnZvLmlkIGFzIHN0cmluZykpKS50by5lcShcbiAgICAgICAgU1dBUk1fUE9MTElOR19USU1FT1VULk1FRElVTV9BQ1RJVkVcbiAgICAgICk7XG4gICAgfSk7XG5cbiAgICBpdCgncmV0dXJucyBJTkFDVElWRSBmb3IgY29udm8gd2l0aCAgYWN0aXZlQXQgb2YgbW9yZSB0aGFuIGEgd2VlaycsICgpID0+IHtcbiAgICAgIGNvbnN0IGNvbnZvID0gZ2V0Q29udmVyc2F0aW9uQ29udHJvbGxlcigpLmdldE9yQ3JlYXRlKFxuICAgICAgICBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5U3RyKCksXG4gICAgICAgIENvbnZlcnNhdGlvblR5cGVFbnVtLkdST1VQXG4gICAgICApO1xuICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCBEYXRlLm5vdygpIC0gMTAwMCAqIDM2MDAgKiAyNCAqIDgpOyAvLyA4IGRheXNcbiAgICAgIGV4cGVjdChzd2FybVBvbGxpbmcuVEVTVF9nZXRQb2xsaW5nVGltZW91dChQdWJLZXkuY2FzdChjb252by5pZCBhcyBzdHJpbmcpKSkudG8uZXEoXG4gICAgICAgIFNXQVJNX1BPTExJTkdfVElNRU9VVC5JTkFDVElWRVxuICAgICAgKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ3BvbGxGb3JBbGxLZXlzJywgKCkgPT4ge1xuICAgIGl0KCdkb2VzIHJ1biBmb3Igb3VyIHB1YmtleSBldmVuIGlmIGFjdGl2ZUF0IGlzIHJlYWxseSBvbGQgJywgYXN5bmMgKCkgPT4ge1xuICAgICAgY29uc3QgY29udm8gPSBnZXRDb252ZXJzYXRpb25Db250cm9sbGVyKCkuZ2V0T3JDcmVhdGUoXG4gICAgICAgIG91ck51bWJlcixcbiAgICAgICAgQ29udmVyc2F0aW9uVHlwZUVudW0uUFJJVkFURVxuICAgICAgKTtcbiAgICAgIGNvbnZvLnNldCgnYWN0aXZlX2F0JywgRGF0ZS5ub3coKSAtIDEwMDAgKiAzNjAwICogMjUpO1xuICAgICAgYXdhaXQgc3dhcm1Qb2xsaW5nLnN0YXJ0KHRydWUpO1xuXG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5jYWxsQ291bnQpLnRvLmVxKDEpO1xuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuZmlyc3RDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW291clB1YmtleSwgZmFsc2VdKTtcbiAgICB9KTtcblxuICAgIGl0KCdkb2VzIHJ1biBmb3Igb3VyIHB1YmtleSBldmVuIGlmIGFjdGl2ZUF0IGlzIHJlY2VudCAnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgb3VyTnVtYmVyLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5QUklWQVRFXG4gICAgICApO1xuICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCBEYXRlLm5vdygpKTtcbiAgICAgIGF3YWl0IHN3YXJtUG9sbGluZy5zdGFydCh0cnVlKTtcblxuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuY2FsbENvdW50KS50by5lcSgxKTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LmZpcnN0Q2FsbC5hcmdzKS50by5kZWVwLmVxKFtvdXJQdWJrZXksIGZhbHNlXSk7XG4gICAgfSk7XG5cbiAgICBpdCgnZG9lcyBydW4gZm9yIGdyb3VwIHB1YmtleSBvbiBzdGFydCBubyBtYXR0ZXIgdGhlIHJlY2VudCB0aW1lc3RhbXAgICcsIGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNvbnZvID0gZ2V0Q29udmVyc2F0aW9uQ29udHJvbGxlcigpLmdldE9yQ3JlYXRlKFxuICAgICAgICBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5U3RyKCksXG4gICAgICAgIENvbnZlcnNhdGlvblR5cGVFbnVtLkdST1VQXG4gICAgICApO1xuICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCBEYXRlLm5vdygpKTtcbiAgICAgIGNvbnN0IGdyb3VwQ29udm9QdWJrZXkgPSBQdWJLZXkuY2FzdChjb252by5pZCBhcyBzdHJpbmcpO1xuICAgICAgc3dhcm1Qb2xsaW5nLmFkZEdyb3VwSWQoZ3JvdXBDb252b1B1YmtleSk7XG4gICAgICBhd2FpdCBzd2FybVBvbGxpbmcuc3RhcnQodHJ1ZSk7XG5cbiAgICAgIC8vIG91ciBwdWJrZXkgd2lsbCBiZSBwb2xsZWQgZm9yLCBoZW5jZSB0aGUgMlxuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuY2FsbENvdW50KS50by5lcSgyKTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LmZpcnN0Q2FsbC5hcmdzKS50by5kZWVwLmVxKFtvdXJQdWJrZXksIGZhbHNlXSk7XG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5zZWNvbmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcbiAgICB9KTtcblxuICAgIGl0KCdkb2VzIHJ1biBmb3IgZ3JvdXAgcHVia2V5IG9uIHN0YXJ0IG5vIG1hdHRlciB0aGUgb2xkIHRpbWVzdGFtcCAnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleVN0cigpLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5HUk9VUFxuICAgICAgKTtcblxuICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCAxKTtcbiAgICAgIGNvbnN0IGdyb3VwQ29udm9QdWJrZXkgPSBQdWJLZXkuY2FzdChjb252by5pZCBhcyBzdHJpbmcpO1xuICAgICAgc3dhcm1Qb2xsaW5nLmFkZEdyb3VwSWQoZ3JvdXBDb252b1B1YmtleSk7XG4gICAgICBhd2FpdCBzd2FybVBvbGxpbmcuc3RhcnQodHJ1ZSk7XG5cbiAgICAgIC8vIG91ciBwdWJrZXkgd2lsbCBiZSBwb2xsZWQgZm9yLCBoZW5jZSB0aGUgMlxuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuY2FsbENvdW50KS50by5lcSgyKTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LmZpcnN0Q2FsbC5hcmdzKS50by5kZWVwLmVxKFtvdXJQdWJrZXksIGZhbHNlXSk7XG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5zZWNvbmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcbiAgICB9KTtcblxuICAgIGl0KCdkb2VzIHJ1biBmb3IgZ3JvdXAgcHVia2V5IG9uIHN0YXJ0IGJ1dCBub3QgYW5vdGhlciB0aW1lIGlmIGFjdGl2ZUF0IGlzIG9sZCAnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleVN0cigpLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5HUk9VUFxuICAgICAgKTtcblxuICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCAxKTsgLy8gcmVhbGx5IG9sZFxuICAgICAgY29uc3QgZ3JvdXBDb252b1B1YmtleSA9IFB1YktleS5jYXN0KGNvbnZvLmlkIGFzIHN0cmluZyk7XG4gICAgICBzd2FybVBvbGxpbmcuYWRkR3JvdXBJZChncm91cENvbnZvUHVia2V5KTtcblxuICAgICAgLy8gdGhpcyBjYWxscyB0aGUgc3R1YiAyIHRpbWVzLCBvbmUgZm9yIG91ciBkaXJlY3QgcHVia2V5IGFuZCBvbmUgZm9yIHRoZSBncm91cFxuICAgICAgYXdhaXQgc3dhcm1Qb2xsaW5nLnN0YXJ0KHRydWUpO1xuXG4gICAgICAvLyB0aGlzIHNob3VsZCBvbmx5IGNhbGwgdGhlIHN0dWIgb25lIG1vcmUgdGltZTogZm9yIG91ciBkaXJlY3QgcHVia2V5IGJ1dCBub3QgZm9yIHRoZSBncm91cCBwdWJrZXlcbiAgICAgIGF3YWl0IHN3YXJtUG9sbGluZy5URVNUX3BvbGxGb3JBbGxLZXlzKCk7XG5cbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LmNhbGxDb3VudCkudG8uZXEoMyk7XG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5maXJzdENhbGwuYXJncykudG8uZGVlcC5lcShbb3VyUHVia2V5LCBmYWxzZV0pO1xuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuc2Vjb25kQ2FsbC5hcmdzKS50by5kZWVwLmVxKFtncm91cENvbnZvUHVia2V5LCB0cnVlXSk7XG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS50aGlyZENhbGwuYXJncykudG8uZGVlcC5lcShbb3VyUHVia2V5LCBmYWxzZV0pO1xuICAgIH0pO1xuXG4gICAgaXQoJ2RvZXMgcnVuIHR3aWNlIGlmIGFjdGl2ZUF0IGxlc3MgdGhhbiBvbmUgaG91ciAnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleVN0cigpLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5HUk9VUFxuICAgICAgKTtcblxuICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCBEYXRlLm5vdygpKTtcbiAgICAgIGNvbnN0IGdyb3VwQ29udm9QdWJrZXkgPSBQdWJLZXkuY2FzdChjb252by5pZCBhcyBzdHJpbmcpO1xuICAgICAgc3dhcm1Qb2xsaW5nLmFkZEdyb3VwSWQoZ3JvdXBDb252b1B1YmtleSk7XG4gICAgICBhd2FpdCBzd2FybVBvbGxpbmcuc3RhcnQodHJ1ZSk7XG4gICAgICBjbG9jay50aWNrKDYwMDApO1xuICAgICAgLy8gbm8gbmVlZCB0byBkbyB0aGF0IGFzIHRoZSB0aWNrIHdpbGwgdHJpZ2dlciBhIGNhbGwgaW4gYWxsIGNhc2VzIGFmdGVyIDUgc2Vjc1xuICAgICAgLy8gYXdhaXQgc3dhcm1Qb2xsaW5nLlRFU1RfcG9sbEZvckFsbEtleXMoKTtcblxuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuY2FsbENvdW50KS50by5lcSg0KTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LnNlY29uZENhbGwuYXJncykudG8uZGVlcC5lcShbZ3JvdXBDb252b1B1YmtleSwgdHJ1ZV0pO1xuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkudGhpcmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW291clB1YmtleSwgZmFsc2VdKTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5Lmxhc3RDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcbiAgICB9KTtcblxuICAgIGl0KCdkb2VzIHJ1biB0d2ljZSBpZiBhY3RpdmVBdCBpcyBpbmFjdGl2ZSBhbmQgd2UgdGljayBsb25nZXIgdGhhbiAyIG1pbnV0ZXMnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleVN0cigpLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5HUk9VUFxuICAgICAgKTtcblxuICAgICAgVEVTVF9wb2xsT25jZUZvcktleVNweS5yZXNldEhpc3RvcnkoKTtcbiAgICAgIGNvbnZvLnNldCgnYWN0aXZlX2F0JywgRGF0ZS5ub3coKSk7XG4gICAgICBjb25zdCBncm91cENvbnZvUHVia2V5ID0gUHViS2V5LmNhc3QoY29udm8uaWQgYXMgc3RyaW5nKTtcbiAgICAgIHN3YXJtUG9sbGluZy5hZGRHcm91cElkKGdyb3VwQ29udm9QdWJrZXkpO1xuICAgICAgLy8gdGhpcyBjYWxsIHRoZSBzdHViIHR3byB0aW1lcyBhbHJlYWR5LCBvbmUgZm9yIG91ciBkaXJlY3QgcHVia2V5IGFuZCBvbmUgZm9yIHRoZSBncm91cFxuICAgICAgYXdhaXQgc3dhcm1Qb2xsaW5nLnN0YXJ0KHRydWUpO1xuICAgICAgY29uc3QgdGltZVRvVGljayA9IDMgKiA2MCAqIDEwMDA7XG4gICAgICBzd2FybVBvbGxpbmcuVEVTVF9mb3JjZVBvbGxlZFRpbWVzdGFtcChncm91cENvbnZvUHVia2V5LCBEYXRlLm5vdygpIC0gdGltZVRvVGljayk7XG4gICAgICAvLyBtb3JlIHRoYW4gd2VlayBvbGQsIHNvIGluYWN0aXZlIGdyb3VwIGJ1dCB3ZSBoYXZlIHRvIHRpY2sgYWZ0ZXIgbW9yZSB0aGFuIDIgbWluXG4gICAgICBjb252by5zZXQoJ2FjdGl2ZV9hdCcsIERhdGUubm93KCkgLSA3ICogMjUgKiAzNjAwICogMTAwMCk7XG4gICAgICBjbG9jay50aWNrKHRpbWVUb1RpY2spO1xuXG4gICAgICAvLyB3ZSBzaG91bGQgaGF2ZSB0d28gbW9yZSBjYWxscyBoZXJlLCBzbyA0IHRvdGFsLlxuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuY2FsbENvdW50KS50by5lcSg0KTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LmZpcnN0Q2FsbC5hcmdzKS50by5kZWVwLmVxKFtvdXJQdWJrZXksIGZhbHNlXSk7XG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5zZWNvbmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcbiAgICAgIGV4cGVjdChURVNUX3BvbGxPbmNlRm9yS2V5U3B5LnRoaXJkQ2FsbC5hcmdzKS50by5kZWVwLmVxKFtvdXJQdWJrZXksIGZhbHNlXSk7XG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5nZXRDYWxscygpWzNdLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcbiAgICB9KTtcblxuICAgIGl0KCdkb2VzIHJ1biBvbmNlIG9ubHkgaWYgZ3JvdXAgaXMgaW5hY3RpdmUgYW5kIHdlIHRpY2sgbGVzcyB0aGFuIDIgbWludXRlcyAnLCBhc3luYyAoKSA9PiB7XG4gICAgICBjb25zdCBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgVGVzdFV0aWxzLmdlbmVyYXRlRmFrZVB1YktleVN0cigpLFxuICAgICAgICBDb252ZXJzYXRpb25UeXBlRW51bS5HUk9VUFxuICAgICAgKTtcbiAgICAgIFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkucmVzZXRIaXN0b3J5KCk7XG5cbiAgICAgIGNvbnZvLnNldCgnYWN0aXZlX2F0JywgRGF0ZS5ub3coKSk7XG4gICAgICBjb25zdCBncm91cENvbnZvUHVia2V5ID0gUHViS2V5LmNhc3QoY29udm8uaWQgYXMgc3RyaW5nKTtcbiAgICAgIHN3YXJtUG9sbGluZy5hZGRHcm91cElkKGdyb3VwQ29udm9QdWJrZXkpO1xuICAgICAgYXdhaXQgc3dhcm1Qb2xsaW5nLnN0YXJ0KHRydWUpO1xuXG4gICAgICAvLyBtb3JlIHRoYW4gYSB3ZWVrIG9sZCwgd2Ugc2hvdWxkIG5vdCB0aWNrIGFmdGVyIGp1c3QgNSBzZWNvbmRzXG4gICAgICBjb252by5zZXQoJ2FjdGl2ZV9hdCcsIERhdGUubm93KCkgLSA3ICogMjQgKiAzNjAwICogMTAwMCAtIDM2MDAgKiAxMDAwKTtcblxuICAgICAgY2xvY2sudGljaygxICogNjAgKiAxMDAwKTtcblxuICAgICAgLy8gd2Ugc2hvdWxkIGhhdmUgb25seSBvbmUgbW9yZSBjYWxsIGhlcmUsIHRoZSBvbmUgZm9yIG91ciBkaXJlY3QgcHVia2V5IGZldGNoXG4gICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5jYWxsQ291bnQpLnRvLmVxKDMpO1xuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuc2Vjb25kQ2FsbC5hcmdzKS50by5kZWVwLmVxKFtncm91cENvbnZvUHVia2V5LCB0cnVlXSk7IC8vIHRoaXMgb25lIGNvbWVzIGZyb20gdGhlIHN3YXJtUG9sbGluZy5zdGFydFxuICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkudGhpcmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW291clB1YmtleSwgZmFsc2VdKTtcbiAgICB9KTtcblxuICAgIGRlc2NyaWJlKCdtdWx0aXBsZSBydW5zJywgKCkgPT4ge1xuICAgICAgbGV0IGNvbnZvOiBDb252ZXJzYXRpb25Nb2RlbDtcbiAgICAgIGxldCBncm91cENvbnZvUHVia2V5OiBQdWJLZXk7XG5cbiAgICAgIGJlZm9yZUVhY2goYXN5bmMgKCkgPT4ge1xuICAgICAgICBjb252byA9IGdldENvbnZlcnNhdGlvbkNvbnRyb2xsZXIoKS5nZXRPckNyZWF0ZShcbiAgICAgICAgICBUZXN0VXRpbHMuZ2VuZXJhdGVGYWtlUHViS2V5U3RyKCksXG4gICAgICAgICAgQ29udmVyc2F0aW9uVHlwZUVudW0uR1JPVVBcbiAgICAgICAgKTtcblxuICAgICAgICBjb252by5zZXQoJ2FjdGl2ZV9hdCcsIERhdGUubm93KCkpO1xuICAgICAgICBncm91cENvbnZvUHVia2V5ID0gUHViS2V5LmNhc3QoY29udm8uaWQgYXMgc3RyaW5nKTtcbiAgICAgICAgc3dhcm1Qb2xsaW5nLmFkZEdyb3VwSWQoZ3JvdXBDb252b1B1YmtleSk7XG4gICAgICAgIGF3YWl0IHN3YXJtUG9sbGluZy5zdGFydCh0cnVlKTtcbiAgICAgIH0pO1xuXG4gICAgICBpdCgnZG9lcyBydW4gdHdpY2UgaWYgYWN0aXZlQXQgaXMgbGVzcyB0aGFuIDIgZGF5cycsIGFzeW5jICgpID0+IHtcbiAgICAgICAgVEVTVF9wb2xsT25jZUZvcktleVNweS5yZXNldEhpc3RvcnkoKTtcbiAgICAgICAgLy8gbGVzcyB0aGFuIDIgZGF5cyBvbGQsIHRoaXMgaXMgYW4gYWN0aXZlIGdyb3VwXG4gICAgICAgIGNvbnZvLnNldCgnYWN0aXZlX2F0JywgRGF0ZS5ub3coKSAtIDIgKiAyNCAqIDM2MDAgKiAxMDAwIC0gMzYwMCAqIDEwMDApO1xuXG4gICAgICAgIGNvbnN0IHRpbWVUb1RpY2sgPSA2ICogMTAwMDtcblxuICAgICAgICBzd2FybVBvbGxpbmcuVEVTVF9mb3JjZVBvbGxlZFRpbWVzdGFtcChjb252by5pZCwgdGltZVRvVGljayk7XG4gICAgICAgIC8vIHdlIHRpY2sgbW9yZSB0aGFuIDUgc2VjXG4gICAgICAgIGNsb2NrLnRpY2sodGltZVRvVGljayk7XG5cbiAgICAgICAgYXdhaXQgc3dhcm1Qb2xsaW5nLlRFU1RfcG9sbEZvckFsbEtleXMoKTtcbiAgICAgICAgLy8gd2UgaGF2ZSA0IGNhbGxzIHRvdGFsLiAyIGZvciBvdXIgZGlyZWN0IHByb21pc2VzIHJ1biBlYWNoIDUgc2Vjb25kcywgYW5kIDIgZm9yIHRoZSBncm91cCBwdWJrZXkgYWN0aXZlIChzbyBydW4gZXZlcnkgNSBzZWMgdG9vKVxuICAgICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5jYWxsQ291bnQpLnRvLmVxKDQpO1xuICAgICAgICAvLyBmaXJzdCB0d28gY2FsbHMgYXJlIG91ciBwdWJrZXlcbiAgICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuZmlyc3RDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW291clB1YmtleSwgZmFsc2VdKTtcbiAgICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuc2Vjb25kQ2FsbC5hcmdzKS50by5kZWVwLmVxKFtncm91cENvbnZvUHVia2V5LCB0cnVlXSk7XG5cbiAgICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkudGhpcmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW291clB1YmtleSwgZmFsc2VdKTtcbiAgICAgICAgZXhwZWN0KFRFU1RfcG9sbE9uY2VGb3JLZXlTcHkuZ2V0Q2FsbHMoKVszXS5hcmdzKS50by5kZWVwLmVxKFtncm91cENvbnZvUHVia2V5LCB0cnVlXSk7XG4gICAgICB9KTtcblxuICAgICAgaXQoJ2RvZXMgcnVuIHR3aWNlIGlmIGFjdGl2ZUF0IGlzIG1vcmUgdGhhbiAyIGRheXMgb2xkIGFuZCB3ZSB0aWNrIG1vcmUgdGhhbiBvbmUgbWludXRlICcsIGFzeW5jICgpID0+IHtcbiAgICAgICAgVEVTVF9wb2xsT25jZUZvcktleVNweS5yZXNldEhpc3RvcnkoKTtcbiAgICAgICAgY29udm8uc2V0KCdhY3RpdmVfYXQnLCBEYXRlLm5vdygpIC0gMiAqIDI1ICogMzYwMCAqIDEwMDApOyAvLyBtZWRpdW0gYWN0aXZlXG5cbiAgICAgICAgY29uc3QgdGltZVRvVGljayA9IDY1ICogMTAwMDtcbiAgICAgICAgc3dhcm1Qb2xsaW5nLlRFU1RfZm9yY2VQb2xsZWRUaW1lc3RhbXAoY29udm8uaWQsIHRpbWVUb1RpY2spO1xuXG4gICAgICAgIGNsb2NrLnRpY2sodGltZVRvVGljayk7IC8vIHNob3VsZCB0aWNrIHR3aWNlIG1vcmUgKG9uZSBtb3JlIG91ciBkaXJlY3QgcHVia2V5IGFuZCBvbmUgZm9yIHRoZSBncm91cClcblxuICAgICAgICBhd2FpdCBzd2FybVBvbGxpbmcuVEVTVF9wb2xsRm9yQWxsS2V5cygpO1xuICAgICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5jYWxsQ291bnQpLnRvLmVxKDQpO1xuXG4gICAgICAgIC8vIGZpcnN0IHR3byBjYWxscyBhcmUgb3VyIHB1YmtleVxuICAgICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5maXJzdENhbGwuYXJncykudG8uZGVlcC5lcShbb3VyUHVia2V5LCBmYWxzZV0pO1xuICAgICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5zZWNvbmRDYWxsLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcblxuICAgICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS50aGlyZENhbGwuYXJncykudG8uZGVlcC5lcShbb3VyUHVia2V5LCBmYWxzZV0pO1xuICAgICAgICBleHBlY3QoVEVTVF9wb2xsT25jZUZvcktleVNweS5nZXRDYWxscygpWzNdLmFyZ3MpLnRvLmRlZXAuZXEoW2dyb3VwQ29udm9QdWJrZXksIHRydWVdKTtcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9KTtcbn0pO1xuIl0sCiAgIm1hcHBpbmdzIjogIjs7Ozs7Ozs7Ozs7Ozs7O0FBRUEsa0JBQWlCO0FBQ2pCLFlBQThCO0FBQzlCLG1CQUF5QjtBQUV6Qiw4QkFBMkI7QUFDM0Isd0JBQTBCO0FBQzFCLG1CQUEwQjtBQUMxQiwyQkFBMEM7QUFDMUMsV0FBc0I7QUFDdEIsdUJBQTZEO0FBRTdELHVCQUFzQztBQUN0QywwQkFJTztBQUNQLG1CQUF1QjtBQUN2QixvQkFBbUM7QUFHbkMsb0JBQUssSUFBSSwrQkFBcUI7QUFDOUIsb0JBQUssT0FBTztBQUVaLE1BQU0sRUFBRSxXQUFXO0FBR25CLDJCQUFTLGdCQUFnQixNQUFNO0FBRTdCLFFBQU0sVUFBVSxNQUFNLGNBQWM7QUFDcEMsUUFBTSxZQUFZLDRCQUFVLG1CQUFtQjtBQUMvQyxRQUFNLFlBQVksVUFBVTtBQUc1QixNQUFJO0FBRUosTUFBSTtBQUVKLE1BQUk7QUFDSixhQUFXLFlBQVk7QUFFckIsWUFBUSxLQUFLLHdCQUFXLDBCQUEwQixFQUFFLFFBQVEsU0FBUztBQUVyRSxZQUFR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