"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const node_assert_1 = require("node:assert");
// eslint-disable-next-line import-x/no-internal-modules
const duplicateBatchDetector_js_1 = require("../../opLifecycle/duplicateBatchDetector.js");
/**
 * Helper function to create (enough of) a BatchStartInfo for testing.
 * Inbound batch may have explicit batchId, or merely clientId and batchStartCsn and batchId must be computed - allow either as inputs
 */
function makeBatch({ sequenceNumber, minimumSequenceNumber, batchId, clientId, batchStartCsn, }) {
    return {
        keyMessage: {
            sequenceNumber,
            minimumSequenceNumber,
        },
        batchId,
        clientId,
        batchStartCsn,
    };
}
describe("DuplicateBatchDetector", () => {
    // expose private members for testing
    let detector;
    let seqNum;
    beforeEach("setup", () => {
        seqNum = 1;
        detector = new duplicateBatchDetector_js_1.DuplicateBatchDetector(undefined /* batchIdsFromSnapshot */);
    });
    afterEach("validation", () => {
        node_assert_1.strict.deepEqual([...detector.seqNumByBatchId.keys()].sort(), [...detector.batchIdsBySeqNum.values()].sort(), "Invariant: seqNumByBatchId and batchIdsBySeqNum should be in sync");
    });
    it("Constructor param is respected", () => {
        const input = [
            [1, "batch1"],
            [2, "batch2"],
        ];
        detector = new duplicateBatchDetector_js_1.DuplicateBatchDetector(input);
        node_assert_1.strict.deepEqual(detector.getRecentBatchInfoForSummary(), input);
    });
    it("First inbound batch is not a duplicate", () => {
        (0, node_assert_1.strict)(detector.seqNumByBatchId.size === 0, "Expected detector to start empty");
        const inboundBatch = makeBatch({
            sequenceNumber: seqNum++,
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        const result = detector.processInboundBatch(inboundBatch);
        node_assert_1.strict.deepEqual(result, { duplicate: false });
    });
    it("Different inbound batches are not duplicates", () => {
        const inboundBatch1 = makeBatch({
            sequenceNumber: seqNum++,
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        const inboundBatch2 = makeBatch({
            sequenceNumber: seqNum++,
            minimumSequenceNumber: 0,
            batchId: "batch2",
        });
        detector.processInboundBatch(inboundBatch1);
        const result = detector.processInboundBatch(inboundBatch2);
        node_assert_1.strict.deepEqual(result, { duplicate: false });
    });
    it("Matching inbound batches are duplicates", () => {
        const inboundBatch1 = makeBatch({
            sequenceNumber: seqNum++, // 1
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        const inboundBatch2 = makeBatch({
            sequenceNumber: seqNum++, // 2
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        detector.processInboundBatch(inboundBatch1);
        const result = detector.processInboundBatch(inboundBatch2);
        node_assert_1.strict.deepEqual(result, { duplicate: true, otherSequenceNumber: 1 });
    });
    it("Matching inbound batches, one with batchId one without, are duplicates", () => {
        const inboundBatch1 = makeBatch({
            sequenceNumber: seqNum++, // 1
            minimumSequenceNumber: 0,
            batchId: "clientId_[33]",
        });
        const inboundBatch2 = makeBatch({
            sequenceNumber: seqNum++, // 2
            minimumSequenceNumber: 0,
            clientId: "clientId",
            batchStartCsn: 33,
        });
        detector.processInboundBatch(inboundBatch1);
        const result = detector.processInboundBatch(inboundBatch2);
        node_assert_1.strict.deepEqual(result, { duplicate: true, otherSequenceNumber: 1 });
    });
    it("Matching inbound batches are duplicates (roundtrip through summary)", () => {
        const inboundBatch1 = makeBatch({
            sequenceNumber: seqNum++, // 1
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        detector.processInboundBatch(inboundBatch1);
        const summaryPayload = JSON.stringify(detector.getRecentBatchInfoForSummary());
        const detector2 = new duplicateBatchDetector_js_1.DuplicateBatchDetector(JSON.parse(summaryPayload));
        const inboundBatch2 = makeBatch({
            sequenceNumber: seqNum++, // 2
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        const result = detector2.processInboundBatch(inboundBatch2);
        node_assert_1.strict.deepEqual(result, { duplicate: true, otherSequenceNumber: 1 });
    });
    it("should clear old batchIds that are no longer at risk for duplicates", () => {
        const inboundBatch1 = makeBatch({
            sequenceNumber: seqNum++, // 1
            minimumSequenceNumber: 0,
            batchId: "batch1",
        });
        const inboundBatch2 = makeBatch({
            sequenceNumber: seqNum++, // 2
            minimumSequenceNumber: 0,
            batchId: "batch2",
        });
        const inboundBatch3 = makeBatch({
            sequenceNumber: seqNum++, // 3
            minimumSequenceNumber: 2,
            batchId: "batch3",
        });
        detector.processInboundBatch(inboundBatch1);
        detector.processInboundBatch(inboundBatch2);
        node_assert_1.strict.deepEqual([...detector.seqNumByBatchId.keys()].sort(), ["batch1", "batch2"], "Incorrect batchIds (after 2)");
        detector.processInboundBatch(inboundBatch3);
        node_assert_1.strict.deepEqual([...detector.seqNumByBatchId.keys()].sort(), ["batch2", "batch3"], "Incorrect batchIds (after 3)");
    });
    describe("getStateForSummary", () => {
        it("If empty, return undefined", () => {
            node_assert_1.strict.equal(detector.batchIdsBySeqNum.size, 0, "PRECONDITION: Expected detector to start empty");
            node_assert_1.strict.equal(detector.getRecentBatchInfoForSummary(), undefined);
        });
        it("If not empty, return batchIds by seqNum (and update telemetryContext)", () => {
            const inboundBatch1 = makeBatch({
                sequenceNumber: seqNum++, // 1
                minimumSequenceNumber: 0,
                batchId: "batch1",
            });
            const inboundBatch2 = makeBatch({
                sequenceNumber: seqNum++, // 2
                minimumSequenceNumber: 0,
                batchId: "batch2",
            });
            detector.processInboundBatch(inboundBatch1);
            detector.processInboundBatch(inboundBatch2);
            let setCalled = 0;
            const telemetryContext = {
                set: (key, subKey, value) => {
                    ++setCalled;
                    node_assert_1.strict.equal(key, "fluid_DuplicateBatchDetector_");
                    node_assert_1.strict.equal(subKey, "recentBatchCount");
                    node_assert_1.strict.equal(value, 2);
                },
            };
            const recentBatchInfo = detector.getRecentBatchInfoForSummary(telemetryContext);
            node_assert_1.strict.deepEqual(recentBatchInfo, [
                [1, "batch1"],
                [2, "batch2"],
            ], "Incorrect recentBatchInfo");
            node_assert_1.strict.equal(setCalled, 1, "Expected telemetryContext.set to be called once");
        });
    });
});
//# sourceMappingURL=duplicateBatchDetector.spec.js.map