"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");
const ops_js_1 = require("../ops.js");
const textSegment_js_1 = require("../textSegment.js");
const testClient_js_1 = require("./testClient.js");
const testUtils_js_1 = require("./testUtils.js");
describe("obliterate", () => {
    let client;
    let refSeq;
    const remoteClient1 = (0, testUtils_js_1.makeRemoteClient)({ clientId: 18 });
    const remoteClient2 = (0, testUtils_js_1.makeRemoteClient)({ clientId: 19 });
    beforeEach(() => {
        client = new testClient_js_1.TestClient({
            mergeTreeEnableObliterate: true,
        });
        client.startOrUpdateCollaboration("local");
        for (const char of "hello world") {
            client.applyMsg(client.makeOpMessage(client.insertTextLocal(client.getLength(), char), client.getCurrentSeq() + 1));
        }
        node_assert_1.strict.equal(client.getText(), "hello world");
        refSeq = client.getCurrentSeq();
    });
    it("removes text", () => {
        client.obliterateRangeLocal(0, client.getLength());
        node_assert_1.strict.equal(client.getText(), "");
    });
    describe("concurrent obliterate and insert", () => {
        it("removes text for obliterate then insert", () => {
            client.mergeTree.obliterateRange(0, client.getLength(), remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: refSeq + 1 }), undefined);
            client.mergeTree.insertSegments(1, [textSegment_js_1.TextSegment.make("more ")], remoteClient2.perspectiveAt({ refSeq }), remoteClient2.stampAt({ seq: refSeq + 2 }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            node_assert_1.strict.equal(client.getText(), "");
        });
        it("removes text for insert then obliterate when deleting entire string", () => {
            client.mergeTree.insertSegments(1, [textSegment_js_1.TextSegment.make("more ")], remoteClient2.perspectiveAt({ refSeq }), remoteClient2.stampAt({ seq: refSeq + 1 }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            client.mergeTree.obliterateRange(0, "hello world".length, remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: refSeq + 2 }), undefined);
            node_assert_1.strict.equal(client.getText(), "");
        });
        it("removes text for insert then obliterate", () => {
            client.mergeTree.insertSegments(5, [textSegment_js_1.TextSegment.make("more ")], remoteClient2.perspectiveAt({ refSeq }), remoteClient2.stampAt({ seq: refSeq + 1 }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            client.mergeTree.obliterateRange(1, "hello world".length, remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: refSeq + 2 }), undefined);
            node_assert_1.strict.equal(client.getText(), "h");
        });
    });
    describe("endpoint behavior", () => {
        it("does not expand to include text inserted at start", () => {
            client.mergeTree.obliterateRange(5, client.getLength(), remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: refSeq + 1 }), undefined);
            client.mergeTree.insertSegments(5, [textSegment_js_1.TextSegment.make("XXX")], remoteClient2.perspectiveAt({ refSeq }), remoteClient2.stampAt({ seq: refSeq + 2 }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            node_assert_1.strict.equal(client.getText(), "helloXXX");
        });
        it("does not expand to include text inserted at end", () => {
            client.mergeTree.obliterateRange(0, "hello".length, remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: refSeq + 1 }), undefined);
            client.mergeTree.insertSegments(5, [textSegment_js_1.TextSegment.make("XXX")], remoteClient2.perspectiveAt({ refSeq }), remoteClient2.stampAt({ seq: refSeq + 2 }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            node_assert_1.strict.equal(client.getText(), "XXX world");
        });
    });
    describe("local obliterate with concurrent inserts", () => {
        it("removes range when pending local obliterate op", () => {
            client.obliterateRangeLocal(0, client.getLength());
            client.mergeTree.insertSegments(1, [textSegment_js_1.TextSegment.make("XXX")], remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: refSeq + 1 }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            node_assert_1.strict.equal(client.getText(), "");
        });
    });
    describe("local references", () => {
        it("cleans up local references once the collab window advances enough", () => {
            const client2 = new testClient_js_1.TestClient({ mergeTreeEnableObliterate: true });
            client2.startOrUpdateCollaboration("client2");
            const obliterateStart = 0;
            const obliterateEnd = client.getLength();
            const startSeg = client.getContainingSegment(obliterateStart);
            const endSeg = client.getContainingSegment(obliterateEnd);
            let seq = refSeq;
            client.mergeTree.obliterateRange(obliterateStart, obliterateEnd, remoteClient1.perspectiveAt({ refSeq }), remoteClient1.stampAt({ seq: ++seq }), undefined);
            client.mergeTree.insertSegments(1, [textSegment_js_1.TextSegment.make("more ")], remoteClient2.perspectiveAt({ refSeq }), remoteClient2.stampAt({ seq: ++seq }), { op: { type: ops_js_1.MergeTreeDeltaType.INSERT } });
            node_assert_1.strict.equal(client.getText(), "");
            startSeg?.segment.localRefs?.walkReferences((ref) => {
                const oblProps = ref.properties?.obliterate;
                (0, node_assert_1.strict)(oblProps?.start !== undefined, "start ref should NOT be removed");
            });
            endSeg?.segment.localRefs?.walkReferences((ref) => {
                const oblProps = ref.properties?.obliterate;
                (0, node_assert_1.strict)(oblProps?.end !== undefined, "end ref should NOT be removed");
            });
            // this will force Zamboni to run
            for (let i = 0; i < 5; i++) {
                const insert = client.makeOpMessage(client.insertTextLocal(client.getLength(), i.toString()), ++seq);
                insert.minimumSequenceNumber = seq - 1;
                client.applyMsg(insert);
                client2.applyMsg(insert);
            }
            // want to check that the start and end segment don't have the obliterate refs on them
            startSeg?.segment.localRefs?.walkReferences((ref) => {
                const oblProps = ref.properties?.obliterate;
                (0, node_assert_1.strict)(oblProps.start === undefined, "start ref should be removed");
            });
            endSeg?.segment.localRefs?.walkReferences((ref) => {
                const oblProps = ref.properties?.obliterate;
                (0, node_assert_1.strict)(oblProps.end === undefined, "end ref should be removed");
            });
        });
    });
});
//# sourceMappingURL=obliterate.spec.js.map