diff --git a/packages/runtime/container-runtime/src/containerCompatibility.ts b/packages/runtime/container-runtime/src/containerCompatibility.ts index f5d495a95e40..638f5ae5e8b4 100644 --- a/packages/runtime/container-runtime/src/containerCompatibility.ts +++ b/packages/runtime/container-runtime/src/containerCompatibility.ts @@ -10,7 +10,7 @@ import { import { configValueToMinVersionForCollab, getConfigsForMinVersionForCollab, - getValidationForRuntimeOptions, + validateConfigMapOverrides, type ConfigMap, type ConfigValidationMap, } from "@fluidframework/runtime-utils/internal"; @@ -101,60 +101,66 @@ export type RuntimeOptionKeysThatRequireExplicitSchemaControl = keyof Omit< * default value for `enableGroupedBatching` will be true because clients running 2.0 or later will be able to understand the format changes associated * with the batching feature. */ -const runtimeOptionsAffectingDocSchemaConfigMap = { - enableGroupedBatching: { - "1.0.0": false, - "2.0.0-defaults": true, - }, - compressionOptions: { - "1.0.0": disabledCompressionConfig, - "2.0.0-defaults": enabledCompressionConfig, - }, - enableRuntimeIdCompressor: { - // For IdCompressorMode, `undefined` represents a logical state (off). - // However, to satisfy the Required<> constraint while - // `exactOptionalPropertyTypes` is `false` (TODO: AB#8215), we need - // to have it defined, so we trick the type checker here. - "1.0.0": undefined, - // We do not yet want to enable idCompressor by default since it will - // increase bundle sizes, and not all customers will benefit from it. - // Therefore, we will require customers to explicitly enable it. We - // are keeping it as a DocSchema affecting option for now as this may - // change in the future. - }, - explicitSchemaControl: { - "1.0.0": false, - // This option's intention is to prevent 1.x clients from joining sessions - // when enabled. This is set to true when the minVersionForCollab is set - // to >=2.0.0 (explicitly). This is different than other 2.0 defaults - // because it was not enabled by default prior to the implementation of - // `minVersionForCollab`. - // `defaultMinVersionForCollab` is set to "2.0.0-defaults" which "2.0.0" - // does not satisfy to avoiding enabling this option by default as of - // `minVersionForCollab` introduction, which could be unexpected. - // Only enable as a default when `minVersionForCollab` is specified at - // 2.0.0+. - "2.0.0": true, - }, - flushMode: { - // Note: 1.x clients are compatible with TurnBased flushing, but here we elect to remain on Immediate flush mode - // as a work-around for inability to send batches larger than 1Mb. Immediate flushing keeps batches smaller as - // fewer messages will be included per flush. - "1.0.0": FlushMode.Immediate, - "2.0.0-defaults": FlushMode.TurnBased, - }, - gcOptions: { - "1.0.0": {}, - // Although sweep is supported in 2.x, it is disabled by default until minVersionForCollab>=3.0.0 to be extra safe. - "3.0.0": { enableGCSweep: true }, - }, - createBlobPayloadPending: { - // This feature is new and disabled by default. In the future we will enable it by default, but we have not - // closed on the version where that will happen yet. Probably a .10 release since blob functionality is not - // exposed on the `@public` API surface. - "1.0.0": undefined, - }, -} as const satisfies ConfigMap; +const runtimeOptionsAffectingDocSchemaConfigMap: ConfigMap = + { + enableGroupedBatching: { + "1.0.0": false, + "2.0.0-defaults": true, + }, + compressionOptions: { + "1.0.0": disabledCompressionConfig, + "2.0.0-defaults": enabledCompressionConfig, + }, + enableRuntimeIdCompressor: { + // For IdCompressorMode, `undefined` represents a logical state (off). + // However, to satisfy the Required<> constraint while + // `exactOptionalPropertyTypes` is `false` (TODO: AB#8215), we need + // to have it defined, so we trick the type checker here. + "1.0.0": undefined, + // We do not yet want to enable idCompressor by default since it will + // increase bundle sizes, and not all customers will benefit from it. + // Therefore, we will require customers to explicitly enable it. We + // are keeping it as a DocSchema affecting option for now as this may + // change in the future. + }, + explicitSchemaControl: { + "1.0.0": false, + // This option's intention is to prevent 1.x clients from joining sessions + // when enabled. This is set to true when the minVersionForCollab is set + // to >=2.0.0 (explicitly). This is different than other 2.0 defaults + // because it was not enabled by default prior to the implementation of + // `minVersionForCollab`. + // `defaultMinVersionForCollab` is set to "2.0.0-defaults" which "2.0.0" + // does not satisfy to avoiding enabling this option by default as of + // `minVersionForCollab` introduction, which could be unexpected. + // Only enable as a default when `minVersionForCollab` is specified at + // 2.0.0+. + "2.0.0": true, + }, + flushMode: { + // Note: 1.x clients are compatible with TurnBased flushing, but here we elect to remain on Immediate flush mode + // as a work-around for inability to send batches larger than 1Mb. Immediate flushing keeps batches smaller as + // fewer messages will be included per flush. + "1.0.0": FlushMode.Immediate, + "2.0.0-defaults": FlushMode.TurnBased, + }, + gcOptions: { + "1.0.0": {}, + // Although sweep is supported in 2.x, it is disabled by default until minVersionForCollab>=3.0.0 to be extra safe. + // Note that enabling this is a significant change, that should likely be announced in the relevant version: + // It would be bad if this simple caused the enablement when when the current package version passed this point without anyone being aware. + // This is configuration targeting a future versions, which is not supported. + // TODO: when preparing 3.0 (or at some later point), consider enabling this by default for clients 3.0 or newer + // (and add user facing documentation indicating this is enabled by that version). + // "3.0.0": { enableGCSweep: true }, + }, + createBlobPayloadPending: { + // This feature is new and disabled by default. In the future we will enable it by default, but we have not + // closed on the version where that will happen yet. Probably a .10 release since blob functionality is not + // exposed on the `@public` API surface. + "1.0.0": undefined, + }, + }; /** * Keys of {@link ContainerRuntimeOptionsInternal} that require explicitSchemaControl to be enabled. @@ -169,37 +175,39 @@ export const runtimeOptionKeysThatRequireExplicitSchemaControl = ( ); }) as RuntimeOptionKeysThatRequireExplicitSchemaControl[]; -const runtimeOptionsAffectingDocSchemaConfigValidationMap = { - enableGroupedBatching: configValueToMinVersionForCollab([ - [false, "1.0.0"], - [true, "2.0.0-defaults"], - ]), - compressionOptions: configValueToMinVersionForCollab([ - [{ ...disabledCompressionConfig }, "1.0.0"], - [{ ...enabledCompressionConfig }, "2.0.0-defaults"], - ]), - enableRuntimeIdCompressor: configValueToMinVersionForCollab([ - [undefined, "1.0.0"], - ["on", "2.0.0-defaults"], - ["delayed", "2.0.0-defaults"], - ]), - explicitSchemaControl: configValueToMinVersionForCollab([ - [false, "1.0.0"], - [true, "2.0.0-defaults"], - ]), - flushMode: configValueToMinVersionForCollab([ - [FlushMode.Immediate, "1.0.0"], - [FlushMode.TurnBased, "2.0.0-defaults"], - ]), - gcOptions: configValueToMinVersionForCollab([ - [{ enableGCSweep: undefined }, "1.0.0"], - [{ enableGCSweep: true }, "2.0.0-defaults"], - ]), - createBlobPayloadPending: configValueToMinVersionForCollab([ - [undefined, "1.0.0"], - [true, "2.40.0"], - ]), -} as const satisfies ConfigValidationMap; +// A lot of the information in this seems redundant with whats defined above. Might be nice to combine them somehow. +const runtimeOptionsAffectingDocSchemaConfigValidationMap: ConfigValidationMap = + { + enableGroupedBatching: configValueToMinVersionForCollab([ + [false, "1.0.0"], + [true, "2.0.0-defaults"], + ]), + compressionOptions: configValueToMinVersionForCollab([ + [{ ...disabledCompressionConfig }, "1.0.0"], + [{ ...enabledCompressionConfig }, "2.0.0-defaults"], + ]), + enableRuntimeIdCompressor: configValueToMinVersionForCollab([ + [undefined, "1.0.0"], + ["on", "2.0.0-defaults"], + ["delayed", "2.0.0-defaults"], + ]), + explicitSchemaControl: configValueToMinVersionForCollab([ + [false, "1.0.0"], + [true, "2.0.0-defaults"], + ]), + flushMode: configValueToMinVersionForCollab([ + [FlushMode.Immediate, "1.0.0"], + [FlushMode.TurnBased, "2.0.0-defaults"], + ]), + gcOptions: configValueToMinVersionForCollab([ + [{ enableGCSweep: undefined }, "1.0.0"], + [{ enableGCSweep: true }, "2.0.0-defaults"], + ]), + createBlobPayloadPending: configValueToMinVersionForCollab([ + [undefined, "1.0.0"], + [true, "2.40.0"], + ]), + }; /** * Returns the default RuntimeOptionsAffectingDocSchema configuration for a given minVersionForCollab. @@ -210,9 +218,7 @@ export function getMinVersionForCollabDefaults( return getConfigsForMinVersionForCollab( minVersionForCollab, runtimeOptionsAffectingDocSchemaConfigMap, - // This is a bad cast away from Partial that getConfigsForCompatMode provides. - // ConfigMap should be restructured to provide RuntimeOptionsAffectingDocSchema guarantee. - ) as RuntimeOptionsAffectingDocSchema; + ); } /** @@ -224,9 +230,9 @@ export function validateRuntimeOptions( minVersionForCollab: MinimumVersionForCollab, runtimeOptions: Partial, ): void { - getValidationForRuntimeOptions( + validateConfigMapOverrides( minVersionForCollab, - runtimeOptions as Partial, + runtimeOptions, runtimeOptionsAffectingDocSchemaConfigValidationMap, ); } diff --git a/packages/runtime/container-runtime/src/containerRuntime.ts b/packages/runtime/container-runtime/src/containerRuntime.ts index f495c7681fff..cc9d66a7911e 100644 --- a/packages/runtime/container-runtime/src/containerRuntime.ts +++ b/packages/runtime/container-runtime/src/containerRuntime.ts @@ -1219,6 +1219,7 @@ export class ContainerRuntime createBlobPayloadPending, }; + semanticVersionToMinimumVersionForCollab(updatedMinVersionForCollab); const runtime = new containerRuntimeCtor( context, registry, @@ -1236,7 +1237,7 @@ export class ContainerRuntime documentSchemaController, featureGatesForTelemetry, provideEntryPoint, - semanticVersionToMinimumVersionForCollab(updatedMinVersionForCollab), + updatedMinVersionForCollab, requestHandler, undefined, // summaryConfiguration recentBatchInfo, diff --git a/packages/runtime/container-runtime/src/summary/documentSchema.ts b/packages/runtime/container-runtime/src/summary/documentSchema.ts index b2040ec921cb..a3a34b78bfd9 100644 --- a/packages/runtime/container-runtime/src/summary/documentSchema.ts +++ b/packages/runtime/container-runtime/src/summary/documentSchema.ts @@ -109,6 +109,8 @@ export interface IDocumentSchemaInfo { * @remarks * We use `SemanticVersion` instead of `MinimumVersionForCollab` since we may open future documents that with a * minVersionForCollab version that `MinimumVersionForCollab` does not support. + * Note that in such a case (where minVersionForCollab is not a valid `MinimumVersionForCollab`), + * loading the document might not work since this version of the runtime may not support it. */ minVersionForCollab: SemanticVersion; } diff --git a/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts b/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts index e3e4052cebd2..6094588be1ed 100644 --- a/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts +++ b/packages/runtime/runtime-definitions/src/compatibilityDefinitions.ts @@ -12,6 +12,10 @@ * * Must be at least {@link @fluidframework/runtime-utils#lowestMinVersionForCollab} and cannot exceed the current version. * + * {@link @fluidframework/runtime-utils#semanticVersionToMinimumVersionForCollab} can be used to check these invariants at runtime. + * Sine TypeScript can not enforce them all for literals in code, + * it may be useful to use `semanticVersionToMinimumVersionForCollab` values which may come from constants in the codebase typed as a `MinimumVersionForCollab`. + * * @privateRemarks * Since this uses the semver notion of "greater" (which might not actually mean a later release, or supporting more features), care must be taken with how this is used. * See remarks for {@link @fluidframework/runtime-utils#MinimumMinorSemanticVersion} for more details. diff --git a/packages/runtime/runtime-utils/src/compatibilityBase.ts b/packages/runtime/runtime-utils/src/compatibilityBase.ts index 301f470d3764..dd422809b70b 100644 --- a/packages/runtime/runtime-utils/src/compatibilityBase.ts +++ b/packages/runtime/runtime-utils/src/compatibilityBase.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. */ -import { assert } from "@fluidframework/core-utils/internal"; +import { assert, fail } from "@fluidframework/core-utils/internal"; import type { MinimumVersionForCollab } from "@fluidframework/runtime-definitions/internal"; import { UsageError } from "@fluidframework/telemetry-utils/internal"; -import { compare, gt, gte, lte, valid } from "semver-ts"; +import { compare, gt, gte, lte, valid, parse } from "semver-ts"; import { pkgVersion } from "./packageVersion.js"; /** - * Our policy is to support N/N-1 compatibility by default, where N is the most - * recent public major release of the runtime. + * Our policy is to support major versions N and N-1, where N is most + * recent public major release of the Fluid Framework Client. * Therefore, if the customer does not provide a minVersionForCollab, we will * default to use N-1. * @@ -68,54 +68,80 @@ export type SemanticVersion = | `${bigint}.${bigint}.${bigint}-${string}`; /** - * Generic type for runtimeOptionsAffectingDocSchemaConfigMap + * Converts a record into a configuration map that associates each key with with an instance of its value type that based on a {@link MinimumMinorSemanticVersion}. + * @remarks + * For a given input {@link @fluidframework/runtime-definitions#MinimumVersionForCollab}, + * the corresponding configuration values can be found by using the entry in the inner objects with the highest {@link MinimumMinorSemanticVersion} + * that does not exceed the given {@link @fluidframework/runtime-definitions#MinimumVersionForCollab}. * + * Use {@link getConfigsForMinVersionForCollab} to retrieve the configuration for a given a {@link @fluidframework/runtime-definitions#MinimumVersionForCollab}. + * + * See the remarks on {@link MinimumMinorSemanticVersion} for some limitation on how ConfigMaps must handle versioning. * @internal */ export type ConfigMap> = { - [K in keyof T]-?: Record; + readonly [K in keyof T]-?: ConfigMapEntry; }; +/** + * Entry in {@link ConfigMap} associating {@link MinimumMinorSemanticVersion} with configuration values that became supported in that version. + * @remarks + * All entries must at least provide an entry for {@link lowestMinVersionForCollab} + * @internal + */ +export interface ConfigMapEntry { + [version: MinimumMinorSemanticVersion]: T; + // Require an entry for the defaultMinVersionForCollab: + // this ensures that all versions of lowestMinVersionForCollab or later have a specified value in the ConfigMap. + [lowestMinVersionForCollab]: T; +} + /** * Generic type for runtimeOptionsAffectingDocSchemaConfigValidationMap * * @internal */ export type ConfigValidationMap> = { - [K in keyof T]-?: (configValue: T[K]) => SemanticVersion | undefined; + readonly [K in keyof T]-?: (configValue: T[K]) => SemanticVersion | undefined; }; /** * Returns a default configuration given minVersionForCollab and configuration version map. * + * @privateRemarks + * The extra `Record` type for the `configMap` is just used to allow the body of this function to be more type-safe due to limitations of generic types in TypeScript. + * It should have no impact on the user of this function. * @internal */ export function getConfigsForMinVersionForCollab>( - minVersionForCollab: SemanticVersion, - configMap: ConfigMap, -): Partial { + minVersionForCollab: MinimumVersionForCollab, + configMap: ConfigMap & Record, +): T { + semanticVersionToMinimumVersionForCollab(minVersionForCollab); const defaultConfigs: Partial = {}; // Iterate over configMap to get default values for each option. - for (const key of Object.keys(configMap)) { - // Type assertion is safe as key comes from Object.keys(configMap) - const config = configMap[key as keyof T]; - // Sort the versions in ascending order so we can short circuit the loop. - const versions = Object.keys(config).sort(compare); + for (const [key, config] of Object.entries(configMap)) { + const entries: [string, unknown][] = Object.entries(config); // Assigning this to a typed variable to convert the "any" into unknown. + // Validate and strongly type the versions from the configMap. + const versions: [MinimumVersionForCollab, unknown][] = entries.map(([version, value]) => { + semanticVersionToMinimumVersionForCollab(version); + return [version, value]; + }); + // Sort the versions in descending order to find the largest compatible entry. + // TODO: Enforcing a sorted order might be a good idea. For now tolerates any order. + versions.sort((a, b) => compare(b[0], a[0])); // For each config, we iterate over the keys and check if minVersionForCollab is greater than or equal to the version. - // If so, we set it as the default value for the option. At the end of the loop we should have the most recent default - // value that is compatible with the version specified as the minVersionForCollab. - for (const version of versions) { + // If so, we set it as the default value for the option. + for (const [version, value] of versions) { if (gte(minVersionForCollab, version)) { - // Type assertion is safe as version is a key from the config object - defaultConfigs[key] = config[version as MinimumMinorSemanticVersion]; - } else { - // If the minVersionForCollab is less than the version, we break out of the loop since we don't need to check - // any later versions. + defaultConfigs[key] = value; break; } } + assert(key in defaultConfigs, "missing config map entry"); } - return defaultConfigs; + // We have populated very key, so casting away the Partial is now safe: + return defaultConfigs as T; } /** @@ -125,9 +151,7 @@ export function getConfigsForMinVersionForCollab>( +export function validateConfigMapOverrides>( minVersionForCollab: SemanticVersion, - runtimeOptions: Partial, + overrides: Partial, validationMap: ConfigValidationMap, ): void { if (minVersionForCollab === defaultMinVersionForCollab) { // If the minVersionForCollab is set to the default value, then we will not validate the runtime options // This is to avoid disruption to users who have not yet set the minVersionForCollab value explicitly. + // TODO: This also skips validation for users which explicitly request defaultMinVersionForCollab which seems like a bug. return; } // Iterate through each runtime option passed in by the user // Type assertion is safe as entries come from runtimeOptions object - for (const [passedRuntimeOption, passedRuntimeOptionValue] of Object.entries( - runtimeOptions, - ) as [keyof T & string, T[keyof T & string]][]) { + for (const [passedRuntimeOption, passedRuntimeOptionValue] of Object.entries(overrides) as [ + keyof T & string, + T[keyof T & string], + ][]) { // Skip if passedRuntimeOption is not in validation map if (!(passedRuntimeOption in validationMap)) { continue; diff --git a/packages/runtime/runtime-utils/src/index.ts b/packages/runtime/runtime-utils/src/index.ts index 24aa55042323..ed9896d4d4ab 100644 --- a/packages/runtime/runtime-utils/src/index.ts +++ b/packages/runtime/runtime-utils/src/index.ts @@ -64,13 +64,15 @@ export { export { configValueToMinVersionForCollab, defaultMinVersionForCollab, - getValidationForRuntimeOptions, + validateConfigMapOverrides, getConfigsForMinVersionForCollab, isValidMinVersionForCollab, semanticVersionToMinimumVersionForCollab, + lowestMinVersionForCollab, } from "./compatibilityBase.js"; export type { ConfigMap, + ConfigMapEntry, ConfigValidationMap, MinimumMinorSemanticVersion, SemanticVersion, diff --git a/packages/runtime/runtime-utils/src/test/compatUtils.spec.ts b/packages/runtime/runtime-utils/src/test/compatUtils.spec.ts index 0833f1a9467e..1c4ca38fb855 100644 --- a/packages/runtime/runtime-utils/src/test/compatUtils.spec.ts +++ b/packages/runtime/runtime-utils/src/test/compatUtils.spec.ts @@ -10,17 +10,23 @@ import { isFluidError } from "@fluidframework/telemetry-utils/internal"; import { getConfigsForMinVersionForCollab, - getValidationForRuntimeOptions, + validateConfigMapOverrides, type ConfigMap, type SemanticVersion, type ConfigValidationMap, configValueToMinVersionForCollab, lowestMinVersionForCollab, checkValidMinVersionForCollabVerbose, + cleanedPackageVersion, + semanticVersionToMinimumVersionForCollab, } from "../compatibilityBase.js"; import { pkgVersion } from "../packageVersion.js"; describe("compatibilityBase", () => { + it("cleanedPackageVersion", () => { + semanticVersionToMinimumVersionForCollab(cleanedPackageVersion); + }); + describe("getConfigsForMinVersionForCollab", () => { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions -- type required for ConfigMap processing type ITestConfigMap = { @@ -29,82 +35,84 @@ describe("compatibilityBase", () => { featureC: string; featureD: string; featureE: string; - featureF: string; + featureF: number; }; const testConfigMap: ConfigMap = { featureA: { - "0.5.0": "a1", "2.0.0": "a2", - "8.0.0": "a4", - "5.0.0": "a3", + "2.0.0-defaults": "a1", + "2.50.0": "a4", + "2.40.0": "a3", + "1.0.0": "a0", }, featureB: { - "0.0.0-defaults": "b1", - "3.0.0": "b2", - "9.0.0": "b4", - "6.0.0": "b3", + "1.0.0": "b1", + "2.30.0": "b2", + "2.60.0": "b4", + "2.46.0": "b3", }, featureC: { "1.0.0": "c1", - "4.0.0": "c2", - "10.0.0": "c4", - "7.0.0": "c3", + "2.40.0": "c2", + "2.70.0": "c4", + "2.50.0": "c3", }, featureD: { - "5.5.0": "d3", - "0.1.0": "d1", + "2.46.0": "d3", "2.5.0": "d2", - "8.5.0": "d4", + "2.55.0": "d4", + "1.0.0": "d1", }, featureE: { - "3.5.0": "e2", - "9.5.0": "e4", - "6.5.0": "e3", - "0.9.0": "e1", + "2.35.0": "e2", + "2.73.0": "e4", + "2.65.0": "e3", + "1.0.0": "e1", }, featureF: { - "4.5.0": "f2", - "1.5.0": "f1", - "10.5.0": "f4", - "7.5.0": "f3", + "1.0.0": 0, + "2.45.0": 2, + "1.5.0": 1, + "2.71.0": 4, + "2.65.0": 3, }, }; const testCases: { - minVersionForCollab: SemanticVersion; - expectedConfig: Partial; + minVersionForCollab: MinimumVersionForCollab; + expectedConfig: ITestConfigMap; }[] = [ { - minVersionForCollab: "0.5.0", + minVersionForCollab: "1.0.0", expectedConfig: { - featureA: "a1", + featureA: "a0", featureB: "b1", - // featureC: undefined, + featureC: "c1", featureD: "d1", - // featureE: undefined, - // featureF: undefined, + featureE: "e1", + featureF: 0, }, }, { - minVersionForCollab: "1.0.0", + minVersionForCollab: "1.5.0", expectedConfig: { - featureA: "a1", + featureA: "a0", featureB: "b1", featureC: "c1", featureD: "d1", featureE: "e1", - // featureF: undefined, + featureF: 1, }, }, { - minVersionForCollab: "1.5.0", + minVersionForCollab: "2.0.0-defaults", expectedConfig: { featureA: "a1", featureB: "b1", featureC: "c1", featureD: "d1", featureE: "e1", - featureF: "f1", + featureF: 1, }, }, { @@ -115,7 +123,7 @@ describe("compatibilityBase", () => { featureC: "c1", featureD: "d1", featureE: "e1", - featureF: "f1", + featureF: 1, }, }, { @@ -126,7 +134,7 @@ describe("compatibilityBase", () => { featureC: "c1", featureD: "d1", featureE: "e1", - featureF: "f1", + featureF: 1, }, }, { @@ -137,84 +145,95 @@ describe("compatibilityBase", () => { featureC: "c1", featureD: "d2", featureE: "e1", - featureF: "f1", + featureF: 1, }, }, { - minVersionForCollab: "3.0.0", + minVersionForCollab: "2.30.0", expectedConfig: { featureA: "a2", featureB: "b2", featureC: "c1", featureD: "d2", featureE: "e1", - featureF: "f1", + featureF: 1, }, }, { - minVersionForCollab: "3.7.2", + minVersionForCollab: "2.37.2", expectedConfig: { featureA: "a2", featureB: "b2", featureC: "c1", featureD: "d2", featureE: "e2", - featureF: "f1", + featureF: 1, }, }, { - minVersionForCollab: "5.0.1", + minVersionForCollab: "2.45.1", expectedConfig: { featureA: "a3", featureB: "b2", featureC: "c2", featureD: "d2", featureE: "e2", - featureF: "f2", + featureF: 2, }, }, { - minVersionForCollab: "6.9.9", + minVersionForCollab: "2.49.9", expectedConfig: { featureA: "a3", featureB: "b3", featureC: "c2", featureD: "d3", - featureE: "e3", - featureF: "f2", + featureE: "e2", + featureF: 2, }, }, { - minVersionForCollab: "8.2.3", + minVersionForCollab: "2.50.0", expectedConfig: { featureA: "a4", featureB: "b3", featureC: "c3", featureD: "d3", - featureE: "e3", - featureF: "f3", + featureE: "e2", + featureF: 2, }, }, { - minVersionForCollab: "9.7.0", + minVersionForCollab: "2.52.3", + expectedConfig: { + featureA: "a4", + featureB: "b3", + featureC: "c3", + featureD: "d3", + featureE: "e2", + featureF: 2, + }, + }, + { + minVersionForCollab: "2.63.0", expectedConfig: { featureA: "a4", featureB: "b4", featureC: "c3", featureD: "d4", - featureE: "e4", - featureF: "f3", + featureE: "e2", + featureF: 2, }, }, { - minVersionForCollab: "10.0.0", + minVersionForCollab: cleanedPackageVersion, expectedConfig: { featureA: "a4", featureB: "b4", featureC: "c4", featureD: "d4", featureE: "e4", - featureF: "f3", + featureF: 4, }, }, ]; @@ -234,7 +253,7 @@ describe("compatibilityBase", () => { } }); - describe("getValidationForRuntimeOptions", () => { + describe("validateRuntimeOptions", () => { type FeatureAType = string; type FeatureBType = boolean; type FeatureCType = object; @@ -252,7 +271,7 @@ describe("compatibilityBase", () => { ["a4", "8.0.0"], ]), featureB: configValueToMinVersionForCollab([ - [false, "0.0.0-defaults"], + [false, "0.0.0"], [true, "3.0.0"], ]), featureC: configValueToMinVersionForCollab([ @@ -339,7 +358,7 @@ describe("compatibilityBase", () => { for (const test of compatibleCases) { it(`does not throw for compatible options: ${JSON.stringify(test)}`, () => { assert.doesNotThrow(() => { - getValidationForRuntimeOptions( + validateConfigMapOverrides( test.minVersionForCollab, test.runtimeOptions, testConfigValidationMap, @@ -351,7 +370,7 @@ describe("compatibilityBase", () => { it(`throws for incompatible options: ${JSON.stringify({ minVersionForCollab: test.minVersionForCollab, runtimeOptions: test.runtimeOptions })}`, () => { assert.throws( () => { - getValidationForRuntimeOptions( + validateConfigMapOverrides( test.minVersionForCollab, test.runtimeOptions, testConfigValidationMap,