diff --git a/common/changes/@itwin/core-backend/rohitptnkr-major-schema-upgrade-investigation_2025-05-30-09-25.json b/common/changes/@itwin/core-backend/rohitptnkr-major-schema-upgrade-investigation_2025-05-30-09-25.json new file mode 100644 index 000000000000..99b35bb89b62 --- /dev/null +++ b/common/changes/@itwin/core-backend/rohitptnkr-major-schema-upgrade-investigation_2025-05-30-09-25.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@itwin/core-backend", + "comment": "", + "type": "none" + } + ], + "packageName": "@itwin/core-backend" +} \ No newline at end of file diff --git a/core/backend/src/test/imodel/IModel.test.ts b/core/backend/src/test/imodel/IModel.test.ts index 01aeed4f8bbc..5db067630afd 100644 --- a/core/backend/src/test/imodel/IModel.test.ts +++ b/core/backend/src/test/imodel/IModel.test.ts @@ -3119,7 +3119,7 @@ describe("iModel", () => { imodel.close(); }); - function createElemProps(_imodel: IModelDb, modId: Id64String, catId: Id64String, className: string): GeometricElementProps { + function createElemProps(_imodel: IModelDb, modId: Id64String, catId: Id64String, className: string): GeometricElementProps { // Create props const elementProps: GeometricElementProps = { classFullName: className, @@ -3303,4 +3303,101 @@ describe("iModel", () => { } testImodel.close(); }); + + function assertSchemaVersion(imodel: IModelDb, schemaName: string, expectedVersion: string) { + const schemaProps = imodel.getSchemaProps(schemaName); + assert.isDefined(schemaProps); + assert.strictEqual(schemaProps.version, expectedVersion); + } + + async function assertClassProperty(imodel: IModelDb, classNameFullName: string, properties: any) { + const testClass = await imodel.schemaContext.getSchemaItem(classNameFullName, EntityClass); + assert.isDefined(testClass); + for (const prop of properties) { + const property = await testClass?.getProperty(prop.propName); + assert.equal(prop.propertyExists, property !== undefined, `Property ${prop.propName} existence check failed for class ${classNameFullName}`); + } + } + + it("Major schema updates should only work for dynamic schemas", async () => { + const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "majorschemaupgrade.bim"); + + // Remove the file if it already exists + if (IModelJsFs.existsSync(imodelPath)) + IModelJsFs.unlinkSync(imodelPath); + + // Create a new empty snapshot iModel + const testImodel = SnapshotDb.createEmpty(imodelPath, { rootSubject: { name: "majorschemaupgrade" } }); + + // Import initial schema and verify version + await testImodel.importSchemaStrings([` + + + + + bis:PhysicalElement + + + `]); + assertSchemaVersion(testImodel, "TestSchema", "01.00.00"); + await assertClassProperty(testImodel, "TestSchema.TestClass", [{ propName: "PropToDelete", propertyExists: true }]); + + // Attempt to delete a property 'PropToDelete' in a major schema update on a non-dynamic schema (should fail) + try { + await testImodel.importSchemaStrings([` + + + + + bis:PhysicalElement + + `]); + assert.fail("Expected an error to be thrown when trying to delete a property in a major schema update on a non-dynamic schema."); + } catch { + // Confirm schema version and property still exist + assertSchemaVersion(testImodel, "TestSchema", "01.00.00"); + await assertClassProperty(testImodel, "TestSchema.TestClass", [{ propName: "PropToDelete", propertyExists: true }]); + } + + // Make the schema dynamic and verify version and properties + await testImodel.importSchemaStrings([` + + + + + + + + + + bis:PhysicalElement + + + + `]); + assertSchemaVersion(testImodel, "TestSchema", "01.00.01"); + await assertClassProperty(testImodel, "TestSchema.TestClass", [{ propName: "PropToDelete", propertyExists: true }, { propName: "AnotherProperty", propertyExists: true }]); + + // Now try to delete a property 'PropToDelete' in a major schema update on a dynamic schema (should succeed) + try { + await testImodel.importSchemaStrings([` + + + + + + + + + + bis:PhysicalElement + + + `]); + assertSchemaVersion(testImodel, "TestSchema", "02.00.01"); + await assertClassProperty(testImodel, "TestSchema.TestClass", [{ propName: "PropToDelete", propertyExists: false }, { propName: "AnotherProperty", propertyExists: true }]); + } catch { + assert.fail("Expected no error to be thrown when deleting a property in a major schema update on a dynamic schema."); + } + }); });