From 4dab18e52f878e029c5f619ee640d5632b917c9e Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 09:50:58 +0300 Subject: [PATCH 1/8] Fixed imodel:connection:auth being called in populate when using service client. Moved out setup for running tests with a specific client to utils/mocha-global-setup-*.ts. Added seperate launch configurations for launching active test with a specific client. --- .vscode/launch.json | 29 ++++++++++++++++--- .../imodel-connection-run.test.ts | 9 ------ .../named-version.test.ts | 10 +++++++ .../native-client-parallel/populate.test.ts | 8 ----- .../short-running.test.ts | 7 ----- .../main-cases/native-client-serial.test.ts | 4 --- .../imodel-connection-run.test.ts | 6 ---- .../named-version.test.ts | 10 +++++++ .../service-client-parallel/populate.test.ts | 8 ----- .../short-running.test.ts | 6 ---- .../main-cases/service-client-serial.test.ts | 4 --- .../utils/mocha-global-setup-native.ts | 11 +++++++ .../utils/mocha-global-setup-service.ts | 6 ++++ package.json | 4 +-- src/commands/imodel/populate.ts | 13 +++++++-- 15 files changed, 74 insertions(+), 61 deletions(-) create mode 100644 integration-tests/main-cases/native-client-parallel/named-version.test.ts create mode 100644 integration-tests/main-cases/service-client-parallel/named-version.test.ts create mode 100644 integration-tests/utils/mocha-global-setup-native.ts create mode 100644 integration-tests/utils/mocha-global-setup-service.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index edc7f452..4eaafae0 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -42,13 +42,13 @@ "--forbid-only", "--timeout", "999999", - "integration-tests/main-cases/*.test.ts" + "integration-tests/main-cases/*.test.ts", ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" }, { - "name": "Debug Active Test", + "name": "Debug Active Test (Service Client)", "skipFiles": [ "/**" ], @@ -59,10 +59,31 @@ "--forbid-only", "--timeout", "999999", - "${file}" + "${file}", + "--require", + "integration-tests/utils/mocha-global-setup-native.ts" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" - } + }, + { + "name": "Debug Active Test (Native Client)", + "skipFiles": [ + "/**" + ], + "type": "node", + "request": "launch", + "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/mocha", + "args": [ + "--forbid-only", + "--timeout", + "999999", + "${file}", + "--require", + "integration-tests/utils/mocha-global-setup-native.ts" + ], + "console": "integratedTerminal", + "internalConsoleOptions": "neverOpen" + }, ] } \ No newline at end of file diff --git a/integration-tests/main-cases/native-client-parallel/imodel-connection-run.test.ts b/integration-tests/main-cases/native-client-parallel/imodel-connection-run.test.ts index 4e93294b..26531de3 100644 --- a/integration-tests/main-cases/native-client-parallel/imodel-connection-run.test.ts +++ b/integration-tests/main-cases/native-client-parallel/imodel-connection-run.test.ts @@ -3,17 +3,8 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { runCommand } from '@oclif/test'; - import iModelConnectionRunTests from '../../imodel/connection/run.test'; -import { nativeLoginToCli } from '../../utils/helpers'; describe('Native Client Tests (imodel connection run)', async () => { - before(async function() { - this.timeout(5 * 60 * 1000); - await nativeLoginToCli(); - await runCommand(`imodel connection auth`); - }) - iModelConnectionRunTests(); }); \ No newline at end of file diff --git a/integration-tests/main-cases/native-client-parallel/named-version.test.ts b/integration-tests/main-cases/native-client-parallel/named-version.test.ts new file mode 100644 index 00000000..499867e3 --- /dev/null +++ b/integration-tests/main-cases/native-client-parallel/named-version.test.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import iModelNamedVersionTests from '../../imodel/named-version.test'; + +describe('Native Client Tests (imodel named-version)', async () => { + iModelNamedVersionTests(); +}); \ No newline at end of file diff --git a/integration-tests/main-cases/native-client-parallel/populate.test.ts b/integration-tests/main-cases/native-client-parallel/populate.test.ts index 602d5636..1304e635 100644 --- a/integration-tests/main-cases/native-client-parallel/populate.test.ts +++ b/integration-tests/main-cases/native-client-parallel/populate.test.ts @@ -3,16 +3,8 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import iModelNamedVersionTests from '../../imodel/named-version.test'; import iModelPopulateTests from '../../imodel/populate.test'; -import { nativeLoginToCli } from '../../utils/helpers'; describe('Native Client Tests (imodel populate)', async () => { - before(async function() { - this.timeout(5 * 60 * 1000); - await nativeLoginToCli(); - }) - - iModelNamedVersionTests(); iModelPopulateTests(); }); \ No newline at end of file diff --git a/integration-tests/main-cases/native-client-parallel/short-running.test.ts b/integration-tests/main-cases/native-client-parallel/short-running.test.ts index 4bb75668..503eb3f9 100644 --- a/integration-tests/main-cases/native-client-parallel/short-running.test.ts +++ b/integration-tests/main-cases/native-client-parallel/short-running.test.ts @@ -4,16 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import accessControlNativeTests from '../../access-control-native/access-control-native.test' -import { nativeLoginToCli } from '../../utils/helpers'; import sharedQuickUseCasesParallel from '../shared-quick-use-cases-parallel'; describe('Native Client Tests', async () => { - before(async function() { - this.timeout(5 * 60 * 1000); - await nativeLoginToCli(); - }) - accessControlNativeTests(); sharedQuickUseCasesParallel(); - }); \ No newline at end of file diff --git a/integration-tests/main-cases/native-client-serial.test.ts b/integration-tests/main-cases/native-client-serial.test.ts index f870a446..76af5712 100644 --- a/integration-tests/main-cases/native-client-serial.test.ts +++ b/integration-tests/main-cases/native-client-serial.test.ts @@ -8,10 +8,6 @@ import iModelConnectionAuthTests from '../imodel-native/connection/auth.test' import { nativeLoginToCli } from '../utils/helpers'; describe('Native Client Tests (serial)', async () => { - before(async () => { - await nativeLoginToCli(); - }) - describe('Authentication Integration Tests', async () => { after(async () => { await nativeLoginToCli(); diff --git a/integration-tests/main-cases/service-client-parallel/imodel-connection-run.test.ts b/integration-tests/main-cases/service-client-parallel/imodel-connection-run.test.ts index f3800910..674ac4b4 100644 --- a/integration-tests/main-cases/service-client-parallel/imodel-connection-run.test.ts +++ b/integration-tests/main-cases/service-client-parallel/imodel-connection-run.test.ts @@ -4,13 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import iModelConnectionRunTests from '../../imodel/connection/run.test'; -import { logoutFromCLI } from '../../utils/helpers'; describe('Service Client Tests (imodel connection run)', async () => { - before(async function() { - this.timeout(5 * 60 * 1000); - await logoutFromCLI(); - }) - iModelConnectionRunTests(); }); \ No newline at end of file diff --git a/integration-tests/main-cases/service-client-parallel/named-version.test.ts b/integration-tests/main-cases/service-client-parallel/named-version.test.ts new file mode 100644 index 00000000..6f9ba0e7 --- /dev/null +++ b/integration-tests/main-cases/service-client-parallel/named-version.test.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Bentley Systems, Incorporated. All rights reserved. +* See LICENSE.md in the project root for license terms and full copyright notice. +*--------------------------------------------------------------------------------------------*/ + +import iModelNamedVersionTests from '../../imodel/named-version.test'; + +describe('Service Client Tests (imodel named-version)', () => { + iModelNamedVersionTests(); +}); \ No newline at end of file diff --git a/integration-tests/main-cases/service-client-parallel/populate.test.ts b/integration-tests/main-cases/service-client-parallel/populate.test.ts index 84f1ac1a..b8f7ea82 100644 --- a/integration-tests/main-cases/service-client-parallel/populate.test.ts +++ b/integration-tests/main-cases/service-client-parallel/populate.test.ts @@ -3,16 +3,8 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import iModelNamedVersionTests from '../../imodel/named-version.test'; import iModelPopulateTests from '../../imodel/populate.test' -import { logoutFromCLI } from '../../utils/helpers'; describe('Service Client Tests (imodel populate)', () => { - before(async function() { - this.timeout(5 * 60 * 1000); - await logoutFromCLI(); - }) - - iModelNamedVersionTests(); iModelPopulateTests(); }); \ No newline at end of file diff --git a/integration-tests/main-cases/service-client-parallel/short-running.test.ts b/integration-tests/main-cases/service-client-parallel/short-running.test.ts index 70416f65..f6663582 100644 --- a/integration-tests/main-cases/service-client-parallel/short-running.test.ts +++ b/integration-tests/main-cases/service-client-parallel/short-running.test.ts @@ -3,14 +3,8 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { logoutFromCLI } from '../../utils/helpers' import sharedQuickUseCasesParallel from '../shared-quick-use-cases-parallel'; describe('Service Client Tests', () => { - before(async function() { - this.timeout(5 * 60 * 1000); - await logoutFromCLI(); - }) - sharedQuickUseCasesParallel(); }); \ No newline at end of file diff --git a/integration-tests/main-cases/service-client-serial.test.ts b/integration-tests/main-cases/service-client-serial.test.ts index 9d80d1fd..3ec6c8ea 100644 --- a/integration-tests/main-cases/service-client-serial.test.ts +++ b/integration-tests/main-cases/service-client-serial.test.ts @@ -5,12 +5,8 @@ import authTests from '../auth/auth.test' import authTestsService from '../auth-service/auth-service.test' -import { logoutFromCLI } from '../utils/helpers' describe('Service Client Tests (serial)', () => { - before(async () => { - await logoutFromCLI(); - }) describe('Authentication Integration Tests', () => { authTestsService(); authTests(); diff --git a/integration-tests/utils/mocha-global-setup-native.ts b/integration-tests/utils/mocha-global-setup-native.ts new file mode 100644 index 00000000..b2eca31b --- /dev/null +++ b/integration-tests/utils/mocha-global-setup-native.ts @@ -0,0 +1,11 @@ +import { runCommand } from "@oclif/test"; +import { expect } from "chai"; + +import { authInfo } from '../../src/services/synchronizationClient/models/connection-auth'; +import { nativeLoginToCli } from "./helpers"; + +export async function mochaGlobalSetup() { + await nativeLoginToCli(); + const { result } = await runCommand(`imodel connection auth`); + expect(result?.isUserAuthorized).to.be.equal(true); +} \ No newline at end of file diff --git a/integration-tests/utils/mocha-global-setup-service.ts b/integration-tests/utils/mocha-global-setup-service.ts new file mode 100644 index 00000000..9443342b --- /dev/null +++ b/integration-tests/utils/mocha-global-setup-service.ts @@ -0,0 +1,6 @@ +import { logoutFromCLI, serviceLoginToCli } from "./helpers"; + +export async function mochaGlobalSetup() { + await logoutFromCLI(); + await serviceLoginToCli(); +} \ No newline at end of file diff --git a/package.json b/package.json index 0eefe4c3..619e9efc 100644 --- a/package.json +++ b/package.json @@ -163,8 +163,8 @@ "prepack": "oclif manifest && oclif readme", "test": "mocha --forbid-only \"integration-tests/main-cases/*.test.ts\"", "test:formatting": "mocha --forbid-only \"integration-tests/main-cases/formatting.test.ts\"", - "test:service": "mocha --forbid-only integration-tests/main-cases/service-client-serial.test.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/service-client-parallel/*.test.ts", - "test:native": "mocha --forbid-only integration-tests/main-cases/native-client-serial.test.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/native-client-parallel/*.test.ts", + "test:service": "mocha --forbid-only integration-tests/main-cases/service-client-serial.test.ts --require integration-tests/utils/mocha-global-setup-service.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/service-client-parallel/*.test.ts --require integration-tests/utils/mocha-global-setup-service.ts", + "test:native": "mocha --forbid-only integration-tests/main-cases/native-client-serial.test.ts --require integration-tests/utils/mocha-global-setup-native.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/native-client-parallel/*.test.ts --require integration-tests/utils/mocha-global-setup-native.ts", "version": "oclif readme && git add README.md", "docs-generator": "node ./bin/run.js docs-generator" }, diff --git a/src/commands/imodel/populate.ts b/src/commands/imodel/populate.ts index 57fc7f6d..b49bb8b7 100644 --- a/src/commands/imodel/populate.ts +++ b/src/commands/imodel/populate.ts @@ -131,9 +131,16 @@ export default class PopulateIModel extends BaseCommand { this.log(`Checking existing connections for iModel ID: ${iModel.id}`); const existingConnections = await this.runCommand('imodel:connection:list', ['--imodel-id', iModel.id]); - const connectionAuth = await this.runCommand('imodel:connection:auth', []); - if (connectionAuth.isUserAuthorized === undefined) { - this.error('User is not authenticated for connection run'); + + const authInfo = await this.runCommand('auth:info', []); + const authType = authInfo.authorizationType === 'Service' ? 'Service' : 'User'; + + if(authType === 'User') { + this.log("Authorizing..."); + const connectionAuth = await this.runCommand('imodel:connection:auth', []); + if (connectionAuth.isUserAuthorized === undefined) { + this.error('User is not authenticated for connection run'); + } } const connectionId = await this.findOrCreateDefaultConnection(existingConnections.connections, fileIds, iModel.id); From 07b2633610fa90900a58c5ea9ea1c493a9e2cce1 Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 13:18:22 +0300 Subject: [PATCH 2/8] Added a check for failures to imodel populate command. Added a few tests for imodel populate command. Moved current used client notification to integration-tests/utils/mocha-global-setup-*.ts. A few misc fixes. --- .vscode/launch.json | 2 +- integration-tests/imodel/populate.test.ts | 72 +++++++++++++++---- .../utils/mocha-global-setup-native.ts | 1 + .../utils/mocha-global-setup-service.ts | 1 + .../utils/run-suite-if-main-module.ts | 7 -- src/commands/imodel/populate.ts | 26 ++++++- .../models/storage-connection.ts | 6 ++ 7 files changed, 93 insertions(+), 22 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 4eaafae0..a9eed570 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -61,7 +61,7 @@ "999999", "${file}", "--require", - "integration-tests/utils/mocha-global-setup-native.ts" + "integration-tests/utils/mocha-global-setup-service.ts" ], "console": "integratedTerminal", "internalConsoleOptions": "neverOpen" diff --git a/integration-tests/imodel/populate.test.ts b/integration-tests/imodel/populate.test.ts index 029b5e5d..b775496b 100644 --- a/integration-tests/imodel/populate.test.ts +++ b/integration-tests/imodel/populate.test.ts @@ -6,13 +6,18 @@ import { runCommand } from '@oclif/test'; import { expect } from 'chai'; -import { createFile, createIModel, createITwin, getRootFolderId } from '../utils/helpers'; +import { populateResponse} from '../../src/commands/imodel/populate'; +import { executionResult } from '../../src/services/synchronizationClient/models/execution-result'; +import { executionState } from '../../src/services/synchronizationClient/models/execution-state'; +import { storageConnection } from '../../src/services/synchronizationClient/models/storage-connection'; +import { storageRun } from '../../src/services/synchronizationClient/models/storage-run'; +import { createIModel, createITwin } from '../utils/helpers'; import runSuiteIfMainModule from '../utils/run-suite-if-main-module'; const tests = () => describe('populate', () => { - const testFileName = 'test.zip'; - const testFilePath = 'integration-tests/test.zip'; - let testFileId: string; + const failingTestFilePath = 'integration-tests/test.zip' + const testFilePath = 'examples/datasets/ExtonCampus.dgn'; + let testIModelId: string; let testITwinId: string; @@ -21,24 +26,67 @@ const tests = () => describe('populate', () => { testITwinId = testITwin.id!; const testIModel = await createIModel(`cli-imodel-integration-test-${new Date().toISOString()}`, testITwinId); testIModelId = testIModel.id!; - const rootFolderId = await getRootFolderId(testITwinId); - const testFile = await createFile(rootFolderId, testFileName, testFilePath); - testFileId = testFile.id!; }); after(async () => { - const { result: fileDeleteResult } = await runCommand(`storage file delete --file-id ${testFileId}`); const { result: iModelDeleteResult } = await runCommand(`imodel delete --imodel-id ${testIModelId}`); const { result: iTwinDeleteResult } = await runCommand(`itwin delete --itwin-id ${testITwinId}`); - expect(fileDeleteResult).to.have.property('result', 'deleted'); expect(iModelDeleteResult).to.have.property('result', 'deleted'); expect(iTwinDeleteResult).to.have.property('result', 'deleted'); }); - it('should populate the iModel with the uploaded file', async () => { - const result = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type SPPID`); - expect(result.result).to.have.property('iModelId', testIModelId); + it.skip('should populate the iModel with the uploaded file', async () => { + const { result: populateResult } = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type MSTN`); + expect(populateResult).to.not.be.undefined; + expect(populateResult!.iTwinId).to.be.equal(testITwinId); + expect(populateResult!.iModelId).to.be.equal(testIModelId); + expect(populateResult!.summary).to.not.be.undefined; + expect(populateResult!.summary.length).to.be.equal(1); + expect(populateResult!.summary[0].connectionId).to.not.be.undefined; + expect(populateResult!.summary[0].runId).to.not.be.undefined; + + const {connectionId, runId} = populateResult!.summary[0]; + const { result: infoResult } = await runCommand(`imodel connection run info -c ${connectionId} --connection-run-id ${runId}`); + expect(infoResult?.state).to.be.equal(executionState.COMPLETED); + expect(infoResult?.result).to.be.equal(executionResult.SUCCESS); + }).timeout(30 * 60 * 1000); + + it.skip('should populate the iModel with the uploaded file (no-wait flag with polling)', async () => { + const { result: populateResult } = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type SPPID --no-wait`); + expect(populateResult).to.not.be.undefined; + expect(populateResult!.iTwinId).to.be.equal(testITwinId); + expect(populateResult!.iModelId).to.be.equal(testIModelId); + expect(populateResult!.summary).to.not.be.undefined; + expect(populateResult!.summary.length).to.be.equal(1); + expect(populateResult!.summary[0].connectionId).to.not.be.undefined; + expect(populateResult!.summary[0].runId).to.not.be.undefined; + + const {connectionId, runId} = populateResult!.summary[0]; + let { result: infoResult } = await runCommand(`imodel connection run info -c ${connectionId} --connection-run-id ${runId}`); + let index = 0; + while(infoResult?.state !== "Completed" && ++index < 15) { + // eslint-disable-next-line no-await-in-loop + await new Promise(r => {setTimeout(r, 3000 * index)}); + + // eslint-disable-next-line no-await-in-loop + const { result } = await runCommand(`imodel connection run info -c ${connectionId} --connection-run-id ${runId}`); + infoResult = result; + } + + expect(infoResult?.state).to.be.equal(executionState.COMPLETED); + expect(infoResult?.result).to.be.equal(executionResult.SUCCESS); + }).timeout(30 * 60 * 1000); + + it('should return an error message if synchronization run completes with a non-success state', async () => { + const { error: populateError } = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${failingTestFilePath} --connector-type IFC`); + expect(populateError).to.not.be.undefined; + expect(populateError?.message).to.match(/Synchronization run .*? resulted in an error. Run 'itp imodel connection run info --connection-id .*? --connection-run-id .*?' for more info./); + + const command = populateError?.message.match(/imodel connection run info --connection-id .*? --connection-run-id .*?'/)![0]?.slice(0,-1); + const { result: infoResult } = await runCommand(command!); + expect(infoResult).to.not.be.undefined; + expect(infoResult?.error).to.not.be.null; }).timeout(30 * 60 * 1000); }); diff --git a/integration-tests/utils/mocha-global-setup-native.ts b/integration-tests/utils/mocha-global-setup-native.ts index b2eca31b..ae89ea7a 100644 --- a/integration-tests/utils/mocha-global-setup-native.ts +++ b/integration-tests/utils/mocha-global-setup-native.ts @@ -8,4 +8,5 @@ export async function mochaGlobalSetup() { await nativeLoginToCli(); const { result } = await runCommand(`imodel connection auth`); expect(result?.isUserAuthorized).to.be.equal(true); + console.log("\n\nRunning tests with native client") } \ No newline at end of file diff --git a/integration-tests/utils/mocha-global-setup-service.ts b/integration-tests/utils/mocha-global-setup-service.ts index 9443342b..31605d9e 100644 --- a/integration-tests/utils/mocha-global-setup-service.ts +++ b/integration-tests/utils/mocha-global-setup-service.ts @@ -3,4 +3,5 @@ import { logoutFromCLI, serviceLoginToCli } from "./helpers"; export async function mochaGlobalSetup() { await logoutFromCLI(); await serviceLoginToCli(); + console.log("\n\nRunning tests with service client") } \ No newline at end of file diff --git a/integration-tests/utils/run-suite-if-main-module.ts b/integration-tests/utils/run-suite-if-main-module.ts index f50102fc..3597e656 100644 --- a/integration-tests/utils/run-suite-if-main-module.ts +++ b/integration-tests/utils/run-suite-if-main-module.ts @@ -6,8 +6,6 @@ import process from 'node:process'; import {fileURLToPath} from 'node:url'; -import { isNativeAuthAccessTokenCached } from './helpers'; - /** * Checks whether the current test file being executed matches the file/pattern, that was provided as an argument to mocha. * This makes it possible to both export tests and be able to run only part of the tests with 'Debug Active Test' launch configuration. @@ -49,11 +47,6 @@ function isMainModule(meta: {url: string}) { */ export default function runSuiteIfMainModule(meta: {url: string}, testSuite: () => Mocha.Suite): void { if (isMainModule(meta)) { - if (isNativeAuthAccessTokenCached()) - console.log("\n\nRunning tests with native client") - else - console.log("\n\nRunning tests with service client") - testSuite(); } } \ No newline at end of file diff --git a/src/commands/imodel/populate.ts b/src/commands/imodel/populate.ts index b49bb8b7..65450fd5 100644 --- a/src/commands/imodel/populate.ts +++ b/src/commands/imodel/populate.ts @@ -18,6 +18,7 @@ import { fileUpload } from "../../services/storage-client/models/file-upload.js" import { itemsWithFolderLink } from "../../services/storage-client/models/items-with-folder-link.js" import { authInfo } from "../../services/synchronizationClient/models/connection-auth.js" import { connectorType } from "../../services/synchronizationClient/models/connector-type.js" +import { executionResult } from "../../services/synchronizationClient/models/execution-result.js"; import { executionState } from "../../services/synchronizationClient/models/execution-state.js" import { sourceFile } from "../../services/synchronizationClient/models/source-file.js" import { storageConnection } from "../../services/synchronizationClient/models/storage-connection.js" @@ -162,12 +163,14 @@ export default class PopulateIModel extends BaseCommand { this.log('Synchronization process completed'); } - return { + const populateResponse: populateResponse = { iModelId: iModel.id, iTwinId: iModel.iTwinId, rootFolderId, summary, - }; + } + + return populateResponse; } private checkAndGetFilesWithConnectors(files: string[], connectorTypes: string[] | undefined) : NewFileInfo[] { @@ -256,6 +259,10 @@ export default class PopulateIModel extends BaseCommand { // eslint-disable-next-line no-unmodified-loop-condition } while (waitForCompletion && storageConnectionRun?.state !== executionState.COMPLETED); + if(waitForCompletion && storageConnectionRun.result !== executionResult.SUCCESS) { + this.error(`Synchronization run ${runId} resulted in an error. Run 'itp imodel connection run info --connection-id ${connectionId} --connection-run-id ${runId}' for more info.`); + } + return runId; } @@ -270,6 +277,21 @@ export default class PopulateIModel extends BaseCommand { } } +export interface populateResponse { + iModelId: string; + iTwinId: string; + rootFolderId: string; + summary: { + connectionId: string; + files: { + connectorType: connectorType; + fileId: string; + fileName: string; + }[]; + runId: string; + }[] +} + interface NewFileInfo { connectorType: connectorType; fileName: string; diff --git a/src/services/synchronizationClient/models/storage-connection.ts b/src/services/synchronizationClient/models/storage-connection.ts index 4a080e87..8acc789e 100644 --- a/src/services/synchronizationClient/models/storage-connection.ts +++ b/src/services/synchronizationClient/models/storage-connection.ts @@ -10,6 +10,12 @@ export type storageConnection = { _links: connectionLinks; authenticationType: authenticationType; displayName: string; + error: storageConnectionError iModelId: string; id: string; }; + +export type storageConnectionError = { + description: string; + errorKey: string; +} From 6b1280be00b83690bdb2256729aa6ed0386009ee Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 13:43:28 +0300 Subject: [PATCH 3/8] Increased default test timeout to 2min. --- integration-tests/imodel/named-version.test.ts | 6 +----- package.json | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/integration-tests/imodel/named-version.test.ts b/integration-tests/imodel/named-version.test.ts index cf2f4cf4..1416ae58 100644 --- a/integration-tests/imodel/named-version.test.ts +++ b/integration-tests/imodel/named-version.test.ts @@ -9,14 +9,13 @@ import { runCommand } from '@oclif/test'; import { expect } from 'chai'; import { changeset } from '../../src/services/changed-elements-client/tracking'; -import { createFile, createIModel, createITwin, getRootFolderId } from '../utils/helpers'; +import { createIModel, createITwin } from '../utils/helpers'; import { resultResponse } from '../utils/result-response'; import runSuiteIfMainModule from '../utils/run-suite-if-main-module'; const tests = () => describe('named-version', () => { const testITwinName = 'ITwinCLI_IntegrationTestITwin_iModelNamedVersion'; const testIModelName = 'ITwinCLI_IntegrationTestIModel_iModelNamedVersion'; - const testFileName = 'test.zip'; const testFilePath = 'integration-tests/test.zip'; let testIModelId: string; let testITwinId: string; @@ -35,9 +34,6 @@ const tests = () => describe('named-version', () => { await runCommand(`changed-elements enable --imodel-id ${testIModelId} --itwin-id ${testITwinId}`); - const rootFolderId = await getRootFolderId(testITwinId); - await createFile(rootFolderId, testFileName, testFilePath); - const result = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type SPPID`); expect(result.result).to.have.property('iModelId', testIModelId); expect(result.result).to.have.property('iTwinId', testITwinId); diff --git a/package.json b/package.json index 619e9efc..8c54cb11 100644 --- a/package.json +++ b/package.json @@ -163,8 +163,8 @@ "prepack": "oclif manifest && oclif readme", "test": "mocha --forbid-only \"integration-tests/main-cases/*.test.ts\"", "test:formatting": "mocha --forbid-only \"integration-tests/main-cases/formatting.test.ts\"", - "test:service": "mocha --forbid-only integration-tests/main-cases/service-client-serial.test.ts --require integration-tests/utils/mocha-global-setup-service.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/service-client-parallel/*.test.ts --require integration-tests/utils/mocha-global-setup-service.ts", - "test:native": "mocha --forbid-only integration-tests/main-cases/native-client-serial.test.ts --require integration-tests/utils/mocha-global-setup-native.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/native-client-parallel/*.test.ts --require integration-tests/utils/mocha-global-setup-native.ts", + "test:service": "mocha --forbid-only integration-tests/main-cases/service-client-serial.test.ts --timeout 120000 --require integration-tests/utils/mocha-global-setup-service.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/service-client-parallel/*.test.ts --timeout 120000 --require integration-tests/utils/mocha-global-setup-service.ts", + "test:native": "mocha --forbid-only integration-tests/main-cases/native-client-serial.test.ts --timeout 120000 --require integration-tests/utils/mocha-global-setup-native.ts && mocha --forbid-only --parallel --jobs 4 integration-tests/main-cases/native-client-parallel/*.test.ts --timeout 120000 --require integration-tests/utils/mocha-global-setup-native.ts", "version": "oclif readme && git add README.md", "docs-generator": "node ./bin/run.js docs-generator" }, From 60722a3c18037c48ddbb0087a57e8eac886d88da Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 14:05:00 +0300 Subject: [PATCH 4/8] Removed tests skips from imodel populate tests. Added some debug logging to named-version.ts before() hook. --- integration-tests/imodel/named-version.test.ts | 4 ++-- integration-tests/imodel/populate.test.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/integration-tests/imodel/named-version.test.ts b/integration-tests/imodel/named-version.test.ts index 1416ae58..81858a67 100644 --- a/integration-tests/imodel/named-version.test.ts +++ b/integration-tests/imodel/named-version.test.ts @@ -24,7 +24,7 @@ const tests = () => describe('named-version', () => { this.timeout(30 * 60 * 1000); const filteredITwins = await runCommand(`itwin list --name ${testITwinName}`); - expect(filteredITwins.result).to.not.be.undefined + expect(filteredITwins.result, `filteredITwins ${JSON.stringify(filteredITwins.error)}`).to.not.be.undefined if(filteredITwins.result!.length === 0) { const testITwin = await createITwin(testITwinName, 'Thing', 'Asset'); @@ -41,7 +41,7 @@ const tests = () => describe('named-version', () => { else { testITwinId = filteredITwins.result![0].id!; const iModels = await runCommand(`imodel list --itwin-id ${testITwinId}`); - expect(iModels.result).to.not.be.undefined; + expect(iModels.result, `iModels ${JSON.stringify(iModels.error)}`).to.not.be.undefined; expect(iModels.result?.length).to.be.equal(1); testIModelId = iModels.result![0].id; } diff --git a/integration-tests/imodel/populate.test.ts b/integration-tests/imodel/populate.test.ts index b775496b..90a0cbe5 100644 --- a/integration-tests/imodel/populate.test.ts +++ b/integration-tests/imodel/populate.test.ts @@ -36,7 +36,7 @@ const tests = () => describe('populate', () => { expect(iTwinDeleteResult).to.have.property('result', 'deleted'); }); - it.skip('should populate the iModel with the uploaded file', async () => { + it('should populate the iModel with the uploaded file', async () => { const { result: populateResult } = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type MSTN`); expect(populateResult).to.not.be.undefined; expect(populateResult!.iTwinId).to.be.equal(testITwinId); @@ -52,7 +52,7 @@ const tests = () => describe('populate', () => { expect(infoResult?.result).to.be.equal(executionResult.SUCCESS); }).timeout(30 * 60 * 1000); - it.skip('should populate the iModel with the uploaded file (no-wait flag with polling)', async () => { + it('should populate the iModel with the uploaded file (no-wait flag with polling)', async () => { const { result: populateResult } = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type SPPID --no-wait`); expect(populateResult).to.not.be.undefined; expect(populateResult!.iTwinId).to.be.equal(testITwinId); From 73c2e97963e61dbde79f3b04d1de26d435580948 Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 14:26:05 +0300 Subject: [PATCH 5/8] Debug changes. --- integration-tests/imodel/named-version.test.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/integration-tests/imodel/named-version.test.ts b/integration-tests/imodel/named-version.test.ts index 81858a67..07d9cfbe 100644 --- a/integration-tests/imodel/named-version.test.ts +++ b/integration-tests/imodel/named-version.test.ts @@ -23,10 +23,11 @@ const tests = () => describe('named-version', () => { before(async function() { this.timeout(30 * 60 * 1000); - const filteredITwins = await runCommand(`itwin list --name ${testITwinName}`); - expect(filteredITwins.result, `filteredITwins ${JSON.stringify(filteredITwins.error)}`).to.not.be.undefined + const { error, result: filteredITwins} = await runCommand(`itwin list --name ${testITwinName}`); + expect(error, JSON.stringify(error)).to.be.undefined; + expect(filteredITwins).to.not.be.undefined - if(filteredITwins.result!.length === 0) { + if(filteredITwins!.length === 0) { const testITwin = await createITwin(testITwinName, 'Thing', 'Asset'); testITwinId = testITwin.id as string; const testIModel = await createIModel(testIModelName, testITwinId); @@ -39,9 +40,9 @@ const tests = () => describe('named-version', () => { expect(result.result).to.have.property('iTwinId', testITwinId); } else { - testITwinId = filteredITwins.result![0].id!; + testITwinId = filteredITwins![0].id!; const iModels = await runCommand(`imodel list --itwin-id ${testITwinId}`); - expect(iModels.result, `iModels ${JSON.stringify(iModels.error)}`).to.not.be.undefined; + expect(iModels.result).to.not.be.undefined; expect(iModels.result?.length).to.be.equal(1); testIModelId = iModels.result![0].id; } From e4f1ea224c2f7587a6c0a14d0ae4b2320718e858 Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 14:44:49 +0300 Subject: [PATCH 6/8] Debug changes. --- integration-tests/imodel/named-version.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/integration-tests/imodel/named-version.test.ts b/integration-tests/imodel/named-version.test.ts index 07d9cfbe..e85ad8ef 100644 --- a/integration-tests/imodel/named-version.test.ts +++ b/integration-tests/imodel/named-version.test.ts @@ -23,7 +23,9 @@ const tests = () => describe('named-version', () => { before(async function() { this.timeout(30 * 60 * 1000); - const { error, result: filteredITwins} = await runCommand(`itwin list --name ${testITwinName}`); + const { error, result: filteredITwins, stderr, stdout} = await runCommand(`itwin list --name ${testITwinName}`); + console.log(stdout); + console.log(stderr); expect(error, JSON.stringify(error)).to.be.undefined; expect(filteredITwins).to.not.be.undefined From 1b5826667d654d99c2ac3f4a92e0118ee1db3087 Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 15:34:59 +0300 Subject: [PATCH 7/8] Fixed a few issues in tests. --- .../imodel/named-version.test.ts | 36 ++++++------------- integration-tests/imodel/populate.test.ts | 5 ++- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/integration-tests/imodel/named-version.test.ts b/integration-tests/imodel/named-version.test.ts index e85ad8ef..50104af0 100644 --- a/integration-tests/imodel/named-version.test.ts +++ b/integration-tests/imodel/named-version.test.ts @@ -3,8 +3,7 @@ * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ -import { IModel, NamedVersion } from '@itwin/imodels-client-management'; -import { ITwin } from '@itwin/itwins-client'; +import { NamedVersion } from '@itwin/imodels-client-management'; import { runCommand } from '@oclif/test'; import { expect } from 'chai'; @@ -16,38 +15,23 @@ import runSuiteIfMainModule from '../utils/run-suite-if-main-module'; const tests = () => describe('named-version', () => { const testITwinName = 'ITwinCLI_IntegrationTestITwin_iModelNamedVersion'; const testIModelName = 'ITwinCLI_IntegrationTestIModel_iModelNamedVersion'; - const testFilePath = 'integration-tests/test.zip'; + const testFilePath = 'examples/datasets/ExtonCampus.dgn'; let testIModelId: string; let testITwinId: string; before(async function() { this.timeout(30 * 60 * 1000); - const { error, result: filteredITwins, stderr, stdout} = await runCommand(`itwin list --name ${testITwinName}`); - console.log(stdout); - console.log(stderr); - expect(error, JSON.stringify(error)).to.be.undefined; - expect(filteredITwins).to.not.be.undefined + const testITwin = await createITwin(testITwinName, 'Thing', 'Asset'); + testITwinId = testITwin.id as string; + const testIModel = await createIModel(testIModelName, testITwinId); + testIModelId = testIModel.id; - if(filteredITwins!.length === 0) { - const testITwin = await createITwin(testITwinName, 'Thing', 'Asset'); - testITwinId = testITwin.id as string; - const testIModel = await createIModel(testIModelName, testITwinId); - testIModelId = testIModel.id; + await runCommand(`changed-elements enable --imodel-id ${testIModelId} --itwin-id ${testITwinId}`); - await runCommand(`changed-elements enable --imodel-id ${testIModelId} --itwin-id ${testITwinId}`); - - const result = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type SPPID`); - expect(result.result).to.have.property('iModelId', testIModelId); - expect(result.result).to.have.property('iTwinId', testITwinId); - } - else { - testITwinId = filteredITwins![0].id!; - const iModels = await runCommand(`imodel list --itwin-id ${testITwinId}`); - expect(iModels.result).to.not.be.undefined; - expect(iModels.result?.length).to.be.equal(1); - testIModelId = iModels.result![0].id; - } + const result = await runCommand(`imodel populate --imodel-id ${testIModelId} --file ${testFilePath} --connector-type MSTN`); + expect(result.result).to.have.property('iModelId', testIModelId); + expect(result.result).to.have.property('iTwinId', testITwinId); }); after(async () => { diff --git a/integration-tests/imodel/populate.test.ts b/integration-tests/imodel/populate.test.ts index 90a0cbe5..f4a4d7c2 100644 --- a/integration-tests/imodel/populate.test.ts +++ b/integration-tests/imodel/populate.test.ts @@ -64,10 +64,9 @@ const tests = () => describe('populate', () => { const {connectionId, runId} = populateResult!.summary[0]; let { result: infoResult } = await runCommand(`imodel connection run info -c ${connectionId} --connection-run-id ${runId}`); - let index = 0; - while(infoResult?.state !== "Completed" && ++index < 15) { + while(infoResult?.state !== "Completed") { // eslint-disable-next-line no-await-in-loop - await new Promise(r => {setTimeout(r, 3000 * index)}); + await new Promise(r => {setTimeout(r, 10_000)}); // eslint-disable-next-line no-await-in-loop const { result } = await runCommand(`imodel connection run info -c ${connectionId} --connection-run-id ${runId}`); From eaf176195be82c3339a743ce326cfd3828802c3e Mon Sep 17 00:00:00 2001 From: Karolis Zukauskas Date: Wed, 14 May 2025 15:56:53 +0300 Subject: [PATCH 8/8] Updated a test. --- integration-tests/imodel/named-version.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests/imodel/named-version.test.ts b/integration-tests/imodel/named-version.test.ts index 50104af0..f3b81e64 100644 --- a/integration-tests/imodel/named-version.test.ts +++ b/integration-tests/imodel/named-version.test.ts @@ -45,7 +45,7 @@ const tests = () => describe('named-version', () => { it('should create a new named version with specified changeset', async () => { const { result: changesets} = await runCommand(`changed-elements changesets --imodel-id ${testIModelId} --itwin-id ${testITwinId}`); expect(changesets).to.not.be.undefined; - expect(changesets!.length).to.be.equal(4); + expect(changesets!.length).to.be.equal(15); const response = await runCommand(`imodel named-version create --imodel-id ${testIModelId} --changeset-id ${changesets![0].id} -n "Version 1.0" -d "Some description of the version"`); expect(response.result).to.not.be.undefined;