diff --git a/packages/test/test-end-to-end-tests/src/test/treeCompat.spec.ts b/packages/test/test-end-to-end-tests/src/test/treeCompat.spec.ts new file mode 100644 index 000000000000..e5d57a8047a1 --- /dev/null +++ b/packages/test/test-end-to-end-tests/src/test/treeCompat.spec.ts @@ -0,0 +1,100 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { strict as assert } from "assert"; +import { lt } from "semver"; + +import { describeCompat, type CompatApis } from "@fluid-private/test-version-utils"; +import { + DataObjectFactoryType, + getContainerEntryPointBackCompat, + type ITestContainerConfig, + type ITestFluidObject, + type ITestObjectProvider, +} from "@fluidframework/test-utils/internal"; +import type { IContainer } from "@fluidframework/container-definitions/internal"; +import { type ITree } from "@fluidframework/tree"; + +const treeId = "sharedTree"; +const baseTestContainerConfig: ITestContainerConfig = { + fluidDataObjectType: DataObjectFactoryType.Test, + runtimeOptions: { + enableRuntimeIdCompressor: "on", + }, +}; + +async function createTreeView( + container: IContainer, + dataRuntimeApi: CompatApis["dataRuntime"], +) { + const { SchemaFactory, TreeViewConfiguration } = dataRuntimeApi.packages.tree; + const schemaFactory = new SchemaFactory("test"); + class TestSchema extends schemaFactory.object("TestSchema", { + foo: [schemaFactory.string], + }) {} + + const treeViewConfig = new TreeViewConfiguration({ schema: TestSchema }); + + const dataObject = await getContainerEntryPointBackCompat(container); + const tree = await dataObject.getSharedObject(treeId); + return tree.viewWith(treeViewConfig); +} + +async function createContainerAndGetTreeView(provider: ITestObjectProvider, apis: CompatApis) { + const { SharedTree } = apis.dataRuntime.dds; + const testContainerConfig: ITestContainerConfig = { + ...baseTestContainerConfig, + registry: [[treeId, SharedTree.getFactory()]], + }; + + const container = await provider.makeTestContainer(testContainerConfig); + return createTreeView(container, apis.dataRuntime); +} + +async function loadContainerAndGetTreeView(provider: ITestObjectProvider, apis: CompatApis) { + const dataRuntimeApi = apis.dataRuntimeForLoading ?? apis.dataRuntime; + const { SharedTree } = dataRuntimeApi.dds; + const testContainerConfig: ITestContainerConfig = { + ...baseTestContainerConfig, + registry: [[treeId, SharedTree.getFactory()]], + }; + + const container = await provider.loadTestContainer(testContainerConfig); + return createTreeView(container, dataRuntimeApi); +} + +describeCompat( + "SharedTree compat tests", + "FullCompat", + (getTestObjectProvider, apis: CompatApis) => { + let provider: ITestObjectProvider; + + beforeEach(function () { + provider = getTestObjectProvider(); + // SharedTree was added in version 2.0.0. Skip all cross-client compat tests in this suite for older versions. + if (apis.mode === "CrossClientCompat") { + const version = apis.dataRuntime.version; + const versionForLoading = apis.dataRuntimeForLoading?.version; + assert( + versionForLoading !== undefined, + "Loading version must be defined for cross-client tests", + ); + if (lt(version, "2.0.0") || lt(versionForLoading, "2.0.0")) { + this.skip(); + } + } + }); + + it("simple schema", async () => { + const treeView1 = await createContainerAndGetTreeView(provider, apis); + assert(treeView1.compatibility.canInitialize, "Incompatible schema"); + treeView1.initialize({ foo: "Hello world" }); + + const treeView2 = await loadContainerAndGetTreeView(provider, apis); + await provider.ensureSynchronized(); + assert.deepEqual(treeView2.root.foo, "Hello world"); + }); + }, +); diff --git a/packages/test/test-version-utils/package.json b/packages/test/test-version-utils/package.json index 653d313d846f..4e203c01d5ba 100644 --- a/packages/test/test-version-utils/package.json +++ b/packages/test/test-version-utils/package.json @@ -99,6 +99,7 @@ "@fluidframework/shared-object-base": "workspace:~", "@fluidframework/telemetry-utils": "workspace:~", "@fluidframework/test-utils": "workspace:~", + "@fluidframework/tree": "workspace:~", "nconf": "^0.12.0", "proper-lockfile": "^4.1.2", "semver": "^7.7.1" diff --git a/packages/test/test-version-utils/src/compatOptions.ts b/packages/test/test-version-utils/src/compatOptions.ts index ae9d78c89e07..8f40edb7d961 100644 --- a/packages/test/test-version-utils/src/compatOptions.ts +++ b/packages/test/test-version-utils/src/compatOptions.ts @@ -12,6 +12,7 @@ import nconf from "nconf"; /** * Different kind of compat version config + * @internal */ export const CompatKind = { None: "None", diff --git a/packages/test/test-version-utils/src/compatUtils.ts b/packages/test/test-version-utils/src/compatUtils.ts index 19f4ccad5e36..65ba6311cbaf 100644 --- a/packages/test/test-version-utils/src/compatUtils.ts +++ b/packages/test/test-version-utils/src/compatUtils.ts @@ -120,23 +120,25 @@ function createGetDataStoreFactoryFunction(api: ReturnType, + apis: Omit, driverConfig?: { type?: TestDriverTypes; config?: FluidTestDriverConfig; diff --git a/packages/test/test-version-utils/src/describeCompat.ts b/packages/test/test-version-utils/src/describeCompat.ts index 9a8a1031777d..72f9942108f9 100644 --- a/packages/test/test-version-utils/src/describeCompat.ts +++ b/packages/test/test-version-utils/src/describeCompat.ts @@ -38,6 +38,7 @@ import { getLoaderApi, CompatApis, getDriverApi, + getCompatModeFromKind, } from "./testApi.js"; import { getRequestedVersion } from "./versionUtils.js"; @@ -197,6 +198,7 @@ function createCompatSuite( * Get versioned APIs for the given config. */ function getVersionedApis(config: CompatConfig): CompatApis { + const mode = getCompatModeFromKind(config.kind); // If this is cross-clients compat scenario, make sure we use the correct versions if (config.kind === CompatKind.CrossClient) { assert( @@ -211,6 +213,7 @@ function getVersionedApis(config: CompatConfig): CompatApis { const dataRuntime = getDataRuntimeApi(config.createVersion); const dataRuntimeForLoading = getDataRuntimeApi(config.loadVersion); return { + mode, containerRuntime: getContainerRuntimeApi(config.createVersion), containerRuntimeForLoading: getContainerRuntimeApi(config.loadVersion), dataRuntime, @@ -228,6 +231,7 @@ function getVersionedApis(config: CompatConfig): CompatApis { getRequestedVersion(testBaseVersion(config.dataRuntime), config.dataRuntime), ); return { + mode, containerRuntime: getContainerRuntimeApi( getRequestedVersion(testBaseVersion(config.containerRuntime), config.containerRuntime), ), diff --git a/packages/test/test-version-utils/src/describeE2eDocs.ts b/packages/test/test-version-utils/src/describeE2eDocs.ts index 6f06c3b453ec..a20abeac886e 100644 --- a/packages/test/test-version-utils/src/describeE2eDocs.ts +++ b/packages/test/test-version-utils/src/describeE2eDocs.ts @@ -33,6 +33,7 @@ import { getContainerRuntimeApi, getDriverApi, CompatApis, + getCompatModeFromKind, } from "./testApi.js"; import { getRequestedVersion } from "./versionUtils.js"; @@ -360,6 +361,7 @@ function createE2EDocCompatSuite( getRequestedVersion(testBaseVersion(config.dataRuntime), config.dataRuntime), ); const apis: CompatApis = { + mode: getCompatModeFromKind(config.kind), containerRuntime: getContainerRuntimeApi( getRequestedVersion( testBaseVersion(config.containerRuntime), diff --git a/packages/test/test-version-utils/src/index.ts b/packages/test/test-version-utils/src/index.ts index 01f41472d418..0c1ed4450a48 100644 --- a/packages/test/test-version-utils/src/index.ts +++ b/packages/test/test-version-utils/src/index.ts @@ -4,6 +4,7 @@ */ export { mochaGlobalSetup } from "./compatConfig.js"; +export { CompatKind } from "./compatOptions.js"; export { getDataStoreFactory, getVersionedTestObjectProvider, @@ -58,6 +59,8 @@ export { DataRuntimeApi, ContainerRuntimeApi, LoaderApi, + CompatMode, + getCompatModeFromKind, } from "./testApi.js"; export { itExpectsSkipsFailureOnSpecificDrivers, diff --git a/packages/test/test-version-utils/src/testApi.ts b/packages/test/test-version-utils/src/testApi.ts index 6e98fb3d8b6a..47e49223e3d0 100644 --- a/packages/test/test-version-utils/src/testApi.ts +++ b/packages/test/test-version-utils/src/testApi.ts @@ -3,13 +3,9 @@ * Licensed under the MIT License. */ -/* eslint-disable import-x/order */ -// Driver API import * as sequenceDeprecated from "@fluid-experimental/sequence-deprecated"; import { SparseMatrix } from "@fluid-experimental/sequence-deprecated"; import { DriverApi } from "@fluid-private/test-drivers"; - -// Loader API import * as agentScheduler from "@fluidframework/agent-scheduler/internal"; import { BaseContainerRuntimeFactory, @@ -20,11 +16,7 @@ import { import * as cell from "@fluidframework/cell/internal"; import { SharedCell } from "@fluidframework/cell/internal"; import { Loader } from "@fluidframework/container-loader/internal"; - -// ContainerRuntime API import { ContainerRuntime } from "@fluidframework/container-runtime/internal"; - -// Data Runtime API import * as counter from "@fluidframework/counter/internal"; import { SharedCounter } from "@fluidframework/counter/internal"; import { SharedArray, SharedSignal } from "@fluidframework/legacy-dds/internal"; @@ -41,8 +33,8 @@ import { ConsensusRegisterCollection } from "@fluidframework/register-collection import * as sequence from "@fluidframework/sequence/internal"; import { SharedString } from "@fluidframework/sequence/internal"; import { TestFluidObjectFactory } from "@fluidframework/test-utils/internal"; - -// ContainerRuntime and Data Runtime API +import * as treeCurrent from "@fluidframework/tree/internal"; +import { SharedTree } from "@fluidframework/tree/internal"; import * as semver from "semver"; // TypeScript generates incorrect imports in the d.ts file if this is not included. @@ -60,27 +52,65 @@ import { loadPackage, versionHasMovedSparsedMatrix, } from "./versionUtils.js"; +import { CompatKind } from "./compatOptions.js"; -/* eslint-enable import-x/order */ - -// List of package that needs to be install for legacy versions -const packageList = [ - "@fluidframework/aqueduct", - "@fluidframework/datastore", - "@fluidframework/test-utils", - "@fluidframework/container-loader", - "@fluidframework/container-runtime", - "@fluidframework/cell", - "@fluidframework/counter", - "@fluidframework/map", - "@fluidframework/matrix", - "@fluidframework/ordered-collection", - "@fluidframework/register-collection", - "@fluidframework/sequence", - "@fluidframework/local-driver", - "@fluidframework/odsp-driver", - "@fluidframework/routerlicious-driver", - "@fluidframework/agent-scheduler", +/** + * The details of a package to install for compatibility testing. + */ +export interface PackageToInstall { + /** The name of the package to install. */ + pkgName: string; + /** + * The minimum version where the package should be installed. + * If the requested version is lower than this, the package will not be installed. This enables certain level of + * compatibility testing for packages which were not yet part of the Fluid Framework at certain versions. + * Tests are responsible to not use APIs from packages which are not installed for the requested version. + * For example, the "\@fluidframework/tree" package was only introduced in version 2.0.0, so for testing + * versions prior to that, the package will not be installed and the test should skip testing these versions. + */ + minVersion: string; +} + +// List of driver API packages to install. +const driverPackageEntries: PackageToInstall[] = [ + { pkgName: "@fluidframework/local-driver", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/odsp-driver", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/routerlicious-driver", minVersion: "0.56.0" }, +]; + +// List of loader API packages to install. +const loaderPackageEntries: PackageToInstall[] = [ + { pkgName: "@fluidframework/container-loader", minVersion: "0.56.0" }, +]; + +// List of container runtime API packages to install. +const containerRuntimePackageEntries: PackageToInstall[] = [ + { pkgName: "@fluidframework/container-runtime", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/aqueduct", minVersion: "0.56.0" }, +]; + +// List of data runtime API packages to install. +const dataRuntimePackageEntries: PackageToInstall[] = [ + { pkgName: "@fluidframework/aqueduct", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/datastore", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/test-utils", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/cell", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/counter", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/map", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/matrix", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/ordered-collection", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/register-collection", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/sequence", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/agent-scheduler", minVersion: "0.56.0" }, + { pkgName: "@fluidframework/tree", minVersion: "2.0.0" }, +]; + +// Combined list of all packages to install. +const packageListToInstall: PackageToInstall[] = [ + ...driverPackageEntries, + ...loaderPackageEntries, + ...containerRuntimePackageEntries, + ...dataRuntimePackageEntries, ]; /** @@ -101,7 +131,7 @@ export const ensurePackageInstalled = async ( ): Promise => { const pkg = await ensureInstalled( getRequestedVersion(baseVersion, version), - packageList, + packageListToInstall, force, ); await Promise.all([ @@ -166,6 +196,7 @@ export const DataRuntimeApi = { SparseMatrix, SharedArray, SharedSignal, + SharedTree, }, /** * Contains all APIs from imported DDS packages. @@ -187,11 +218,50 @@ export const DataRuntimeApi = { sequence, sequenceDeprecated, agentScheduler, + tree: treeCurrent, }, }; // #endregion +/** + * Helper to load a package if the requested version is compatible. + * @param pkgEntry - The package entry to check and load. + * @param versionToInstall - The version of the package to install. + * @param modulePath - The path to the module. + * @returns The loaded package or undefined if not compatible. + */ +async function loadIfCompatible( + pkgEntry: PackageToInstall, + versionToInstall: string, + modulePath: string, +): Promise { + // Check if the requested version satisfies the minVersion requirement + if (semver.gte(versionToInstall, pkgEntry.minVersion)) { + return loadPackage(modulePath, pkgEntry.pkgName); + } + return undefined; +} + +/** + * Helper to load multiple packages if their requested versions are compatible. + * @param packageEntries - The package entries to check and load. + * @param version - The version of the packages to install. + * @param modulePath - The path to the module. + * @returns An object containing the loaded packages. + */ +async function loadPackages( + packageEntries: PackageToInstall[], + version: string, + modulePath: string, +) { + const loadedPackages = {}; + for (const pkgEntry of packageEntries) { + loadedPackages[pkgEntry.pkgName] = await loadIfCompatible(pkgEntry, version, modulePath); + } + return loadedPackages; +} + async function loadLoader(baseVersion: string, requested?: number | string): Promise { const requestedStr = getRequestedVersion(baseVersion, requested); if (semver.satisfies(pkgVersion, requestedStr)) { @@ -200,9 +270,10 @@ async function loadLoader(baseVersion: string, requested?: number | string): Pro const { version, modulePath } = checkInstalled(requestedStr); if (!loaderCache.has(version)) { - const loader = { + const loadedPackages = await loadPackages(loaderPackageEntries, version, modulePath); + const loader: typeof LoaderApi = { version, - Loader: (await loadPackage(modulePath, "@fluidframework/container-loader")).Loader, + Loader: loadedPackages["@fluidframework/container-loader"].Loader, }; loaderCache.set(version, loader); } @@ -219,10 +290,14 @@ async function loadContainerRuntime( const { version, modulePath } = checkInstalled(requestedStr); if (!containerRuntimeCache.has(version)) { - const [containerRuntimePkg, aqueductPkg] = await Promise.all([ - loadPackage(modulePath, "@fluidframework/container-runtime"), - loadPackage(modulePath, "@fluidframework/aqueduct"), - ]); + const loadedPackages = await loadPackages( + containerRuntimePackageEntries, + version, + modulePath, + ); + + const aqueductPkg = loadedPackages["@fluidframework/aqueduct"]; + const containerRuntimePkg = loadedPackages["@fluidframework/container-runtime"]; /* eslint-disable @typescript-eslint/no-shadow */ const { ContainerRuntime } = containerRuntimePkg; @@ -230,7 +305,7 @@ async function loadContainerRuntime( aqueductPkg; /* eslint-enable @typescript-eslint/no-shadow */ - const containerRuntime = { + const containerRuntime: typeof ContainerRuntimeApi = { version, BaseContainerRuntimeFactory, ContainerRuntime, @@ -251,67 +326,51 @@ async function loadDataRuntime( const { version, modulePath } = checkInstalled(requestedStr); if (!dataRuntimeCache.has(version)) { /* eslint-disable @typescript-eslint/no-shadow */ - const [ - { DataObject, DataObjectFactory }, - datastore, - { TestFluidObjectFactory }, - map, - sequence, - cell, - counter, - matrix, - orderedCollection, - registerCollection, - sequenceDeprecated, - agentScheduler, - ] = await Promise.all([ - loadPackage(modulePath, "@fluidframework/aqueduct"), - loadPackage(modulePath, "@fluidframework/datastore"), - loadPackage(modulePath, "@fluidframework/test-utils"), - loadPackage(modulePath, "@fluidframework/map"), - loadPackage(modulePath, "@fluidframework/sequence"), - loadPackage(modulePath, "@fluidframework/cell"), - loadPackage(modulePath, "@fluidframework/counter"), - loadPackage(modulePath, "@fluidframework/matrix"), - loadPackage(modulePath, "@fluidframework/ordered-collection"), - loadPackage(modulePath, "@fluidframework/register-collection"), - loadPackage( - modulePath, - versionHasMovedSparsedMatrix(version) - ? "@fluid-experimental/sequence-deprecated" - : "@fluidframework/sequence", - ), - loadPackage(modulePath, "@fluidframework/agent-scheduler"), - ]); - const { FluidDataStoreRuntime } = datastore; - const { SharedCell } = cell; - const { SharedCounter } = counter; - const { SharedDirectory, SharedMap } = map; - const { SharedMatrix } = matrix; - const { ConsensusQueue } = orderedCollection; - const { ConsensusRegisterCollection } = registerCollection; - const { SharedString } = sequence; - const { SparseMatrix } = sequenceDeprecated; - /* eslint-enable @typescript-eslint/no-shadow */ - const dataRuntime = { + const loadedPackages = await loadPackages(dataRuntimePackageEntries, version, modulePath); + + // Load sequenceDeprecated separately as it has special handling. + const sequenceDeprecated = await loadPackage( + modulePath, + versionHasMovedSparsedMatrix(version) + ? "@fluid-experimental/sequence-deprecated" + : "@fluidframework/sequence", + ); + + // Destructure loaded packages + const aqueduct = loadedPackages["@fluidframework/aqueduct"]; + const datastore = loadedPackages["@fluidframework/datastore"]; + const testUtils = loadedPackages["@fluidframework/test-utils"]; + const cell = loadedPackages["@fluidframework/cell"]; + const counter = loadedPackages["@fluidframework/counter"]; + const map = loadedPackages["@fluidframework/map"]; + const matrix = loadedPackages["@fluidframework/matrix"]; + const orderedCollection = loadedPackages["@fluidframework/ordered-collection"]; + const registerCollection = loadedPackages["@fluidframework/register-collection"]; + const sequence = loadedPackages["@fluidframework/sequence"]; + const agentScheduler = loadedPackages["@fluidframework/agent-scheduler"]; + const tree = loadedPackages["@fluidframework/tree"]; + + /* eslint-enable @typescript-eslint/no-shadow */ + const dataRuntime: typeof DataRuntimeApi = { version, - DataObject, - DataObjectFactory, - FluidDataStoreRuntime, - TestFluidObjectFactory, + DataObject: aqueduct?.DataObject, + DataObjectFactory: aqueduct?.DataObjectFactory, + FluidDataStoreRuntime: datastore?.FluidDataStoreRuntime, + TestFluidObjectFactory: testUtils?.TestFluidObjectFactory, dds: { - SharedCell, - SharedCounter, - SharedDirectory, - SharedMap, - SharedMatrix, - ConsensusQueue, - ConsensusRegisterCollection, - SharedString, - SparseMatrix, + SharedCell: cell?.SharedCell, + SharedCounter: counter?.SharedCounter, + SharedDirectory: map?.SharedDirectory, + SharedMap: map?.SharedMap, + SharedMatrix: matrix?.SharedMatrix, + ConsensusQueue: orderedCollection?.ConsensusQueue, + ConsensusRegisterCollection: registerCollection?.ConsensusRegisterCollection, + SharedString: sequence?.SharedString, + SparseMatrix: sequenceDeprecated?.SparseMatrix, SharedArray, SharedSignal, + SharedTree: tree?.SharedTree, }, packages: { datastore, @@ -324,6 +383,7 @@ async function loadDataRuntime( registerCollection, sequenceDeprecated, agentScheduler, + tree, }, }; dataRuntimeCache.set(version, dataRuntime); @@ -338,9 +398,9 @@ async function loadDriver(baseVersion: string, requested?: number | string): Pro const { version, modulePath } = checkInstalled(requestedStr); if (!driverCache.has(version)) { + const loadedPackages = await loadPackages(driverPackageEntries, version, modulePath); const [ { LocalDocumentServiceFactory, LocalResolver, createLocalResolverCreateNewRequest }, - { LocalDeltaConnectionServer }, { OdspDocumentServiceFactory, OdspDriverUrlResolver, @@ -348,12 +408,19 @@ async function loadDriver(baseVersion: string, requested?: number | string): Pro createOdspUrl, }, { RouterliciousDocumentServiceFactory }, - ] = await Promise.all([ - loadPackage(modulePath, "@fluidframework/local-driver"), - loadPackage(modulePath, "@fluidframework/server-local-server"), - loadPackage(modulePath, "@fluidframework/odsp-driver"), - loadPackage(modulePath, "@fluidframework/routerlicious-driver"), - ]); + ] = [ + loadedPackages["@fluidframework/local-driver"], + loadedPackages["@fluidframework/odsp-driver"], + loadedPackages["@fluidframework/routerlicious-driver"], + ]; + + // Load the "@fluidframework/server-local-server" package directly without checking for version compatibility. + // This is because the server packages have different versions that client packages and the versions requested + // do not apply to server packages. + const { LocalDeltaConnectionServer } = await loadPackage( + modulePath, + "@fluidframework/server-local-server", + ); const LocalDriverApi: typeof DriverApi.LocalDriverApi = { version, @@ -470,10 +537,39 @@ export function getDriverApi(requestedStr: string): typeof DriverApi { return driverCache.get(version) ?? throwNotFound("Driver", version); } +/** + * The compatibility mode that a test is running in. That can be useful in scenarios where the tests + * want to alter their behavior based on the compat mode. + * For example, some tests may want to run in "LayerCompat" mode but skip running in "CrossClientCompat" mode + * because they are the feature they are testing was not available in versions that cross client compat requires. + * + * @internal + */ +export type CompatMode = "None" | "LayerCompat" | "CrossClientCompat"; + +/** + * Returns the CompatMode for a given CompatKind. + * @param kind - The CompatKind to convert. + * @returns The corresponding CompatMode. + * + * @internal + */ +export function getCompatModeFromKind(kind: CompatKind): CompatMode { + switch (kind) { + case CompatKind.None: + return "None"; + case CompatKind.CrossClient: + return "CrossClientCompat"; + default: + return "LayerCompat"; + } +} + /** * @internal */ export interface CompatApis { + mode: CompatMode; containerRuntime: ReturnType; dataRuntime: ReturnType; dds: ReturnType["dds"]; diff --git a/packages/test/test-version-utils/src/versionUtils.ts b/packages/test/test-version-utils/src/versionUtils.ts index aa5066ba4e27..325598218c44 100644 --- a/packages/test/test-version-utils/src/versionUtils.ts +++ b/packages/test/test-version-utils/src/versionUtils.ts @@ -23,7 +23,7 @@ import { lock } from "proper-lockfile"; import * as semver from "semver"; import { pkgVersion } from "./packageVersion.js"; -import { InstalledPackage } from "./testApi.js"; +import { InstalledPackage, type PackageToInstall } from "./testApi.js"; // Assuming this file is in `lib`, so go to `..\node_modules\.legacy` as the install location const baseModulePath = fileURLToPath(new URL("../node_modules/.legacy", import.meta.url)); @@ -215,7 +215,7 @@ async function ensureModulePath(version: string, modulePath: string) { */ export async function ensureInstalled( requested: string, - packageList: string[], + packageList: PackageToInstall[], force: boolean, ): Promise { if (requested === pkgVersion) { @@ -230,7 +230,12 @@ export async function ensureInstalled( await ensureModulePath(version, modulePath); - const adjustedPackageList = [...packageList]; + // Adjust package list based on the minVersion for each package. If the requested version is + // less than the minVersion, skip that package. + const adjustedPackageList = packageList + .filter((entry) => semver.gte(version, entry.minVersion)) + .map((entry) => entry.pkgName); + if (versionHasMovedSparsedMatrix(version)) { adjustedPackageList.push("@fluid-experimental/sequence-deprecated"); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b430bc6235b5..66041ec32def 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15281,6 +15281,9 @@ importers: '@fluidframework/test-utils': specifier: workspace:~ version: link:../test-utils + '@fluidframework/tree': + specifier: workspace:~ + version: link:../../dds/tree nconf: specifier: ^0.12.0 version: 0.12.1