UNPKG

@itwin/core-backend

Version:
193 lines • 9.99 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ import { assert, expect } from "chai"; import * as path from "path"; import * as sinon from "sinon"; import { RpcRegistry } from "@itwin/core-common"; import { BriefcaseManager } from "../BriefcaseManager"; import { SnapshotDb } from "../IModelDb"; import { IModelHost, KnownLocations } from "../IModelHost"; import { Schemas } from "../Schema"; import { KnownTestLocations } from "./KnownTestLocations"; import { AzureServerStorage } from "@itwin/object-storage-azure"; import { TestUtils } from "./TestUtils"; import { IModelTestUtils } from "./IModelTestUtils"; import { Logger, LogLevel } from "@itwin/core-bentley"; import { overrideSyncNativeLogLevels } from "../internal/NativePlatform"; import { _getHubAccess, _hubAccess } from "../internal/Symbols"; describe("IModelHost", () => { const opts = { cacheDir: TestUtils.getCacheDir() }; beforeEach(async () => { await TestUtils.shutdownBackend(); }); afterEach(async () => { sinon.restore(); }); after(async () => { await TestUtils.startBackend(); }); it("valid default configuration", async () => { await IModelHost.startup(opts); // Valid registered implemented RPCs expect(RpcRegistry.instance.implementationClasses.size).to.equal(4); expect(RpcRegistry.instance.implementationClasses.get("IModelReadRpcInterface")).to.exist; expect(RpcRegistry.instance.implementationClasses.get("IModelTileRpcInterface")).to.exist; expect(RpcRegistry.instance.implementationClasses.get("SnapshotIModelRpcInterface")).to.exist; expect(RpcRegistry.instance.implementationClasses.get("DevToolsRpcInterface")).to.exist; expect(Schemas.getRegisteredSchema("BisCore")).to.exist; expect(Schemas.getRegisteredSchema("Generic")).to.exist; expect(Schemas.getRegisteredSchema("Functional")).to.exist; }); it("should properly cleanup beforeExit event listeners on shutdown", async () => { const beforeCount = process.listenerCount("beforeExit"); for (let i = 0; i <= 15; i++) { await IModelHost.startup(); await IModelHost.shutdown(); } const afterCount = process.listenerCount("beforeExit"); expect(beforeCount).to.be.equal(afterCount); }); it("should call logger sync function", async () => { let nSyncCalls = 0; overrideSyncNativeLogLevels(() => ++nSyncCalls); await IModelHost.startup(opts); expect(nSyncCalls).to.equal(0); Logger.setLevel("test-cat", LogLevel.Warning); expect(nSyncCalls).to.equal(1); overrideSyncNativeLogLevels(undefined); }); it("should raise onAfterStartup events", async () => { const eventHandler = sinon.spy(); IModelHost.onAfterStartup.addOnce(eventHandler); await IModelHost.startup(opts); expect(eventHandler.calledOnce).to.be.true; }); it("should raise onBeforeShutdown events", async () => { await TestUtils.startBackend(); const eventHandler = sinon.spy(); IModelHost.onBeforeShutdown.addOnce(eventHandler); const filename = IModelTestUtils.resolveAssetFile("GetSetAutoHandledStructProperties.bim"); const workspaceClose = sinon.spy(IModelHost.appWorkspace, "close"); const saveSettings = IModelHost.appWorkspace.settings; const settingClose = sinon.spy(saveSettings, "close"); expect(workspaceClose.callCount).eq(0); expect(settingClose.callCount).eq(0); expect(saveSettings._remove).to.not.be.undefined; // shutdown should close any opened iModels. Make sure that happens const imodel1 = SnapshotDb.openFile(filename, { key: "imodel1" }); const imodel2 = SnapshotDb.openFile(filename, { key: "imodel2" }); const imodel3 = SnapshotDb.openFile(filename, { key: "imodel3" }); const imodel4 = SnapshotDb.openFile(filename, { key: "imodel4" }); assert.notEqual(imodel1, imodel2); assert.notEqual(imodel2, imodel3); expect(imodel1.isOpen).to.be.true; expect(imodel2.isOpen).to.be.true; expect(imodel3.isOpen).to.be.true; imodel4.close(); // make sure it gets removed so we don't try to close it again on shutdown await TestUtils.shutdownBackend(); expect(eventHandler.calledOnce).to.be.true; assert.isFalse(imodel1.isOpen, "shutdown should close iModel1"); assert.isFalse(imodel2.isOpen, "shutdown should close iModel2"); assert.isFalse(imodel3.isOpen, "shutdown should close iModel3"); expect(workspaceClose.callCount).eq(1); expect(settingClose.callCount).eq(1); expect(saveSettings._remove).to.be.undefined; }); it("should auto-shutdown on process beforeExit event", async () => { await TestUtils.startBackend(); expect(IModelHost.isValid).to.be.true; const eventHandler = sinon.spy(); IModelHost.onBeforeShutdown.addOnce(eventHandler); process.emit("beforeExit", 0); await new Promise((resolve) => setImmediate(resolve)); expect(eventHandler.calledOnce).to.be.true; expect(IModelHost.isValid).to.be.false; }); it("should set the briefcase cache directory to expected locations", async () => { const config = {}; const cacheSubDir = "imodels"; // Test cache default location await IModelHost.shutdown(); await IModelHost.startup(config); let expectedDir = path.join(IModelHost.cacheDir, cacheSubDir); assert.strictEqual(expectedDir, BriefcaseManager.cacheDir); // Test custom cache location await IModelHost.shutdown(); config.cacheDir = KnownLocations.tmpdir; await IModelHost.startup(config); expectedDir = path.join(KnownLocations.tmpdir, cacheSubDir); assert.strictEqual(expectedDir, BriefcaseManager.cacheDir); }); it("should set Azure cloud storage provider for tile cache given credentials", async () => { const config = {}; config.tileCacheAzureCredentials = { account: "testAccount", accessKey: "testAccessKey", }; await IModelHost.startup(config); assert.isDefined(IModelHost.tileStorage); assert.isDefined(IModelHost.tileStorage.storage); assert.instanceOf(IModelHost.tileStorage.storage, AzureServerStorage); assert.equal((IModelHost.tileStorage?.storage)._config.baseUrl, `https://${config.tileCacheAzureCredentials.account}.blob.core.windows.net`); }); it("should set Azure cloud storage provider for tile cache with custom baseUrl", async () => { const config = {}; config.tileCacheAzureCredentials = { account: "testAccount", accessKey: "testAccessKey", baseUrl: "https://custom.blob.core.windows.net", }; await IModelHost.startup(config); assert.isDefined(IModelHost.tileStorage); assert.isDefined(IModelHost.tileStorage.storage); assert.instanceOf(IModelHost.tileStorage.storage, AzureServerStorage); assert.equal((IModelHost.tileStorage?.storage)._config.baseUrl, config.tileCacheAzureCredentials.baseUrl); }); it("should set custom cloud storage provider for tile cache", async () => { const config = {}; config.tileCacheStorage = {}; await IModelHost.startup(config); assert.isDefined(IModelHost.tileStorage); assert.equal(IModelHost.tileStorage.storage, config.tileCacheStorage); }); it("should throw if both tileCacheStorage and tileCacheAzureCredentials are set", async () => { const config = {}; config.tileCacheAzureCredentials = { account: "testAccount", accessKey: "testAccessKey", }; config.tileCacheStorage = {}; await expect(IModelHost.startup(config)).to.be.rejectedWith("Cannot use both Azure and custom cloud storage providers for tile cache."); }); it("should use local cache if cloud storage provider for tile cache is not set", async () => { await IModelHost.startup(opts); assert.isUndefined(IModelHost.tileStorage); }); it("should cleanup tileStorage on shutdown", async () => { const config = {}; config.tileCacheStorage = {}; await IModelHost.startup(config); assert.equal(IModelHost.tileStorage?.storage, config.tileCacheStorage); await IModelHost.shutdown(); assert.isUndefined(IModelHost.tileStorage); }); it("should throw if hubAccess is undefined and getter is called", async () => { await IModelHost.startup(opts); expect(IModelHost[_getHubAccess]()).undefined; expect(() => IModelHost[_hubAccess]).throws(); }); it("computeSchemaChecksum", () => { const assetsDir = path.join(KnownTestLocations.assetsDir, "ECSchemaOps"); const schemaXmlPath = path.join(assetsDir, "SchemaA.ecschema.xml"); let referencePaths = [assetsDir]; let sha1 = IModelHost.computeSchemaChecksum({ schemaXmlPath, referencePaths }); expect(sha1).equal("3ac6578060902aa0b8426b61d62045fdf7fa0b2b"); expect(() => IModelHost.computeSchemaChecksum({ schemaXmlPath, referencePaths, exactMatch: true })).throws("Failed to read schema SchemaA.ecschema"); referencePaths = [path.join(assetsDir, "exact-match")]; sha1 = IModelHost.computeSchemaChecksum({ schemaXmlPath, referencePaths, exactMatch: true }); expect(sha1).equal("2a618664fbba1df7c05f27d7c0e8f58de250003b"); }); }); //# sourceMappingURL=IModelHost.test.js.map