Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*!
* 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<ITestFluidObject>(container);
const tree = await dataObject.getSharedObject<ITree>(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("TreeDataObject", "FullCompat", (getTestObjectProvider, apis: CompatApis) => {
let provider: ITestObjectProvider;

beforeEach(function () {
// SharedTree was added in version 2.0.0. Skip all cross-client compat tests in this suite for older versions.
// The test framework installs the latest SharedTree version for these older versions as a workaround
// so, technically these tests could work. However, idCompressor is not supported by the runtime in these versions,
// which causes the tests to fail.
const version = apis.containerRuntime.version;
const versionForLoading = apis.containerRuntimeForLoading?.version;
if (
versionForLoading !== undefined &&
(lt(version, "2.0.0") || lt(versionForLoading, "2.0.0"))
Comment on lines +79 to +80
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will not skip when versionForLoading === undefined even if lt(version, "2.0.0") === true.
Is that intentional? What happens when the test runs in that configuration?

) {
this.skip();
}

provider = getTestObjectProvider();
});

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");
});
});
1 change: 1 addition & 0 deletions packages/test/test-version-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
13 changes: 4 additions & 9 deletions packages/test/test-version-utils/src/compatUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,12 @@ function createGetDataStoreFactoryFunction(api: ReturnType<typeof getDataRuntime
function convertRegistry(registry: ChannelFactoryRegistry = []): ChannelFactoryRegistry {
const oldRegistry: [string | undefined, IChannelFactory][] = [];
for (const [key, factory] of registry) {
if (factory.type === "https://graph.microsoft.com/types/tree") {
oldRegistry.push([key, factory]);
} else {
const oldFactory = registryMapping[factory.type];
if (oldFactory === undefined) {
throw Error(`Invalid or unimplemented channel factory: ${factory.type}`);
}
oldRegistry.push([key, oldFactory]);
const oldFactory = registryMapping[factory.type];
if (oldFactory === undefined) {
throw Error(`Invalid or unimplemented channel factory: ${factory.type}`);
}
oldRegistry.push([key, oldFactory]);
}

return oldRegistry;
}

Expand Down
16 changes: 16 additions & 0 deletions packages/test/test-version-utils/src/testApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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";
import * as treeCurrent from "@fluidframework/tree/internal";
import { SharedTree } from "@fluidframework/tree/internal";

// ContainerRuntime and Data Runtime API
import * as semver from "semver";
Expand Down Expand Up @@ -81,6 +83,7 @@ const packageList = [
"@fluidframework/odsp-driver",
"@fluidframework/routerlicious-driver",
"@fluidframework/agent-scheduler",
"@fluidframework/tree",
];

/**
Expand Down Expand Up @@ -166,6 +169,7 @@ export const DataRuntimeApi = {
SparseMatrix,
SharedArray,
SharedSignal,
SharedTree,
},
/**
* Contains all APIs from imported DDS packages.
Expand All @@ -187,6 +191,7 @@ export const DataRuntimeApi = {
sequence,
sequenceDeprecated,
agentScheduler,
tree: treeCurrent,
},
};

Expand Down Expand Up @@ -283,6 +288,14 @@ async function loadDataRuntime(
),
loadPackage(modulePath, "@fluidframework/agent-scheduler"),
]);
// SharedTree was added in version 2.0.0.
// For earlier versions, load it from the current package. This means that cross-client compat tests
// will use the latest SharedTree version for slow trains. However, this is acceptable since it will
// give cross-client coverage for fast trains.
const tree = semver.lt(version, "2.0.0")
? treeCurrent
: await loadPackage(modulePath, "@fluidframework/tree");

const { FluidDataStoreRuntime } = datastore;
const { SharedCell } = cell;
const { SharedCounter } = counter;
Expand All @@ -292,6 +305,7 @@ async function loadDataRuntime(
const { ConsensusRegisterCollection } = registerCollection;
const { SharedString } = sequence;
const { SparseMatrix } = sequenceDeprecated;
const { SharedTree } = tree;
/* eslint-enable @typescript-eslint/no-shadow */

const dataRuntime = {
Expand All @@ -312,6 +326,7 @@ async function loadDataRuntime(
SparseMatrix,
SharedArray,
SharedSignal,
SharedTree,
},
packages: {
datastore,
Expand All @@ -324,6 +339,7 @@ async function loadDataRuntime(
registerCollection,
sequenceDeprecated,
agentScheduler,
tree,
},
};
dataRuntimeCache.set(version, dataRuntime);
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading