"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
const assert_1 = require("assert");
const constants_1 = require("../constants.cjs");
const mergeTreeNodes_1 = require("../mergeTreeNodes.cjs");
const ops_1 = require("../ops.cjs");
const textSegment_1 = require("../textSegment.cjs");
const mergeTree_1 = require("../mergeTree.cjs");
const testUtils_1 = require("./testUtils.cjs");
describe("MergeTree", () => {
    let mergeTree;
    const remoteClientId = 35;
    const localClientId = 17;
    let currentSequenceNumber;
    const annotateStart = 1;
    const markerPosition = annotateStart + 2;
    const annotateEnd = markerPosition + 2;
    const splitPos = Math.floor((annotateEnd - annotateStart) / 2) + annotateStart;
    beforeEach(() => {
        mergeTree = new mergeTree_1.MergeTree();
        (0, testUtils_1.insertSegments)({
            mergeTree,
            pos: 0,
            segments: [textSegment_1.TextSegment.make("hello world!")],
            refSeq: constants_1.UniversalSequenceNumber,
            clientId: constants_1.LocalClientId,
            seq: constants_1.UniversalSequenceNumber,
            opArgs: undefined,
        });
        currentSequenceNumber = 0;
        (0, testUtils_1.insertSegments)({
            mergeTree,
            pos: markerPosition,
            segments: [mergeTreeNodes_1.Marker.make(ops_1.ReferenceType.Tile)],
            refSeq: currentSequenceNumber,
            clientId: remoteClientId,
            seq: ++currentSequenceNumber,
            opArgs: undefined,
        });
    });
    describe("annotateRange", () => {
        describe("not collaborating", () => {
            it("remote", () => {
                mergeTree.annotateRange(annotateStart, annotateEnd, {
                    propertySource: "remote",
                }, currentSequenceNumber, remoteClientId, currentSequenceNumber + 1, undefined);
                const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                const segment = segmentInfo.segment;
                assert_1.strict.equal(segment?.properties?.propertySource, "remote");
            });
            it("local", () => {
                mergeTree.annotateRange(annotateStart, annotateEnd, {
                    propertySource: "local",
                }, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                const segment = segmentInfo.segment;
                assert_1.strict.equal(segment.properties?.propertySource, "local");
            });
        });
        describe("collaborating", () => {
            beforeEach(() => {
                mergeTree.startCollaboration(localClientId, 
                /* minSeq: */ currentSequenceNumber, 
                /* currentSeq: */ currentSequenceNumber);
            });
            describe("local first", () => {
                const props = {
                    propertySource: "local",
                };
                beforeEach(() => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, props, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                });
                it("unsequenced local", () => {
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                });
                it("unsequenced local after unsequenced local", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        secondProperty: "local",
                    }, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.secondProperty, "local");
                });
                it("unsequenced local split", () => {
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    const splitSegment = segment.splitAt(splitPos);
                    assert_1.strict.equal(splitSegment.properties?.propertySource, "local");
                });
                it("unsequenced local after unsequenced local split", () => {
                    const secondChangeProps = {
                        secondChange: 1,
                    };
                    mergeTree.annotateRange(annotateStart, annotateEnd, secondChangeProps, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    const splitOnlyProps = {
                        splitOnly: 1,
                    };
                    mergeTree.annotateRange(splitPos, annotateEnd, splitOnlyProps, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    const splitSegmentInfo = mergeTree.getContainingSegment(splitPos, currentSequenceNumber, localClientId);
                    const splitSegment = splitSegmentInfo.segment;
                    assert_1.strict.equal(segment.segmentGroups.size, 2);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.secondChange, 1);
                    (0, assert_1.strict)(!segment.properties?.splitOnly);
                    assert_1.strict.equal(splitSegment.segmentGroups.size, 3);
                    assert_1.strict.equal(splitSegment.properties?.propertySource, "local");
                    assert_1.strict.equal(splitSegment.properties?.secondChange, 1);
                    assert_1.strict.equal(splitSegment.properties?.splitOnly, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    assert_1.strict.equal(segment.segmentGroups.size, 1);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.secondChange, 1);
                    (0, assert_1.strict)(!segment.properties?.splitOnly);
                    assert_1.strict.equal(splitSegment.segmentGroups.size, 2);
                    assert_1.strict.equal(splitSegment.properties?.propertySource, "local");
                    assert_1.strict.equal(splitSegment.properties?.secondChange, 1);
                    assert_1.strict.equal(splitSegment.properties?.splitOnly, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props: secondChangeProps,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    assert_1.strict.equal(segment.segmentGroups.size, 0);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.secondChange, 1);
                    (0, assert_1.strict)(!segment.properties?.splitOnly);
                    assert_1.strict.equal(splitSegment.segmentGroups.size, 1);
                    assert_1.strict.equal(splitSegment.properties?.propertySource, "local");
                    assert_1.strict.equal(splitSegment.properties?.secondChange, 1);
                    assert_1.strict.equal(splitSegment.properties?.splitOnly, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: splitPos,
                            pos2: annotateEnd,
                            props: splitOnlyProps,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    assert_1.strict.equal(segment.segmentGroups.size, 0);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.secondChange, 1);
                    (0, assert_1.strict)(!segment.properties?.splitOnly);
                    assert_1.strict.equal(splitSegment.segmentGroups.size, 0);
                    assert_1.strict.equal(splitSegment.properties?.propertySource, "local");
                    assert_1.strict.equal(splitSegment.properties?.secondChange, 1);
                    assert_1.strict.equal(splitSegment.properties?.splitOnly, 1);
                });
                it("unsequenced local before remote", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteProperty: 1,
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.segmentGroups.size, 1);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.remoteProperty, 1);
                });
                it("sequenced local", () => {
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.segmentGroups.size, 0);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                });
                it("sequenced local before remote", () => {
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteProperty: 1,
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.segmentGroups.size, 0);
                    assert_1.strict.equal(segment.properties?.propertySource, "remote");
                    assert_1.strict.equal(segment.properties?.remoteProperty, 1);
                });
                it("three local changes", () => {
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    const props2 = {
                        propertySource: "local2",
                        secondSource: 1,
                    };
                    mergeTree.annotateRange(annotateStart, annotateEnd, props2, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    assert_1.strict.equal(segment.properties?.propertySource, "local2");
                    assert_1.strict.equal(segment.properties?.secondSource, 1);
                    const props3 = {
                        thirdSource: 1,
                    };
                    mergeTree.annotateRange(annotateStart, annotateEnd, props3, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    assert_1.strict.equal(segment.properties?.propertySource, "local2");
                    assert_1.strict.equal(segment.properties?.secondSource, 1);
                    assert_1.strict.equal(segment.properties?.thirdSource, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    assert_1.strict.equal(segment.properties?.propertySource, "local2");
                    assert_1.strict.equal(segment.properties?.secondSource, 1);
                    assert_1.strict.equal(segment.properties?.thirdSource, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props: props2,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    assert_1.strict.equal(segment.properties?.propertySource, "local2");
                    assert_1.strict.equal(segment.properties?.secondSource, 1);
                    assert_1.strict.equal(segment.properties?.thirdSource, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props: props3,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    assert_1.strict.equal(segment.properties?.propertySource, "local2");
                    assert_1.strict.equal(segment.properties?.secondSource, 1);
                    assert_1.strict.equal(segment.properties?.thirdSource, 1);
                });
                it("two local changes with interleaved remote", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        secondSource: "local2",
                    }, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteOnly: 1,
                        secondSource: "remote",
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.remoteOnly, 1);
                    assert_1.strict.equal(segment.properties?.propertySource, "remote");
                    assert_1.strict.equal(segment.properties?.secondSource, "local2");
                });
            });
            describe("remote first", () => {
                beforeEach(() => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteProperty: 1,
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    (0, assert_1.strict)(segmentInfo.segment?.segmentGroups.empty);
                });
                it("remote only", () => {
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.propertySource, "remote");
                    assert_1.strict.equal(segment.properties?.remoteProperty, 1);
                });
                it("split remote", () => {
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    const splitSegment = segment.splitAt(1);
                    assert_1.strict.equal(splitSegment.properties?.propertySource, "remote");
                    assert_1.strict.equal(splitSegment.properties?.remoteProperty, 1);
                });
                it("remote before unsequenced local", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "local",
                    }, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.remoteProperty, 1);
                });
                it("remote before sequenced local", () => {
                    const props = {
                        propertySource: "local",
                    };
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    (0, assert_1.strict)(segmentInfo.segment?.segmentGroups.empty);
                    mergeTree.annotateRange(annotateStart, annotateEnd, props, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    assert_1.strict.equal(segmentInfo.segment?.segmentGroups.size, 1);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    (0, assert_1.strict)(segmentInfo.segment?.segmentGroups.empty);
                    assert_1.strict.equal(segmentInfo.segment?.properties?.propertySource, "local");
                    assert_1.strict.equal(segmentInfo.segment?.properties?.remoteProperty, 1);
                });
            });
            describe("local with rewrite first", () => {
                const props = {
                    propertySource: "local",
                };
                beforeEach(() => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, props, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                });
                it("unsequenced local after unsequenced local", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "local2",
                        secondProperty: "local",
                    }, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.propertySource, "local2");
                    assert_1.strict.equal(segment.properties?.secondProperty, "local");
                });
                it("unsequenced local before remote", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteProperty: 1,
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.segmentGroups.size, 1);
                    assert_1.strict.equal(segment.properties?.propertySource, "local");
                    assert_1.strict.equal(segment.properties?.remoteProperty, 1);
                });
                it("sequenced local before remote", () => {
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteProperty: 1,
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.segmentGroups.size, 0);
                    assert_1.strict.equal(segment.properties?.propertySource, "remote");
                    assert_1.strict.equal(segment.properties?.remoteProperty, 1);
                });
                it("two local changes with interleaved remote", () => {
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        secondSource: "local2",
                    }, currentSequenceNumber, localClientId, constants_1.UnassignedSequenceNumber, undefined);
                    mergeTree.ackPendingSegment({
                        op: {
                            pos1: annotateStart,
                            pos2: annotateEnd,
                            props,
                            type: ops_1.MergeTreeDeltaType.ANNOTATE,
                        },
                        sequencedMessage: {
                            sequenceNumber: ++currentSequenceNumber,
                        },
                    });
                    mergeTree.annotateRange(annotateStart, annotateEnd, {
                        propertySource: "remote",
                        remoteOnly: 1,
                        secondSource: "remote",
                    }, currentSequenceNumber, remoteClientId, ++currentSequenceNumber, undefined);
                    const segmentInfo = mergeTree.getContainingSegment(annotateStart, currentSequenceNumber, localClientId);
                    const segment = segmentInfo.segment;
                    assert_1.strict.equal(segment.properties?.remoteOnly, 1);
                    assert_1.strict.equal(segment.properties?.propertySource, "remote");
                    assert_1.strict.equal(segment.properties?.secondSource, "local2");
                });
            });
        });
    });
});
//# sourceMappingURL=mergeTree.annotate.spec.cjs.map