From 442cb76c288ca75919d71f3c0e1e3f5d4263ebbe Mon Sep 17 00:00:00 2001 From: Vlad Sudzilouski Date: Tue, 16 Apr 2024 15:05:03 -0400 Subject: [PATCH] Fix dehydrate tests (attempt #2): Use proper versions of DDS, DataStore, runtime, Loader in versioned tests (#20650) An extraction from #20621 Reason for changes These tests explode when I change interface between DDS & DataStore Description These tests are not written correctly. They mix arbitrary version of DDS with the latest version of FluidDataStoreRuntime (through TestFluidObjectFactory). We never supported such mixes - version of DDS has to match version or data store! Using matching TestFluidObjectFactory is easy, but then we start running into somewhat similar problem of mixing FluidDataStoreRuntime & ContainerRuntime. We support only N/N-1 mixes here, and nothing more. So we have to use the version provided by test framework, thus you see some dance above CodeLoader to get right factory. And while we are at it, it would be great for the test to use Loader version provided by test framework, not latest version. This breaks for old versions as the way test is written it works only for latest structure of the document. Previously test framework was running a ton of combos, but most of them were actually the same, as even when it was supplied different versions of loader, we were using only latest version of loader. So, the right fix here - use loader version provided by framework but skip all the tests that use old version of the loader, thus getting same result, but with fewer iterations (and faster tests). As part of fixing it, use proper back-compat compatible ways to get to entry points at loader & runtime layers. --- .../test/deRehydrateContainerTests.spec.ts | 96 +++++++++++++------ 1 file changed, 67 insertions(+), 29 deletions(-) diff --git a/packages/test/test-end-to-end-tests/src/test/deRehydrateContainerTests.spec.ts b/packages/test/test-end-to-end-tests/src/test/deRehydrateContainerTests.spec.ts index 3481a474e7dc..dcb3954a11c4 100644 --- a/packages/test/test-end-to-end-tests/src/test/deRehydrateContainerTests.spec.ts +++ b/packages/test/test-end-to-end-tests/src/test/deRehydrateContainerTests.spec.ts @@ -30,10 +30,13 @@ import { LoaderContainerTracker, LocalCodeLoader, TestFluidObject, - TestFluidObjectFactory, createDocumentId, + getContainerEntryPointBackCompat, + getDataStoreEntryPointBackCompat, } from "@fluidframework/test-utils/internal"; +import { createDataStoreFactory } from "@fluidframework/runtime-utils/internal"; import * as semver from "semver"; +import { pkgVersion } from "../packageVersion.js"; // eslint-disable-next-line import/no-internal-modules import type { SnapshotWithBlobs } from "../../../../loader/container-loader/lib/serializedStateManager.js"; @@ -197,7 +200,8 @@ describeCompat( async function createDetachedContainerAndGetEntryPoint() { const container: IContainer = await loader.createDetachedContainer(codeDetails); // Get the root dataStore from the detached container. - const defaultDataStore = (await container.getEntryPoint()) as TestFluidObject; + const defaultDataStore = + await getContainerEntryPointBackCompat(container); return { container, defaultDataStore, @@ -205,7 +209,8 @@ describeCompat( } function createTestLoader(): Loader { - const factory: TestFluidObjectFactory = new TestFluidObjectFactory([ + // It's important to use data store runtime of the same version as DDSs! + const factory = new apis.dataRuntime.TestFluidObjectFactory([ [sharedStringId, SharedString.getFactory()], [sharedMapId, SharedMap.getFactory()], [crcId, ConsensusRegisterCollection.getFactory()], @@ -216,8 +221,22 @@ describeCompat( [sparseMatrixId, SparseMatrix.getFactory()], [sharedCounterId, SharedCounter.getFactory()], ]); - const codeLoader = new LocalCodeLoader([[codeDetails, factory]], {}); - const testLoader = new Loader({ + + // This dance is to ensure that we get reasonable version of ContainerRuntime. + // If we do not set IRuntimeFactory property, LocalCodeLoader will use ContainerRuntime from current version + // We only support limited (N/N-1) compatibility for container runtime and data stores, so that will not work. + // Use version supplied by test framework + const defaultFactory = createDataStoreFactory("default", factory); + (defaultFactory as any).IRuntimeFactory = + new apis.containerRuntime.ContainerRuntimeFactoryWithDefaultDataStore({ + defaultFactory, + registryEntries: [[defaultFactory.type, Promise.resolve(defaultFactory)]], + runtimeOptions: {}, + }); + const codeLoader = new LocalCodeLoader([[codeDetails, defaultFactory]], {}); + + // Use Loader supplied by test framework. + const testLoader = new apis.loader.Loader({ urlResolver: provider.urlResolver, documentServiceFactory: provider.documentServiceFactory, codeLoader, @@ -229,7 +248,8 @@ describeCompat( const createPeerDataStore = async (containerRuntime: IContainerRuntimeBase) => { const dataStore = await containerRuntime.createDataStore(["default"]); - const peerDataStore = (await dataStore.entryPoint.get()) as ITestFluidObject; + const peerDataStore = + await getDataStoreEntryPointBackCompat(dataStore); return { peerDataStore, peerDataStoreRuntimeChannel: peerDataStore.channel, @@ -237,7 +257,7 @@ describeCompat( }; async function getDataObjectFromContainer(container: IContainer, key: string) { - const entryPoint = (await container.getEntryPoint()) as TestFluidObject; + const entryPoint = await getContainerEntryPointBackCompat(container); const handle: IFluidHandle | undefined = entryPoint.root.get(key); assert(handle !== undefined, `handle for [${key}] must exist`); return handle.get(); @@ -255,8 +275,16 @@ describeCompat( beforeEach("createLoader", async function () { provider = getTestObjectProvider(); if ( - semver.compare(provider.driver.version, "0.46.0") === -1 && - (provider.driver.type === "routerlicious" || provider.driver.type === "tinylicious") + // These tests use dedicated (same) version loader, container runtime, DDSs. + // Thus there is no value in running more pairs that are essentially exactly the same as other tests. + provider.type === "TestObjectProviderWithVersionedLoad" || + // These tests only work with the latest version of loader - + // they do make certain assumptions that are not valid for older loaders. This check could be relaxed in + // the future. + apis.loader.version !== pkgVersion || + (semver.compare(provider.driver.version, "0.46.0") === -1 && + (provider.driver.type === "routerlicious" || + provider.driver.type === "tinylicious")) ) { this.skip(); } @@ -399,9 +427,9 @@ describeCompat( await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); // Check for default data store - const entryPoint = await container2.getEntryPoint(); - assert.notStrictEqual(entryPoint, undefined, "Component should exist!!"); - const defaultDataStore = entryPoint as TestFluidObject; + const defaultDataStore = + await getContainerEntryPointBackCompat(container2); + assert.notStrictEqual(defaultDataStore, undefined, "Component should exist!!"); // Check for dds const sharedMap = await defaultDataStore.getSharedObject(sharedMapId); @@ -451,9 +479,9 @@ describeCompat( await container2.attach(request); // Check for default data store - const entryPoint = await container2.getEntryPoint(); - assert.notStrictEqual(entryPoint, undefined, "Component should exist!!"); - const defaultDataStore = entryPoint as TestFluidObject; + const defaultDataStore = + await getContainerEntryPointBackCompat(container2); + assert.notStrictEqual(defaultDataStore, undefined, "Component should exist!!"); // Check for dds const sharedMap = await defaultDataStore.getSharedObject(sharedMapId); @@ -502,9 +530,9 @@ describeCompat( } // Check for default data store - const entryPoint = await container1.getEntryPoint(); - assert.notStrictEqual(entryPoint, undefined, "Component should exist!!"); - const defaultDataStore = entryPoint as TestFluidObject; + const defaultDataStore = + await getContainerEntryPointBackCompat(container1); + assert.notStrictEqual(defaultDataStore, undefined, "Component should exist!!"); // Check for dds const sharedMap = await defaultDataStore.getSharedObject(sharedMapId); @@ -548,7 +576,8 @@ describeCompat( const { container } = await createDetachedContainerAndGetEntryPoint(); const snapshotTree = container.serialize(); - const defaultDataStore = (await container.getEntryPoint()) as TestFluidObject; + const defaultDataStore = + await getContainerEntryPointBackCompat(container); assert( defaultDataStore.context.storage !== undefined, "Storage should be present in detached data store", @@ -564,7 +593,8 @@ describeCompat( const container2: IContainer = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); - const defaultDataStore2 = (await container2.getEntryPoint()) as TestFluidObject; + const defaultDataStore2 = + await getContainerEntryPointBackCompat(container2); assert( defaultDataStore2.context.storage !== undefined, "Storage should be present in rehydrated data store", @@ -582,7 +612,8 @@ describeCompat( it("Change contents of dds, then rehydrate and then check summary", async () => { const { container } = await createDetachedContainerAndGetEntryPoint(); - const defaultDataStoreBefore = (await container.getEntryPoint()) as TestFluidObject; + const defaultDataStoreBefore = + await getContainerEntryPointBackCompat(container); const sharedStringBefore = await defaultDataStoreBefore.getSharedObject(sharedStringId); const intervalsBefore = sharedStringBefore.getIntervalCollection("intervals"); @@ -651,7 +682,8 @@ describeCompat( const container2 = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); - const defaultComponentAfter = (await container2.getEntryPoint()) as TestFluidObject; + const defaultComponentAfter = + await getContainerEntryPointBackCompat(container2); const sharedStringAfter = await defaultComponentAfter.getSharedObject(sharedStringId); const intervalsAfter = sharedStringAfter.getIntervalCollection("intervals"); @@ -703,7 +735,8 @@ describeCompat( it("Rehydrate container from summary, change contents of dds and then check summary", async () => { const { container } = await createDetachedContainerAndGetEntryPoint(); let str = "AA"; - const defaultComponent1 = (await container.getEntryPoint()) as TestFluidObject; + const defaultComponent1 = + await getContainerEntryPointBackCompat(container); const sharedString1 = await defaultComponent1.getSharedObject(sharedStringId); sharedString1.insertText(0, str); @@ -712,7 +745,7 @@ describeCompat( const container2 = await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); const defaultDataStoreBefore = - (await container2.getEntryPoint()) as TestFluidObject; + await getContainerEntryPointBackCompat(container2); const sharedStringBefore = await defaultDataStoreBefore.getSharedObject(sharedStringId); const sharedMapBefore = @@ -722,7 +755,8 @@ describeCompat( sharedMapBefore.set("0", str); await container2.attach(request); - const defaultComponentAfter = (await container.getEntryPoint()) as TestFluidObject; + const defaultComponentAfter = + await getContainerEntryPointBackCompat(container); const sharedStringAfter = await defaultComponentAfter.getSharedObject(sharedStringId); const sharedMapAfter = @@ -879,7 +913,7 @@ describeCompat( await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); const rehydratedEntryPoint = - (await rehydratedContainer.getEntryPoint()) as TestFluidObject; + await getContainerEntryPointBackCompat(rehydratedContainer); const rehydratedRootOfDataStore = await rehydratedEntryPoint.getSharedObject(sharedMapId); const dataStore2Handle: IFluidHandle | undefined = @@ -915,7 +949,7 @@ describeCompat( await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); const rehydratedEntryPoint = - (await rehydratedContainer.getEntryPoint()) as TestFluidObject; + await getContainerEntryPointBackCompat(rehydratedContainer); const rootOfDds2 = await rehydratedEntryPoint.getSharedObject(sharedMapId); const dds2Handle: IFluidHandle | undefined = rootOfDds2.get(dds2Key); @@ -958,7 +992,9 @@ describeCompat( await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); const rehydratedEntryPoint = - (await rehydratedContainer.getEntryPoint()) as TestFluidObject; + await getContainerEntryPointBackCompat( + rehydratedContainer, + ); const rootOfDds2 = await rehydratedEntryPoint.getSharedObject(sharedMapId); const dds2Handle: IFluidHandle | undefined = @@ -1020,7 +1056,9 @@ describeCompat( await loader.rehydrateDetachedContainerFromSnapshot(snapshotTree); const rehydratedEntryPoint = - (await rehydratedContainer.getEntryPoint()) as TestFluidObject; + await getContainerEntryPointBackCompat( + rehydratedContainer, + ); const rehydratedRootOfDataStore2 = await rehydratedEntryPoint.getSharedObject(sharedMapId); const dataStore2Handle: IFluidHandle | undefined =