/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { strict as assert } from "node:assert";
import { MockLogger, createChildLogger, mixinMonitoringContext, } from "@fluidframework/telemetry-utils/internal";
import { useFakeTimers } from "sinon";
import { GCNodeType, GarbageCollector, defaultInactiveTimeoutMs, defaultSessionExpiryDurationMs, defaultSweepGracePeriodMs, gcGenerationOptionName, gcTestModeKey, gcVersionUpgradeToV4Key, nextGCVersion, oneDayMs, runSessionExpiryKey, stableGCVersion, disableThrowOnTombstoneLoadKey, } from "../../gc/index.js";
import { pkgVersion } from "../../packageVersion.js";
import { createTestConfigProvider } from "./gcUnitTestHelpers.js";
describe("Garbage Collection configurations", () => {
    const testPkgPath = ["testPkg"];
    const configProvider = createTestConfigProvider();
    let mockLogger;
    let mc;
    let clock;
    // The default GC data returned by `getGCData` on which GC is run. Update this to update the referenced graph.
    let defaultGCData = { gcNodes: {} };
    const customSessionExpiryDurationMs = defaultSessionExpiryDurationMs + 1;
    const testOverrideInactiveTimeoutKey = "Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs";
    const testOverrideTombstoneTimeoutKey = "Fluid.GarbageCollection.TestOverride.TombstoneTimeoutMs";
    const testOverrideSessionExpiryMsKey = "Fluid.GarbageCollection.TestOverride.SessionExpiryMs";
    let gc;
    const createGcWithPrivateMembers = (gcMetadata, gcOptions, isSummarizerClient) => {
        const metadata = gcMetadata && {
            summaryFormatVersion: 1,
            message: undefined,
            ...gcMetadata,
        };
        return createGarbageCollector({ metadata, gcOptions }, undefined /* gcBlobsMap */, undefined /* closeFn */, isSummarizerClient);
    };
    function createGarbageCollector(createParams = {}, gcBlobsMap = new Map(), closeFn = () => { }, isSummarizerClient = true) {
        const getNodeType = (nodePath) => {
            if (nodePath.split("/").length !== 2) {
                return GCNodeType.Other;
            }
            return GCNodeType.DataStore;
        };
        // The runtime to be passed to the garbage collector.
        const gcRuntime = {
            getGCData: async (fullGC) => defaultGCData,
            updateUsedRoutes: (usedRoutes) => {
                return { totalNodeCount: 0, unusedNodeCount: 0 };
            },
            deleteSweepReadyNodes: (sweepReadyRoutes) => {
                return [];
            },
            updateTombstonedRoutes: (tombstoneRoutes) => { },
            getNodeType,
            getCurrentReferenceTimestampMs: () => Date.now(),
            closeFn,
        };
        return GarbageCollector.create({
            ...createParams,
            runtime: gcRuntime,
            gcOptions: createParams.gcOptions ?? {},
            baseSnapshot: createParams.baseSnapshot,
            baseLogger: createChildLogger({ logger: mc.logger }),
            existing: createParams.metadata !== undefined /* existing */,
            metadata: createParams.metadata,
            createContainerMetadata: {
                createContainerRuntimeVersion: pkgVersion,
                createContainerTimestamp: Date.now(),
            },
            isSummarizerClient,
            readAndParseBlob: async (id) => gcBlobsMap.get(id),
            getNodePackagePath: async (nodeId) => testPkgPath,
            getLastSummaryTimestampMs: () => Date.now(),
            submitMessage: (message) => { },
        });
    }
    before(() => {
        clock = useFakeTimers();
    });
    beforeEach(() => {
        gc = undefined;
        mockLogger = new MockLogger();
        // To ensure inactive timeout is less than tombstone timeout.
        configProvider.set(testOverrideInactiveTimeoutKey, 1);
        mc = mixinMonitoringContext(mockLogger, configProvider);
    });
    afterEach(() => {
        clock.reset();
        configProvider.clear();
        defaultGCData = { gcNodes: {} };
        gc?.dispose();
    });
    after(() => {
        clock.restore();
    });
    describe("Existing container", () => {
        it("No metadata", () => {
            gc = createGcWithPrivateMembers({});
            assert(!gc.configs.gcAllowed, "gcAllowed incorrect");
            assert.equal(gc.configs.sweepEnabled, false, "sweepEnabled incorrect");
            assert(gc.configs.sessionExpiryTimeoutMs === undefined, "sessionExpiryTimeoutMs incorrect");
            assert(gc.configs.tombstoneTimeoutMs === undefined, "tombstoneTimeoutMs incorrect");
            assert.equal(gc.configs.gcVersionInBaseSnapshot, 0, "gcVersionInBaseSnapshot incorrect");
        });
        it("gcFeature 0", () => {
            gc = createGcWithPrivateMembers({ gcFeature: 0 });
            assert(!gc.configs.gcAllowed, "gcAllowed incorrect");
            assert.equal(gc.configs.gcVersionInBaseSnapshot, 0, "gcVersionInBaseSnapshot incorrect");
        });
        it("gcFeature 0, Sweep allowed but disabled", () => {
            gc = createGcWithPrivateMembers({ gcFeature: 0, gcFeatureMatrix: { gcGeneration: 0 } }, { [gcGenerationOptionName]: 0, enableGCSweep: true });
            assert(!gc.configs.gcAllowed, "gcAllowed incorrect");
            assert(gc.configs.sweepAllowed, "sweepAllowed incorrect");
            assert(!gc.configs.sweepEnabled, "sweepEnabled incorrect");
            assert.equal(gc.configs.gcVersionInBaseSnapshot, 0, "gcVersionInBaseSnapshot incorrect");
        });
        it("gcFeature 1", () => {
            gc = createGcWithPrivateMembers({ gcFeature: 1 });
            assert(gc.configs.gcAllowed, "gcAllowed incorrect");
            assert.equal(gc.configs.gcVersionInBaseSnapshot, 1, "gcVersionInBaseSnapshot incorrect");
        });
        it("sweepEnabled value ignored", () => {
            gc = createGcWithPrivateMembers({ sweepEnabled: true }, { [gcGenerationOptionName]: 1 });
            assert(!gc.configs.sweepEnabled, "sweepEnabled incorrect");
        });
        it("sessionExpiryTimeoutMs set (tombstoneTimeoutMs unset)", () => {
            gc = createGcWithPrivateMembers({
                sessionExpiryTimeoutMs: customSessionExpiryDurationMs,
            });
            assert.equal(gc.configs.sessionExpiryTimeoutMs, customSessionExpiryDurationMs, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, customSessionExpiryDurationMs + 6 * oneDayMs, "tombstoneTimeoutMs incorrect");
        });
        it("tombstoneTimeoutMs set", () => {
            gc = createGcWithPrivateMembers({ tombstoneTimeoutMs: 123 });
            assert.equal(gc.configs.tombstoneTimeoutMs, 123, "tombstoneTimeoutMs incorrect");
        });
        it("Metadata Roundtrip", () => {
            const inputMetadata = {
                sweepEnabled: true, // ignored
                gcFeature: 1,
                sessionExpiryTimeoutMs: customSessionExpiryDurationMs,
                tombstoneTimeoutMs: 123,
                gcFeatureMatrix: { gcGeneration: 1 },
            };
            gc = createGcWithPrivateMembers(inputMetadata, {
                [gcGenerationOptionName]: 2, // 2 should not replace already-persisted value of 1
            });
            const outputMetadata = gc.getMetadata();
            const expectedOutputMetadata = {
                ...inputMetadata,
                sweepEnabled: false, // Hardcoded, not used
                gcFeature: stableGCVersion,
            };
            assert.deepEqual(outputMetadata, expectedOutputMetadata, "getMetadata returned different metadata than loaded from");
        });
        it("Metadata Roundtrip - old file with tombstoneGeneration", () => {
            const inputMetadata = {
                sweepEnabled: true, // ignored
                gcFeature: 1,
                sessionExpiryTimeoutMs: customSessionExpiryDurationMs,
                tombstoneTimeoutMs: 123,
                gcFeatureMatrix: { tombstoneGeneration: 1 }, // legacy file before gcGeneration
            };
            gc = createGcWithPrivateMembers(inputMetadata, {
                [gcGenerationOptionName]: 2, // Will not be persisted - legacy file will only ever be stamped with tombstoneGeneration
            });
            const outputMetadata = gc.getMetadata();
            const expectedOutputMetadata = {
                ...inputMetadata,
                sweepEnabled: false, // Hardcoded, not used
                gcFeature: stableGCVersion,
            };
            assert.deepEqual(outputMetadata, expectedOutputMetadata, "getMetadata returned different metadata than loaded from");
        });
        it("Metadata Roundtrip transition to gcGeneration from tombstoneGeneration", () => {
            const inputMetadata = {
                sweepEnabled: true, // ignored
                gcFeature: 1,
                sessionExpiryTimeoutMs: customSessionExpiryDurationMs,
                tombstoneTimeoutMs: 123,
                gcFeatureMatrix: { gcGeneration: 1 },
            };
            // An app may write both Generation options to ease the transition. Only gcGeneration will be persisted
            // (and previous to the change introducing gcGeneration, only tombstoneGeneration would have been persisted).
            gc = createGcWithPrivateMembers(inputMetadata, {
                [gcGenerationOptionName]: 1,
                gcTombstoneGeneration: 1, // Legacy - will not be persisted but is ok to pass in
            });
            const outputMetadata = gc.getMetadata();
            const expectedOutputMetadata = {
                ...inputMetadata,
                sweepEnabled: false, // Hardcoded, not used
                gcFeature: stableGCVersion,
            };
            assert.deepEqual(outputMetadata, expectedOutputMetadata, "getMetadata returned different metadata than loaded from");
        });
        it("Metadata Roundtrip with GC version upgrade to v4 enabled", () => {
            configProvider.set(gcVersionUpgradeToV4Key, true);
            const inputMetadata = {
                sweepEnabled: true, // ignored
                gcFeature: 1,
                sessionExpiryTimeoutMs: customSessionExpiryDurationMs,
                tombstoneTimeoutMs: 123,
                gcFeatureMatrix: { gcGeneration: 1 },
            };
            gc = createGcWithPrivateMembers(inputMetadata);
            const outputMetadata = gc.getMetadata();
            const expectedOutputMetadata = {
                ...inputMetadata,
                sweepEnabled: false, // Hardcoded, not used
                gcFeature: nextGCVersion,
            };
            assert.deepEqual(outputMetadata, expectedOutputMetadata, "getMetadata returned different metadata than loaded from");
        });
        it("Metadata Roundtrip with GC version upgrade to v4 disabled", () => {
            configProvider.set(gcVersionUpgradeToV4Key, false);
            const inputMetadata = {
                sweepEnabled: true, // ignored
                gcFeature: 1,
                sessionExpiryTimeoutMs: customSessionExpiryDurationMs,
                tombstoneTimeoutMs: 123,
                gcFeatureMatrix: { gcGeneration: 1 },
            };
            gc = createGcWithPrivateMembers(inputMetadata);
            const outputMetadata = gc.getMetadata();
            const expectedOutputMetadata = {
                ...inputMetadata,
                sweepEnabled: false, // Hardcoded, not used
                gcFeature: stableGCVersion,
            };
            assert.deepEqual(outputMetadata, expectedOutputMetadata, "getMetadata returned different metadata than loaded from");
        });
    });
    describe("New Container", () => {
        it("No options", () => {
            gc = createGcWithPrivateMembers(undefined /* metadata */, {});
            assert(gc.configs.gcAllowed, "gcAllowed incorrect");
            assert.equal(gc.configs.sweepEnabled, false, "sweepEnabled incorrect");
            assert(gc.configs.sessionExpiryTimeoutMs !== undefined, "sessionExpiryTimeoutMs incorrect");
            assert(gc.configs.tombstoneTimeoutMs !== undefined, "tombstoneTimeoutMs incorrect");
            assert.equal(gc.configs.gcVersionInEffect, stableGCVersion, "gcVersionInEffect incorrect");
        });
        it("Sweep allowed via gcGeneration", () => {
            gc = createGcWithPrivateMembers(undefined /* metadata */, {
                [gcGenerationOptionName]: 1,
            });
            assert(gc.configs.gcAllowed, "gcAllowed incorrect");
            assert(gc.configs.sweepAllowed, "sweepAllowed incorrect");
            assert(gc.configs.tombstoneTimeoutMs !== undefined, "tombstoneTimeoutMs incorrect");
            assert(gc.configs.sessionExpiryTimeoutMs !== undefined, "sessionExpiryTimeoutMs incorrect");
        });
        it("Sweep allowed but disabled with sessionExpiry off", () => {
            configProvider.set(runSessionExpiryKey, false);
            gc = createGcWithPrivateMembers(undefined /* metadata */, {
                [gcGenerationOptionName]: 1,
            });
            assert(gc.configs.gcAllowed, "gcAllowed incorrect");
            assert(gc.configs.sessionExpiryTimeoutMs === undefined, "sessionExpiryTimeoutMs incorrect");
            assert(gc.configs.tombstoneTimeoutMs === undefined, "tombstoneTimeoutMs incorrect");
            assert(gc.configs.sweepAllowed, "sweepAllowed incorrect");
            assert(!gc.configs.sweepEnabled, "sweepEnabled incorrect");
        });
        it("TestOverride.TombstoneTimeoutMs set, sessionExpiry on", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 123);
            gc = createGcWithPrivateMembers(undefined /* metadata */, {});
            assert(gc.configs.sessionExpiryTimeoutMs === defaultSessionExpiryDurationMs, "sessionExpiryTimeoutMs incorrect");
            assert(gc.configs.tombstoneTimeoutMs === 123, "tombstoneTimeoutMs incorrect");
            assert(gc.configs.sweepAllowed, "sweepAllowed incorrect");
            assert(!gc.configs.sweepEnabled, "sweepEnabled incorrect");
        });
        it("TestOverride.TombstoneTimeoutMs set, sessionExpiry off", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 123);
            configProvider.set(runSessionExpiryKey, false);
            gc = createGcWithPrivateMembers(undefined /* metadata */, {});
            assert(gc.configs.sessionExpiryTimeoutMs === undefined, "sessionExpiryTimeoutMs incorrect");
            assert(gc.configs.tombstoneTimeoutMs === 123, "tombstoneTimeoutMs incorrect");
            assert(gc.configs.sweepAllowed, "sweepAllowed incorrect");
            assert(!gc.configs.sweepEnabled, "sweepEnabled incorrect");
        });
        it("Metadata Roundtrip", () => {
            const expectedMetadata = {
                sweepEnabled: false, // hardcoded, not used
                gcFeature: stableGCVersion,
                sessionExpiryTimeoutMs: defaultSessionExpiryDurationMs,
                tombstoneTimeoutMs: defaultSessionExpiryDurationMs + 6 * oneDayMs,
                gcFeatureMatrix: { gcGeneration: 2 },
            };
            gc = createGcWithPrivateMembers(undefined /* metadata */, {
                [gcGenerationOptionName]: 2,
            });
            const outputMetadata = gc.getMetadata();
            assert.deepEqual(outputMetadata, expectedMetadata, "getMetadata returned different metadata than expected");
        });
        it("Metadata Roundtrip with GC version upgrade to v4 enabled", () => {
            configProvider.set(gcVersionUpgradeToV4Key, true);
            const expectedMetadata = {
                sweepEnabled: false, // hardcoded, not used
                gcFeature: nextGCVersion,
                sessionExpiryTimeoutMs: defaultSessionExpiryDurationMs,
                tombstoneTimeoutMs: defaultSessionExpiryDurationMs + 6 * oneDayMs,
                gcFeatureMatrix: undefined,
            };
            gc = createGcWithPrivateMembers(undefined /* metadata */, {});
            const outputMetadata = gc.getMetadata();
            assert.deepEqual(outputMetadata, expectedMetadata, "getMetadata returned different metadata than expected");
        });
        it("Metadata Roundtrip transition to gcGeneration from tombstoneGeneration", () => {
            const expectedMetadata = {
                sweepEnabled: false, // hardcoded, not used
                gcFeature: stableGCVersion,
                sessionExpiryTimeoutMs: defaultSessionExpiryDurationMs,
                tombstoneTimeoutMs: defaultSessionExpiryDurationMs + 6 * oneDayMs,
                gcFeatureMatrix: {
                    gcGeneration: 2,
                    // tombstoneGeneration will not be persisted
                },
            };
            // An app may write both Generation options to ease the transition. Only gcGeneration will be persisted
            // (and previous to the change introducing gcGeneration, only tombstoneGeneration would have been persisted).
            gc = createGcWithPrivateMembers(undefined /* metadata */, {
                [gcGenerationOptionName]: 2,
                gcTombstoneGeneration: 2, // Legacy - will not be persisted but is ok to pass in
            });
            const outputMetadata = gc.getMetadata();
            assert.deepEqual(outputMetadata, expectedMetadata, "getMetadata returned different metadata than expected");
        });
    });
    describe("Session Expiry and Tombstone Timeout", () => {
        beforeEach(() => {
            configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", 1); // To ensure it's less than tombstone timeout
        });
        // Config sources for Session Expiry:
        // 1. defaultSessionExpiryDurationMs in code
        // 2. IGCRuntimeOptions.sessionExpiryTimeoutMs
        // 3. IGCMetadata.sessionExpiryTimeoutMs
        // 4. "Fluid.GarbageCollection.TestOverride.SessionExpiryMs" setting
        // Config sources for Tombstone Timeout:
        // 1. IGCMetadata.tombstoneTimeoutMs
        // 2. IGCMetadata_Deprecated.sweepTimeoutMs (backfill from before two-stage sweep)
        // 3. Computed from Session Expiry, fixed upper bound for Snapshot Expiry and a fixed buffer (on create, or to backfill existing)
        // 4. "Fluid.GarbageCollection.TestOverride.TombstoneTimeoutMs" setting (only applicable on create)
        it("defaultSessionExpiryDurationMs", () => {
            gc = createGcWithPrivateMembers();
            assert.equal(gc.configs.sessionExpiryTimeoutMs, defaultSessionExpiryDurationMs, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer.defaultTimeout, defaultSessionExpiryDurationMs, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, defaultSessionExpiryDurationMs + 6 * oneDayMs, "tombstoneTimeoutMs incorrect");
        });
        it("defaultSessionExpiryDurationMs, TestOverride.TombstoneTimeoutMs set", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 7890);
            gc = createGcWithPrivateMembers();
            assert.equal(gc.configs.sessionExpiryTimeoutMs, defaultSessionExpiryDurationMs, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer.defaultTimeout, defaultSessionExpiryDurationMs, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, 7890, "tombstoneTimeoutMs incorrect");
        });
        it("IGCRuntimeOptions.sessionExpiryTimeoutMs", () => {
            gc = createGcWithPrivateMembers(undefined /* metadata */, {
                sessionExpiryTimeoutMs: 123,
            });
            assert.equal(gc.configs.sessionExpiryTimeoutMs, 123, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer.defaultTimeout, 123, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, 123 + 6 * oneDayMs, "tombstoneTimeoutMs incorrect");
        });
        it("IGCMetadata.sessionExpiryTimeoutMs, backfill tombstoneTimeoutMs", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 1337); // Should be ignored
            gc = createGcWithPrivateMembers({ sessionExpiryTimeoutMs: 456 } /* metadata */);
            assert.equal(gc.configs.sessionExpiryTimeoutMs, 456, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer.defaultTimeout, 456, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, 456 + 6 * oneDayMs, "tombstoneTimeoutMs incorrect");
        });
        it("IGCMetadata.sessionExpiryTimeoutMs and IGCMetadata.tombstoneTimeoutMs", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 1337); // Should be ignored
            gc = createGcWithPrivateMembers({ sessionExpiryTimeoutMs: 456, tombstoneTimeoutMs: 789 } /* metadata */);
            assert.equal(gc.configs.sessionExpiryTimeoutMs, 456, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer.defaultTimeout, 456, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, 789, "tombstoneTimeoutMs incorrect");
        });
        it("IGCMetadata.tombstoneTimeoutMs only", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 1337); // Should be ignored
            // This could happen if you used TestOverride.TombstoneTimeoutMs but had SessionExpiry disabled, then loaded that container.
            gc = createGcWithPrivateMembers({ tombstoneTimeoutMs: 789 } /* metadata */);
            assert.equal(gc.configs.sessionExpiryTimeoutMs, undefined, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer, undefined, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, 789, "tombstoneTimeoutMs incorrect");
        });
        it("IGCMetadata.tombstoneTimeoutMs - backfill from sweepTimeoutMs", () => {
            configProvider.set(testOverrideTombstoneTimeoutKey, 1337); // Should be ignored
            const metadata = {
                sweepTimeoutMs: 789, // Snapshot was generated by code built before the rename
            };
            gc = createGcWithPrivateMembers(metadata);
            assert.equal(gc.configs.sessionExpiryTimeoutMs, undefined, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer, undefined, "sessionExpiryTimer incorrect");
            assert.equal(gc.configs.tombstoneTimeoutMs, 789, "tombstoneTimeoutMs incorrect");
        });
        it("RunSessionExpiry setting turned off", () => {
            configProvider.set(runSessionExpiryKey, false);
            configProvider.set(testOverrideSessionExpiryMsKey, 123); // This override should be ignored
            gc = createGcWithPrivateMembers();
            assert.equal(gc.configs.sessionExpiryTimeoutMs, undefined, "sessionExpiryTimeoutMs should be undefined if runSessionExpiryKey setting is false");
            assert.equal(gc.sessionExpiryTimer, undefined, "sessionExpiryTimer should be undefined if it's disabled");
            assert.equal(gc.configs.tombstoneTimeoutMs, undefined, "tombstoneTimeoutMs incorrect");
        });
        it("RunSessionExpiry setting turned off, TestOverride.TombstoneTimeoutMs set", () => {
            configProvider.set(runSessionExpiryKey, false);
            configProvider.set(testOverrideTombstoneTimeoutKey, 7890);
            configProvider.set(testOverrideSessionExpiryMsKey, 123); // This override should be ignored
            gc = createGcWithPrivateMembers();
            assert.equal(gc.configs.sessionExpiryTimeoutMs, undefined, "sessionExpiryTimeoutMs should be undefined if runSessionExpiryKey setting is false");
            assert.equal(gc.sessionExpiryTimer, undefined, "sessionExpiryTimer should be undefined if it's disabled");
            assert.equal(gc.configs.tombstoneTimeoutMs, 7890, "tombstoneTimeoutMs incorrect");
        });
        function testSessionExpiryMsOverride() {
            const expectedTombstoneTimeoutMs = defaultSessionExpiryDurationMs + 6 * oneDayMs;
            assert(!!gc, "PRECONDITION: gc must be set before calling this helper");
            assert.equal(gc.configs.sessionExpiryTimeoutMs, defaultSessionExpiryDurationMs, "sessionExpiryTimeoutMs incorrect");
            assert.equal(gc.sessionExpiryTimer.defaultTimeout, 789, "sessionExpiry used for timer should be the override value");
            assert.equal(gc.configs.tombstoneTimeoutMs, expectedTombstoneTimeoutMs, "tombstoneTimeoutMs incorrect");
            const expectedMetadata = {
                sweepEnabled: false,
                gcFeature: stableGCVersion,
                sessionExpiryTimeoutMs: defaultSessionExpiryDurationMs,
                tombstoneTimeoutMs: expectedTombstoneTimeoutMs,
                gcFeatureMatrix: undefined,
            };
            const outputMetadata = gc.getMetadata();
            assert.deepEqual(outputMetadata, expectedMetadata, "getMetadata returned different metadata than expected");
        }
        it("TestOverride.SessionExpiryMs setting applied to timeout but not written to file - New Container", () => {
            configProvider.set(testOverrideSessionExpiryMsKey, 789);
            gc = createGcWithPrivateMembers();
            testSessionExpiryMsOverride();
        });
        it("TestOverride.SessionExpiryMs setting applied to timeout but not written to file - Existing Container", () => {
            configProvider.set(testOverrideSessionExpiryMsKey, 789);
            gc = createGcWithPrivateMembers({
                sessionExpiryTimeoutMs: defaultSessionExpiryDurationMs,
                gcFeature: 1,
            } /* metadata */);
            testSessionExpiryMsOverride();
        });
    });
    describe("Session Behavior (e.g. 'shouldRun' fields)", () => {
        beforeEach(() => {
            configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", 1); // To ensure it's less than tombstone timeout
        });
        describe("shouldRunGC", () => {
            it("shouldRunGC should be true when gcVersionInEffect is newer than gcVersionInBaseSnapshot", () => {
                const gcVersionInBaseSnapshot = stableGCVersion - 1;
                gc = createGcWithPrivateMembers({ gcFeature: gcVersionInBaseSnapshot });
                assert.equal(gc.configs.gcAllowed, true, "PRECONDITION: gcAllowed set incorrectly");
                assert.equal(gc.configs.gcVersionInBaseSnapshot, gcVersionInBaseSnapshot, "gcVersionInBaseSnapshot set incorrectly");
            });
            it("shouldRunGC should be true when gcVersionInEffect is older than gcVersionInBaseSnapshot", () => {
                const gcVersionInBaseSnapshot = nextGCVersion + 1;
                gc = createGcWithPrivateMembers({ gcFeature: gcVersionInBaseSnapshot });
                assert.equal(gc.configs.gcAllowed, true, "PRECONDITION: gcAllowed set incorrectly");
                assert.equal(gc.configs.gcVersionInBaseSnapshot, gcVersionInBaseSnapshot, "gcVersionInBaseSnapshot set incorrectly");
            });
        });
        describe("sweepEnabled", () => {
            const testCases = [
                {
                    gcAllowed_doc: false, // Veto
                    sweepEnabled_doc: true,
                    sweepEnabled_session: true,
                    expectedSweepEnabled: false,
                },
                {
                    gcAllowed_doc: true,
                    sweepEnabled_doc: false, // Veto
                    sweepEnabled_session: true,
                    expectedSweepEnabled: false,
                },
                {
                    gcAllowed_doc: true,
                    sweepEnabled_doc: true,
                    sweepEnabled_session: false, // Veto
                    expectedSweepEnabled: false,
                },
                {
                    gcAllowed_doc: true,
                    sweepEnabled_doc: true,
                    sweepEnabled_session: true,
                    expectedSweepEnabled: true,
                },
            ];
            for (const testCase of testCases) {
                it(`Test Case ${JSON.stringify(testCase)}`, () => {
                    gc = createGcWithPrivateMembers({
                        gcFeature: testCase.gcAllowed_doc ? stableGCVersion : 0,
                        gcFeatureMatrix: { gcGeneration: 1 },
                        sessionExpiryTimeoutMs: defaultSessionExpiryDurationMs,
                    } /* metadata */, {
                        enableGCSweep: testCase.sweepEnabled_session ? true : undefined,
                        [gcGenerationOptionName]: testCase.sweepEnabled_doc ? 1 : 2,
                    } /* gcOptions */);
                    assert.equal(gc.configs.gcAllowed, testCase.gcAllowed_doc, "PRECONDITION: gcAllowed set incorrectly");
                    assert.equal(gc.configs.sweepEnabled, testCase.expectedSweepEnabled, `sweepEnabled not set as expected`);
                });
            }
        });
        describe("inactiveTimeoutMs", () => {
            beforeEach(() => {
                // Remove setting added in outer describe block
                configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", undefined);
            });
            const testCases = [
                { testOverride: 123, option: 456, expectedResult: 123 },
                { option: 456, expectedResult: 456 },
                { expectedResult: defaultInactiveTimeoutMs },
            ];
            for (const testCase of testCases) {
                it(`Test Case ${JSON.stringify(testCase)}`, () => {
                    configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", testCase.testOverride);
                    gc = createGcWithPrivateMembers(undefined /* metadata */, {
                        inactiveTimeoutMs: testCase.option,
                    });
                    assert.equal(gc.configs.inactiveTimeoutMs, testCase.expectedResult, "inactiveTimeoutMs not set as expected");
                });
            }
            it("inactiveTimeout must not be greater than tombstoneTimeout", () => {
                configProvider.set("Fluid.GarbageCollection.TestOverride.InactiveTimeoutMs", Number.MAX_VALUE);
                assert.throws(() => {
                    gc = createGcWithPrivateMembers();
                }, (e) => e.errorType === "usageError", "inactiveTimeout must not be greater than tombstoneTimeout");
            });
        });
        describe("sweepGracePeriodMs", () => {
            const testCases = [
                { option: 123, expectedResult: 123 },
                { option: 0, expectedResult: 0 },
                { option: undefined, expectedResult: defaultSweepGracePeriodMs },
            ];
            for (const testCase of testCases) {
                it(`Test Case ${JSON.stringify(testCase)}`, () => {
                    gc = createGcWithPrivateMembers({} /* metadata */, {
                        sweepGracePeriodMs: testCase.option,
                    });
                    assert.equal(gc.configs.sweepGracePeriodMs, testCase.expectedResult);
                });
            }
            it("sweepGracePeriodMs must be non-negative", () => {
                assert.throws(() => {
                    gc = createGcWithPrivateMembers({} /* metadata */, {
                        sweepGracePeriodMs: -1,
                    });
                }, (e) => e.errorType === "usageError", "sweepGracePeriodMs must be non-negative");
            });
        });
        describe("testMode", () => {
            const testCases = [
                { setting: true, option: false, expectedResult: true },
                { setting: false, option: true, expectedResult: false },
                { option: true, expectedResult: true },
                { expectedResult: false },
            ];
            for (const testCase of testCases) {
                it(`Test Case ${JSON.stringify(testCase)}`, () => {
                    configProvider.set(gcTestModeKey, testCase.setting);
                    gc = createGcWithPrivateMembers(undefined /* metadata */, {
                        runGCInTestMode: testCase.option,
                    });
                    assert.equal(gc.configs.testMode, testCase.expectedResult, "testMode not set as expected");
                });
            }
        });
    });
    describe("throwOnTombstoneLoad (using new container)", () => {
        beforeEach(() => {
            configProvider.set(testOverrideSessionExpiryMsKey, defaultSessionExpiryDurationMs); // Required for sweep to be enabled
        });
        it("Old 'enable' option false (ignored)", () => {
            const gcThrowOnTombstoneLoadOptionName_old = "gcThrowOnTombstoneLoad";
            gc = createGcWithPrivateMembers(undefined /* metadata */, { enableGCSweep: true, [gcThrowOnTombstoneLoadOptionName_old]: false }, false /* isSummarizerClient */);
            assert.equal(gc.configs.throwOnTombstoneLoad, true, "throwOnTombstoneLoad incorrect");
        });
        it("throwOnTombstoneLoad disabled via override", () => {
            configProvider.set(disableThrowOnTombstoneLoadKey, true);
            gc = createGcWithPrivateMembers(undefined /* metadata */, { enableGCSweep: true }, false /* isSummarizerClient */);
            assert.equal(gc.configs.throwOnTombstoneLoad, false, "throwOnTombstoneLoad incorrect");
        });
        it("throwOnTombstoneLoad cannot be enabled via override if sweep is disabled", () => {
            configProvider.set(disableThrowOnTombstoneLoadKey, false);
            gc = createGcWithPrivateMembers(undefined /* metadata */, { enableGCSweep: undefined }, false /* isSummarizerClient */);
            assert.equal(gc.configs.throwOnTombstoneLoad, false, "throwOnTombstoneLoad incorrect");
        });
    });
});
//# sourceMappingURL=gcConfigs.spec.js.map