@itwin/core-backend
Version:
iTwin.js backend components
435 lines • 18.1 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
import { expect } from "chai";
import { IModelTestUtils } from "../IModelTestUtils";
import { IModelHost } from "../../IModelHost";
import { TestUtils } from "../TestUtils";
describe("SettingsSchemas", () => {
// SettingsSchema tests change the state of the IModelHost object. They should always clear
// the current state before and after they run so they're not affected by, nor influence, other tests running in the same process.
const restartSession = async () => {
await IModelHost.shutdown();
await TestUtils.startBackend();
};
before(async () => {
await restartSession();
});
after(async () => {
await restartSession();
});
it("add groups", async () => {
const schemas = IModelHost.settingsSchemas;
// can't add a group with no name
expect(() => schemas.addGroup({})).throws(`has no "schemaPrefix" member`);
schemas.addGroup({
schemaPrefix: "title-test",
title: "Title Test",
description: "schema with a user-facing title",
settingDefs: {
setting: {
type: "string",
},
},
});
expect(schemas.groups.get("title-test")?.title).equals("Title Test");
expect(schemas.groups.get("title-test")?.description).equals("schema with a user-facing title");
expect(schemas.settingDefs.get("title-test/setting").type).equals("string");
schemas.addFile(IModelTestUtils.resolveAssetFile("TestSettings.schema.json"));
expect(schemas.groups.get("testApp")?.title).equals("Test App");
expect(schemas.groups.get("testApp")?.description).equals("the settings for test application 1");
expect(schemas.settingDefs.get("testApp/list/openMode").type).equals("string");
expect(schemas.settingDefs.get("testApp/list/openMode").default).equals("singleClick");
expect(schemas.settingDefs.get("testApp/tree/blah").default).equals(true);
});
it("resolveSchema resolves object extends chains recursively", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-object";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test object resolution",
typeDefs: {
nestedBase: {
type: "object",
required: ["nestedReq"],
properties: {
nestedReq: { type: "integer" },
nestedBaseOnly: { type: "string" },
},
},
baseThing: {
type: "object",
required: ["baseReq"],
properties: {
baseReq: { type: "string" },
overridden: { type: "string" },
baseOnly: { type: "number" },
nested: {
type: "object",
extends: `${prefix}/nestedBase`,
properties: {
nestedChildOnly: { type: "boolean" },
},
},
},
},
},
settingDefs: {
thing: {
type: "object",
extends: `${prefix}/baseThing`,
required: ["derivedReq"],
properties: {
derivedReq: { type: "boolean" },
overridden: { type: "number" },
},
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/thing`);
expect(resolved.type).to.equal("object");
expect(resolved).to.not.have.property("extends");
expect(resolved.required).to.have.members(["baseReq", "derivedReq"]);
expect(resolved.properties).to.include.keys("baseReq", "baseOnly", "derivedReq", "overridden", "nested");
expect(resolved.properties?.overridden.type).to.equal("number");
expect(resolved.properties?.baseOnly.type).to.equal("number");
const nested = resolved.properties?.nested;
expect(nested?.type).to.equal("object");
expect(nested).to.not.have.property("extends");
expect(nested?.required).to.have.members(["nestedReq"]);
expect(nested?.properties).to.include.keys("nestedReq", "nestedBaseOnly", "nestedChildOnly");
const resolvedPreservingExtends = schemas.getResolvedSettingDef(`${prefix}/thing`, { preserveExtends: true });
expect(resolvedPreservingExtends.extends).to.equal(`${prefix}/baseThing`);
expect(resolvedPreservingExtends.properties?.overridden.type).to.equal("number");
const nestedPreservingExtends = resolvedPreservingExtends.properties?.nested;
expect(nestedPreservingExtends?.extends).to.equal(`${prefix}/nestedBase`);
expect(nestedPreservingExtends?.properties).to.include.keys("nestedReq", "nestedBaseOnly", "nestedChildOnly");
}
finally {
schemas.removeGroup(prefix);
}
});
it("resolveSchema includes inherited properties from object typedefs", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-object-inherited-props";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test inherited object properties",
typeDefs: {
baseThing: {
type: "object",
properties: {
inheritedName: { type: "string" },
inheritedEnabled: { type: "boolean" },
},
},
},
settingDefs: {
thing: {
type: "object",
extends: `${prefix}/baseThing`,
properties: {
localCount: { type: "integer" },
},
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/thing`);
expect(resolved.properties).to.include.keys("inheritedName", "inheritedEnabled", "localCount");
expect(resolved.properties?.inheritedName.type).to.equal("string");
expect(resolved.properties?.inheritedEnabled.type).to.equal("boolean");
expect(resolved.properties?.localCount.type).to.equal("integer");
}
finally {
schemas.removeGroup(prefix);
}
});
it("resolveSchema deduplicates required properties inherited from object typedefs", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-object-required";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test required property deduplication",
typeDefs: {
baseThing: {
type: "object",
required: ["sharedReq", "baseReq"],
properties: {
sharedReq: { type: "string" },
baseReq: { type: "boolean" },
derivedReq: { type: "integer" },
},
},
},
settingDefs: {
thing: {
type: "object",
extends: `${prefix}/baseThing`,
required: ["sharedReq", "derivedReq"],
properties: {
sharedReq: { type: "string" },
derivedReq: { type: "integer" },
},
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/thing`);
expect(resolved.required).to.have.members(["sharedReq", "baseReq", "derivedReq"]);
expect(resolved.required?.filter((name) => name === "sharedReq")).to.have.lengthOf(1);
}
finally {
schemas.removeGroup(prefix);
}
});
it("resolveSchema resolves array items through extends", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-array";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test array resolution",
typeDefs: {
stringList: {
type: "array",
combineArray: true,
minItems: 1,
description: "base array typedef",
items: { type: "string" },
},
},
settingDefs: {
names: {
type: "array",
extends: `${prefix}/stringList`,
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/names`);
expect(resolved.type).to.equal("array");
expect(resolved).to.not.have.property("extends");
expect(resolved.combineArray).to.equal(true);
expect(resolved.minItems).to.equal(1);
expect(resolved.description).to.equal("base array typedef");
expect(resolved.items?.type).to.equal("string");
expect(resolved.items).to.not.have.property("extends");
}
finally {
schemas.removeGroup(prefix);
}
});
it("resolveSchema lets derived array schema properties override inherited typedef properties", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-array-overrides";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test array override precedence",
typeDefs: {
stringList: {
type: "array",
combineArray: true,
minItems: 1,
description: "base array typedef",
items: { type: "string" },
},
},
settingDefs: {
names: {
type: "array",
extends: `${prefix}/stringList`,
combineArray: false,
minItems: 3,
description: "derived array schema",
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/names`);
expect(resolved.combineArray).to.equal(false);
expect(resolved.minItems).to.equal(3);
expect(resolved.description).to.equal("derived array schema");
expect(resolved.items?.type).to.equal("string");
}
finally {
schemas.removeGroup(prefix);
}
});
it("resolveSchema resolves array items through multi-level typedef inheritance", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-array-multilevel";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test multi-level array typedef inheritance",
typeDefs: {
c: {
type: "array",
combineArray: true,
items: { type: "string" },
},
b: {
type: "array",
extends: `${prefix}/c`,
},
},
settingDefs: {
names: {
type: "array",
extends: `${prefix}/b`,
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/names`);
expect(resolved.combineArray).to.equal(true);
expect(resolved.items?.type).to.equal("string");
}
finally {
schemas.removeGroup(prefix);
}
});
it("getResolvedSettingDef returns a resolved setting schema by name", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-setting-schema";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test resolved setting schema lookup",
typeDefs: {
stringList: {
type: "array",
combineArray: true,
items: { type: "string" },
},
},
settingDefs: {
names: {
type: "array",
extends: `${prefix}/stringList`,
},
},
});
try {
const resolved = schemas.getResolvedSettingDef(`${prefix}/names`);
expect(resolved).to.not.be.undefined;
expect(resolved?.type).to.equal("array");
expect(resolved).to.not.have.property("extends");
expect(resolved?.combineArray).to.equal(true);
expect(resolved?.items?.type).to.equal("string");
expect(schemas.getResolvedSettingDef(`${prefix}/missing`)).to.be.undefined;
}
finally {
schemas.removeGroup(prefix);
}
});
it("getResolvedSettingDef resolves built-in workspace settings schemas", () => {
const schemas = IModelHost.settingsSchemas;
const resolved = schemas.getResolvedSettingDef("itwin/core/workspace/settingsWorkspaces");
expect(resolved).to.not.be.undefined;
expect(resolved?.type).to.equal("array");
expect(resolved?.items?.type).to.equal("object");
expect(resolved?.items).to.not.have.property("extends");
expect(resolved?.items?.required).to.have.members(["containerId", "baseUri"]);
expect(resolved?.items?.properties).to.include.keys("dbName", "baseUri", "containerId", "storageType", "resourceName", "priority");
expect(resolved?.items?.properties?.storageType.default).to.equal("azure");
});
it("getResolvedSettingDef throws on circular typedef references", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-cycle";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test circular typedef resolution",
typeDefs: {
a: {
type: "object",
extends: `${prefix}/b`,
properties: {
aOnly: { type: "string" },
},
},
b: {
type: "object",
extends: `${prefix}/a`,
properties: {
bOnly: { type: "string" },
},
},
},
settingDefs: {
thing: {
type: "object",
extends: `${prefix}/a`,
},
},
});
try {
expect(() => schemas.getResolvedSettingDef(`${prefix}/thing`)).to.throw("circular typeDef reference detected");
}
finally {
schemas.removeGroup(prefix);
}
});
it("getResolvedSettingDef throws on nested recursive typedef references", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-nested-cycle";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test nested recursive typedef resolution",
typeDefs: {
node: {
type: "object",
properties: {
child: {
type: "object",
extends: `${prefix}/node`,
},
},
},
},
settingDefs: {
root: {
type: "object",
extends: `${prefix}/node`,
},
},
});
try {
expect(() => schemas.getResolvedSettingDef(`${prefix}/root`)).to.throw("circular typeDef reference detected");
}
finally {
schemas.removeGroup(prefix);
}
});
it("getResolvedSettingDef throws when an extends target cannot be found", () => {
const schemas = IModelHost.settingsSchemas;
const prefix = "resolve-schema-missing-typedef";
schemas.removeGroup(prefix);
schemas.addGroup({
schemaPrefix: prefix,
description: "schema used to test missing typedef resolution",
settingDefs: {
thing: {
type: "object",
extends: `${prefix}/missingType`,
},
},
});
try {
expect(() => schemas.getResolvedSettingDef(`${prefix}/thing`)).to.throw(`typeDef ${prefix}/missingType does not exist`);
}
finally {
schemas.removeGroup(prefix);
}
});
});
//# sourceMappingURL=SettingsSchemas.test.js.map