"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 client_utils_1 = require("@fluid-internal/client-utils");
const internal_1 = require("@fluidframework/telemetry-utils/internal");
const lz4js_1 = require("lz4js");
const index_js_1 = require("../../index.js");
const index_js_2 = require("../../opLifecycle/index.js");
function generateTestOpContents(valueIndex) {
    return {
        address: `testDataStoreId`,
        contents: { type: "op", content: `value${valueIndex}` },
    };
}
function generateCompressedBatchMessage(length) {
    const batch = [];
    for (let i = 0; i < length; i++) {
        // Actual Op and contents aren't important. Values are not realistic.
        batch.push({
            type: index_js_1.ContainerMessageType.FluidDataStoreOp,
            contents: generateTestOpContents(i),
        });
    }
    const contentsAsBuffer = new TextEncoder().encode(JSON.stringify(batch));
    const compressedContents = (0, lz4js_1.compress)(contentsAsBuffer);
    const compressedContent = client_utils_1.IsoBuffer.from(compressedContents).toString("base64");
    const messageBase = {
        contents: { packedContents: compressedContent },
        metadata: { meta: "data" },
        clientId: "clientId",
        sequenceNumber: 1,
        minimumSequenceNumber: 1,
        clientSequenceNumber: 1,
        referenceSequenceNumber: 1,
        type: "type",
        timestamp: 1,
        compression: "lz4",
    };
    // Single compressed message won't have batch metadata
    if (length === 1) {
        return messageBase;
    }
    return {
        ...messageBase,
        // TODO: It's not clear if this shallow clone is required, as opposed to just setting "batch" to false.
        metadata: { ...messageBase.metadata, batch: true },
    };
}
const emptyMessage = {
    contents: undefined,
    clientId: "clientId",
    sequenceNumber: 1,
    minimumSequenceNumber: 1,
    clientSequenceNumber: 1,
    referenceSequenceNumber: 1,
    type: "type",
    timestamp: 1,
};
const endBatchEmptyMessage = {
    contents: {},
    metadata: { batch: false },
    clientId: "clientId",
    sequenceNumber: 1,
    minimumSequenceNumber: 1,
    clientSequenceNumber: 1,
    referenceSequenceNumber: 1,
    type: "type",
    timestamp: 1,
};
describe("OpDecompressor", () => {
    const mockLogger = new internal_1.MockLogger();
    let decompressor;
    beforeEach(() => {
        mockLogger.clear();
        decompressor = new index_js_2.OpDecompressor(mockLogger);
    });
    it("Processes single compressed op", () => {
        let message = generateCompressedBatchMessage(1);
        decompressor.decompressAndStore(message);
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        message = decompressor.unroll(message);
        node_assert_1.strict.deepEqual(message.contents.contents, generateTestOpContents(0));
        node_assert_1.strict.strictEqual(message.metadata?.compressed, undefined);
        node_assert_1.strict.strictEqual(message.compression, undefined);
    });
    // Back-compat self healing mechanism for ADO:3538
    it("Processes single compressed op without compression markers", () => {
        let message = {
            ...generateCompressedBatchMessage(1),
            compression: undefined,
        };
        decompressor.decompressAndStore(message);
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        message = decompressor.unroll(message);
        node_assert_1.strict.deepEqual(message.contents.contents, generateTestOpContents(0));
        node_assert_1.strict.strictEqual(message.metadata?.compressed, undefined);
        node_assert_1.strict.strictEqual(message.compression, undefined);
        mockLogger.assertMatch([
            {
                eventName: "OpDecompressor:LegacyCompression",
                category: "generic",
            },
        ]);
    });
    it("Expecting only lz4 compression", () => {
        node_assert_1.strict.throws(() => decompressor.decompressAndStore({
            ...generateCompressedBatchMessage(5),
            compression: "gzip",
        }));
    });
    it("Processes multiple compressed ops", () => {
        const rootMessage = generateCompressedBatchMessage(5);
        decompressor.decompressAndStore(rootMessage);
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const firstMessage = decompressor.unroll(rootMessage);
        node_assert_1.strict.deepEqual(firstMessage.contents.contents, generateTestOpContents(0));
        node_assert_1.strict.strictEqual(firstMessage.metadata?.compressed, undefined);
        node_assert_1.strict.strictEqual(firstMessage.compression, undefined);
        for (let i = 1; i < 4; i++) {
            node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
            const message = decompressor.unroll(emptyMessage);
            node_assert_1.strict.deepEqual(message.contents.contents, generateTestOpContents(i));
            node_assert_1.strict.strictEqual(message.metadata?.compressed, undefined);
            node_assert_1.strict.strictEqual(message.compression, undefined);
        }
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const lastMessage = decompressor.unroll(endBatchEmptyMessage);
        node_assert_1.strict.deepEqual(lastMessage.contents.contents, generateTestOpContents(4));
        node_assert_1.strict.strictEqual(lastMessage.metadata?.compressed, undefined);
        node_assert_1.strict.strictEqual(lastMessage.compression, undefined);
    });
    it("Expecting empty messages in the middle of the compressed batch", () => {
        const rootMessage = generateCompressedBatchMessage(5);
        decompressor.decompressAndStore(rootMessage);
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const firstMessage = decompressor.unroll(rootMessage);
        node_assert_1.strict.deepEqual(firstMessage.contents.contents, generateTestOpContents(0));
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        node_assert_1.strict.throws(() => decompressor.unroll({ ...emptyMessage, contents: {} }));
    });
    it("Processes multiple batches of compressed ops", () => {
        const rootMessage = generateCompressedBatchMessage(5);
        decompressor.decompressAndStore(rootMessage);
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const firstMessage = decompressor.unroll(rootMessage);
        node_assert_1.strict.deepEqual(firstMessage.contents.contents, generateTestOpContents(0));
        for (let i = 1; i < 4; i++) {
            node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
            const message = decompressor.unroll(emptyMessage);
            node_assert_1.strict.deepEqual(message.contents.contents, generateTestOpContents(i));
        }
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const lastMessage = decompressor.unroll(endBatchEmptyMessage);
        node_assert_1.strict.deepEqual(lastMessage.contents.contents, generateTestOpContents(4));
        const nextRootMessage = generateCompressedBatchMessage(3);
        decompressor.decompressAndStore(nextRootMessage);
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const nextFirstMessage = decompressor.unroll(nextRootMessage);
        node_assert_1.strict.deepEqual(nextFirstMessage.contents.contents, generateTestOpContents(0));
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const middleMessage = decompressor.unroll(emptyMessage);
        node_assert_1.strict.deepEqual(middleMessage.contents.contents, generateTestOpContents(1));
        node_assert_1.strict.equal(decompressor.currentlyUnrolling, true);
        const endBatchMessage = decompressor.unroll(endBatchEmptyMessage);
        node_assert_1.strict.deepEqual(endBatchMessage.contents.contents, generateTestOpContents(2));
    });
    it("Ignores ops without compression", () => {
        const rootMessages = [
            {
                // Back-compat self healing mechanism for ADO:3538,
                // the message should have a `packedContents` property.
                contents: { some: "contents" },
                metadata: { meta: "data" },
                clientId: "clientId",
                sequenceNumber: 1,
                term: 1,
                minimumSequenceNumber: 1,
                clientSequenceNumber: 1,
                referenceSequenceNumber: 1,
                type: "type",
                timestamp: 1,
            },
            {
                // Back-compat self healing mechanism for ADO:3538,
                contents: { packedContents: "packedContents is not base64 encoded" },
                metadata: { meta: "data" },
                clientId: "clientId",
                sequenceNumber: 1,
                term: 1,
                minimumSequenceNumber: 1,
                clientSequenceNumber: 1,
                referenceSequenceNumber: 1,
                type: "type",
                timestamp: 1,
            },
            {
                // Back-compat self healing mechanism for ADO:3538,
                contents: { packedContents: "YmFzZTY0IGNvbnRlbnQ=", some: "contents" },
                metadata: { meta: "data" },
                clientId: "clientId",
                sequenceNumber: 1,
                term: 1,
                minimumSequenceNumber: 1,
                clientSequenceNumber: 1,
                referenceSequenceNumber: 1,
                type: "type",
                timestamp: 1,
            },
            {
                metadata: { meta: "data" },
                clientId: "clientId",
                sequenceNumber: 1,
                term: 1,
                minimumSequenceNumber: 1,
                clientSequenceNumber: 1,
                referenceSequenceNumber: 1,
                type: "type",
                timestamp: 1,
            },
        ];
        for (const rootMessage of rootMessages) {
            node_assert_1.strict.equal(decompressor.isCompressedMessage(rootMessage), false);
        }
    });
});
//# sourceMappingURL=opDecompressor.spec.js.map