"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 driver_definitions_1 = require("@fluidframework/driver-definitions");
const internal_1 = require("@fluidframework/runtime-definitions/internal");
const internal_2 = require("@fluidframework/runtime-utils/internal");
const internal_3 = require("@fluidframework/telemetry-utils/internal");
const index_js_1 = require("../summary/index.js");
describe("Runtime", () => {
    describe("Summarization", () => {
        describe("Summarizer Node", () => {
            const names = ["root", "mid", "leaf"];
            const ids = ["rootId", "midId", "leafId"];
            let rootNode;
            let midNode;
            let leafNode;
            const logger = (0, internal_3.createChildLogger)();
            let summarizeCalls = [0, 0, 0];
            function assertSummarizeCalls(...expected) {
                for (let i = 0; i < expected.length; i++) {
                    (0, node_assert_1.strict)(expected[i] === summarizeCalls[i], `unexpected ${names[i]} summarize call count: ${expected[i]} !== ${summarizeCalls[i]}`);
                }
            }
            const getSummarizeInternalFn = (depth) => async (fullTree) => {
                summarizeCalls[depth]++;
                return {
                    id: ids[depth],
                    pathPartsForChildren: undefined, // extra path parts between nodes
                    stats: (0, internal_2.mergeStats)(),
                    summary: { type: driver_definitions_1.SummaryType.Tree, tree: {} },
                };
            };
            function createRoot({ changeSeq = 1, refSeq, ...config } = {}) {
                rootNode = (0, index_js_1.createRootSummarizerNode)(logger, getSummarizeInternalFn(0), changeSeq, refSeq, config);
            }
            function createMid(createParam, config) {
                midNode = rootNode.createChild(getSummarizeInternalFn(1), ids[1], createParam, config);
            }
            function createLeaf(createParam, config) {
                leafNode = midNode?.createChild(getSummarizeInternalFn(2), ids[2], createParam, config);
            }
            function expectThrow(fn, failMsg, errMsg, ...expectedErrors) {
                try {
                    fn();
                    throw new Error(`${failMsg}: Expected to fail`);
                }
                catch (error) {
                    (0, node_assert_1.strict)(expectedErrors.includes(error.message), errMsg);
                }
            }
            async function expectReject(fn, failMsg, errMsg, ...expectedErrors) {
                try {
                    await fn();
                    throw new Error(`${failMsg}: Expected to reject`);
                }
                catch (error) {
                    (0, node_assert_1.strict)(expectedErrors.includes(error.message), errMsg);
                }
            }
            const summaryRefSeq = 123;
            const fakeOp = (sequenceNumber) => 
            // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
            ({ sequenceNumber });
            beforeEach(() => {
                summarizeCalls = [0, 0, 0];
            });
            describe("Create Child", () => {
                it("Should fail to create child with same id", () => {
                    createRoot();
                    createMid({ type: internal_1.CreateSummarizerNodeSource.Local });
                    expectThrow(() => createMid({ type: internal_1.CreateSummarizerNodeSource.Local }), "create child", "child node with same id already exists", "0x1ab");
                });
            });
            describe("Start Summary", () => {
                it("Should fail startSummary if previous attempt is not completed/cleared", () => {
                    createRoot();
                    rootNode.startSummary(11, logger, 0);
                    expectThrow(() => rootNode.startSummary(12, logger, 0), "start summary", "wip referenceSequenceNumber and logger are still set", "0x19f", "0x1a0");
                });
                it("Should succeed startSummary if previous attempt is completed", async () => {
                    createRoot();
                    rootNode.startSummary(11, logger, 0);
                    await rootNode.summarize(false);
                    rootNode.completeSummary("test-handle");
                    rootNode.startSummary(12, logger, 0); // This is 0 since we did not "ack" the latest summary
                });
                it("Should succeed startSummary if previous attempt is cleared", () => {
                    createRoot();
                    rootNode.startSummary(11, logger, 0);
                    rootNode.clearSummary();
                    rootNode.startSummary(12, logger, 0); // This is 0 since we did not "ack" the latest summary
                });
                it("Should succeed startSummary with the right sequence number", async () => {
                    createRoot();
                    rootNode.startSummary(11, logger, 0);
                    await rootNode.summarize(false);
                    rootNode.completeSummary("test-handle");
                    // Refreshing should be necessary for startSummary to occur
                    await rootNode.refreshLatestSummary("test-handle", 11);
                    const result = rootNode.startSummary(12, logger, 11);
                    node_assert_1.strict.strictEqual(result.invalidNodes, 0, "startSummary have succeeded");
                });
                it("Should fail startSummary when missing refresh", async () => {
                    createRoot();
                    // Need one latest summary
                    rootNode.startSummary(11, logger, 0);
                    await rootNode.summarize(false);
                    rootNode.completeSummary("test-handle");
                    await rootNode.refreshLatestSummary("test-handle", 11);
                    // Summary with missing refresh
                    rootNode.startSummary(12, logger, 11);
                    await rootNode.summarize(false);
                    rootNode.completeSummary("test-handle");
                    // Failing to refresh the root node should generate failing summaries
                    const result = rootNode.startSummary(21, logger, 12);
                    node_assert_1.strict.strictEqual(result.invalidNodes, 1, "startSummary fails due to no refresh");
                    node_assert_1.strict.deepEqual(result.mismatchNumbers, new Set(["12-11"]), "startSummary should have mismatched numbers");
                });
                it("Should fail startSummary with the wrong sequence number", async () => {
                    createRoot();
                    rootNode.startSummary(11, logger, 0);
                    await rootNode.summarize(false);
                    rootNode.completeSummary("test-handle");
                    await rootNode.refreshLatestSummary("test-handle", 11);
                    const result = rootNode.startSummary(12, logger, 0); // 0 is wrong here (so we can get invalid results)
                    node_assert_1.strict.strictEqual(result.invalidNodes, 1, "expected failure wrong ref seq");
                    node_assert_1.strict.deepEqual(result.mismatchNumbers, new Set(["0-11"]), "startSummary have succeeded");
                });
            });
            describe("Validate Summary", () => {
                it("summary validation should fail if summarize not called on root node", () => {
                    createRoot();
                    rootNode.startSummary(11, logger, 0);
                    // Validate summary fails by calling validateSummary.
                    const expectedResult = {
                        success: false,
                        reason: "NodeDidNotSummarize",
                        id: {
                            tag: internal_3.TelemetryDataTag.CodeArtifact,
                            value: "",
                        },
                        retryAfterSeconds: 1,
                    };
                    const result = rootNode.validateSummary();
                    node_assert_1.strict.deepStrictEqual(result, expectedResult, "validate summary should have failed at the root node");
                });
                it("summary validation should fail if summarize not called on child node", async () => {
                    createRoot();
                    createMid({ type: internal_1.CreateSummarizerNodeSource.Local });
                    createLeaf({ type: internal_1.CreateSummarizerNodeSource.Local });
                    rootNode.startSummary(11, logger, 0);
                    await rootNode.summarize(false);
                    await leafNode?.summarize(false);
                    const midNodeId = `/${ids[1]}`;
                    // Validate summary fails by calling validateSummary.
                    const expectedResult = {
                        success: false,
                        reason: "NodeDidNotSummarize",
                        id: {
                            tag: internal_3.TelemetryDataTag.CodeArtifact,
                            value: midNodeId,
                        },
                        retryAfterSeconds: 1,
                    };
                    const result = rootNode.validateSummary();
                    node_assert_1.strict.deepStrictEqual(result, expectedResult, "validate summary should have failed at the mid node");
                });
                it("summary validation should fail if summarize not called on leaf node", async () => {
                    createRoot();
                    createMid({ type: internal_1.CreateSummarizerNodeSource.Local });
                    createLeaf({ type: internal_1.CreateSummarizerNodeSource.Local });
                    rootNode.startSummary(11, logger, 0);
                    await rootNode.summarize(false);
                    await midNode?.summarize(false);
                    const leafNodeId = `/${ids[1]}/${ids[2]}`;
                    // Validate summary fails by calling validateSummary.
                    const expectedResult = {
                        success: false,
                        reason: "NodeDidNotSummarize",
                        id: {
                            tag: internal_3.TelemetryDataTag.CodeArtifact,
                            value: leafNodeId,
                        },
                        retryAfterSeconds: 1,
                    };
                    const result = rootNode.validateSummary();
                    node_assert_1.strict.deepStrictEqual(result, expectedResult, "validate summary should have failed at the leaf node");
                });
            });
            describe("Summarize", () => {
                it("Should fail completeSummary if startSummary is not called", async () => {
                    createRoot();
                    await expectReject(async () => rootNode.completeSummary("handle"), "summarize", "no wip referenceSequenceNumber or logger", "0x1a4");
                    assertSummarizeCalls(0, 0, 0);
                });
                it("Should fail validateSummary if startSummary is not called", async () => {
                    createRoot();
                    await expectReject(async () => rootNode.validateSummary(), "summarize", "no wip referenceSequenceNumber or logger", "0x6fc", "0x6fd");
                    assertSummarizeCalls(0, 0, 0);
                });
                it("Should call summarize internal with later op", async () => {
                    createRoot({ refSeq: 11 });
                    rootNode.recordChange(fakeOp(12));
                    rootNode.startSummary(99, logger, 11);
                    const result = await rootNode.summarize(false);
                    assertSummarizeCalls(1, 0, 0);
                    (0, node_assert_1.strict)(result.summary.type === driver_definitions_1.SummaryType.Tree, "should be tree");
                });
                it("Should call summarize internal with later invalidate", async () => {
                    createRoot({ refSeq: 11 });
                    rootNode.invalidate(12);
                    rootNode.startSummary(99, logger, 11);
                    const result = await rootNode.summarize(false);
                    assertSummarizeCalls(1, 0, 0);
                    (0, node_assert_1.strict)(result.summary.type === driver_definitions_1.SummaryType.Tree, "should be tree");
                });
                it("Should not call summarize internal and instead use handle", async () => {
                    createRoot({ refSeq: 11 });
                    rootNode.recordChange(fakeOp(11));
                    rootNode.startSummary(99, logger, 11);
                    const result = await rootNode.summarize(false);
                    assertSummarizeCalls(0, 0, 0);
                    (0, node_assert_1.strict)(result.summary.type === driver_definitions_1.SummaryType.Handle, "should be handle");
                });
                it("Should call summarize internal always when fullTree true", async () => {
                    createRoot({ refSeq: 11 });
                    rootNode.recordChange(fakeOp(10));
                    rootNode.startSummary(99, logger, 11);
                    const result = await rootNode.summarize(true);
                    assertSummarizeCalls(1, 0, 0);
                    (0, node_assert_1.strict)(result.summary.type === driver_definitions_1.SummaryType.Tree, "should be tree");
                });
            });
            describe("Refresh Latest Summary", () => {
                it("Should not refresh latest if already passed ref seq number", async () => {
                    createRoot({ refSeq: summaryRefSeq });
                    const result = await rootNode.refreshLatestSummary("test-handle", summaryRefSeq);
                    (0, node_assert_1.strict)(!result.isSummaryTracked, "we already got this summary");
                });
                it("Should refresh from pending", async () => {
                    createRoot();
                    const proposalHandle = "test-handle";
                    rootNode.startSummary(10, logger, 0);
                    await rootNode.summarize(false);
                    rootNode.completeSummary(proposalHandle);
                    const result = await rootNode.refreshLatestSummary(proposalHandle, summaryRefSeq);
                    (0, node_assert_1.strict)(result.isSummaryTracked, "should be tracked");
                    (0, node_assert_1.strict)(result.isSummaryNewer === true, "should be newer");
                });
                it("should fail refresh when summary is in progress", async () => {
                    createRoot();
                    const proposalHandle = "test-handle";
                    const referenceSeqNum = 10;
                    rootNode.startSummary(referenceSeqNum, logger, 0);
                    await rootNode.summarize(false);
                    await node_assert_1.strict.rejects(async () => rootNode.refreshLatestSummary(proposalHandle, summaryRefSeq), (error) => {
                        const correctErrorMessage = error.message === "UnexpectedRefreshDuringSummarize";
                        const correctInProgressRefSeq = error.inProgressSummaryRefSeq === referenceSeqNum;
                        return correctErrorMessage && correctInProgressRefSeq;
                    }, "Refresh should fail if called when summary is in progress");
                });
            });
        });
    });
});
//# sourceMappingURL=summarizerNode.spec.js.map