@itwin/core-backend 5.10.0-dev.5 → 5.10.0-dev.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (503) hide show
  1. package/CHANGELOG.md +28 -1
  2. package/lib/cjs/BackendHubAccess.js.map +1 -1
  3. package/lib/cjs/BackendLoggerCategory.js.map +1 -1
  4. package/lib/cjs/BisCoreSchema.js.map +1 -1
  5. package/lib/cjs/BlobContainerService.js.map +1 -1
  6. package/lib/cjs/BriefcaseManager.d.ts +14 -62
  7. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  8. package/lib/cjs/BriefcaseManager.js +29 -162
  9. package/lib/cjs/BriefcaseManager.js.map +1 -1
  10. package/lib/cjs/CatalogDb.js.map +1 -1
  11. package/lib/cjs/Category.d.ts +4 -4
  12. package/lib/cjs/Category.js.map +1 -1
  13. package/lib/cjs/ChangeSummaryManager.js +2 -2
  14. package/lib/cjs/ChangeSummaryManager.js.map +1 -1
  15. package/lib/cjs/ChangedElementsDb.js.map +1 -1
  16. package/lib/cjs/ChangesetECAdaptor.d.ts +6 -6
  17. package/lib/cjs/ChangesetECAdaptor.js +252 -252
  18. package/lib/cjs/ChangesetECAdaptor.js.map +1 -1
  19. package/lib/cjs/ChangesetReader.js.map +1 -1
  20. package/lib/cjs/ChangesetReaderTypes.js.map +1 -1
  21. package/lib/cjs/ChannelControl.js.map +1 -1
  22. package/lib/cjs/CheckpointManager.js.map +1 -1
  23. package/lib/cjs/ClassRegistry.js +5 -5
  24. package/lib/cjs/ClassRegistry.js.map +1 -1
  25. package/lib/cjs/CloudSqlite.js.map +1 -1
  26. package/lib/cjs/CodeService.js.map +1 -1
  27. package/lib/cjs/CodeSpecs.d.ts +3 -3
  28. package/lib/cjs/CodeSpecs.js.map +1 -1
  29. package/lib/cjs/ConcurrentQuery.js.map +1 -1
  30. package/lib/cjs/CustomViewState3dCreator.js.map +1 -1
  31. package/lib/cjs/DevTools.js.map +1 -1
  32. package/lib/cjs/DisplayStyle.d.ts +2 -2
  33. package/lib/cjs/DisplayStyle.js.map +1 -1
  34. package/lib/cjs/ECDb.js.map +1 -1
  35. package/lib/cjs/ECSchemaXmlContext.js.map +1 -1
  36. package/lib/cjs/ECSqlRowExecutor.js.map +1 -1
  37. package/lib/cjs/ECSqlStatement.js.map +1 -1
  38. package/lib/cjs/ECSqlSyncReader.js.map +1 -1
  39. package/lib/cjs/EditTxn.js.map +1 -1
  40. package/lib/cjs/Element.d.ts +10 -10
  41. package/lib/cjs/Element.d.ts.map +1 -1
  42. package/lib/cjs/Element.js +2 -2
  43. package/lib/cjs/Element.js.map +1 -1
  44. package/lib/cjs/ElementAspect.d.ts +1 -1
  45. package/lib/cjs/ElementAspect.js.map +1 -1
  46. package/lib/cjs/ElementGraphics.js.map +1 -1
  47. package/lib/cjs/ElementTreeWalker.d.ts +5 -5
  48. package/lib/cjs/ElementTreeWalker.js.map +1 -1
  49. package/lib/cjs/Entity.js.map +1 -1
  50. package/lib/cjs/EntityReferences.js.map +1 -1
  51. package/lib/cjs/ExportGraphics.js.map +1 -1
  52. package/lib/cjs/ExternalSource.d.ts +2 -2
  53. package/lib/cjs/ExternalSource.d.ts.map +1 -1
  54. package/lib/cjs/ExternalSource.js.map +1 -1
  55. package/lib/cjs/FontFile.js.map +1 -1
  56. package/lib/cjs/GeoCoordConfig.js.map +1 -1
  57. package/lib/cjs/GeographicCRSServices.js.map +1 -1
  58. package/lib/cjs/GeometrySummary.js +47 -47
  59. package/lib/cjs/GeometrySummary.js.map +1 -1
  60. package/lib/cjs/IModelDb.d.ts +22 -22
  61. package/lib/cjs/IModelDb.js +28 -28
  62. package/lib/cjs/IModelDb.js.map +1 -1
  63. package/lib/cjs/IModelDbFonts.js.map +1 -1
  64. package/lib/cjs/IModelElementCloneContext.js.map +1 -1
  65. package/lib/cjs/IModelHost.js.map +1 -1
  66. package/lib/cjs/IModelIncrementalSchemaLocater.js.map +1 -1
  67. package/lib/cjs/IModelJsFs.d.ts +2 -0
  68. package/lib/cjs/IModelJsFs.d.ts.map +1 -1
  69. package/lib/cjs/IModelJsFs.js +14 -0
  70. package/lib/cjs/IModelJsFs.js.map +1 -1
  71. package/lib/cjs/ImageSourceConversion.js.map +1 -1
  72. package/lib/cjs/IpcHost.js.map +1 -1
  73. package/lib/cjs/LineStyle.d.ts +6 -6
  74. package/lib/cjs/LineStyle.js.map +1 -1
  75. package/lib/cjs/LocalHub.js +1 -1
  76. package/lib/cjs/LocalHub.js.map +1 -1
  77. package/lib/cjs/LocalhostIpcHost.js.map +1 -1
  78. package/lib/cjs/LockControl.js.map +1 -1
  79. package/lib/cjs/Material.d.ts +1 -1
  80. package/lib/cjs/Material.js.map +1 -1
  81. package/lib/cjs/Model.d.ts +6 -6
  82. package/lib/cjs/Model.d.ts.map +1 -1
  83. package/lib/cjs/Model.js.map +1 -1
  84. package/lib/cjs/NativeAppStorage.js.map +1 -1
  85. package/lib/cjs/NativeHost.js.map +1 -1
  86. package/lib/cjs/NavigationRelationship.js.map +1 -1
  87. package/lib/cjs/PartialChangeUnifier.js +7 -7
  88. package/lib/cjs/PartialChangeUnifier.js.map +1 -1
  89. package/lib/cjs/PromiseMemoizer.js.map +1 -1
  90. package/lib/cjs/PropertyStore.js.map +1 -1
  91. package/lib/cjs/Relationship.d.ts +10 -10
  92. package/lib/cjs/Relationship.js +6 -6
  93. package/lib/cjs/Relationship.js.map +1 -1
  94. package/lib/cjs/RpcBackend.js.map +1 -1
  95. package/lib/cjs/SQLiteDb.js.map +1 -1
  96. package/lib/cjs/Schema.js.map +1 -1
  97. package/lib/cjs/SchemaSync.js.map +1 -1
  98. package/lib/cjs/SchemaUtils.js.map +1 -1
  99. package/lib/cjs/SheetIndex.d.ts +4 -4
  100. package/lib/cjs/SheetIndex.d.ts.map +1 -1
  101. package/lib/cjs/SheetIndex.js.map +1 -1
  102. package/lib/cjs/SqliteChangesetReader.js.map +1 -1
  103. package/lib/cjs/SqliteStatement.js.map +1 -1
  104. package/lib/cjs/StashManager.js.map +1 -1
  105. package/lib/cjs/Texture.d.ts +1 -1
  106. package/lib/cjs/Texture.js.map +1 -1
  107. package/lib/cjs/TileStorage.js.map +1 -1
  108. package/lib/cjs/TxnManager.d.ts +3 -0
  109. package/lib/cjs/TxnManager.d.ts.map +1 -1
  110. package/lib/cjs/TxnManager.js +95 -13
  111. package/lib/cjs/TxnManager.js.map +1 -1
  112. package/lib/cjs/ViewDefinition.d.ts +6 -6
  113. package/lib/cjs/ViewDefinition.d.ts.map +1 -1
  114. package/lib/cjs/ViewDefinition.js.map +1 -1
  115. package/lib/cjs/ViewStateHydrator.js.map +1 -1
  116. package/lib/cjs/ViewStore.js.map +1 -1
  117. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +1 -1
  118. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  119. package/lib/cjs/annotations/FrameGeometry.js.map +1 -1
  120. package/lib/cjs/annotations/LeaderGeometry.js.map +1 -1
  121. package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
  122. package/lib/cjs/annotations/TextAnnotationGeometry.js.map +1 -1
  123. package/lib/cjs/annotations/TextBlockGeometry.js.map +1 -1
  124. package/lib/cjs/annotations/TextBlockLayout.js.map +1 -1
  125. package/lib/cjs/assets/IModelChange.02.00.00.ecschema.xml +90 -90
  126. package/lib/cjs/assets/Settings/Schemas/Base.Schema.json +32 -32
  127. package/lib/cjs/assets/Settings/Schemas/Gcs.schema.json +27 -27
  128. package/lib/cjs/assets/Settings/Schemas/Workspace.Schema.json +94 -94
  129. package/lib/cjs/assets/Settings/backend.setting.json5 +21 -21
  130. package/lib/cjs/core-backend.js.map +1 -1
  131. package/lib/cjs/domains/FunctionalElements.d.ts +1 -1
  132. package/lib/cjs/domains/FunctionalElements.d.ts.map +1 -1
  133. package/lib/cjs/domains/FunctionalElements.js.map +1 -1
  134. package/lib/cjs/domains/FunctionalSchema.js.map +1 -1
  135. package/lib/cjs/domains/GenericElements.d.ts +2 -2
  136. package/lib/cjs/domains/GenericElements.d.ts.map +1 -1
  137. package/lib/cjs/domains/GenericElements.js.map +1 -1
  138. package/lib/cjs/domains/GenericSchema.js.map +1 -1
  139. package/lib/cjs/internal/ChangesetConflictArgs.js.map +1 -1
  140. package/lib/cjs/internal/ChannelAdmin.d.ts +2 -2
  141. package/lib/cjs/internal/ChannelAdmin.d.ts.map +1 -1
  142. package/lib/cjs/internal/ChannelAdmin.js.map +1 -1
  143. package/lib/cjs/internal/ElementLRUCache.js.map +1 -1
  144. package/lib/cjs/internal/FontFileImpl.js.map +1 -1
  145. package/lib/cjs/internal/HubMock.js.map +1 -1
  146. package/lib/cjs/internal/IModelDbFontsImpl.js.map +1 -1
  147. package/lib/cjs/internal/IntegrityCheck.js.map +1 -1
  148. package/lib/cjs/internal/NativePlatform.js.map +1 -1
  149. package/lib/cjs/internal/NoLocks.js.map +1 -1
  150. package/lib/cjs/internal/OnlineStatus.js.map +1 -1
  151. package/lib/cjs/internal/ServerBasedLocks.js +33 -33
  152. package/lib/cjs/internal/ServerBasedLocks.js.map +1 -1
  153. package/lib/cjs/internal/Symbols.js.map +1 -1
  154. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  155. package/lib/cjs/internal/cross-package.js.map +1 -1
  156. package/lib/cjs/internal/workspace/SettingsEditorImpl.js.map +1 -1
  157. package/lib/cjs/internal/workspace/SettingsImpl.js.map +1 -1
  158. package/lib/cjs/internal/workspace/SettingsSchemasImpl.js.map +1 -1
  159. package/lib/cjs/internal/workspace/WorkspaceImpl.js.map +1 -1
  160. package/lib/cjs/internal/workspace/WorkspaceSqliteDb.js.map +1 -1
  161. package/lib/cjs/rpc/multipart.js.map +1 -1
  162. package/lib/cjs/rpc/tracing.js.map +1 -1
  163. package/lib/cjs/rpc/web/logging.js.map +1 -1
  164. package/lib/cjs/rpc/web/request.js.map +1 -1
  165. package/lib/cjs/rpc/web/response.js.map +1 -1
  166. package/lib/cjs/rpc-impl/DevToolsRpcImpl.js.map +1 -1
  167. package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
  168. package/lib/cjs/rpc-impl/IModelTileRpcImpl.js.map +1 -1
  169. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  170. package/lib/cjs/rpc-impl/SnapshotIModelRpcImpl.js.map +1 -1
  171. package/lib/cjs/workspace/Settings.js.map +1 -1
  172. package/lib/cjs/workspace/SettingsDb.js.map +1 -1
  173. package/lib/cjs/workspace/SettingsEditor.js.map +1 -1
  174. package/lib/cjs/workspace/SettingsSchemas.js.map +1 -1
  175. package/lib/cjs/workspace/Workspace.js.map +1 -1
  176. package/lib/cjs/workspace/WorkspaceEditor.js.map +1 -1
  177. package/lib/esm/BackendHubAccess.js.map +1 -1
  178. package/lib/esm/BackendLoggerCategory.js.map +1 -1
  179. package/lib/esm/BisCoreSchema.js.map +1 -1
  180. package/lib/esm/BlobContainerService.js.map +1 -1
  181. package/lib/esm/BriefcaseManager.d.ts +14 -62
  182. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  183. package/lib/esm/BriefcaseManager.js +30 -163
  184. package/lib/esm/BriefcaseManager.js.map +1 -1
  185. package/lib/esm/CatalogDb.js.map +1 -1
  186. package/lib/esm/Category.d.ts +4 -4
  187. package/lib/esm/Category.js.map +1 -1
  188. package/lib/esm/ChangeSummaryManager.js +2 -2
  189. package/lib/esm/ChangeSummaryManager.js.map +1 -1
  190. package/lib/esm/ChangedElementsDb.js.map +1 -1
  191. package/lib/esm/ChangesetECAdaptor.d.ts +6 -6
  192. package/lib/esm/ChangesetECAdaptor.js +252 -252
  193. package/lib/esm/ChangesetECAdaptor.js.map +1 -1
  194. package/lib/esm/ChangesetReader.js.map +1 -1
  195. package/lib/esm/ChangesetReaderTypes.js.map +1 -1
  196. package/lib/esm/ChannelControl.js.map +1 -1
  197. package/lib/esm/CheckpointManager.js.map +1 -1
  198. package/lib/esm/ClassRegistry.js +5 -5
  199. package/lib/esm/ClassRegistry.js.map +1 -1
  200. package/lib/esm/CloudSqlite.js.map +1 -1
  201. package/lib/esm/CodeService.js.map +1 -1
  202. package/lib/esm/CodeSpecs.d.ts +3 -3
  203. package/lib/esm/CodeSpecs.js.map +1 -1
  204. package/lib/esm/ConcurrentQuery.js.map +1 -1
  205. package/lib/esm/CustomViewState3dCreator.js.map +1 -1
  206. package/lib/esm/DevTools.js.map +1 -1
  207. package/lib/esm/DisplayStyle.d.ts +2 -2
  208. package/lib/esm/DisplayStyle.js.map +1 -1
  209. package/lib/esm/ECDb.js.map +1 -1
  210. package/lib/esm/ECSchemaXmlContext.js.map +1 -1
  211. package/lib/esm/ECSqlRowExecutor.js.map +1 -1
  212. package/lib/esm/ECSqlStatement.js.map +1 -1
  213. package/lib/esm/ECSqlSyncReader.js.map +1 -1
  214. package/lib/esm/EditTxn.js.map +1 -1
  215. package/lib/esm/Element.d.ts +10 -10
  216. package/lib/esm/Element.d.ts.map +1 -1
  217. package/lib/esm/Element.js +2 -2
  218. package/lib/esm/Element.js.map +1 -1
  219. package/lib/esm/ElementAspect.d.ts +1 -1
  220. package/lib/esm/ElementAspect.js.map +1 -1
  221. package/lib/esm/ElementGraphics.js.map +1 -1
  222. package/lib/esm/ElementTreeWalker.d.ts +5 -5
  223. package/lib/esm/ElementTreeWalker.js.map +1 -1
  224. package/lib/esm/Entity.js.map +1 -1
  225. package/lib/esm/EntityReferences.js.map +1 -1
  226. package/lib/esm/ExportGraphics.js.map +1 -1
  227. package/lib/esm/ExternalSource.d.ts +2 -2
  228. package/lib/esm/ExternalSource.d.ts.map +1 -1
  229. package/lib/esm/ExternalSource.js.map +1 -1
  230. package/lib/esm/FontFile.js.map +1 -1
  231. package/lib/esm/GeoCoordConfig.js.map +1 -1
  232. package/lib/esm/GeographicCRSServices.js.map +1 -1
  233. package/lib/esm/GeometrySummary.js +47 -47
  234. package/lib/esm/GeometrySummary.js.map +1 -1
  235. package/lib/esm/IModelDb.d.ts +22 -22
  236. package/lib/esm/IModelDb.js +28 -28
  237. package/lib/esm/IModelDb.js.map +1 -1
  238. package/lib/esm/IModelDbFonts.js.map +1 -1
  239. package/lib/esm/IModelElementCloneContext.js.map +1 -1
  240. package/lib/esm/IModelHost.js.map +1 -1
  241. package/lib/esm/IModelIncrementalSchemaLocater.js.map +1 -1
  242. package/lib/esm/IModelJsFs.d.ts +2 -0
  243. package/lib/esm/IModelJsFs.d.ts.map +1 -1
  244. package/lib/esm/IModelJsFs.js +14 -0
  245. package/lib/esm/IModelJsFs.js.map +1 -1
  246. package/lib/esm/ImageSourceConversion.js.map +1 -1
  247. package/lib/esm/IpcHost.js.map +1 -1
  248. package/lib/esm/LineStyle.d.ts +6 -6
  249. package/lib/esm/LineStyle.js.map +1 -1
  250. package/lib/esm/LocalHub.js +1 -1
  251. package/lib/esm/LocalHub.js.map +1 -1
  252. package/lib/esm/LocalhostIpcHost.js.map +1 -1
  253. package/lib/esm/LockControl.js.map +1 -1
  254. package/lib/esm/Material.d.ts +1 -1
  255. package/lib/esm/Material.js.map +1 -1
  256. package/lib/esm/Model.d.ts +6 -6
  257. package/lib/esm/Model.d.ts.map +1 -1
  258. package/lib/esm/Model.js.map +1 -1
  259. package/lib/esm/NativeAppStorage.js.map +1 -1
  260. package/lib/esm/NativeHost.js.map +1 -1
  261. package/lib/esm/NavigationRelationship.js.map +1 -1
  262. package/lib/esm/PartialChangeUnifier.js +7 -7
  263. package/lib/esm/PartialChangeUnifier.js.map +1 -1
  264. package/lib/esm/PromiseMemoizer.js.map +1 -1
  265. package/lib/esm/PropertyStore.js.map +1 -1
  266. package/lib/esm/Relationship.d.ts +10 -10
  267. package/lib/esm/Relationship.js +6 -6
  268. package/lib/esm/Relationship.js.map +1 -1
  269. package/lib/esm/RpcBackend.js.map +1 -1
  270. package/lib/esm/SQLiteDb.js.map +1 -1
  271. package/lib/esm/Schema.js.map +1 -1
  272. package/lib/esm/SchemaSync.js.map +1 -1
  273. package/lib/esm/SchemaUtils.js.map +1 -1
  274. package/lib/esm/SheetIndex.d.ts +4 -4
  275. package/lib/esm/SheetIndex.d.ts.map +1 -1
  276. package/lib/esm/SheetIndex.js.map +1 -1
  277. package/lib/esm/SqliteChangesetReader.js.map +1 -1
  278. package/lib/esm/SqliteStatement.js.map +1 -1
  279. package/lib/esm/StashManager.js.map +1 -1
  280. package/lib/esm/Texture.d.ts +1 -1
  281. package/lib/esm/Texture.js.map +1 -1
  282. package/lib/esm/TileStorage.js.map +1 -1
  283. package/lib/esm/TxnManager.d.ts +3 -0
  284. package/lib/esm/TxnManager.d.ts.map +1 -1
  285. package/lib/esm/TxnManager.js +96 -14
  286. package/lib/esm/TxnManager.js.map +1 -1
  287. package/lib/esm/ViewDefinition.d.ts +6 -6
  288. package/lib/esm/ViewDefinition.d.ts.map +1 -1
  289. package/lib/esm/ViewDefinition.js.map +1 -1
  290. package/lib/esm/ViewStateHydrator.js.map +1 -1
  291. package/lib/esm/ViewStore.js.map +1 -1
  292. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +1 -1
  293. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  294. package/lib/esm/annotations/FrameGeometry.js.map +1 -1
  295. package/lib/esm/annotations/LeaderGeometry.js.map +1 -1
  296. package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
  297. package/lib/esm/annotations/TextAnnotationGeometry.js.map +1 -1
  298. package/lib/esm/annotations/TextBlockGeometry.js.map +1 -1
  299. package/lib/esm/annotations/TextBlockLayout.js.map +1 -1
  300. package/lib/esm/core-backend.js.map +1 -1
  301. package/lib/esm/domains/FunctionalElements.d.ts +1 -1
  302. package/lib/esm/domains/FunctionalElements.d.ts.map +1 -1
  303. package/lib/esm/domains/FunctionalElements.js.map +1 -1
  304. package/lib/esm/domains/FunctionalSchema.js.map +1 -1
  305. package/lib/esm/domains/GenericElements.d.ts +2 -2
  306. package/lib/esm/domains/GenericElements.d.ts.map +1 -1
  307. package/lib/esm/domains/GenericElements.js.map +1 -1
  308. package/lib/esm/domains/GenericSchema.js.map +1 -1
  309. package/lib/esm/internal/ChangesetConflictArgs.js.map +1 -1
  310. package/lib/esm/internal/ChannelAdmin.d.ts +2 -2
  311. package/lib/esm/internal/ChannelAdmin.d.ts.map +1 -1
  312. package/lib/esm/internal/ChannelAdmin.js.map +1 -1
  313. package/lib/esm/internal/ElementLRUCache.js.map +1 -1
  314. package/lib/esm/internal/FontFileImpl.js.map +1 -1
  315. package/lib/esm/internal/HubMock.js.map +1 -1
  316. package/lib/esm/internal/IModelDbFontsImpl.js.map +1 -1
  317. package/lib/esm/internal/IntegrityCheck.js.map +1 -1
  318. package/lib/esm/internal/NativePlatform.js.map +1 -1
  319. package/lib/esm/internal/NoLocks.js.map +1 -1
  320. package/lib/esm/internal/OnlineStatus.js.map +1 -1
  321. package/lib/esm/internal/ServerBasedLocks.js +33 -33
  322. package/lib/esm/internal/ServerBasedLocks.js.map +1 -1
  323. package/lib/esm/internal/Symbols.js.map +1 -1
  324. package/lib/esm/internal/annotations/fields.js.map +1 -1
  325. package/lib/esm/internal/cross-package.js.map +1 -1
  326. package/lib/esm/internal/workspace/SettingsEditorImpl.js.map +1 -1
  327. package/lib/esm/internal/workspace/SettingsImpl.js.map +1 -1
  328. package/lib/esm/internal/workspace/SettingsSchemasImpl.js.map +1 -1
  329. package/lib/esm/internal/workspace/WorkspaceImpl.js.map +1 -1
  330. package/lib/esm/internal/workspace/WorkspaceSqliteDb.js.map +1 -1
  331. package/lib/esm/rpc/multipart.js.map +1 -1
  332. package/lib/esm/rpc/tracing.js.map +1 -1
  333. package/lib/esm/rpc/web/logging.js.map +1 -1
  334. package/lib/esm/rpc/web/request.js.map +1 -1
  335. package/lib/esm/rpc/web/response.js.map +1 -1
  336. package/lib/esm/rpc-impl/DevToolsRpcImpl.js.map +1 -1
  337. package/lib/esm/rpc-impl/IModelReadRpcImpl.js.map +1 -1
  338. package/lib/esm/rpc-impl/IModelTileRpcImpl.js.map +1 -1
  339. package/lib/esm/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  340. package/lib/esm/rpc-impl/SnapshotIModelRpcImpl.js.map +1 -1
  341. package/lib/esm/test/AdvancedEqual.js.map +1 -1
  342. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  343. package/lib/esm/test/AttachDb.test.js +11 -11
  344. package/lib/esm/test/AttachDb.test.js.map +1 -1
  345. package/lib/esm/test/ElementDrivesElement.test.js +23 -23
  346. package/lib/esm/test/ElementDrivesElement.test.js.map +1 -1
  347. package/lib/esm/test/ElementLRUCache.test.js.map +1 -1
  348. package/lib/esm/test/GeometryTestUtil.js.map +1 -1
  349. package/lib/esm/test/IModelHost.test.js.map +1 -1
  350. package/lib/esm/test/IModelTestUtils.js.map +1 -1
  351. package/lib/esm/test/ImageSourceConversion.test.js.map +1 -1
  352. package/lib/esm/test/IpcHost.test.js.map +1 -1
  353. package/lib/esm/test/KnownTestLocations.js.map +1 -1
  354. package/lib/esm/test/PrintElementTree.js.map +1 -1
  355. package/lib/esm/test/PropertyDb.test.js.map +1 -1
  356. package/lib/esm/test/RevisionUtility.js.map +1 -1
  357. package/lib/esm/test/SchemaUtils.test.js +25 -25
  358. package/lib/esm/test/SchemaUtils.test.js.map +1 -1
  359. package/lib/esm/test/SequentialLogMatcher.js.map +1 -1
  360. package/lib/esm/test/SquashSchemaAndDataChanges.test.js +129 -129
  361. package/lib/esm/test/SquashSchemaAndDataChanges.test.js.map +1 -1
  362. package/lib/esm/test/TestChangeSetUtility.js.map +1 -1
  363. package/lib/esm/test/TestEditTxn.js.map +1 -1
  364. package/lib/esm/test/TestUtils.js.map +1 -1
  365. package/lib/esm/test/annotations/Fields.test.js +53 -53
  366. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  367. package/lib/esm/test/annotations/FrameGeometry.test.js.map +1 -1
  368. package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
  369. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  370. package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
  371. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/old.config.js.map +1 -1
  372. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.js.map +1 -1
  373. package/lib/esm/test/categories/Category.test.js.map +1 -1
  374. package/lib/esm/test/codespec/CodeSpec.test.js.map +1 -1
  375. package/lib/esm/test/ecdb/CTE.test.js +88 -88
  376. package/lib/esm/test/ecdb/CTE.test.js.map +1 -1
  377. package/lib/esm/test/ecdb/ConcurrentQuery.test.js +19 -19
  378. package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
  379. package/lib/esm/test/ecdb/ConcurrentQueryLoad.test.js +15 -15
  380. package/lib/esm/test/ecdb/ConcurrentQueryLoad.test.js.map +1 -1
  381. package/lib/esm/test/ecdb/ECDb.test.js +72 -72
  382. package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
  383. package/lib/esm/test/ecdb/ECDbTestHelper.js.map +1 -1
  384. package/lib/esm/test/ecdb/ECSchemaXmlContext.test.js.map +1 -1
  385. package/lib/esm/test/ecdb/ECSqlAst.test.js +65 -65
  386. package/lib/esm/test/ecdb/ECSqlAst.test.js.map +1 -1
  387. package/lib/esm/test/ecdb/ECSqlQuery.test.js +4 -4
  388. package/lib/esm/test/ecdb/ECSqlQuery.test.js.map +1 -1
  389. package/lib/esm/test/ecdb/ECSqlStatement.test.js +332 -332
  390. package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
  391. package/lib/esm/test/ecdb/ECSqlSyncReader.test.js.map +1 -1
  392. package/lib/esm/test/ecdb/QueryReaders.test.js +31 -31
  393. package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
  394. package/lib/esm/test/ecdb/SqliteStatement.test.js.map +1 -1
  395. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
  396. package/lib/esm/test/ecsql/src/ECSqlTestGenerator.js +21 -21
  397. package/lib/esm/test/ecsql/src/ECSqlTestGenerator.js.map +1 -1
  398. package/lib/esm/test/ecsql/src/ECSqlTestParser.js.map +1 -1
  399. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
  400. package/lib/esm/test/element/DeleteDefinitionElements.test.js.map +1 -1
  401. package/lib/esm/test/element/ElementAspect.test.js +22 -22
  402. package/lib/esm/test/element/ElementAspect.test.js.map +1 -1
  403. package/lib/esm/test/element/ElementDependencyGraph.test.js.map +1 -1
  404. package/lib/esm/test/element/ElementRoundTrip.test.js +139 -139
  405. package/lib/esm/test/element/ElementRoundTrip.test.js.map +1 -1
  406. package/lib/esm/test/element/ExcludedElements.test.js.map +1 -1
  407. package/lib/esm/test/element/ExternalSource.test.js.map +1 -1
  408. package/lib/esm/test/element/NullStructArray.test.js +13 -13
  409. package/lib/esm/test/element/NullStructArray.test.js.map +1 -1
  410. package/lib/esm/test/element/ProjectInformationRecord.test.js.map +1 -1
  411. package/lib/esm/test/element/SheetInformationAspect.test.js.map +1 -1
  412. package/lib/esm/test/element/UrlLink.test.js.map +1 -1
  413. package/lib/esm/test/font/FontFile.test.js.map +1 -1
  414. package/lib/esm/test/font/IModelDbFonts.test.js.map +1 -1
  415. package/lib/esm/test/hubaccess/ApplyChangeset.test.js +32 -32
  416. package/lib/esm/test/hubaccess/ApplyChangeset.test.js.map +1 -1
  417. package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
  418. package/lib/esm/test/hubaccess/CheckpointManager.test.js.map +1 -1
  419. package/lib/esm/test/hubaccess/Rebase.test.js +56 -56
  420. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
  421. package/lib/esm/test/hubaccess/SemanticRebase.test.js +2387 -213
  422. package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
  423. package/lib/esm/test/imageData.js.map +1 -1
  424. package/lib/esm/test/imodel/Code.test.js.map +1 -1
  425. package/lib/esm/test/imodel/ElementTreeWalker.test.js.map +1 -1
  426. package/lib/esm/test/imodel/GetTextureImage.test.js.map +1 -1
  427. package/lib/esm/test/imodel/IModel.test.js +44 -44
  428. package/lib/esm/test/imodel/IModel.test.js.map +1 -1
  429. package/lib/esm/test/imodel/ProjectExtents.test.js.map +1 -1
  430. package/lib/esm/test/imodel/SchemaXmlImport.test.js +21 -21
  431. package/lib/esm/test/imodel/SchemaXmlImport.test.js.map +1 -1
  432. package/lib/esm/test/incrementalSchemaLocater/ECSqlQueries.test.js.map +1 -1
  433. package/lib/esm/test/incrementalSchemaLocater/IncrementalLoading.test.js.map +1 -1
  434. package/lib/esm/test/incrementalSchemaLocater/TestContext.js.map +1 -1
  435. package/lib/esm/test/index.js.map +1 -1
  436. package/lib/esm/test/misc/DevTools.test.js.map +1 -1
  437. package/lib/esm/test/misc/EntitySubClasses.test.js.map +1 -1
  438. package/lib/esm/test/misc/GeoServices.test.js.map +1 -1
  439. package/lib/esm/test/misc/PromiseMemoizer.test.js.map +1 -1
  440. package/lib/esm/test/native/DgnDbWorker.test.js.map +1 -1
  441. package/lib/esm/test/rpc/response.test.js.map +1 -1
  442. package/lib/esm/test/schema/ClassRegistry.test.js +99 -99
  443. package/lib/esm/test/schema/ClassRegistry.test.js.map +1 -1
  444. package/lib/esm/test/schema/FunctionalDomain.test.js.map +1 -1
  445. package/lib/esm/test/schema/GenericDomain.test.js.map +1 -1
  446. package/lib/esm/test/schema/IModelSchemaContext.test.js +9 -9
  447. package/lib/esm/test/schema/IModelSchemaContext.test.js.map +1 -1
  448. package/lib/esm/test/schema/SchemaImportCallbacks.test.js +19 -19
  449. package/lib/esm/test/schema/SchemaImportCallbacks.test.js.map +1 -1
  450. package/lib/esm/test/sheetindex/SheetIndex.test.js.map +1 -1
  451. package/lib/esm/test/standalone/ChangeMerge.test.js.map +1 -1
  452. package/lib/esm/test/standalone/ChangesetReader.test.js +246 -246
  453. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  454. package/lib/esm/test/standalone/CustomViewState3dCreator.test.js.map +1 -1
  455. package/lib/esm/test/standalone/DeleteElements.test.js.map +1 -1
  456. package/lib/esm/test/standalone/DisplayStyle.test.js.map +1 -1
  457. package/lib/esm/test/standalone/Drawing.test.js.map +1 -1
  458. package/lib/esm/test/standalone/EditTxn.test.js.map +1 -1
  459. package/lib/esm/test/standalone/ElementGraphics.test.js.map +1 -1
  460. package/lib/esm/test/standalone/ElementMesh.test.js.map +1 -1
  461. package/lib/esm/test/standalone/ExportGraphics.test.js +14 -14
  462. package/lib/esm/test/standalone/ExportGraphics.test.js.map +1 -1
  463. package/lib/esm/test/standalone/GeometryChangeEvents.test.js.map +1 -1
  464. package/lib/esm/test/standalone/GeometryStream.test.js.map +1 -1
  465. package/lib/esm/test/standalone/HubMock.test.js.map +1 -1
  466. package/lib/esm/test/standalone/IModelLimits.test.js.map +1 -1
  467. package/lib/esm/test/standalone/IModelWrite.test.js +27 -27
  468. package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
  469. package/lib/esm/test/standalone/ITwinWorkspace.test.js.map +1 -1
  470. package/lib/esm/test/standalone/InlineGeometryPartReferences.test.js.map +1 -1
  471. package/lib/esm/test/standalone/IntegrityCheck.test.js.map +1 -1
  472. package/lib/esm/test/standalone/MergeConflict.test.js.map +1 -1
  473. package/lib/esm/test/standalone/NativeAppStorage.test.js.map +1 -1
  474. package/lib/esm/test/standalone/RenderMaterialElement.test.js.map +1 -1
  475. package/lib/esm/test/standalone/RenderTimeline.test.js.map +1 -1
  476. package/lib/esm/test/standalone/SQLiteDb.test.js.map +1 -1
  477. package/lib/esm/test/standalone/SQliteChangesetReaderAndChangesetECAdaptor.test.js +135 -135
  478. package/lib/esm/test/standalone/SQliteChangesetReaderAndChangesetECAdaptor.test.js.map +1 -1
  479. package/lib/esm/test/standalone/SchemaUtils.test.js.map +1 -1
  480. package/lib/esm/test/standalone/SectionDrawing.test.js.map +1 -1
  481. package/lib/esm/test/standalone/ServerBasedLocks.test.js.map +1 -1
  482. package/lib/esm/test/standalone/Setting.test.js.map +1 -1
  483. package/lib/esm/test/standalone/Settings.test.js.map +1 -1
  484. package/lib/esm/test/standalone/SettingsSchemas.test.js.map +1 -1
  485. package/lib/esm/test/standalone/SnapshotDb.test.js.map +1 -1
  486. package/lib/esm/test/standalone/StandaloneDb.test.js +20 -20
  487. package/lib/esm/test/standalone/StandaloneDb.test.js.map +1 -1
  488. package/lib/esm/test/standalone/Texture.test.js.map +1 -1
  489. package/lib/esm/test/standalone/TileCache.test.js.map +1 -1
  490. package/lib/esm/test/standalone/TileTree.test.js.map +1 -1
  491. package/lib/esm/test/standalone/TxnManager.test.js.map +1 -1
  492. package/lib/esm/test/standalone/ViewDefinition.test.js.map +1 -1
  493. package/lib/esm/test/standalone/ViewStoreDb.test.js.map +1 -1
  494. package/lib/esm/test/standalone/Workspace.test.js.map +1 -1
  495. package/lib/esm/test/standalone/iModelDb.test.js.map +1 -1
  496. package/lib/esm/test/workspace/SettingsDb.test.js.map +1 -1
  497. package/lib/esm/workspace/Settings.js.map +1 -1
  498. package/lib/esm/workspace/SettingsDb.js.map +1 -1
  499. package/lib/esm/workspace/SettingsEditor.js.map +1 -1
  500. package/lib/esm/workspace/SettingsSchemas.js.map +1 -1
  501. package/lib/esm/workspace/Workspace.js.map +1 -1
  502. package/lib/esm/workspace/WorkspaceEditor.js.map +1 -1
  503. package/package.json +14 -14
@@ -3,7 +3,7 @@
3
3
  * See LICENSE.md in the project root for license terms and full copyright notice.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import { Id64 } from "@itwin/core-bentley";
6
- import { Code, IModel, SubCategoryAppearance } from "@itwin/core-common";
6
+ import { Code, IModel, QueryRowFormat, SubCategoryAppearance } from "@itwin/core-common";
7
7
  import * as chai from "chai";
8
8
  import { HubWrappers, IModelTestUtils, KnownTestLocations } from "..";
9
9
  import { BriefcaseManager, ChannelControl, DrawingCategory, IModelJsFs } from "../../core-backend";
@@ -55,167 +55,371 @@ class TestIModel {
55
55
  /** Reusable schema definitions for testing rebase with schema transformations */
56
56
  static schemas = {
57
57
  /** Base schema v01.00.00 with classes A, C, D */
58
- v01x00x00: `<?xml version="1.0" encoding="UTF-8"?>
59
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
60
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
61
- <ECEntityClass typeName="A">
62
- <BaseClass>bis:GraphicalElement2d</BaseClass>
63
- <ECProperty propertyName="PropA" typeName="string"/>
64
- </ECEntityClass>
65
- <ECEntityClass typeName="C">
66
- <BaseClass>A</BaseClass>
67
- <ECProperty propertyName="PropC" typeName="string"/>
68
- </ECEntityClass>
69
- <ECEntityClass typeName="D">
70
- <BaseClass>A</BaseClass>
71
- <ECProperty propertyName="PropD" typeName="string"/>
72
- </ECEntityClass>
58
+ v01x00x00: `<?xml version="1.0" encoding="UTF-8"?>
59
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.00" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
60
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
61
+ <ECEntityClass typeName="A">
62
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
63
+ <ECProperty propertyName="PropA" typeName="string"/>
64
+ </ECEntityClass>
65
+ <ECEntityClass typeName="C">
66
+ <BaseClass>A</BaseClass>
67
+ <ECProperty propertyName="PropC" typeName="string"/>
68
+ </ECEntityClass>
69
+ <ECEntityClass typeName="D">
70
+ <BaseClass>A</BaseClass>
71
+ <ECProperty propertyName="PropD" typeName="string"/>
72
+ </ECEntityClass>
73
73
  </ECSchema>`,
74
74
  /** v01.00.01 - Adds PropC2 to class C (trivial additive change) */
75
- v01x00x01AddPropC2: `<?xml version="1.0" encoding="UTF-8"?>
76
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
77
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
78
- <ECEntityClass typeName="A">
79
- <BaseClass>bis:GraphicalElement2d</BaseClass>
80
- <ECProperty propertyName="PropA" typeName="string"/>
81
- </ECEntityClass>
82
- <ECEntityClass typeName="C">
83
- <BaseClass>A</BaseClass>
84
- <ECProperty propertyName="PropC" typeName="string"/>
85
- <ECProperty propertyName="PropC2" typeName="string"/>
86
- </ECEntityClass>
87
- <ECEntityClass typeName="D">
88
- <BaseClass>A</BaseClass>
89
- <ECProperty propertyName="PropD" typeName="string"/>
90
- </ECEntityClass>
75
+ v01x00x01AddPropC2: `<?xml version="1.0" encoding="UTF-8"?>
76
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
77
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
78
+ <ECEntityClass typeName="A">
79
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
80
+ <ECProperty propertyName="PropA" typeName="string"/>
81
+ </ECEntityClass>
82
+ <ECEntityClass typeName="C">
83
+ <BaseClass>A</BaseClass>
84
+ <ECProperty propertyName="PropC" typeName="string"/>
85
+ <ECProperty propertyName="PropC2" typeName="string"/>
86
+ </ECEntityClass>
87
+ <ECEntityClass typeName="D">
88
+ <BaseClass>A</BaseClass>
89
+ <ECProperty propertyName="PropD" typeName="string"/>
90
+ </ECEntityClass>
91
91
  </ECSchema>`,
92
92
  /** v01.00.02 - Adds PropD2 to class D (trivial additive change) */
93
- v01x00x02AddPropD2: `<?xml version="1.0" encoding="UTF-8"?>
94
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
95
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
96
- <ECEntityClass typeName="A">
97
- <BaseClass>bis:GraphicalElement2d</BaseClass>
98
- <ECProperty propertyName="PropA" typeName="string"/>
99
- </ECEntityClass>
100
- <ECEntityClass typeName="C">
101
- <BaseClass>A</BaseClass>
102
- <ECProperty propertyName="PropC" typeName="string"/>
103
- <ECProperty propertyName="PropC2" typeName="string"/>
104
- </ECEntityClass>
105
- <ECEntityClass typeName="D">
106
- <BaseClass>A</BaseClass>
107
- <ECProperty propertyName="PropD" typeName="string"/>
108
- <ECProperty propertyName="PropD2" typeName="string"/>
109
- </ECEntityClass>
93
+ v01x00x02AddPropD2: `<?xml version="1.0" encoding="UTF-8"?>
94
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
95
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
96
+ <ECEntityClass typeName="A">
97
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
98
+ <ECProperty propertyName="PropA" typeName="string"/>
99
+ </ECEntityClass>
100
+ <ECEntityClass typeName="C">
101
+ <BaseClass>A</BaseClass>
102
+ <ECProperty propertyName="PropC" typeName="string"/>
103
+ <ECProperty propertyName="PropC2" typeName="string"/>
104
+ </ECEntityClass>
105
+ <ECEntityClass typeName="D">
106
+ <BaseClass>A</BaseClass>
107
+ <ECProperty propertyName="PropD" typeName="string"/>
108
+ <ECProperty propertyName="PropD2" typeName="string"/>
109
+ </ECEntityClass>
110
110
  </ECSchema>`,
111
111
  /** v01.00.02 - Moves PropC from C to A (requires data transformation) on top of v01.00.01 */
112
- v01x00x02MovePropCToA: `<?xml version="1.0" encoding="UTF-8"?>
113
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
114
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
115
- <ECEntityClass typeName="A">
116
- <BaseClass>bis:GraphicalElement2d</BaseClass>
117
- <ECProperty propertyName="PropA" typeName="string"/>
118
- <ECProperty propertyName="PropC" typeName="string"/>
119
- </ECEntityClass>
120
- <ECEntityClass typeName="C">
121
- <BaseClass>A</BaseClass>
122
- <ECProperty propertyName="PropC2" typeName="string"/>
123
- </ECEntityClass>
124
- <ECEntityClass typeName="D">
125
- <BaseClass>A</BaseClass>
126
- <ECProperty propertyName="PropD" typeName="string"/>
127
- </ECEntityClass>
112
+ v01x00x02MovePropCToA: `<?xml version="1.0" encoding="UTF-8"?>
113
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
114
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
115
+ <ECEntityClass typeName="A">
116
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
117
+ <ECProperty propertyName="PropA" typeName="string"/>
118
+ <ECProperty propertyName="PropC" typeName="string"/>
119
+ </ECEntityClass>
120
+ <ECEntityClass typeName="C">
121
+ <BaseClass>A</BaseClass>
122
+ <ECProperty propertyName="PropC2" typeName="string"/>
123
+ </ECEntityClass>
124
+ <ECEntityClass typeName="D">
125
+ <BaseClass>A</BaseClass>
126
+ <ECProperty propertyName="PropD" typeName="string"/>
127
+ </ECEntityClass>
128
128
  </ECSchema>`,
129
129
  /** v01.00.03 - Builds on top of v01.00.02 and in addition moves PropD to base, so we can have incoming and local transforming changes */
130
- v01x00x03MovePropCAndD: `<?xml version="1.0" encoding="UTF-8"?>
131
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.03" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
132
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
133
- <ECEntityClass typeName="A">
134
- <BaseClass>bis:GraphicalElement2d</BaseClass>
135
- <ECProperty propertyName="PropA" typeName="string"/>
136
- <ECProperty propertyName="PropC" typeName="string"/>
137
- <ECProperty propertyName="PropD" typeName="string"/>
138
- </ECEntityClass>
139
- <ECEntityClass typeName="C">
140
- <BaseClass>A</BaseClass>
141
- <ECProperty propertyName="PropC2" typeName="string"/>
142
- </ECEntityClass>
143
- <ECEntityClass typeName="D">
144
- <BaseClass>A</BaseClass>
145
- <ECProperty propertyName="PropD2" typeName="string"/>
146
- </ECEntityClass>
130
+ v01x00x03MovePropCAndD: `<?xml version="1.0" encoding="UTF-8"?>
131
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.03" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
132
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
133
+ <ECEntityClass typeName="A">
134
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
135
+ <ECProperty propertyName="PropA" typeName="string"/>
136
+ <ECProperty propertyName="PropC" typeName="string"/>
137
+ <ECProperty propertyName="PropD" typeName="string"/>
138
+ </ECEntityClass>
139
+ <ECEntityClass typeName="C">
140
+ <BaseClass>A</BaseClass>
141
+ <ECProperty propertyName="PropC2" typeName="string"/>
142
+ </ECEntityClass>
143
+ <ECEntityClass typeName="D">
144
+ <BaseClass>A</BaseClass>
145
+ <ECProperty propertyName="PropD2" typeName="string"/>
146
+ </ECEntityClass>
147
147
  </ECSchema>`,
148
148
  /** v01.00.01 (incompatible variant) - Adds PropC3 instead of PropC2 to class C (same version) */
149
- v01x00x01AddPropC3Incompatible: `<?xml version="1.0" encoding="UTF-8"?>
150
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
151
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
152
- <ECEntityClass typeName="A">
153
- <BaseClass>bis:GraphicalElement2d</BaseClass>
154
- <ECProperty propertyName="PropA" typeName="string"/>
155
- </ECEntityClass>
156
- <ECEntityClass typeName="C">
157
- <BaseClass>A</BaseClass>
158
- <ECProperty propertyName="PropC" typeName="string"/>
159
- <ECProperty propertyName="PropC3" typeName="string"/>
160
- </ECEntityClass>
161
- <ECEntityClass typeName="D">
162
- <BaseClass>A</BaseClass>
163
- <ECProperty propertyName="PropD" typeName="string"/>
164
- </ECEntityClass>
149
+ v01x00x01AddPropC3Incompatible: `<?xml version="1.0" encoding="UTF-8"?>
150
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
151
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
152
+ <ECEntityClass typeName="A">
153
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
154
+ <ECProperty propertyName="PropA" typeName="string"/>
155
+ </ECEntityClass>
156
+ <ECEntityClass typeName="C">
157
+ <BaseClass>A</BaseClass>
158
+ <ECProperty propertyName="PropC" typeName="string"/>
159
+ <ECProperty propertyName="PropC3" typeName="string"/>
160
+ </ECEntityClass>
161
+ <ECEntityClass typeName="D">
162
+ <BaseClass>A</BaseClass>
163
+ <ECProperty propertyName="PropD" typeName="string"/>
164
+ </ECEntityClass>
165
165
  </ECSchema>`,
166
166
  /** v01.00.02 (incompatible variant) - Adds PropC3 instead of PropC2 to class C (higher version) */
167
- v01x00x02AddPropC3Incompatible: `<?xml version="1.0" encoding="UTF-8"?>
168
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
169
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
170
- <ECEntityClass typeName="A">
171
- <BaseClass>bis:GraphicalElement2d</BaseClass>
172
- <ECProperty propertyName="PropA" typeName="string"/>
173
- </ECEntityClass>
174
- <ECEntityClass typeName="C">
175
- <BaseClass>A</BaseClass>
176
- <ECProperty propertyName="PropC" typeName="string"/>
177
- <ECProperty propertyName="PropC3" typeName="string"/>
178
- </ECEntityClass>
179
- <ECEntityClass typeName="D">
180
- <BaseClass>A</BaseClass>
181
- <ECProperty propertyName="PropD" typeName="string"/>
182
- </ECEntityClass>
167
+ v01x00x02AddPropC3Incompatible: `<?xml version="1.0" encoding="UTF-8"?>
168
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
169
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
170
+ <ECEntityClass typeName="A">
171
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
172
+ <ECProperty propertyName="PropA" typeName="string"/>
173
+ </ECEntityClass>
174
+ <ECEntityClass typeName="C">
175
+ <BaseClass>A</BaseClass>
176
+ <ECProperty propertyName="PropC" typeName="string"/>
177
+ <ECProperty propertyName="PropC3" typeName="string"/>
178
+ </ECEntityClass>
179
+ <ECEntityClass typeName="D">
180
+ <BaseClass>A</BaseClass>
181
+ <ECProperty propertyName="PropD" typeName="string"/>
182
+ </ECEntityClass>
183
183
  </ECSchema>`,
184
184
  /** v01.00.02 (incompatible variant) - Adds PropC2 (higher version, different type) */
185
- v01x00x02AddPropC2Incompatible: `<?xml version="1.0" encoding="UTF-8"?>
186
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
187
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
188
- <ECEntityClass typeName="A">
189
- <BaseClass>bis:GraphicalElement2d</BaseClass>
190
- <ECProperty propertyName="PropA" typeName="string"/>
191
- </ECEntityClass>
192
- <ECEntityClass typeName="C">
193
- <BaseClass>A</BaseClass>
194
- <ECProperty propertyName="PropC" typeName="string"/>
195
- <ECProperty propertyName="PropC2" typeName="int"/>
196
- </ECEntityClass>
197
- <ECEntityClass typeName="D">
198
- <BaseClass>A</BaseClass>
199
- <ECProperty propertyName="PropD" typeName="string"/>
200
- </ECEntityClass>
185
+ v01x00x02AddPropC2Incompatible: `<?xml version="1.0" encoding="UTF-8"?>
186
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
187
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
188
+ <ECEntityClass typeName="A">
189
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
190
+ <ECProperty propertyName="PropA" typeName="string"/>
191
+ </ECEntityClass>
192
+ <ECEntityClass typeName="C">
193
+ <BaseClass>A</BaseClass>
194
+ <ECProperty propertyName="PropC" typeName="string"/>
195
+ <ECProperty propertyName="PropC2" typeName="int"/>
196
+ </ECEntityClass>
197
+ <ECEntityClass typeName="D">
198
+ <BaseClass>A</BaseClass>
199
+ <ECProperty propertyName="PropD" typeName="string"/>
200
+ </ECEntityClass>
201
201
  </ECSchema>`,
202
202
  /** v01.00.03 - Adds PropC2 as string (used to test incompatibility when reinstated on top of v01.00.02 with PropC2:int) */
203
- v01x00x03AddPropC2: `<?xml version="1.0" encoding="UTF-8"?>
204
- <ECSchema schemaName="TestDomain" alias="td" version="01.00.03" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
205
- <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
206
- <ECEntityClass typeName="A">
207
- <BaseClass>bis:GraphicalElement2d</BaseClass>
208
- <ECProperty propertyName="PropA" typeName="string"/>
209
- </ECEntityClass>
210
- <ECEntityClass typeName="C">
211
- <BaseClass>A</BaseClass>
212
- <ECProperty propertyName="PropC" typeName="string"/>
213
- <ECProperty propertyName="PropC2" typeName="string"/>
214
- </ECEntityClass>
215
- <ECEntityClass typeName="D">
216
- <BaseClass>A</BaseClass>
217
- <ECProperty propertyName="PropD" typeName="string"/>
218
- </ECEntityClass>
203
+ v01x00x03AddPropC2: `<?xml version="1.0" encoding="UTF-8"?>
204
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.03" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
205
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
206
+ <ECEntityClass typeName="A">
207
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
208
+ <ECProperty propertyName="PropA" typeName="string"/>
209
+ </ECEntityClass>
210
+ <ECEntityClass typeName="C">
211
+ <BaseClass>A</BaseClass>
212
+ <ECProperty propertyName="PropC" typeName="string"/>
213
+ <ECProperty propertyName="PropC2" typeName="string"/>
214
+ </ECEntityClass>
215
+ <ECEntityClass typeName="D">
216
+ <BaseClass>A</BaseClass>
217
+ <ECProperty propertyName="PropD" typeName="string"/>
218
+ </ECEntityClass>
219
+ </ECSchema>`,
220
+ };
221
+ /** Additional schemas for extended edge-case tests */
222
+ static extendedSchemas = {
223
+ /** v01x00x01 - Adds CUniqueAspect class (ElementUniqueAspect subclass) with AspectProp for aspect rebase tests */
224
+ v01x00x01WithAspect: `<?xml version="1.0" encoding="UTF-8"?>
225
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
226
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
227
+ <ECEntityClass typeName="A">
228
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
229
+ <ECProperty propertyName="PropA" typeName="string"/>
230
+ </ECEntityClass>
231
+ <ECEntityClass typeName="C">
232
+ <BaseClass>A</BaseClass>
233
+ <ECProperty propertyName="PropC" typeName="string"/>
234
+ </ECEntityClass>
235
+ <ECEntityClass typeName="D">
236
+ <BaseClass>A</BaseClass>
237
+ <ECProperty propertyName="PropD" typeName="string"/>
238
+ </ECEntityClass>
239
+ <ECEntityClass typeName="CUniqueAspect" modifier="None">
240
+ <BaseClass>bis:ElementUniqueAspect</BaseClass>
241
+ <ECProperty propertyName="AspectProp" typeName="string"/>
242
+ </ECEntityClass>
243
+ </ECSchema>`,
244
+ /** v01x00x02 - Extends v01WithAspect by adding AspectProp2 to CUniqueAspect (trivial aspect schema evolution) */
245
+ v01x00x02WithAspectProp2: `<?xml version="1.0" encoding="UTF-8"?>
246
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
247
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
248
+ <ECEntityClass typeName="A">
249
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
250
+ <ECProperty propertyName="PropA" typeName="string"/>
251
+ </ECEntityClass>
252
+ <ECEntityClass typeName="C">
253
+ <BaseClass>A</BaseClass>
254
+ <ECProperty propertyName="PropC" typeName="string"/>
255
+ </ECEntityClass>
256
+ <ECEntityClass typeName="D">
257
+ <BaseClass>A</BaseClass>
258
+ <ECProperty propertyName="PropD" typeName="string"/>
259
+ </ECEntityClass>
260
+ <ECEntityClass typeName="CUniqueAspect" modifier="None">
261
+ <BaseClass>bis:ElementUniqueAspect</BaseClass>
262
+ <ECProperty propertyName="AspectProp" typeName="string"/>
263
+ <ECProperty propertyName="AspectProp2" typeName="string"/>
264
+ </ECEntityClass>
265
+ </ECSchema>`,
266
+ /** v01x00x01 - Adds new entity class E extending A with PropE (tests new class addition) */
267
+ v01x00x01AddClassE: `<?xml version="1.0" encoding="UTF-8"?>
268
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
269
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
270
+ <ECEntityClass typeName="A">
271
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
272
+ <ECProperty propertyName="PropA" typeName="string"/>
273
+ </ECEntityClass>
274
+ <ECEntityClass typeName="C">
275
+ <BaseClass>A</BaseClass>
276
+ <ECProperty propertyName="PropC" typeName="string"/>
277
+ </ECEntityClass>
278
+ <ECEntityClass typeName="D">
279
+ <BaseClass>A</BaseClass>
280
+ <ECProperty propertyName="PropD" typeName="string"/>
281
+ </ECEntityClass>
282
+ <ECEntityClass typeName="E">
283
+ <BaseClass>A</BaseClass>
284
+ <ECProperty propertyName="PropE" typeName="string"/>
285
+ </ECEntityClass>
286
+ </ECSchema>`,
287
+ /** v01x00x02 - Extends v01AddClassE by adding PropE2 to class E */
288
+ v01x00x02AddClassEPropE2: `<?xml version="1.0" encoding="UTF-8"?>
289
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
290
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
291
+ <ECEntityClass typeName="A">
292
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
293
+ <ECProperty propertyName="PropA" typeName="string"/>
294
+ </ECEntityClass>
295
+ <ECEntityClass typeName="C">
296
+ <BaseClass>A</BaseClass>
297
+ <ECProperty propertyName="PropC" typeName="string"/>
298
+ </ECEntityClass>
299
+ <ECEntityClass typeName="D">
300
+ <BaseClass>A</BaseClass>
301
+ <ECProperty propertyName="PropD" typeName="string"/>
302
+ </ECEntityClass>
303
+ <ECEntityClass typeName="E">
304
+ <BaseClass>A</BaseClass>
305
+ <ECProperty propertyName="PropE" typeName="string"/>
306
+ <ECProperty propertyName="PropE2" typeName="string"/>
307
+ </ECEntityClass>
308
+ </ECSchema>`,
309
+ /** v01x00x01 - Adds multi-type properties (int, double, boolean) to class C for type-variation tests */
310
+ v01x00x01MultiTypeProps: `<?xml version="1.0" encoding="UTF-8"?>
311
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
312
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
313
+ <ECEntityClass typeName="A">
314
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
315
+ <ECProperty propertyName="PropA" typeName="string"/>
316
+ </ECEntityClass>
317
+ <ECEntityClass typeName="C">
318
+ <BaseClass>A</BaseClass>
319
+ <ECProperty propertyName="PropC" typeName="string"/>
320
+ <ECProperty propertyName="PropCInt" typeName="int"/>
321
+ <ECProperty propertyName="PropCDouble" typeName="double"/>
322
+ <ECProperty propertyName="PropCBool" typeName="boolean"/>
323
+ </ECEntityClass>
324
+ <ECEntityClass typeName="D">
325
+ <BaseClass>A</BaseClass>
326
+ <ECProperty propertyName="PropD" typeName="string"/>
327
+ </ECEntityClass>
328
+ </ECSchema>`,
329
+ /** v01x00x02 - Extends v01MultiTypeProps by adding PropD2 to class D */
330
+ v01x00x02MultiTypePropsExtended: `<?xml version="1.0" encoding="UTF-8"?>
331
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
332
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
333
+ <ECEntityClass typeName="A">
334
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
335
+ <ECProperty propertyName="PropA" typeName="string"/>
336
+ </ECEntityClass>
337
+ <ECEntityClass typeName="C">
338
+ <BaseClass>A</BaseClass>
339
+ <ECProperty propertyName="PropC" typeName="string"/>
340
+ <ECProperty propertyName="PropCInt" typeName="int"/>
341
+ <ECProperty propertyName="PropCDouble" typeName="double"/>
342
+ <ECProperty propertyName="PropCBool" typeName="boolean"/>
343
+ </ECEntityClass>
344
+ <ECEntityClass typeName="D">
345
+ <BaseClass>A</BaseClass>
346
+ <ECProperty propertyName="PropD" typeName="string"/>
347
+ <ECProperty propertyName="PropD2" typeName="string"/>
348
+ </ECEntityClass>
349
+ </ECSchema>`,
350
+ /** v01x00x01 - Adds multi-type properties (int, double, boolean) to class C for type-variation tests */
351
+ v01x00x02MultiTypePropsMovePropDToA: `<?xml version="1.0" encoding="UTF-8"?>
352
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
353
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
354
+ <ECEntityClass typeName="A">
355
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
356
+ <ECProperty propertyName="PropA" typeName="string"/>
357
+ <ECProperty propertyName="PropD" typeName="string"/>
358
+ </ECEntityClass>
359
+ <ECEntityClass typeName="C">
360
+ <BaseClass>A</BaseClass>
361
+ <ECProperty propertyName="PropC" typeName="string"/>
362
+ <ECProperty propertyName="PropCInt" typeName="int"/>
363
+ <ECProperty propertyName="PropCDouble" typeName="double"/>
364
+ <ECProperty propertyName="PropCBool" typeName="boolean"/>
365
+ </ECEntityClass>
366
+ <ECEntityClass typeName="D">
367
+ <BaseClass>A</BaseClass>
368
+ </ECEntityClass>
369
+ </ECSchema>`,
370
+ /** v01x00x02 - Moves PropD from class D to class A (transforming change for D, analogous to MovePropCToA) */
371
+ v01x00x02MovePropDToA: `<?xml version="1.0" encoding="UTF-8"?>
372
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
373
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
374
+ <ECEntityClass typeName="A">
375
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
376
+ <ECProperty propertyName="PropA" typeName="string"/>
377
+ <ECProperty propertyName="PropD" typeName="string"/>
378
+ </ECEntityClass>
379
+ <ECEntityClass typeName="C">
380
+ <BaseClass>A</BaseClass>
381
+ <ECProperty propertyName="PropC" typeName="string"/>
382
+ </ECEntityClass>
383
+ <ECEntityClass typeName="D">
384
+ <BaseClass>A</BaseClass>
385
+ </ECEntityClass>
386
+ </ECSchema>`,
387
+ /** v01x00x01 - Adds a binary property (PropCBin) to class C */
388
+ v01x00x01WithBinaryProp: `<?xml version="1.0" encoding="UTF-8"?>
389
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.01" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
390
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
391
+ <ECEntityClass typeName="A">
392
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
393
+ <ECProperty propertyName="PropA" typeName="string"/>
394
+ </ECEntityClass>
395
+ <ECEntityClass typeName="C">
396
+ <BaseClass>A</BaseClass>
397
+ <ECProperty propertyName="PropC" typeName="string"/>
398
+ <ECProperty propertyName="PropCBin" typeName="binary"/>
399
+ </ECEntityClass>
400
+ <ECEntityClass typeName="D">
401
+ <BaseClass>A</BaseClass>
402
+ <ECProperty propertyName="PropD" typeName="string"/>
403
+ </ECEntityClass>
404
+ </ECSchema>`,
405
+ /** v01x00x02 - Extends v01x00x01WithBinaryProp with PropD2 on class D (trivial additive change) */
406
+ v01x00x02WithBinaryPropAndPropD2: `<?xml version="1.0" encoding="UTF-8"?>
407
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
408
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
409
+ <ECEntityClass typeName="A">
410
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
411
+ <ECProperty propertyName="PropA" typeName="string"/>
412
+ </ECEntityClass>
413
+ <ECEntityClass typeName="C">
414
+ <BaseClass>A</BaseClass>
415
+ <ECProperty propertyName="PropC" typeName="string"/>
416
+ <ECProperty propertyName="PropCBin" typeName="binary"/>
417
+ </ECEntityClass>
418
+ <ECEntityClass typeName="D">
419
+ <BaseClass>A</BaseClass>
420
+ <ECProperty propertyName="PropD" typeName="string"/>
421
+ <ECProperty propertyName="PropD2" typeName="string"/>
422
+ </ECEntityClass>
219
423
  </ECSchema>`,
220
424
  };
221
425
  /**
@@ -285,12 +489,15 @@ class TestIModel {
285
489
  }
286
490
  updateElement(txn, elementId, updates) {
287
491
  const briefcase = txn.iModel;
288
- const element = briefcase.elements.getElement(elementId);
492
+ const element = briefcase.elements.getElementProps(elementId);
289
493
  Object.assign(element, updates);
290
- txn.updateElement(element.toJSON());
494
+ txn.updateElement(element);
495
+ }
496
+ getElementProps(briefcase, elementId) {
497
+ return briefcase.elements.getElementProps(elementId);
291
498
  }
292
- getElement(briefcase, elementId) {
293
- return briefcase.elements.getElement(elementId);
499
+ getModelProps(briefcase, modelId) {
500
+ return briefcase.models.tryGetModelProps(modelId);
294
501
  }
295
502
  checkIfFolderExists(briefcase, txnId, isSchemaFolder) {
296
503
  if (isSchemaFolder)
@@ -301,6 +508,88 @@ class TestIModel {
301
508
  const folderPath = BriefcaseManager.getBasePathForSemanticRebaseLocalFiles(briefcase);
302
509
  return IModelJsFs.existsSync(folderPath);
303
510
  }
511
+ /**
512
+ * Insert a UniqueAspect onto an element within the given EditTxn.
513
+ * @param txn Active EditTxn
514
+ * @param elementId Owning element's Id
515
+ * @param aspectClassName Full class name, e.g. "TestDomain:CUniqueAspect"
516
+ * @param properties Additional property key/value pairs
517
+ * @returns The new aspect's ECInstanceId
518
+ */
519
+ insertAspect(txn, elementId, aspectClassName, properties) {
520
+ const aspectProps = {
521
+ classFullName: aspectClassName,
522
+ element: { id: elementId, relClassName: "BisCore.ElementOwnsUniqueAspect" },
523
+ ...properties,
524
+ };
525
+ return txn.insertAspect(aspectProps);
526
+ }
527
+ /**
528
+ * Update a UniqueAspect property within the given EditTxn.
529
+ * Reads the existing aspect, merges updates, writes back.
530
+ */
531
+ updateAspect(txn, elementId, aspectClassName, updates) {
532
+ const briefcase = txn.iModel;
533
+ const aspects = briefcase.elements.getAspects(elementId, aspectClassName);
534
+ chai.expect(aspects.length).to.be.greaterThan(0, "Expected at least one aspect to update");
535
+ const aspect = aspects[0];
536
+ Object.assign(aspect, updates);
537
+ txn.updateAspect(aspect.toJSON());
538
+ }
539
+ /**
540
+ * Delete a UniqueAspect within the given EditTxn.
541
+ * Reads the existing aspect, then deletes it by instanceId.
542
+ */
543
+ deleteAspect(txn, elementId, aspectClassName) {
544
+ const briefcase = txn.iModel;
545
+ const aspects = briefcase.elements.getAspects(elementId, aspectClassName);
546
+ chai.expect(aspects.length).to.be.greaterThan(0, "Expected at least one aspect to delete");
547
+ txn.deleteAspect(aspects[0].id);
548
+ }
549
+ /**
550
+ * Read a UniqueAspect from a briefcase and return it as a plain object.
551
+ */
552
+ getAspect(briefcase, elementId, aspectClassName) {
553
+ const aspects = briefcase.elements.getAspects(elementId, aspectClassName);
554
+ return aspects.length > 0 ? aspects[0] : undefined;
555
+ }
556
+ /**
557
+ * Open and return a third briefcase connected to this iModel (useful for three-briefcase tests).
558
+ * The caller is responsible for closing the returned briefcase.
559
+ */
560
+ async openExtraBriefcase(accessToken = "extra-user") {
561
+ const extra = await HubWrappers.downloadAndOpenBriefcase({
562
+ iTwinId: HubMock.iTwinId,
563
+ iModelId: this.iModelId,
564
+ accessToken,
565
+ });
566
+ extra.channels.addAllowedChannel(ChannelControl.sharedChannelName);
567
+ return extra;
568
+ }
569
+ /**
570
+ * Execute an ECSql SELECT and collect all rows into a Map keyed by ECInstanceId.
571
+ * The SELECT must list ECInstanceId as the first column. Any additional columns are captured
572
+ * by the caller-supplied names and stored in the returned row objects.
573
+ *
574
+ * Rows are returned using {@link QueryRowFormat.UseJsPropertyNames} so:
575
+ * ECInstanceId → row.id
576
+ * ec_className(ECClassId) → row.className (when aliased as `className`)
577
+ * PropA → row.propA
578
+ * PropC2 → row.propC2
579
+ * etc.
580
+ *
581
+ * @param briefcase The open iModel to query.
582
+ * @param ecsql Complete ECSql SELECT statement whose first projected column is ECInstanceId.
583
+ */
584
+ static async queryToMap(briefcase, ecsql) {
585
+ const result = new Map();
586
+ const reader = briefcase.createQueryReader(ecsql, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames });
587
+ for await (const row of reader) {
588
+ const r = row.toRow();
589
+ result.set(r.id, r);
590
+ }
591
+ return result;
592
+ }
304
593
  shutdown() {
305
594
  this.far.close();
306
595
  this.local.close();
@@ -360,7 +649,7 @@ describe("Semantic Rebase", function () {
360
649
  // Local pulls and rebases local changes onto incoming schema change
361
650
  await pullChanges(localTxn);
362
651
  // Verify: local changes preserved, schema updated
363
- const element = t.getElement(t.local, elementId);
652
+ const element = t.getElementProps(t.local, elementId);
364
653
  chai.expect(element.propA).to.equal("local_update_a", "Local property update should be preserved");
365
654
  chai.expect(element.propC).to.equal("value_c", "Original propC should be preserved");
366
655
  const schema = t.local.getSchemaProps("TestDomain");
@@ -399,7 +688,7 @@ describe("Semantic Rebase", function () {
399
688
  await pullChanges(localTxn);
400
689
  chai.expect(t.checkIfFolderExists(t.local, txnProps.id, true)).to.be.true; // after rebase the folder should be there until push is called
401
690
  // Verify: incoming data changes applied, local schema preserved
402
- const element = t.getElement(t.local, elementId);
691
+ const element = t.getElementProps(t.local, elementId);
403
692
  chai.expect(element.propA).to.equal("far_update_a", "Incoming property update should be applied");
404
693
  chai.expect(element.propC).to.equal("value_c", "Original propC should be preserved");
405
694
  const schema = t.local.getSchemaProps("TestDomain");
@@ -442,10 +731,10 @@ describe("Semantic Rebase", function () {
442
731
  await pullChanges(localTxn);
443
732
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // its data changes on both sides semantic rebase is not used
444
733
  // Verify: both changes applied to their respective elements
445
- const element1 = t.getElement(t.local, elementId1);
734
+ const element1 = t.getElementProps(t.local, elementId1);
446
735
  chai.expect(element1.propA).to.equal("value_a1", "Element 1 propA should be unchanged");
447
736
  chai.expect(element1.propC).to.equal("far_update_c", "Element 1 incoming update should be applied");
448
- const element2 = t.getElement(t.local, elementId2);
737
+ const element2 = t.getElementProps(t.local, elementId2);
449
738
  chai.expect(element2.propA).to.equal("local_update_a", "Element 2 local update should be preserved");
450
739
  chai.expect(element2.propC).to.equal("value_c2", "Element 2 propC should be unchanged");
451
740
  });
@@ -500,35 +789,6 @@ describe("Semantic Rebase", function () {
500
789
  const schema = t.local.getSchemaProps("TestDomain");
501
790
  chai.expect(schema.version).to.equal("01.00.02", "Incoming schema (newer) should win, local should not override");
502
791
  });
503
- it("local trivial schema changes onto incoming identical schema changes", async () => {
504
- t = await TestIModel.initialize("TrivialSchemaIdentical");
505
- const localTxn = startTestTxn(t.local, "local trivial schema changes onto incoming identical schema changes local");
506
- const farTxn = startTestTxn(t.far, "local trivial schema changes onto incoming identical schema changes far");
507
- // Far imports v01.00.01 (adds PropC2)
508
- await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
509
- // Verify that we're holding a shared lock (not exclusive) for semantic rebase
510
- chai.expect(t.far.locks.holdsSharedLock(IModel.repositoryModelId)).to.be.true;
511
- chai.expect(t.far.holdsSchemaLock).to.be.false;
512
- const txnProps = t.far.txns.getLastSavedTxnProps();
513
- chai.expect(txnProps).to.not.be.undefined;
514
- chai.expect(txnProps.type).to.equal("Schema");
515
- chai.expect(t.checkIfFolderExists(t.far, txnProps.id, true)).to.be.true;
516
- await pushChanges(farTxn, "add PropC2 to class C");
517
- chai.expect(t.checkIfFolderExists(t.far, txnProps.id, true)).to.be.false; // after push the folder should not be there
518
- // Local imports the same v01.00.01 (adds PropC2)
519
- await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
520
- const txnPropsLocal = t.local.txns.getLastSavedTxnProps();
521
- chai.expect(txnPropsLocal).to.not.be.undefined;
522
- chai.expect(txnPropsLocal.type).to.equal("Schema");
523
- chai.expect(t.checkIfFolderExists(t.local, txnPropsLocal.id, true)).to.be.true;
524
- // Local pulls and rebases
525
- await pullChanges(localTxn);
526
- chai.expect(t.checkIfFolderExists(t.local, txnPropsLocal.id, true)).to.be.false; // after rebase the folder should not be there as both are identical
527
- chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // there should not be a rebase folder because the rebase folder is deleted if it contains nothing after rebase
528
- // Verify: schema preserved (both sides identical)
529
- const schema = t.local.getSchemaProps("TestDomain");
530
- chai.expect(schema.version).to.equal("01.00.01", "Schema should be v01.00.01");
531
- });
532
792
  it("local trivial schema changes onto incoming identical schema changes with data changes on both sides", async () => {
533
793
  t = await TestIModel.initialize("TrivialSchemaIdenticalWithData");
534
794
  const localTxn = startTestTxn(t.local, "local trivial schema changes onto incoming identical schema changes with data local");
@@ -571,11 +831,11 @@ describe("Semantic Rebase", function () {
571
831
  const schema = t.local.getSchemaProps("TestDomain");
572
832
  chai.expect(schema.version).to.equal("01.00.01", "Schema should be v01.00.01");
573
833
  // Verify: both elements exist with their original properties
574
- const farElement = t.getElement(t.local, farElementId);
834
+ const farElement = t.getElementProps(t.local, farElementId);
575
835
  chai.expect(farElement.propA).to.equal("far_value_a", "Far element propA should be preserved");
576
836
  chai.expect(farElement.propC).to.equal("far_value_c", "Far element propC should be preserved");
577
837
  chai.expect(farElement.propC2).to.equal("far_value_c2", "Far element propC2 should be preserved");
578
- const localElement = t.getElement(t.local, localElementId);
838
+ const localElement = t.getElementProps(t.local, localElementId);
579
839
  chai.expect(localElement.propA).to.equal("local_value_a", "Local element propA should be preserved");
580
840
  chai.expect(localElement.propC).to.equal("local_value_c", "Local element propC should be preserved");
581
841
  chai.expect(localElement.propC2).to.equal("local_value_c2", "Local element propC2 should be preserved");
@@ -691,10 +951,10 @@ describe("Semantic Rebase", function () {
691
951
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // after push the folder should not be there
692
952
  // Verify: both elements have PropC intact, schema transformed locally
693
953
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
694
- const farElement = t.getElement(t.local, farElementId);
954
+ const farElement = t.getElementProps(t.local, farElementId);
695
955
  chai.expect(farElement.propA).to.equal("far_value_a", "Far element propA should be preserved");
696
956
  chai.expect(farElement.propC).to.equal("far_value_c", "Far element propC should be preserved after transform");
697
- const localElement = t.getElement(t.local, localElementId);
957
+ const localElement = t.getElementProps(t.local, localElementId);
698
958
  chai.expect(localElement.propA).to.equal("local_value_a", "Local element propA should be preserved");
699
959
  chai.expect(localElement.propC).to.equal("local_value_c", "Local element propC should be preserved after transform");
700
960
  });
@@ -728,10 +988,10 @@ describe("Semantic Rebase", function () {
728
988
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // after push the folder should not be there
729
989
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
730
990
  // Verify: both elements have PropC intact after incoming transform
731
- const farElement = t.getElement(t.local, farElementId);
991
+ const farElement = t.getElementProps(t.local, farElementId);
732
992
  chai.expect(farElement.propA).to.equal("far_value_a", "Far element propA should be preserved");
733
993
  chai.expect(farElement.propC).to.equal("far_value_c", "Far element propC should be preserved after incoming transform");
734
- const localElement = t.getElement(t.local, localElementId);
994
+ const localElement = t.getElementProps(t.local, localElementId);
735
995
  chai.expect(localElement.propA).to.equal("local_value_a", "Local element propA should be preserved");
736
996
  chai.expect(localElement.propC).to.equal("local_value_c", "Local element propC should be preserved after incoming transform");
737
997
  });
@@ -781,16 +1041,16 @@ describe("Semantic Rebase", function () {
781
1041
  t.far.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
782
1042
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
783
1043
  // Verify: all elements have both PropC and PropD intact
784
- const farElemC = t.getElement(t.local, farElementC);
1044
+ const farElemC = t.getElementProps(t.local, farElementC);
785
1045
  chai.expect(farElemC.propA).to.equal("far_value_a_c", "Far element C propA should be preserved");
786
1046
  chai.expect(farElemC.propC).to.equal("far_value_c", "Far element C propC should be preserved after both transforms");
787
- const farElemD = t.getElement(t.local, farElementD);
1047
+ const farElemD = t.getElementProps(t.local, farElementD);
788
1048
  chai.expect(farElemD.propA).to.equal("far_value_a_d", "Far element D propA should be preserved");
789
1049
  chai.expect(farElemD.propD).to.equal("far_value_d", "Far element D propD should be preserved after both transforms");
790
- const localElemC = t.getElement(t.local, localElementC);
1050
+ const localElemC = t.getElementProps(t.local, localElementC);
791
1051
  chai.expect(localElemC.propA).to.equal("local_value_a_c", "Local element C propA should be preserved");
792
1052
  chai.expect(localElemC.propC).to.equal("local_value_c", "Local element C propC should be preserved after both transforms");
793
- const localElemD = t.getElement(t.local, localElementD);
1053
+ const localElemD = t.getElementProps(t.local, localElementD);
794
1054
  chai.expect(localElemD.propA).to.equal("local_value_a_d", "Local element D propA should be preserved");
795
1055
  chai.expect(localElemD.propD).to.equal("local_value_d", "Local element D propD should be preserved after both transforms");
796
1056
  });
@@ -822,7 +1082,7 @@ describe("Semantic Rebase", function () {
822
1082
  await t.local.locks.acquireLocks({ exclusive: elementId });
823
1083
  t.updateElement(localTxn, elementId, { propC: "local_modified_c" });
824
1084
  localTxn.saveChanges("local update propC");
825
- let element = t.getElement(t.local, elementId);
1085
+ let element = t.getElementProps(t.local, elementId);
826
1086
  chai.expect(element.propA).to.equal("initial_value_a", "PropA should be unchanged");
827
1087
  chai.expect(element.propC).to.equal("local_modified_c", "PropC should have the local modified value before incoming transform");
828
1088
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // no schema change yet on local so no rebase folder
@@ -832,7 +1092,7 @@ describe("Semantic Rebase", function () {
832
1092
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false;
833
1093
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
834
1094
  // Verify: PropC has the modified local value after the transform
835
- element = t.getElement(t.local, elementId);
1095
+ element = t.getElementProps(t.local, elementId);
836
1096
  chai.expect(element.propA).to.equal("initial_value_a", "PropA should be unchanged");
837
1097
  chai.expect(element.propC).to.equal("local_modified_c", "PropC should have the local modified value after incoming transform");
838
1098
  const schema = t.local.getSchemaProps("TestDomain");
@@ -868,10 +1128,10 @@ describe("Semantic Rebase", function () {
868
1128
  await pushChanges(localTxn, "far move PropC to A");
869
1129
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // after push the folder should not be there
870
1130
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
871
- const elementFar = t.getElement(t.local, elementIdFar);
1131
+ const elementFar = t.getElementProps(t.local, elementIdFar);
872
1132
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
873
1133
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
874
- const elementLocal = t.getElement(t.local, elementIdLocal);
1134
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
875
1135
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
876
1136
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
877
1137
  const schema = t.local.getSchemaProps("TestDomain");
@@ -925,10 +1185,10 @@ describe("Semantic Rebase", function () {
925
1185
  await pullChanges(localTxn);
926
1186
  t.local.clearCaches();
927
1187
  // Verify: both local data changes preserved after incoming transform
928
- const element1 = t.getElement(t.local, elementId1);
1188
+ const element1 = t.getElementProps(t.local, elementId1);
929
1189
  chai.expect(element1.propA).to.equal("first_update_a", "First element propA update should be preserved");
930
1190
  chai.expect(element1.propC).to.equal("initial_c", "First element propC should be preserved after transform");
931
- const element2 = t.getElement(t.local, elementId2);
1191
+ const element2 = t.getElementProps(t.local, elementId2);
932
1192
  chai.expect(element2.propA).to.equal("second_element_a", "Second element propA should be preserved");
933
1193
  chai.expect(element2.propC).to.equal("second_element_c", "Second element propC should be preserved after transform");
934
1194
  const schema = t.local.getSchemaProps("TestDomain");
@@ -967,10 +1227,10 @@ describe("Semantic Rebase", function () {
967
1227
  await pullChanges(localTxn);
968
1228
  t.local.clearCaches();
969
1229
  // Verify: both incoming data changes applied, local schema transformation preserved
970
- const element1 = t.getElement(t.local, elementId1);
1230
+ const element1 = t.getElementProps(t.local, elementId1);
971
1231
  chai.expect(element1.propA).to.equal("far_first_update_a", "First element incoming update should be applied");
972
1232
  chai.expect(element1.propC).to.equal("initial_c", "First element propC should be preserved after transform");
973
- const element2 = t.getElement(t.local, elementId2);
1233
+ const element2 = t.getElementProps(t.local, elementId2);
974
1234
  chai.expect(element2.propA).to.equal("far_second_element_a", "Second element should exist with correct propA");
975
1235
  chai.expect(element2.propC).to.equal("far_second_element_c", "Second element propC should be preserved after transform");
976
1236
  const schema = t.local.getSchemaProps("TestDomain");
@@ -991,6 +1251,7 @@ describe("Semantic Rebase", function () {
991
1251
  // Verify: element was not saved, schema was not imported
992
1252
  const schema = t.local.getSchemaProps("TestDomain");
993
1253
  chai.expect(schema.version).to.equal("01.00.00", "Schema should remain at v01.00.00");
1254
+ chai.expect(t.local.isOpen).to.be.true;
994
1255
  });
995
1256
  });
996
1257
  /**
@@ -1041,10 +1302,10 @@ describe("Semantic Rebase with indirect changes", function () {
1041
1302
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // there should not be a rebase folder because no schema change on either side
1042
1303
  chai.expect(Id64.isValidId64(elementIdFar) && Id64.isValidId64(elementIdLocal)).to.be.true;
1043
1304
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
1044
- const elementFar = t.getElement(t.local, elementIdFar);
1305
+ const elementFar = t.getElementProps(t.local, elementIdFar);
1045
1306
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
1046
1307
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
1047
- const elementLocal = t.getElement(t.local, elementIdLocal);
1308
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
1048
1309
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
1049
1310
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
1050
1311
  });
@@ -1083,10 +1344,10 @@ describe("Semantic Rebase with indirect changes", function () {
1083
1344
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false;
1084
1345
  chai.expect(Id64.isValidId64(elementIdFar) && Id64.isValidId64(elementIdLocal)).to.be.true;
1085
1346
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
1086
- const elementFar = t.getElement(t.local, elementIdFar);
1347
+ const elementFar = t.getElementProps(t.local, elementIdFar);
1087
1348
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
1088
1349
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
1089
- const elementLocal = t.getElement(t.local, elementIdLocal);
1350
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
1090
1351
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
1091
1352
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
1092
1353
  const schema = t.local.getSchemaProps("TestDomain");
@@ -1132,10 +1393,10 @@ describe("Semantic Rebase with indirect changes", function () {
1132
1393
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // after push the folder should not be there
1133
1394
  chai.expect(Id64.isValidId64(elementIdFar) && Id64.isValidId64(elementIdLocal)).to.be.true;
1134
1395
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
1135
- const elementFar = t.getElement(t.local, elementIdFar);
1396
+ const elementFar = t.getElementProps(t.local, elementIdFar);
1136
1397
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
1137
1398
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
1138
- const elementLocal = t.getElement(t.local, elementIdLocal);
1399
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
1139
1400
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
1140
1401
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
1141
1402
  const schema = t.local.getSchemaProps("TestDomain");
@@ -1176,10 +1437,10 @@ describe("Semantic Rebase with indirect changes", function () {
1176
1437
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false;
1177
1438
  chai.expect(Id64.isValidId64(elementIdFar) && Id64.isValidId64(elementIdLocal)).to.be.true;
1178
1439
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
1179
- const elementFar = t.getElement(t.local, elementIdFar);
1440
+ const elementFar = t.getElementProps(t.local, elementIdFar);
1180
1441
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
1181
1442
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
1182
- const elementLocal = t.getElement(t.local, elementIdLocal);
1443
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
1183
1444
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
1184
1445
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
1185
1446
  const schema = t.local.getSchemaProps("TestDomain");
@@ -1225,10 +1486,10 @@ describe("Semantic Rebase with indirect changes", function () {
1225
1486
  chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // schema change is no op and data changes are generated on the fly and removed once rebased so rebase folder should not be there
1226
1487
  chai.expect(Id64.isValidId64(elementIdFar) && Id64.isValidId64(elementIdLocal)).to.be.true;
1227
1488
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
1228
- const elementFar = t.getElement(t.local, elementIdFar);
1489
+ const elementFar = t.getElementProps(t.local, elementIdFar);
1229
1490
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
1230
1491
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
1231
- const elementLocal = t.getElement(t.local, elementIdLocal);
1492
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
1232
1493
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
1233
1494
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
1234
1495
  const schema = t.local.getSchemaProps("TestDomain");
@@ -1268,14 +1529,1927 @@ describe("Semantic Rebase with indirect changes", function () {
1268
1529
  chai.expect(t.checkIfFolderExists(t.local, localTxnProps.id, true)).to.be.false; // after push the schema folder should not be there
1269
1530
  chai.expect(Id64.isValidId64(elementIdFar) && Id64.isValidId64(elementIdLocal)).to.be.true;
1270
1531
  t.local.clearCaches(); // Clear caches to ensure we read transformed properties from iModel
1271
- const elementFar = t.getElement(t.local, elementIdFar);
1532
+ const elementFar = t.getElementProps(t.local, elementIdFar);
1272
1533
  chai.expect(elementFar.propA).to.equal("far_value_a", "PropA should be unchanged");
1273
1534
  chai.expect(elementFar.propC).to.equal("far_value_c", "PropC should be unchanged");
1274
- const elementLocal = t.getElement(t.local, elementIdLocal);
1535
+ const elementLocal = t.getElementProps(t.local, elementIdLocal);
1275
1536
  chai.expect(elementLocal.propA).to.equal("local_value_a", "PropA should be unchanged");
1276
1537
  chai.expect(elementLocal.propC).to.equal("local_value_c", "PropC should be unchanged");
1277
1538
  const schema = t.local.getSchemaProps("TestDomain");
1278
1539
  chai.expect(schema.version).to.equal("01.00.02", "Schema should be transformed to v01.00.02");
1279
1540
  });
1280
1541
  });
1542
+ /**
1543
+ * Test suite for data conflicts, conflict handlers, lifecycle events, and mixed schema+conflict scenarios during semantic rebase.
1544
+ */
1545
+ describe("Semantic Rebase - Data Correctness Under Conflict", function () {
1546
+ this.timeout(60000);
1547
+ let t;
1548
+ before(async () => {
1549
+ await TestUtils.shutdownBackend();
1550
+ await TestUtils.startBackend({ useSemanticRebase: true });
1551
+ });
1552
+ afterEach(() => {
1553
+ if (t) {
1554
+ t.shutdown();
1555
+ t = undefined;
1556
+ }
1557
+ });
1558
+ after(async () => {
1559
+ await TestUtils.shutdownBackend();
1560
+ await TestUtils.startBackend();
1561
+ });
1562
+ // ─── Section F: Conflicts with Schema Changes ────────────────────────────────
1563
+ // Every test below verifies ELEMENT DATA CORRECTNESS after semantic rebase.
1564
+ it("F1: local data patch on element survives transforming schema rebase: propC value preserved after column migration", async () => {
1565
+ t = await TestIModel.initialize("F1ConflictDuringTransformingSchemaRebase");
1566
+ let localTxn = startTestTxn(t.local, "F1 local");
1567
+ let farTxn = startTestTxn(t.far, "F1 far");
1568
+ // Create shared element with propC populated
1569
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1570
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
1571
+ farTxn.saveChanges("create shared element");
1572
+ await pushChanges(farTxn, "create shared element");
1573
+ farTxn = startTestTxn(t.far, "F1 far 2");
1574
+ await pullChanges(localTxn);
1575
+ localTxn = startTestTxn(t.local, "F1 local 2");
1576
+ // Local locks elementId and makes its data change BEFORE far pushes.
1577
+ // If local tried to lock after far's push (at a newer changeset index),
1578
+ // doesBriefcaseRequirePullBeforeLock would throw PullIsRequired.
1579
+ await t.local.locks.acquireLocks({ exclusive: elementId });
1580
+ t.updateElement(localTxn, elementId, { propC: "local_updated_c" });
1581
+ localTxn.saveChanges("local update propC");
1582
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
1583
+ // Far pushes a trivial schema only — no data change on elementId, no exclusive lock on it.
1584
+ // Far's schema import acquires only a shared lock on repositoryModelId and never touches
1585
+ // elementId's lock record, so local's already-held exclusive lock is not contended.
1586
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1587
+ await pushChanges(farTxn, "far push trivial schema v01");
1588
+ // Local pulls:
1589
+ // Incoming: trivial schema (v01) applied.
1590
+ // Rebase:
1591
+ // 1. Local's transforming schema txn reinstated → v02 applied (PropC column migrated from C to A)
1592
+ // 2. Local's data patch reinstated → propC set to "local_updated_c"
1593
+ // Bug scenario: if applyInstancePatch fails to map propC to the migrated A.PropC column,
1594
+ // the value "local_updated_c" would be silently dropped and "initial_c" would remain.
1595
+ await pullChanges(localTxn);
1596
+ t.local.clearCaches();
1597
+ const element = t.getElementProps(t.local, elementId);
1598
+ chai.expect(element.propC).to.equal("local_updated_c", "Local propC value should survive after transforming schema rebase");
1599
+ const schema = t.local.getSchemaProps("TestDomain");
1600
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v01.00.02 after rebase");
1601
+ });
1602
+ it("F3: local element deletion + incoming transforming schema change: delete is reinstated, element stays gone and checking GeometricGuid of Model [BUG]", async () => {
1603
+ t = await TestIModel.initialize("F2DeleteIncomingTransform");
1604
+ let localTxn = startTestTxn(t.local, "F2 local");
1605
+ let farTxn = startTestTxn(t.far, "F2 far");
1606
+ // Create shared element (shared model lock — no exclusive lock record on elementId)
1607
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1608
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
1609
+ farTxn.saveChanges("create shared element");
1610
+ await pushChanges(farTxn, "create shared element");
1611
+ farTxn = startTestTxn(t.far, "F2 far 2");
1612
+ await pullChanges(localTxn);
1613
+ localTxn = startTestTxn(t.local, "F2 local 2");
1614
+ // Far imports transforming schema (moves PropC from C to A); no exclusive lock on elementId
1615
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
1616
+ await pushChanges(farTxn, "far import transforming schema");
1617
+ // Local deletes the element — safe because far never exclusively locked elementId,
1618
+ // so lastExclusiveReleaseChangesetIndex for elementId is still undefined.
1619
+ await t.local.locks.acquireLocks({ exclusive: elementId });
1620
+ localTxn.deleteElement(elementId);
1621
+ localTxn.saveChanges("local delete element");
1622
+ const drawingModel = t.getModelProps(t.local, t.drawingModelId);
1623
+ const geometricGuidBefore = drawingModel.geometryGuid;
1624
+ // Local pulls - incoming transforming schema applied, then local deletion reinstated
1625
+ await pullChanges(localTxn);
1626
+ t.local.clearCaches();
1627
+ chai.expect(() => t.getElementProps(t.local, elementId)).to.throw(`element not found`);
1628
+ const drawingModelAfter = t.getModelProps(t.local, t.drawingModelId);
1629
+ const geometricGuidAfter = drawingModelAfter.geometryGuid;
1630
+ chai.expect(geometricGuidAfter).to.not.equal(geometricGuidBefore, "GeometricGuid of the model should not remain the same after rebase"); // BUG should exactly be same
1631
+ const schema = t.local.getSchemaProps("TestDomain");
1632
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be updated to v01.00.02");
1633
+ });
1634
+ it("F4: incoming element deletion + local transforming schema change: schema upgrade survives, element absent", async () => {
1635
+ t = await TestIModel.initialize("F4IncomingDeleteLocalTransform");
1636
+ let localTxn = startTestTxn(t.local, "F4 local");
1637
+ let farTxn = startTestTxn(t.far, "F4 far");
1638
+ // Create shared element
1639
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1640
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
1641
+ farTxn.saveChanges("create shared element");
1642
+ await pushChanges(farTxn, "create shared element");
1643
+ farTxn = startTestTxn(t.far, "F4 far 2");
1644
+ await pullChanges(localTxn);
1645
+ localTxn = startTestTxn(t.local, "F4 local 2");
1646
+ // Far exclusively locks and deletes the element, then pushes
1647
+ await t.far.locks.acquireLocks({ exclusive: elementId });
1648
+ farTxn.deleteElement(elementId);
1649
+ farTxn.saveChanges("far delete element");
1650
+ await pushChanges(farTxn, "far delete element");
1651
+ // Local imports transforming schema only.
1652
+ // NOTE: do NOT lock elementId here. After far's exclusive lock + push, the element's
1653
+ // lastExclusiveReleaseChangesetIndex is set at a newer changeset index than local's head.
1654
+ // acquireLocks on elementId from local would throw PullIsRequired via
1655
+ // doesBriefcaseRequirePullBeforeLock. The schema import alone is sufficient to test that
1656
+ // semantic rebase handles the case where an incoming changeset deleted an element that
1657
+ // the local schema txn has no data patches for.
1658
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
1659
+ // Local pulls - far's delete applied as incoming changeset; local schema txn reinstated.
1660
+ // No data patch on elementId → no NotFound conflict. Schema upgrade succeeds cleanly.
1661
+ await pullChanges(localTxn);
1662
+ t.local.clearCaches();
1663
+ // Element is gone (deleted by incoming changeset)
1664
+ chai.expect(() => t.getElementProps(t.local, elementId)).to.throw(`element not found`);
1665
+ // Schema was upgraded successfully despite the element deletion
1666
+ const schema = t.local.getSchemaProps("TestDomain");
1667
+ chai.expect(schema.version).to.equal("01.00.02", "Schema upgrade should survive when incoming changeset deleted an element");
1668
+ });
1669
+ it("F5: insert → update → delete of same element across three sequential local data txns; incoming schema → element absent and no stale ECInstanceId", async () => {
1670
+ // This test probes the ordering of sequential data patches.
1671
+ // If patches are applied out of order the update will hit NotFound (element not yet inserted)
1672
+ // or the delete will resurrect a value that the update produced.
1673
+ t = await TestIModel.initialize("F5InsertUpdateDeleteChain");
1674
+ const localTxn = startTestTxn(t.local, "F5 local");
1675
+ const farTxn = startTestTxn(t.far, "F5 far");
1676
+ // Far pushes a data change to create an incoming changeset
1677
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1678
+ t.insertElement(farTxn, "TestDomain:C", { propA: "far_a", propC: "far_c" });
1679
+ farTxn.saveChanges("far insert element");
1680
+ await pushChanges(farTxn, "far create element");
1681
+ // Local schema txn — semantic rebase trigger
1682
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1683
+ // Local data txn1: insert element E1 (shared model lock; never locked by far)
1684
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
1685
+ const e1Id = t.insertElement(localTxn, "TestDomain:C", { propA: "e1_initial", propC: "e1_c" });
1686
+ localTxn.saveChanges("local insert E1 (txn1)");
1687
+ // Local data txn2: update E1 (local inserted it → no lock violation)
1688
+ t.updateElement(localTxn, e1Id, { propA: "e1_updated", propC2: "e1_c2" });
1689
+ localTxn.saveChanges("local update E1 (txn2)");
1690
+ // Local data txn3: delete E1
1691
+ localTxn.deleteElement(e1Id);
1692
+ localTxn.saveChanges("local delete E1 (txn3)");
1693
+ await pullChanges(localTxn);
1694
+ chai.expect(() => t.getElementProps(t.local, e1Id)).to.throw(`element not found`);
1695
+ });
1696
+ it("F6: local inserts elements of three different classes across separate data txns; incoming trivial schema; ECInstanceIds and classNames preserved", async () => {
1697
+ // This test probes class-resolution in constructPatchInstance (resolves classFullName from
1698
+ // ECClassId / $meta.classFullName / $meta.fallbackClassId) for multiple BIS subclasses.
1699
+ // If class resolution is wrong, insertInstance will fail or create elements under the wrong class.
1700
+ t = await TestIModel.initialize("F6ThreeClassInserts");
1701
+ const localTxn = startTestTxn(t.local, "F6 local");
1702
+ const farTxn = startTestTxn(t.far, "F6 far");
1703
+ // Far pushes an incoming data change
1704
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1705
+ const farElemId = t.insertElement(farTxn, "TestDomain:A", { propA: "far_a" });
1706
+ farTxn.saveChanges("far insert element");
1707
+ await pushChanges(farTxn, "far create element");
1708
+ // Local schema txn — semantic rebase trigger (trivial: adds PropC2)
1709
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1710
+ // Local data txns: insert one element per class (A, C, D) in three separate txns.
1711
+ // All use shared model lock — none of these elements were ever locked by far.
1712
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
1713
+ const aId = t.insertElement(localTxn, "TestDomain:A", { propA: "local_a_only" });
1714
+ localTxn.saveChanges("local insert A (txn1)");
1715
+ const cId = t.insertElement(localTxn, "TestDomain:C", { propA: "local_c_a", propC: "local_c_val" });
1716
+ localTxn.saveChanges("local insert C (txn2)");
1717
+ const dId = t.insertElement(localTxn, "TestDomain:D", { propA: "local_d_a", propD: "local_d_val" });
1718
+ localTxn.saveChanges("local insert D (txn3)");
1719
+ // Capture ECInstanceIds before pull — they must be identical after rebase (forceUseId)
1720
+ const prePullIds = { aId, cId, dId };
1721
+ await pullChanges(localTxn);
1722
+ t.local.clearCaches();
1723
+ // Verify each element: ECInstanceId preserved, classFullName correct, props intact
1724
+ const aElem = t.getElementProps(t.local, prePullIds.aId);
1725
+ chai.expect(aElem.id).to.equal(prePullIds.aId, "Class A ECInstanceId must be preserved (forceUseId)");
1726
+ chai.expect(aElem.classFullName).to.equal("TestDomain:A", "Class A classFullName must be correct");
1727
+ chai.expect(aElem.propA).to.equal("local_a_only");
1728
+ const cElem = t.getElementProps(t.local, prePullIds.cId);
1729
+ chai.expect(cElem.id).to.equal(prePullIds.cId, "Class C ECInstanceId must be preserved (forceUseId)");
1730
+ chai.expect(cElem.classFullName).to.equal("TestDomain:C", "Class C classFullName must be correct");
1731
+ chai.expect(cElem.propA).to.equal("local_c_a");
1732
+ chai.expect(cElem.propC).to.equal("local_c_val");
1733
+ const dElem = t.getElementProps(t.local, prePullIds.dId);
1734
+ chai.expect(dElem.id).to.equal(prePullIds.dId, "Class D ECInstanceId must be preserved (forceUseId)");
1735
+ chai.expect(dElem.classFullName).to.equal("TestDomain:D", "Class D classFullName must be correct");
1736
+ chai.expect(dElem.propA).to.equal("local_d_a");
1737
+ chai.expect(dElem.propD).to.equal("local_d_val");
1738
+ // Also confirm all three IDs appear in an ECSqlReader scan (polymorphic query on base class A)
1739
+ const allRows = await TestIModel.queryToMap(t.local, `SELECT ECInstanceId, ec_className(ECClassId) AS className FROM TestDomain.A`);
1740
+ chai.expect(allRows.has(prePullIds.aId), "A element must appear in ECSqlReader scan").to.be.true;
1741
+ chai.expect(allRows.has(prePullIds.cId), "C element must appear in ECSqlReader scan").to.be.true;
1742
+ chai.expect(allRows.has(prePullIds.dId), "D element must appear in ECSqlReader scan").to.be.true;
1743
+ // Confirm className is resolved correctly in the ECSql results
1744
+ chai.expect(allRows.get(prePullIds.cId)?.className).to.include("C", "ECSqlReader className for C element must include 'C'");
1745
+ chai.expect(allRows.get(prePullIds.dId)?.className).to.include("D", "ECSqlReader className for D element must include 'D'");
1746
+ // Far's element must also be present and unmodified
1747
+ const farElem = t.getElementProps(t.local, farElemId);
1748
+ chai.expect(farElem.propA).to.equal("far_a");
1749
+ const schema = t.local.getSchemaProps("TestDomain");
1750
+ chai.expect(schema.version).to.equal("01.00.01");
1751
+ });
1752
+ });
1753
+ /**
1754
+ * Multi-step schema upgrade chains.
1755
+ * Tests scenarios where one or both sides import schemas in multiple sequential steps before the rebase.
1756
+ */
1757
+ describe("Semantic Rebase - Multi-Step Schema Upgrade Chains", function () {
1758
+ this.timeout(60000);
1759
+ let t;
1760
+ before(async () => {
1761
+ await TestUtils.shutdownBackend();
1762
+ await TestUtils.startBackend({ useSemanticRebase: true });
1763
+ });
1764
+ afterEach(() => {
1765
+ if (t) {
1766
+ t.shutdown();
1767
+ t = undefined;
1768
+ }
1769
+ });
1770
+ after(async () => {
1771
+ await TestUtils.shutdownBackend();
1772
+ await TestUtils.startBackend();
1773
+ });
1774
+ it("G1: local imports schema in two chained steps (v01→v02) before pulling; incoming has v01 only", async () => {
1775
+ // Local: v01 then v02. Incoming (far): only v01.
1776
+ // Expected: local v02 wins because it is strictly newer.
1777
+ t = await TestIModel.initialize("G1LocalChainedSchemaUpgrade");
1778
+ const farTxn = startTestTxn(t.far, "G1 far");
1779
+ const localTxn = startTestTxn(t.local, "G1 local");
1780
+ // Far imports v01 and pushes
1781
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1782
+ const farTxnProps = t.far.txns.getLastSavedTxnProps();
1783
+ await pushChanges(farTxn, "far import v01");
1784
+ chai.expect(t.checkIfFolderExists(t.far, farTxnProps.id, true)).to.be.false; // cleaned up on push
1785
+ // Local: import v01, then immediately upgrade to v02 (chain before any pull)
1786
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1787
+ const localTxnPropsV01 = t.local.txns.getLastSavedTxnProps();
1788
+ chai.expect(localTxnPropsV01).to.not.be.undefined;
1789
+ chai.expect(t.checkIfFolderExists(t.local, localTxnPropsV01.id, true)).to.be.true;
1790
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
1791
+ const localTxnPropsV02 = t.local.txns.getLastSavedTxnProps();
1792
+ chai.expect(localTxnPropsV02).to.not.be.undefined;
1793
+ chai.expect(t.checkIfFolderExists(t.local, localTxnPropsV02.id, true)).to.be.true;
1794
+ // Local pulls: incoming has v01, local already at v02 → local v02 txn wins, v01 local txn is a no-op
1795
+ await pullChanges(localTxn);
1796
+ // v01 local txn became no-op (incoming v01 arrived and covers it)
1797
+ chai.expect(t.checkIfFolderExists(t.local, localTxnPropsV01.id, true)).to.be.false;
1798
+ // v02 is still pending push
1799
+ chai.expect(t.checkIfFolderExists(t.local, localTxnPropsV02.id, true)).to.be.true;
1800
+ const schema = t.local.getSchemaProps("TestDomain");
1801
+ chai.expect(schema.version).to.equal("01.00.02", "Local v02 should win after chain import rebase");
1802
+ // PropD2 (from v02) should be visible
1803
+ const classD = await t.local.schemaContext.getSchemaItem("TestDomain", "D", EntityClass);
1804
+ chai.expect(await classD.getProperty("PropD2")).to.exist;
1805
+ });
1806
+ it("G2: incoming has two sequential schema changesets (v01 then v02), local has only data changes", async () => {
1807
+ // Far: schema v01 (cs1) then schema v02 (cs2).
1808
+ // Local: data change only → rebased on top of both schema changesets.
1809
+ t = await TestIModel.initialize("G2IncomingTwoSchemaChangesets");
1810
+ let farTxn = startTestTxn(t.far, "G2 far");
1811
+ let localTxn = startTestTxn(t.local, "G2 local");
1812
+ // Create shared element on far, push
1813
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1814
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
1815
+ farTxn.saveChanges("create element");
1816
+ await pushChanges(farTxn, "create element");
1817
+ farTxn = startTestTxn(t.far, "G2 far 2");
1818
+ // Local pulls to get element
1819
+ await pullChanges(localTxn);
1820
+ localTxn = startTestTxn(t.local, "G2 local 2");
1821
+ // Far: schema changeset 1 (v01)
1822
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1823
+ await pushChanges(farTxn, "far v01");
1824
+ farTxn = startTestTxn(t.far, "G2 far 3");
1825
+ // Far: schema changeset 2 (v02)
1826
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
1827
+ await pushChanges(farTxn, "far v02");
1828
+ // Local: data change only (update element propA)
1829
+ await t.local.locks.acquireLocks({ exclusive: elementId });
1830
+ t.updateElement(localTxn, elementId, { propA: "local_updated_a" });
1831
+ localTxn.saveChanges("local update propA");
1832
+ // Local pulls: both far schema changesets are incoming data, local data rebase on top
1833
+ await pullChanges(localTxn);
1834
+ const element = t.getElementProps(t.local, elementId);
1835
+ chai.expect(element.propA).to.equal("local_updated_a", "Local data change should be preserved after two incoming schema changesets");
1836
+ chai.expect(element.propC).to.equal("initial_c", "propC should be unchanged");
1837
+ const schema = t.local.getSchemaProps("TestDomain");
1838
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after two incoming schema changesets");
1839
+ // Both PropC2 and PropD2 should exist
1840
+ const classC = await t.local.schemaContext.getSchemaItem("TestDomain", "C", EntityClass);
1841
+ chai.expect(await classC.getProperty("PropC2")).to.exist;
1842
+ const classD = await t.local.schemaContext.getSchemaItem("TestDomain", "D", EntityClass);
1843
+ chai.expect(await classD.getProperty("PropD2")).to.exist;
1844
+ });
1845
+ it("G3: local imports schema in two steps with data between them; incoming has data only", async () => {
1846
+ // Local: data txn → schema v01 txn → data txn → schema v02 txn.
1847
+ // Incoming (far): data changes to different elements.
1848
+ // Expected: all four local txns preserved in order, far data also visible.
1849
+ t = await TestIModel.initialize("G3LocalSchemaDataInterleaved");
1850
+ let farTxn = startTestTxn(t.far, "G3 far");
1851
+ let localTxn = startTestTxn(t.local, "G3 local");
1852
+ // Create two shared elements via far and push
1853
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1854
+ const sharedId1 = t.insertElement(farTxn, "TestDomain:C", { propA: "shared_a1", propC: "shared_c1" });
1855
+ const sharedId2 = t.insertElement(farTxn, "TestDomain:C", { propA: "shared_a2", propC: "shared_c2" });
1856
+ farTxn.saveChanges("far create shared elements");
1857
+ await pushChanges(farTxn, "far create shared elements");
1858
+ farTxn = startTestTxn(t.far, "G3 far 2");
1859
+ // Both pull
1860
+ await pullChanges(localTxn);
1861
+ localTxn = startTestTxn(t.local, "G3 local 2");
1862
+ // Far: update sharedId1 and push (data only)
1863
+ await t.far.locks.acquireLocks({ exclusive: sharedId1 });
1864
+ t.updateElement(farTxn, sharedId1, { propA: "far_updated_a1" });
1865
+ farTxn.saveChanges("far update sharedId1");
1866
+ await pushChanges(farTxn, "far update sharedId1");
1867
+ // Local: txn1 - update sharedId2 (data)
1868
+ await t.local.locks.acquireLocks({ exclusive: sharedId2 });
1869
+ t.updateElement(localTxn, sharedId2, { propA: "local_updated_a2" });
1870
+ localTxn.saveChanges("local txn1 update sharedId2");
1871
+ // Local: txn2 - schema v01 (adds PropC2)
1872
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1873
+ // Local: txn3 - insert new element using new PropC2
1874
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
1875
+ const localNewId = t.insertElement(localTxn, "TestDomain:C", { propA: "new_local_a", propC: "new_local_c", propC2: "new_local_c2" });
1876
+ localTxn.saveChanges("local txn3 insert element with PropC2");
1877
+ // Local: txn4 - schema v02 (adds PropD2)
1878
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
1879
+ // Local pulls and rebases all four local txns onto incoming data change
1880
+ await pullChanges(localTxn);
1881
+ t.local.clearCaches();
1882
+ // sharedId1 should have far's update
1883
+ const elem1 = t.getElementProps(t.local, sharedId1);
1884
+ chai.expect(elem1.propA).to.equal("far_updated_a1", "Far update to sharedId1 should be applied");
1885
+ // sharedId2 should have local's update
1886
+ const elem2 = t.getElementProps(t.local, sharedId2);
1887
+ chai.expect(elem2.propA).to.equal("local_updated_a2", "Local update to sharedId2 should be preserved");
1888
+ // New locally-inserted element with PropC2 should exist
1889
+ const newElem = t.getElementProps(t.local, localNewId);
1890
+ chai.expect(newElem.propA).to.equal("new_local_a", "Locally-inserted element should be preserved");
1891
+ chai.expect(newElem.propC2).to.equal("new_local_c2", "PropC2 from local element should be preserved");
1892
+ // Schema should be at v02 (both local schema imports preserved)
1893
+ const schema = t.local.getSchemaProps("TestDomain");
1894
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after two local schema imports");
1895
+ });
1896
+ it("G4: three successive schema increments from three different pushes, local rebases all", async () => {
1897
+ // far pushes v01, then v02, then v03 as separate changesets.
1898
+ // local has a single data change it needs to rebase on top of all three.
1899
+ t = await TestIModel.initialize("G4ThreeSuccessiveSchemaIncrements");
1900
+ let farTxn = startTestTxn(t.far, "G4 far");
1901
+ let localTxn = startTestTxn(t.local, "G4 local");
1902
+ // Create shared element on far
1903
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1904
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "init_a", propC: "init_c" });
1905
+ farTxn.saveChanges("far create element");
1906
+ await pushChanges(farTxn, "far create element");
1907
+ farTxn = startTestTxn(t.far, "G4 far 2");
1908
+ // Both pull to sync
1909
+ await pullChanges(localTxn);
1910
+ localTxn = startTestTxn(t.local, "G4 local 2");
1911
+ // Far pushes v01
1912
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
1913
+ await pushChanges(farTxn, "far v01");
1914
+ farTxn = startTestTxn(t.far, "G4 far 3");
1915
+ // Far pushes v02
1916
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
1917
+ await pushChanges(farTxn, "far v02");
1918
+ farTxn = startTestTxn(t.far, "G4 far 4");
1919
+ // Far pushes v03 (moves PropC and PropD to A)
1920
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x03MovePropCAndD]);
1921
+ await pushChanges(farTxn, "far v03");
1922
+ // Local: data change only
1923
+ await t.local.locks.acquireLocks({ exclusive: elementId });
1924
+ t.updateElement(localTxn, elementId, { propA: "local_updated_a", propC: "local_updated_c" });
1925
+ localTxn.saveChanges("local update element");
1926
+ // Local pulls and rebases through all three schema changesets
1927
+ await pullChanges(localTxn);
1928
+ t.local.clearCaches();
1929
+ // Local data change should survive all three schema transforms
1930
+ const element = t.getElementProps(t.local, elementId);
1931
+ chai.expect(element.propA).to.equal("local_updated_a", "Local propA should be preserved after three schema increments");
1932
+ chai.expect(element.propC).to.equal("local_updated_c", "Local propC should be preserved after three schema increments");
1933
+ const schema = t.local.getSchemaProps("TestDomain");
1934
+ chai.expect(schema.version).to.equal("01.00.03", "Schema should be at v03 after three incoming schema increments");
1935
+ });
1936
+ });
1937
+ /**
1938
+ * ElementAspect changes during semantic rebase.
1939
+ * Tests that aspect insert/update/delete operations are correctly captured and reinstated.
1940
+ */
1941
+ describe("Semantic Rebase - ElementAspect Changes", function () {
1942
+ this.timeout(60000);
1943
+ let t;
1944
+ before(async () => {
1945
+ await TestUtils.shutdownBackend();
1946
+ await TestUtils.startBackend({ useSemanticRebase: true });
1947
+ });
1948
+ afterEach(() => {
1949
+ if (t) {
1950
+ t.shutdown();
1951
+ t = undefined;
1952
+ }
1953
+ });
1954
+ after(async () => {
1955
+ await TestUtils.shutdownBackend();
1956
+ await TestUtils.startBackend();
1957
+ });
1958
+ it("H1: local inserts UniqueAspect; incoming trivial schema change → aspect preserved after rebase", async () => {
1959
+ t = await TestIModel.initialize("H1AspectInsertIncomingTrivial");
1960
+ let farTxn = startTestTxn(t.far, "H1 far");
1961
+ let localTxn = startTestTxn(t.local, "H1 local");
1962
+ // Set up base schema with aspect class on both sides
1963
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01WithAspect]);
1964
+ await pushChanges(farTxn, "import aspect schema");
1965
+ farTxn = startTestTxn(t.far, "H1 far 2");
1966
+ await pullChanges(localTxn);
1967
+ localTxn = startTestTxn(t.local, "H1 local 2");
1968
+ // Create an element on far, push
1969
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
1970
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "elem_a", propC: "elem_c" });
1971
+ farTxn.saveChanges("far create element");
1972
+ await pushChanges(farTxn, "far create element");
1973
+ farTxn = startTestTxn(t.far, "H1 far 3");
1974
+ // Local pulls element
1975
+ await pullChanges(localTxn);
1976
+ localTxn = startTestTxn(t.local, "H1 local 3");
1977
+ // Far imports trivial schema (adds AspectProp2 to CUniqueAspect)
1978
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02WithAspectProp2]);
1979
+ await pushChanges(farTxn, "far add AspectProp2");
1980
+ // Local inserts a UniqueAspect onto the element
1981
+ await t.local.locks.acquireLocks({ exclusive: elementId });
1982
+ t.insertAspect(localTxn, elementId, "TestDomain:CUniqueAspect", { aspectProp: "local_aspect_value" });
1983
+ localTxn.saveChanges("local insert aspect");
1984
+ // Local pulls: data rebase must preserve the aspect insertion
1985
+ await pullChanges(localTxn);
1986
+ t.local.clearCaches();
1987
+ // Aspect should still exist with correct value
1988
+ const aspect = t.getAspect(t.local, elementId, "TestDomain:CUniqueAspect");
1989
+ chai.expect(aspect, "Aspect should exist after rebase").to.not.be.undefined;
1990
+ chai.expect(aspect.aspectProp).to.equal("local_aspect_value", "AspectProp should be preserved after rebase");
1991
+ const schema = t.local.getSchemaProps("TestDomain");
1992
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 with AspectProp2 added");
1993
+ });
1994
+ it("H2: local inserts UniqueAspect; incoming transforming schema change → aspect preserved after transform", async () => {
1995
+ t = await TestIModel.initialize("H2AspectInsertIncomingTransform");
1996
+ let farTxn = startTestTxn(t.far, "H2 far");
1997
+ let localTxn = startTestTxn(t.local, "H2 local");
1998
+ // Set up base schema with aspect class on both sides
1999
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01WithAspect]);
2000
+ await pushChanges(farTxn, "import aspect schema");
2001
+ farTxn = startTestTxn(t.far, "H2 far 2");
2002
+ await pullChanges(localTxn);
2003
+ localTxn = startTestTxn(t.local, "H2 local 2");
2004
+ // Create element on far, push
2005
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2006
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "elem_a", propC: "elem_c" });
2007
+ farTxn.saveChanges("far create element");
2008
+ await pushChanges(farTxn, "far create element");
2009
+ farTxn = startTestTxn(t.far, "H2 far 3");
2010
+ // Local pulls element
2011
+ await pullChanges(localTxn);
2012
+ localTxn = startTestTxn(t.local, "H2 local 3");
2013
+ // Far imports transforming schema: moves PropC from C to A (v02 also brings AspectProp2)
2014
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02WithAspectProp2]);
2015
+ await pushChanges(farTxn, "far import transforming schema with aspect change");
2016
+ // Local inserts a UniqueAspect
2017
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2018
+ t.insertAspect(localTxn, elementId, "TestDomain:CUniqueAspect", { aspectProp: "aspect_before_transform" });
2019
+ localTxn.saveChanges("local insert aspect");
2020
+ // Local pulls: aspect is captured then reinstated after schema transform
2021
+ await pullChanges(localTxn);
2022
+ t.local.clearCaches();
2023
+ // Aspect should still exist
2024
+ const aspect = t.getAspect(t.local, elementId, "TestDomain:CUniqueAspect");
2025
+ chai.expect(aspect, "Aspect should exist after transforming schema rebase").to.not.be.undefined;
2026
+ chai.expect(aspect.aspectProp, "AspectProp value should be preserved").to.equal("aspect_before_transform");
2027
+ // Element itself should also be intact
2028
+ const element = t.getElementProps(t.local, elementId);
2029
+ chai.expect(element.propA, "Element propA should be preserved").to.equal("elem_a");
2030
+ });
2031
+ it("H3: local updates UniqueAspect property; incoming trivial schema change → aspect update preserved", async () => {
2032
+ t = await TestIModel.initialize("H3AspectUpdateIncomingTrivial");
2033
+ let farTxn = startTestTxn(t.far, "H3 far");
2034
+ let localTxn = startTestTxn(t.local, "H3 local");
2035
+ // Both get the aspect schema first
2036
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01WithAspect]);
2037
+ await pushChanges(farTxn, "import aspect schema");
2038
+ farTxn = startTestTxn(t.far, "H3 far 2");
2039
+ await pullChanges(localTxn);
2040
+ localTxn = startTestTxn(t.local, "H3 local 2");
2041
+ // Far creates element + inserts aspect, pushes
2042
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2043
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "elem_a", propC: "elem_c" });
2044
+ t.insertAspect(farTxn, elementId, "TestDomain:CUniqueAspect", { aspectProp: "initial_aspect" });
2045
+ farTxn.saveChanges("far create element + aspect");
2046
+ await pushChanges(farTxn, "far create element + aspect");
2047
+ farTxn = startTestTxn(t.far, "H3 far 3");
2048
+ // Local pulls to get the element and aspect
2049
+ await pullChanges(localTxn);
2050
+ localTxn = startTestTxn(t.local, "H3 local 3");
2051
+ // Far imports trivial schema (adds AspectProp2 to CUniqueAspect), pushes
2052
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02WithAspectProp2]);
2053
+ await pushChanges(farTxn, "far add AspectProp2");
2054
+ // Local updates the aspect's AspectProp
2055
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2056
+ t.updateAspect(localTxn, elementId, "TestDomain:CUniqueAspect", { aspectProp: "updated_aspect_value" });
2057
+ localTxn.saveChanges("local update aspect");
2058
+ // Local pulls: aspect update should survive the schema rebase
2059
+ await pullChanges(localTxn);
2060
+ t.local.clearCaches();
2061
+ const aspect = t.getAspect(t.local, elementId, "TestDomain:CUniqueAspect");
2062
+ chai.expect(aspect).to.not.be.undefined;
2063
+ chai.expect(aspect.aspectProp).to.equal("updated_aspect_value", "Aspect update should be preserved after trivial schema rebase");
2064
+ const schema = t.local.getSchemaProps("TestDomain");
2065
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02");
2066
+ });
2067
+ it("H4: local deletes UniqueAspect; incoming trivial schema change → aspect stays deleted after rebase", async () => {
2068
+ t = await TestIModel.initialize("H4AspectDeleteIncomingTrivial");
2069
+ let farTxn = startTestTxn(t.far, "H4 far");
2070
+ let localTxn = startTestTxn(t.local, "H4 local");
2071
+ // Both get the aspect schema
2072
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01WithAspect]);
2073
+ await pushChanges(farTxn, "import aspect schema");
2074
+ farTxn = startTestTxn(t.far, "H4 far 2");
2075
+ await pullChanges(localTxn);
2076
+ localTxn = startTestTxn(t.local, "H4 local 2");
2077
+ // Far creates element + aspect, pushes
2078
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2079
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "elem_a", propC: "elem_c" });
2080
+ t.insertAspect(farTxn, elementId, "TestDomain:CUniqueAspect", { aspectProp: "initial_aspect" });
2081
+ farTxn.saveChanges("far create element + aspect");
2082
+ await pushChanges(farTxn, "far create element + aspect");
2083
+ farTxn = startTestTxn(t.far, "H4 far 3");
2084
+ // Local pulls to get the element and aspect
2085
+ await pullChanges(localTxn);
2086
+ localTxn = startTestTxn(t.local, "H4 local 3");
2087
+ // Far imports trivial schema, pushes
2088
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02WithAspectProp2]);
2089
+ await pushChanges(farTxn, "far add AspectProp2");
2090
+ // Local deletes the aspect
2091
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2092
+ t.deleteAspect(localTxn, elementId, "TestDomain:CUniqueAspect");
2093
+ localTxn.saveChanges("local delete aspect");
2094
+ // Local pulls: aspect deletion should be preserved after schema rebase
2095
+ await pullChanges(localTxn);
2096
+ t.local.clearCaches();
2097
+ // The element should still exist but have no aspect
2098
+ const element = t.getElementProps(t.local, elementId);
2099
+ chai.expect(element, "Element should still exist after rebase").to.not.be.undefined;
2100
+ const aspects = t.local.elements.getAspects(elementId, "TestDomain:CUniqueAspect");
2101
+ chai.expect(aspects.length).to.equal(0, "Aspect should be gone after deletion is reinstated");
2102
+ const schema = t.local.getSchemaProps("TestDomain");
2103
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02");
2104
+ });
2105
+ it("H5: local inserts aspect; incoming adds same aspect class schema change AND data → both preserved", async () => {
2106
+ t = await TestIModel.initialize("H5AspectInsertIncomingSchemaAndData");
2107
+ let farTxn = startTestTxn(t.far, "H5 far");
2108
+ let localTxn = startTestTxn(t.local, "H5 local");
2109
+ // Both get the base aspect schema
2110
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01WithAspect]);
2111
+ await pushChanges(farTxn, "import aspect schema");
2112
+ farTxn = startTestTxn(t.far, "H5 far 2");
2113
+ await pullChanges(localTxn);
2114
+ localTxn = startTestTxn(t.local, "H5 local 2");
2115
+ // Far creates two elements and inserts aspects on them
2116
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2117
+ const farElementId = t.insertElement(farTxn, "TestDomain:C", { propA: "far_a", propC: "far_c" });
2118
+ t.insertAspect(farTxn, farElementId, "TestDomain:CUniqueAspect", { aspectProp: "far_aspect" });
2119
+ farTxn.saveChanges("far create element + aspect");
2120
+ // Far also upgrades the schema to v02 (adds AspectProp2)
2121
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02WithAspectProp2]);
2122
+ await pushChanges(farTxn, "far schema upgrade + element with aspect");
2123
+ // Local creates its own element and inserts an aspect with both AspectProp values
2124
+ // (local is still at v01 with only AspectProp; we insert only that)
2125
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2126
+ const localElementId = t.insertElement(localTxn, "TestDomain:C", { propA: "local_a", propC: "local_c" });
2127
+ t.insertAspect(localTxn, localElementId, "TestDomain:CUniqueAspect", { aspectProp: "local_aspect" });
2128
+ localTxn.saveChanges("local create element + aspect");
2129
+ // Local pulls: incoming has schema v02 + far's element+aspect; local data rebase on top
2130
+ await pullChanges(localTxn);
2131
+ t.local.clearCaches();
2132
+ // Far's element and aspect should be present
2133
+ const farAspect = t.getAspect(t.local, farElementId, "TestDomain:CUniqueAspect");
2134
+ chai.expect(farAspect).to.not.be.undefined;
2135
+ chai.expect(farAspect.aspectProp).to.equal("far_aspect");
2136
+ // Local's element and aspect should also be preserved
2137
+ const localAspect = t.getAspect(t.local, localElementId, "TestDomain:CUniqueAspect");
2138
+ chai.expect(localAspect).to.not.be.undefined;
2139
+ chai.expect(localAspect.aspectProp).to.equal("local_aspect", "Local aspect should survive rebase onto incoming schema+data");
2140
+ const schema = t.local.getSchemaProps("TestDomain");
2141
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02");
2142
+ });
2143
+ });
2144
+ /**
2145
+ * Property type variations during semantic rebase.
2146
+ * Ensures int, double, and boolean property values are preserved correctly through rebase.
2147
+ */
2148
+ describe("Semantic Rebase - Property Type Variations", function () {
2149
+ this.timeout(60000);
2150
+ let t;
2151
+ before(async () => {
2152
+ await TestUtils.shutdownBackend();
2153
+ await TestUtils.startBackend({ useSemanticRebase: true });
2154
+ });
2155
+ afterEach(() => {
2156
+ if (t) {
2157
+ t.shutdown();
2158
+ t = undefined;
2159
+ }
2160
+ });
2161
+ after(async () => {
2162
+ await TestUtils.shutdownBackend();
2163
+ await TestUtils.startBackend();
2164
+ });
2165
+ it("I1: int, double, and boolean properties preserved through trivial schema rebase", async () => {
2166
+ // Schema v01 adds int/double/bool props to class C.
2167
+ // Local sets those values on an element, far imports v02 (adds PropD2 on D).
2168
+ // After rebase: multi-type values should survive unchanged.
2169
+ t = await TestIModel.initialize("I1MultiTypePropsRebase");
2170
+ let farTxn = startTestTxn(t.far, "I1 far");
2171
+ let localTxn = startTestTxn(t.local, "I1 local");
2172
+ // Both get the multi-type schema (v01)
2173
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01MultiTypeProps]);
2174
+ await pushChanges(farTxn, "import multi-type schema v01");
2175
+ farTxn = startTestTxn(t.far, "I1 far 2");
2176
+ await pullChanges(localTxn);
2177
+ localTxn = startTestTxn(t.local, "I1 local 2");
2178
+ // Create element with all multi-type properties on local, push
2179
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2180
+ const elementId = t.insertElement(localTxn, "TestDomain:C", {
2181
+ propA: "string_val",
2182
+ propC: "another_string",
2183
+ propCInt: 42,
2184
+ propCDouble: 3.14159,
2185
+ propCBool: true,
2186
+ });
2187
+ localTxn.saveChanges("local create multi-type element");
2188
+ await pushChanges(localTxn, "create multi-type element");
2189
+ localTxn = startTestTxn(t.local, "I1 local 3");
2190
+ // Far imports v02 (adds PropD2 — trivial, unrelated change), pushes
2191
+ await pullChanges(farTxn);
2192
+ farTxn = startTestTxn(t.far, "I1 far 3");
2193
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02MultiTypePropsExtended]);
2194
+ await pushChanges(farTxn, "far import v02 with PropD2");
2195
+ // Local updates the int and bool properties
2196
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2197
+ t.updateElement(localTxn, elementId, { propCInt: 100, propCBool: false });
2198
+ localTxn.saveChanges("local update int and bool");
2199
+ // Local pulls: data rebase should preserve all type values
2200
+ await pullChanges(localTxn);
2201
+ t.local.clearCaches();
2202
+ const element = t.getElementProps(t.local, elementId);
2203
+ chai.expect(element.propCInt).to.equal(100, "Int property update should be preserved");
2204
+ chai.expect(element.propCDouble).to.equal(3.14159, "Double property should remain from original insert");
2205
+ chai.expect(element.propCBool).to.equal(false, "Boolean property update should be preserved");
2206
+ chai.expect(element.propC).to.equal("another_string", "String property should be preserved");
2207
+ const schema = t.local.getSchemaProps("TestDomain");
2208
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02");
2209
+ });
2210
+ it("I2: int, double, and boolean properties preserved through transforming schema rebase", async () => {
2211
+ // Local creates element with multi-type props, far imports transforming schema (moves PropD to A).
2212
+ // After rebase: all multi-type values should survive even though the schema layout changed for D.
2213
+ t = await TestIModel.initialize("I2MultiTypePropsTransformRebase");
2214
+ let farTxn = startTestTxn(t.far, "I2 far");
2215
+ let localTxn = startTestTxn(t.local, "I2 local");
2216
+ // Both get multi-type schema (v01)
2217
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01MultiTypeProps]);
2218
+ await pushChanges(farTxn, "import multi-type schema v01");
2219
+ farTxn = startTestTxn(t.far, "I2 far 2");
2220
+ await pullChanges(localTxn);
2221
+ localTxn = startTestTxn(t.local, "I2 local 2");
2222
+ // Far creates a shared element, push
2223
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2224
+ const elementId = t.insertElement(farTxn, "TestDomain:C", {
2225
+ propA: "shared_a",
2226
+ propC: "shared_c",
2227
+ propCInt: 7,
2228
+ propCDouble: 2.718,
2229
+ propCBool: true,
2230
+ });
2231
+ farTxn.saveChanges("far create multi-type element");
2232
+ await pushChanges(farTxn, "far create element");
2233
+ farTxn = startTestTxn(t.far, "I2 far 3");
2234
+ // Both pull to sync
2235
+ await pullChanges(localTxn);
2236
+ localTxn = startTestTxn(t.local, "I2 local 3");
2237
+ // Far imports transforming schema (v02 moves PropD to A)
2238
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02MultiTypePropsMovePropDToA]);
2239
+ await pushChanges(farTxn, "far move PropD to A");
2240
+ // Local updates the multi-type properties on the element
2241
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2242
+ t.updateElement(localTxn, elementId, { propCInt: 99, propCDouble: 1.414, propCBool: false });
2243
+ localTxn.saveChanges("local update multi-type props");
2244
+ // Local pulls: data rebase with schema transform
2245
+ await pullChanges(localTxn);
2246
+ t.local.clearCaches();
2247
+ const element = t.getElementProps(t.local, elementId);
2248
+ chai.expect(element.propCInt).to.equal(99, "Int property should be preserved after transforming schema rebase");
2249
+ chai.expect(element.propCDouble).to.closeTo(1.414, 0.0001, "Double property should be preserved");
2250
+ chai.expect(element.propCBool).to.equal(false, "Boolean property should be preserved");
2251
+ const schema = t.local.getSchemaProps("TestDomain");
2252
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02");
2253
+ });
2254
+ it("I3: element with null/undefined property values rebased correctly through schema transform", async () => {
2255
+ // Element created with some properties left unset (null/undefined).
2256
+ // Rebase should not fail and unset properties should remain absent.
2257
+ t = await TestIModel.initialize("I3NullPropertyRebase");
2258
+ let farTxn = startTestTxn(t.far, "I3 far");
2259
+ let localTxn = startTestTxn(t.local, "I3 local");
2260
+ // Create element on local with only propA set (propC left unset)
2261
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2262
+ const elementId = t.insertElement(localTxn, "TestDomain:C", { propA: "only_a" });
2263
+ // propC is intentionally not set (will be undefined/null)
2264
+ localTxn.saveChanges("create element with partial props");
2265
+ await pushChanges(localTxn, "create partially populated element");
2266
+ localTxn = startTestTxn(t.local, "I3 local 2");
2267
+ // Far imports transforming schema (moves PropC from C to A), pushes
2268
+ await pullChanges(farTxn);
2269
+ farTxn = startTestTxn(t.far, "I3 far 2");
2270
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
2271
+ await pushChanges(farTxn, "far move PropC to A");
2272
+ // Local makes a data change to update propA
2273
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2274
+ t.updateElement(localTxn, elementId, { propA: "updated_a" });
2275
+ localTxn.saveChanges("local update propA only");
2276
+ // Local pulls: data rebase should handle null/absent propC gracefully
2277
+ await pullChanges(localTxn);
2278
+ t.local.clearCaches();
2279
+ const element = t.getElementProps(t.local, elementId);
2280
+ chai.expect(element.propA).to.equal("updated_a", "PropA update should be preserved");
2281
+ // propC was never set so it should still be absent or null after the transform
2282
+ chai.expect(element.propC === undefined || element.propC === null || element.propC === "").to.be.true,
2283
+ "PropC should remain absent/null after rebase when it was never set";
2284
+ });
2285
+ it("I4: binary (UInt8Array) property values preserved through insert and update during trivial schema rebase", async () => {
2286
+ // Schema v01 adds a binary property (PropCBin) to class C.
2287
+ // Local inserts an element with a Uint8Array value, then updates it to a different Uint8Array.
2288
+ // Far imports v02 (adds PropD2 — trivial, unrelated change) to trigger semantic rebase.
2289
+ // After rebase: the updated binary value should survive as a Uint8Array with the correct bytes.
2290
+ t = await TestIModel.initialize("I4BinaryPropRebase");
2291
+ let farTxn = startTestTxn(t.far, "I4 far");
2292
+ let localTxn = startTestTxn(t.local, "I4 local");
2293
+ // Both sides get the binary-prop schema (v01)
2294
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01WithBinaryProp]);
2295
+ await pushChanges(farTxn, "import binary-prop schema v01");
2296
+ farTxn = startTestTxn(t.far, "I4 far 2");
2297
+ await pullChanges(localTxn);
2298
+ localTxn = startTestTxn(t.local, "I4 local 2");
2299
+ // Local inserts an element with an initial binary value, pushes so both sides are in sync
2300
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2301
+ const initialBin = new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]);
2302
+ const elementId = t.insertElement(localTxn, "TestDomain:C", {
2303
+ propA: "binary_test",
2304
+ propC: "some_string",
2305
+ propCBin: initialBin,
2306
+ });
2307
+ localTxn.saveChanges("local insert element with binary prop");
2308
+ await pushChanges(localTxn, "create element with binary prop");
2309
+ localTxn = startTestTxn(t.local, "I4 local 3");
2310
+ // Far imports v02 (adds PropD2 — trivial change), pushes to trigger rebase on local's next pull
2311
+ await pullChanges(farTxn);
2312
+ farTxn = startTestTxn(t.far, "I4 far 3");
2313
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x02WithBinaryPropAndPropD2]);
2314
+ await pushChanges(farTxn, "far import v02 with PropD2");
2315
+ // Local updates the binary property to a new Uint8Array value (NOT pushed yet)
2316
+ const updatedBin = new Uint8Array([0x01, 0x02, 0x03, 0x04, 0x05]);
2317
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2318
+ t.updateElement(localTxn, elementId, { propCBin: updatedBin });
2319
+ localTxn.saveChanges("local update binary prop");
2320
+ // Local pulls: semantic rebase should reinstate the binary property update
2321
+ await pullChanges(localTxn);
2322
+ t.local.clearCaches();
2323
+ const element = t.getElementProps(t.local, elementId);
2324
+ chai.expect(element.propCBin).to.be.instanceOf(Uint8Array, "Binary property should be returned as a Uint8Array");
2325
+ chai.expect(Array.from(element.propCBin)).to.deep.equal(Array.from(updatedBin), "Updated binary value should be preserved byte-for-byte after semantic rebase");
2326
+ chai.expect(element.propC).to.equal("some_string", "String property should be unchanged");
2327
+ chai.expect(element.propA).to.equal("binary_test", "PropA should be unchanged");
2328
+ const schema = t.local.getSchemaProps("TestDomain");
2329
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after rebase");
2330
+ });
2331
+ });
2332
+ /**
2333
+ * Both sides delete the same element.
2334
+ * Edge case where both local and far delete the same element independently.
2335
+ */
2336
+ describe("Semantic Rebase - Both Sides Delete Same Element", function () {
2337
+ this.timeout(60000);
2338
+ let t;
2339
+ before(async () => {
2340
+ await TestUtils.shutdownBackend();
2341
+ await TestUtils.startBackend({ useSemanticRebase: true });
2342
+ });
2343
+ afterEach(() => {
2344
+ if (t) {
2345
+ t.shutdown();
2346
+ t = undefined;
2347
+ }
2348
+ });
2349
+ after(async () => {
2350
+ await TestUtils.shutdownBackend();
2351
+ await TestUtils.startBackend();
2352
+ });
2353
+ it("J2: far deletes element, local makes data change to a DIFFERENT element + schema import → element gone, other changes preserved", async () => {
2354
+ t = await TestIModel.initialize("J2FarDeleteLocalSchemaAndData");
2355
+ let farTxn = startTestTxn(t.far, "J2 far");
2356
+ let localTxn = startTestTxn(t.local, "J2 local");
2357
+ // Create two shared elements
2358
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2359
+ const deletedElementId = t.insertElement(farTxn, "TestDomain:C", { propA: "del_a", propC: "del_c" });
2360
+ const keepElementId = t.insertElement(farTxn, "TestDomain:D", { propA: "keep_a", propD: "keep_d" });
2361
+ farTxn.saveChanges("create two elements");
2362
+ await pushChanges(farTxn, "create two elements");
2363
+ farTxn = startTestTxn(t.far, "J2 far 2");
2364
+ await pullChanges(localTxn);
2365
+ localTxn = startTestTxn(t.local, "J2 local 2");
2366
+ // Far imports schema + deletes one element
2367
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2368
+ await t.far.locks.acquireLocks({ exclusive: deletedElementId });
2369
+ farTxn.deleteElement(deletedElementId);
2370
+ farTxn.saveChanges("far delete element");
2371
+ await pushChanges(farTxn, "far schema + delete element");
2372
+ // Local: imports a different schema upgrade + updates the other element
2373
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2374
+ await t.local.locks.acquireLocks({ exclusive: keepElementId });
2375
+ t.updateElement(localTxn, keepElementId, { propA: "local_updated_keep_a" });
2376
+ localTxn.saveChanges("local update keep element");
2377
+ // Local pulls
2378
+ await pullChanges(localTxn);
2379
+ t.local.clearCaches();
2380
+ // Deleted element should be gone
2381
+ chai.expect(() => t.getElementProps(t.local, deletedElementId)).to.throw(`element not found`, "Deleted element should not be found after rebase");
2382
+ // Kept element should have local's update
2383
+ const keepElement = t.getElementProps(t.local, keepElementId);
2384
+ chai.expect(keepElement.propA).to.equal("local_updated_keep_a", "Keep element's local update should be preserved");
2385
+ // Schema should be at v01
2386
+ const schema = t.local.getSchemaProps("TestDomain");
2387
+ chai.expect(schema.version).to.equal("01.00.01", "Schema should be at v01");
2388
+ });
2389
+ });
2390
+ /**
2391
+ * Three-briefcase scenarios.
2392
+ * Tests interactions when three separate briefcases are involved in schema+data operations.
2393
+ */
2394
+ describe("Semantic Rebase - Three Briefcase Scenarios", function () {
2395
+ this.timeout(60000);
2396
+ let t;
2397
+ before(async () => {
2398
+ await TestUtils.shutdownBackend();
2399
+ await TestUtils.startBackend({ useSemanticRebase: true });
2400
+ });
2401
+ afterEach(() => {
2402
+ if (t) {
2403
+ t.shutdown();
2404
+ t = undefined;
2405
+ }
2406
+ });
2407
+ after(async () => {
2408
+ await TestUtils.shutdownBackend();
2409
+ await TestUtils.startBackend();
2410
+ });
2411
+ it("K1: schema from extra briefcase, data from far, local rebases both → all changes preserved", async () => {
2412
+ // extra imports schema, pushes.
2413
+ // far creates data element, pushes.
2414
+ // local has local data change.
2415
+ // local pulls: must rebase on top of schema+data from two different sources.
2416
+ t = await TestIModel.initialize("K1ThreeBriefcaseSchemaAndData");
2417
+ const farTxn = startTestTxn(t.far, "K1 far");
2418
+ const localTxn = startTestTxn(t.local, "K1 local");
2419
+ // Extra briefcase imports schema and pushes
2420
+ const extra = await t.openExtraBriefcase("extra-user-k1");
2421
+ try {
2422
+ extra.channels.addAllowedChannel(ChannelControl.sharedChannelName);
2423
+ const extraTxn = startTestTxn(extra, "K1 extra");
2424
+ await importSchemaStrings(extraTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2425
+ await pushChanges(extraTxn, "extra import schema v01");
2426
+ }
2427
+ finally {
2428
+ // Keep extra open for the test duration; it's closed here after push
2429
+ extra.close();
2430
+ }
2431
+ // Far pulls schema, creates element with PropC2, pushes
2432
+ await pullChanges(farTxn);
2433
+ const farTxn2 = startTestTxn(t.far, "K1 far 2");
2434
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2435
+ const farElementId = t.insertElement(farTxn2, "TestDomain:C", { propA: "far_a", propC: "far_c", propC2: "far_c2" });
2436
+ farTxn2.saveChanges("far create element");
2437
+ await pushChanges(farTxn2, "far create element");
2438
+ // Local makes a data change (creates its own element, still at old schema)
2439
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2440
+ const localElementId = t.insertElement(localTxn, "TestDomain:C", { propA: "local_a", propC: "local_c" });
2441
+ localTxn.saveChanges("local create element");
2442
+ // Local pulls: must get schema from extra + element from far, then rebase local element
2443
+ await pullChanges(localTxn);
2444
+ t.local.clearCaches();
2445
+ // Far's element should be visible
2446
+ const farElement = t.getElementProps(t.local, farElementId);
2447
+ chai.expect(farElement.propA).to.equal("far_a", "Far element should be visible after rebase");
2448
+ chai.expect(farElement.propC2).to.equal("far_c2", "Far element PropC2 should be preserved");
2449
+ // Local's element should also be preserved
2450
+ const localElement = t.getElementProps(t.local, localElementId);
2451
+ chai.expect(localElement.propA).to.equal("local_a", "Local element should be preserved after rebase");
2452
+ chai.expect(localElement.propC).to.equal("local_c", "Local element propC should be preserved");
2453
+ const schema = t.local.getSchemaProps("TestDomain");
2454
+ chai.expect(schema.version).to.equal("01.00.01", "Schema should be at v01 from extra briefcase");
2455
+ });
2456
+ it("K2: two sequential schema changes from different briefcases, local rebases correctly through both", async () => {
2457
+ // extra-a pushes schema v01.
2458
+ // extra-b pulls v01, pushes schema v02.
2459
+ // local makes data change and must pull both schema changesets.
2460
+ t = await TestIModel.initialize("K2TwoSchemaSourcesSequential");
2461
+ const localTxn = startTestTxn(t.local, "K2 local");
2462
+ // Create shared element via far and push
2463
+ const farTxn = startTestTxn(t.far, "K2 far");
2464
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2465
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "shared_a", propC: "shared_c" });
2466
+ farTxn.saveChanges("far create shared element");
2467
+ await pushChanges(farTxn, "create shared element");
2468
+ // Local pulls element
2469
+ await pullChanges(localTxn);
2470
+ const localTxn2 = startTestTxn(t.local, "K2 local 2");
2471
+ // extra-a pushes schema v01
2472
+ const extraA = await t.openExtraBriefcase("extra-a-k2");
2473
+ try {
2474
+ const extraATxn = startTestTxn(extraA, "K2 extra-a");
2475
+ await importSchemaStrings(extraATxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2476
+ await pushChanges(extraATxn, "extra-a import schema v01");
2477
+ }
2478
+ finally {
2479
+ extraA.close();
2480
+ }
2481
+ // extra-b pulls v01, pushes schema v02 (adds PropD2)
2482
+ const extraB = await t.openExtraBriefcase("extra-b-k2");
2483
+ try {
2484
+ await extraB.pullChanges(); // picks up v01
2485
+ const extraBTxn = startTestTxn(extraB, "K2 extra-b");
2486
+ await importSchemaStrings(extraBTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
2487
+ await pushChanges(extraBTxn, "extra-b import schema v02");
2488
+ }
2489
+ finally {
2490
+ extraB.close();
2491
+ }
2492
+ // Local makes data change: update shared element's propA
2493
+ await t.local.locks.acquireLocks({ exclusive: elementId });
2494
+ t.updateElement(localTxn2, elementId, { propA: "local_updated_a" });
2495
+ localTxn2.saveChanges("local update propA");
2496
+ // Local pulls: must process v01 + v02 schema changesets and then rebase local data
2497
+ await pullChanges(localTxn2);
2498
+ t.local.clearCaches();
2499
+ const element = t.getElementProps(t.local, elementId);
2500
+ chai.expect(element.propA).to.equal("local_updated_a", "Local data change should be preserved after two schema changesets from different sources");
2501
+ const schema = t.local.getSchemaProps("TestDomain");
2502
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after two schema changesets");
2503
+ });
2504
+ });
2505
+ /**
2506
+ * Multiple pulls without push between them.
2507
+ * Tests that semantic rebase state is handled correctly when the local briefcase
2508
+ * pulls multiple times before pushing, accumulating rebase operations.
2509
+ */
2510
+ describe("Semantic Rebase - Multiple Pulls Without Push", function () {
2511
+ this.timeout(60000);
2512
+ let t;
2513
+ before(async () => {
2514
+ await TestUtils.shutdownBackend();
2515
+ await TestUtils.startBackend({ useSemanticRebase: true });
2516
+ });
2517
+ afterEach(() => {
2518
+ if (t) {
2519
+ t.shutdown();
2520
+ t = undefined;
2521
+ }
2522
+ });
2523
+ after(async () => {
2524
+ await TestUtils.shutdownBackend();
2525
+ await TestUtils.startBackend();
2526
+ });
2527
+ it("L2: local makes two separate schema imports across two pull cycles without pushing", async () => {
2528
+ // Pull 1: local has schema v01, far has data → rebase, local wins with v01
2529
+ // Pull 2 (no push): local imports v02, far pushes more data → rebase, local wins with v02
2530
+ t = await TestIModel.initialize("L2TwoSchemaImportsTwoPulls");
2531
+ let farTxn = startTestTxn(t.far, "L2 far");
2532
+ let localTxn = startTestTxn(t.local, "L2 local");
2533
+ // Create shared element
2534
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2535
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
2536
+ farTxn.saveChanges("create element");
2537
+ await pushChanges(farTxn, "create element");
2538
+ farTxn = startTestTxn(t.far, "L2 far 2");
2539
+ await pullChanges(localTxn);
2540
+ localTxn = startTestTxn(t.local, "L2 local 2");
2541
+ // Far pushes data change for pull cycle 1
2542
+ await t.far.locks.acquireLocks({ exclusive: elementId });
2543
+ t.updateElement(farTxn, elementId, { propC: "far_c_update_1" });
2544
+ farTxn.saveChanges("far data round 1");
2545
+ await pushChanges(farTxn, "far data round 1");
2546
+ farTxn = startTestTxn(t.far, "L2 far 3");
2547
+ // Local: schema v01 import
2548
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2549
+ // Pull cycle 1
2550
+ await pullChanges(localTxn);
2551
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.01", "Schema should be v01 after first pull");
2552
+ // Far pushes data change for pull cycle 2
2553
+ localTxn = startTestTxn(t.local, "L2 local 3");
2554
+ await t.far.locks.acquireLocks({ exclusive: elementId });
2555
+ t.updateElement(farTxn, elementId, { propC: "far_c_update_2" });
2556
+ farTxn.saveChanges("far data round 2");
2557
+ await pushChanges(farTxn, "far data round 2");
2558
+ // Local: schema v02 import (still not pushed v01 yet)
2559
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
2560
+ // Pull cycle 2 without push
2561
+ await pullChanges(localTxn);
2562
+ t.local.clearCaches();
2563
+ // Schema should be at v02 (local upgrade wins)
2564
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.02", "Schema should be v02 after second pull without push");
2565
+ // Both far data changes should be present
2566
+ const element = t.getElementProps(t.local, elementId);
2567
+ chai.expect(element.propC).to.equal("far_c_update_2", "Far data round 2 should be applied");
2568
+ });
2569
+ it("L3: pull when there are no incoming changes (already up to date) → no rebase folders created", async () => {
2570
+ t = await TestIModel.initialize("L3PullNoIncomingChanges");
2571
+ const localTxn = startTestTxn(t.local, "L3 local");
2572
+ // Local imports schema (creates rebase folder)
2573
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2574
+ const localTxnProps = t.local.txns.getLastSavedTxnProps();
2575
+ chai.expect(localTxnProps).to.not.be.undefined;
2576
+ chai.expect(t.checkIfFolderExists(t.local, localTxnProps.id, true)).to.be.true;
2577
+ // Pull when far has nothing new — no rebase should happen
2578
+ // (local is ahead of far's schema; there are no incoming changesets)
2579
+ const rebaseBasePathBeforePull = BriefcaseManager.getBasePathForSemanticRebaseLocalFiles(t.local);
2580
+ await pullChanges(localTxn);
2581
+ // Schema folder should still exist (local schema is pending push)
2582
+ const rebaseBasePathAfterPull = BriefcaseManager.getBasePathForSemanticRebaseLocalFiles(t.local);
2583
+ chai.expect(IModelJsFs.existsSync(rebaseBasePathAfterPull)).to.be.true;
2584
+ chai.expect(rebaseBasePathBeforePull).to.equal(rebaseBasePathAfterPull, "Rebase path should be unchanged");
2585
+ const schema = t.local.getSchemaProps("TestDomain");
2586
+ chai.expect(schema.version).to.equal("01.00.01", "Local schema should still be v01 after no-op pull");
2587
+ });
2588
+ });
2589
+ /**
2590
+ * New class addition to schema.
2591
+ * Tests that newly added entity classes and their instances survive semantic rebase.
2592
+ */
2593
+ describe("Semantic Rebase - New Class Addition to Schema", function () {
2594
+ this.timeout(60000);
2595
+ let t;
2596
+ before(async () => {
2597
+ await TestUtils.shutdownBackend();
2598
+ await TestUtils.startBackend({ useSemanticRebase: true });
2599
+ });
2600
+ afterEach(() => {
2601
+ if (t) {
2602
+ t.shutdown();
2603
+ t = undefined;
2604
+ }
2605
+ });
2606
+ after(async () => {
2607
+ await TestUtils.shutdownBackend();
2608
+ await TestUtils.startBackend();
2609
+ });
2610
+ it("N1: far adds new class E to schema; local creates instances of existing class A + pulls → both visible", async () => {
2611
+ t = await TestIModel.initialize("N1FarAddsNewClassLocalCreatesData");
2612
+ const farTxn = startTestTxn(t.far, "N1 far");
2613
+ const localTxn = startTestTxn(t.local, "N1 local");
2614
+ // Far imports schema with new class E, creates an element of class E, pushes
2615
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01AddClassE]);
2616
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2617
+ const farElementE = t.insertElement(farTxn, "TestDomain:E", { propA: "far_e_a", propE: "far_e" });
2618
+ farTxn.saveChanges("far create element E");
2619
+ await pushChanges(farTxn, "far schema+element E");
2620
+ // Local creates an instance of class C (old class) and pulls
2621
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2622
+ const localElementC = t.insertElement(localTxn, "TestDomain:C", { propA: "local_c_a", propC: "local_c" });
2623
+ localTxn.saveChanges("local create element C");
2624
+ await pullChanges(localTxn);
2625
+ t.local.clearCaches();
2626
+ // Far's element E should be visible on local
2627
+ const elemE = t.getElementProps(t.local, farElementE);
2628
+ chai.expect(elemE.propA).to.equal("far_e_a", "Far element E should be visible after rebase");
2629
+ chai.expect(elemE.propE).to.equal("far_e", "PropE of element E should be correct");
2630
+ // Local's element C should be preserved
2631
+ const elemC = t.getElementProps(t.local, localElementC);
2632
+ chai.expect(elemC.propA).to.equal("local_c_a", "Local element C should be preserved after rebase");
2633
+ const schema = t.local.getSchemaProps("TestDomain");
2634
+ chai.expect(schema.version).to.equal("01.00.01", "Schema should be at v01 with class E added");
2635
+ });
2636
+ it("N2: both sides add same new class E → local version wins when higher, all instances preserved", async () => {
2637
+ t = await TestIModel.initialize("N2BothSidesAddNewClass");
2638
+ const farTxn = startTestTxn(t.far, "N2 far");
2639
+ const localTxn = startTestTxn(t.local, "N2 local");
2640
+ // Far imports v01 (adds class E with PropE), pushes
2641
+ await importSchemaStrings(farTxn, [TestIModel.extendedSchemas.v01x00x01AddClassE]);
2642
+ await pushChanges(farTxn, "far import v01 with class E");
2643
+ // Local imports v02 (adds class E with PropE + PropE2 — higher version), does NOT push yet
2644
+ await importSchemaStrings(localTxn, [TestIModel.extendedSchemas.v01x00x02AddClassEPropE2]);
2645
+ // Local pulls: local v02 > far v01 → local wins
2646
+ await pullChanges(localTxn);
2647
+ t.local.clearCaches();
2648
+ const schema = t.local.getSchemaProps("TestDomain");
2649
+ chai.expect(schema.version).to.equal("01.00.02", "Local v02 should win over far's v01");
2650
+ // Both PropE and PropE2 should be present on class E
2651
+ const classE = await t.local.schemaContext.getSchemaItem("TestDomain", "E", EntityClass);
2652
+ chai.expect(classE).to.not.be.undefined;
2653
+ chai.expect(await classE.getProperty("PropE")).to.exist;
2654
+ chai.expect(await classE.getProperty("PropE2")).to.exist;
2655
+ });
2656
+ it("N3: local adds new class E with instances; incoming has transforming schema change on existing class → both preserved", async () => {
2657
+ t = await TestIModel.initialize("N3LocalNewClassIncomingTransform");
2658
+ let farTxn = startTestTxn(t.far, "N3 far");
2659
+ let localTxn = startTestTxn(t.local, "N3 local");
2660
+ // Create a C element on far, push, then both pull
2661
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2662
+ const cElementId = t.insertElement(farTxn, "TestDomain:C", { propA: "c_initial_a", propC: "c_initial_c" });
2663
+ farTxn.saveChanges("create C element");
2664
+ await pushChanges(farTxn, "create C element");
2665
+ farTxn = startTestTxn(t.far, "N3 far 2");
2666
+ await pullChanges(localTxn);
2667
+ localTxn = startTestTxn(t.local, "N3 local 2");
2668
+ // Far imports transforming schema: moves PropC from C to A, AND adds class E (v01.00.02 + E)
2669
+ // We use a combined schema for this
2670
+ const v01x00x02WithBothChanges = `<?xml version="1.0" encoding="UTF-8"?>
2671
+ <ECSchema schemaName="TestDomain" alias="td" version="01.00.02" xmlns="http://www.bentley.com/schemas/Bentley.ECXML.3.2">
2672
+ <ECSchemaReference name="BisCore" version="01.00.23" alias="bis"/>
2673
+ <ECEntityClass typeName="A">
2674
+ <BaseClass>bis:GraphicalElement2d</BaseClass>
2675
+ <ECProperty propertyName="PropA" typeName="string"/>
2676
+ <ECProperty propertyName="PropC" typeName="string"/>
2677
+ </ECEntityClass>
2678
+ <ECEntityClass typeName="C">
2679
+ <BaseClass>A</BaseClass>
2680
+ </ECEntityClass>
2681
+ <ECEntityClass typeName="D">
2682
+ <BaseClass>A</BaseClass>
2683
+ <ECProperty propertyName="PropD" typeName="string"/>
2684
+ </ECEntityClass>
2685
+ <ECEntityClass typeName="E">
2686
+ <BaseClass>A</BaseClass>
2687
+ <ECProperty propertyName="PropE" typeName="string"/>
2688
+ </ECEntityClass>
2689
+ </ECSchema>`;
2690
+ await importSchemaStrings(farTxn, [v01x00x02WithBothChanges]);
2691
+ await pushChanges(farTxn, "far import transform+new class");
2692
+ // Local imports v01 (just adds class E) and creates two elements: one C, one E
2693
+ await importSchemaStrings(localTxn, [TestIModel.extendedSchemas.v01x00x01AddClassE]);
2694
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2695
+ const localElemE = t.insertElement(localTxn, "TestDomain:E", { propA: "local_e_a", propE: "local_e" });
2696
+ localTxn.saveChanges("local create E element");
2697
+ // Local also updates the C element that far's transform will affect
2698
+ await t.local.locks.acquireLocks({ exclusive: cElementId });
2699
+ t.updateElement(localTxn, cElementId, { propC: "local_updated_c" });
2700
+ localTxn.saveChanges("local update C element PropC");
2701
+ // Local pulls: far's transform schema (v02) > local's v01 schema → incoming wins for schema
2702
+ // But local's data changes (E element + C element update) should survive
2703
+ await pullChanges(localTxn);
2704
+ t.local.clearCaches();
2705
+ // Far's schema (v02) should win since it has higher version
2706
+ const schema = t.local.getSchemaProps("TestDomain");
2707
+ chai.expect(schema.version).to.equal("01.00.02", "Far's schema v02 should win over local's v01");
2708
+ // Local E element should exist
2709
+ const elemE = t.getElementProps(t.local, localElemE);
2710
+ chai.expect(elemE.propA).to.equal("local_e_a", "Local element E should be preserved");
2711
+ chai.expect(elemE.propE).to.equal("local_e", "PropE should be preserved");
2712
+ // C element should have local's PropC update (PropC is now on class A in v02)
2713
+ const elemC = t.getElementProps(t.local, cElementId);
2714
+ chai.expect(elemC.propC).to.equal("local_updated_c", "Local PropC update should be preserved after schema transform");
2715
+ });
2716
+ });
2717
+ /**
2718
+ * Guard conditions and error paths for semantic rebase.
2719
+ * Tests boundary conditions like importing schema while rebasing, concurrent pull attempts, etc.
2720
+ */
2721
+ describe("Semantic Rebase - Guard Conditions and Error Paths", function () {
2722
+ this.timeout(60000);
2723
+ let t;
2724
+ before(async () => {
2725
+ await TestUtils.shutdownBackend();
2726
+ await TestUtils.startBackend({ useSemanticRebase: true });
2727
+ });
2728
+ afterEach(() => {
2729
+ if (t) {
2730
+ t.shutdown();
2731
+ t = undefined;
2732
+ }
2733
+ });
2734
+ after(async () => {
2735
+ await TestUtils.shutdownBackend();
2736
+ await TestUtils.startBackend();
2737
+ });
2738
+ it("P1: importing schema during active rebase (via onRebaseTxnBegin hook) throws 'Cannot import schemas while rebasing'", async () => {
2739
+ t = await TestIModel.initialize("P1ImportSchemaWhileRebasing");
2740
+ const farTxn = startTestTxn(t.far, "P1 far");
2741
+ const localTxn = startTestTxn(t.local, "P1 local");
2742
+ // Far imports schema and pushes (triggers semantic rebase path on local)
2743
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2744
+ await pushChanges(farTxn, "far import schema v01");
2745
+ // Local imports schema to ensure it takes the semantic rebase code path
2746
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2747
+ // Hook into onRebaseTxnBegin to attempt a schema import during rebase
2748
+ let importErrorDuringRebase;
2749
+ t.local.txns.rebaser.onRebaseTxnBegin.addOnce(async () => {
2750
+ try {
2751
+ // This import should fail: importing schemas while rebasing is not allowed
2752
+ await t.local.importSchemaStrings([TestIModel.schemas.v01x00x02AddPropD2]);
2753
+ }
2754
+ catch (e) {
2755
+ importErrorDuringRebase = e;
2756
+ }
2757
+ });
2758
+ // Pull triggers the rebase which fires the hook
2759
+ try {
2760
+ await pullChanges(localTxn);
2761
+ }
2762
+ catch {
2763
+ // rebase might throw due to the bad import inside; that's acceptable
2764
+ }
2765
+ chai.expect(importErrorDuringRebase).to.not.be.undefined, "Schema import during rebase should throw";
2766
+ chai.expect(importErrorDuringRebase?.message ?? "").to.include("rebasing", "Error message should mention rebasing");
2767
+ });
2768
+ it("P3: after a failed rebase, briefcase is not stuck and remains usable", async () => {
2769
+ // If a rebase fails (e.g., incompatible schema), the briefcase should remain recoverable.
2770
+ // This test verifies the briefcase stays open and queryable after the failed pull.
2771
+ t = await TestIModel.initialize("P3RecoveryAfterFailedRebase");
2772
+ let farTxn = startTestTxn(t.far, "P3 far");
2773
+ let localTxn = startTestTxn(t.local, "P3 local");
2774
+ // Create shared element, push
2775
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2776
+ t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
2777
+ farTxn.saveChanges("create element");
2778
+ await pushChanges(farTxn, "create element");
2779
+ farTxn = startTestTxn(t.far, "P3 far 2");
2780
+ await pullChanges(localTxn);
2781
+ localTxn = startTestTxn(t.local, "P3 local 2");
2782
+ // Far imports schema with PropC2 as int (will cause conflict with local's string PropC2)
2783
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2784
+ await pushChanges(farTxn, "far import v01 PropC2 string");
2785
+ // Local imports incompatible schema (PropC2 as int, higher version → will conflict with far's string)
2786
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02AddPropC2Incompatible]);
2787
+ // First pull attempt: should fail with "ECSchema Upgrade failed"
2788
+ await chai.expect(pullChanges(localTxn)).to.be.rejectedWith("ECSchema Upgrade failed");
2789
+ // Briefcase should still be openable and queryable after failure
2790
+ // (BriefcaseManager should have rolled back the failed rebase)
2791
+ const schemaAfterFailure = t.local.getSchemaProps("TestDomain");
2792
+ // Schema version is unclear after abort, but the DB should still be functional
2793
+ chai.expect(schemaAfterFailure, "Schema query should succeed after failed rebase").to.not.be.undefined;
2794
+ // The briefcase is still open and the DB is usable
2795
+ chai.expect(t.local.isOpen, "Briefcase should still be open after failed rebase").to.be.true;
2796
+ });
2797
+ it("P4: a second local schema import succeeds even when semantic rebase state folders already exist", async () => {
2798
+ // Tests that semantic rebase state folders do not block further local schema imports.
2799
+ // After a schema import + pull that creates rebase folders, a second schema import on local
2800
+ // must still go through cleanly (the semantic rebase folders should NOT block a new import).
2801
+ t = await TestIModel.initialize("P4SecondImportAfterRebaseState");
2802
+ let farTxn = startTestTxn(t.far, "P4 far");
2803
+ let localTxn = startTestTxn(t.local, "P4 local");
2804
+ // Far pushes data change
2805
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2806
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
2807
+ farTxn.saveChanges("create element");
2808
+ await pushChanges(farTxn, "create element");
2809
+ farTxn = startTestTxn(t.far, "P4 far 2");
2810
+ await pullChanges(localTxn);
2811
+ localTxn = startTestTxn(t.local, "P4 local 2");
2812
+ // Far pushes another data update
2813
+ await t.far.locks.acquireLocks({ exclusive: elementId });
2814
+ t.updateElement(farTxn, elementId, { propA: "far_updated_a" });
2815
+ farTxn.saveChanges("far update");
2816
+ await pushChanges(farTxn, "far update element");
2817
+ // Local imports schema v01
2818
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2819
+ const firstSchemaTxnProps = t.local.txns.getLastSavedTxnProps();
2820
+ chai.expect(firstSchemaTxnProps).to.not.be.undefined;
2821
+ chai.expect(t.checkIfFolderExists(t.local, firstSchemaTxnProps.id, true)).to.be.true;
2822
+ // Local pulls (rebase: local schema onto far data)
2823
+ await pullChanges(localTxn);
2824
+ localTxn = startTestTxn(t.local, "P4 local after pull");
2825
+ // Schema folder should persist (local schema v01 is still pending push)
2826
+ chai.expect(t.checkIfFolderExists(t.local, firstSchemaTxnProps.id, true)).to.be.true;
2827
+ // Now local wants to import schema v02 (further upgrade) — this should succeed
2828
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
2829
+ const secondSchemaTxnProps = t.local.txns.getLastSavedTxnProps();
2830
+ chai.expect(secondSchemaTxnProps).to.not.be.undefined;
2831
+ chai.expect(t.checkIfFolderExists(t.local, secondSchemaTxnProps.id, true)).to.be.true;
2832
+ const schema = t.local.getSchemaProps("TestDomain");
2833
+ chai.expect(schema.version).to.equal("01.00.02", "Local should be at v02 after second schema import");
2834
+ });
2835
+ });
2836
+ /**
2837
+ * Complex insert-update-delete sequences.
2838
+ * Tests scenarios where local txns contain a mix of insert, update, and delete operations
2839
+ * that need to be correctly captured and reinstated during semantic rebase.
2840
+ */
2841
+ describe("Semantic Rebase - Complex Insert-Update-Delete Sequences", function () {
2842
+ this.timeout(60000);
2843
+ let t;
2844
+ before(async () => {
2845
+ await TestUtils.shutdownBackend();
2846
+ await TestUtils.startBackend({ useSemanticRebase: true });
2847
+ });
2848
+ afterEach(() => {
2849
+ if (t) {
2850
+ t.shutdown();
2851
+ t = undefined;
2852
+ }
2853
+ });
2854
+ after(async () => {
2855
+ await TestUtils.shutdownBackend();
2856
+ await TestUtils.startBackend();
2857
+ });
2858
+ it("O1: local insert → update → delete of same element in three txns; incoming transforming schema → element stays deleted", async () => {
2859
+ // Sequence: local inserts elem, updates it, then deletes it — all before pulling.
2860
+ // Incoming: transforming schema change.
2861
+ // Expected: element remains deleted (all three local txns reinstated correctly).
2862
+ t = await TestIModel.initialize("O1InsertUpdateDeleteSequence");
2863
+ const farTxn = startTestTxn(t.far, "O1 far");
2864
+ const localTxn = startTestTxn(t.local, "O1 local");
2865
+ // Far imports transforming schema and pushes
2866
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
2867
+ await pushChanges(farTxn, "far import transforming schema");
2868
+ // Local: txn1 - insert element
2869
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2870
+ const tempElementId = t.insertElement(localTxn, "TestDomain:C", { propA: "temp_a", propC: "temp_c" });
2871
+ localTxn.saveChanges("local txn1 insert temp element");
2872
+ // Local: txn2 - update the element
2873
+ t.updateElement(localTxn, tempElementId, { propA: "temp_updated_a" });
2874
+ localTxn.saveChanges("local txn2 update temp element");
2875
+ // Local: txn3 - delete the element
2876
+ localTxn.deleteElement(tempElementId);
2877
+ localTxn.saveChanges("local txn3 delete temp element");
2878
+ // Local pulls: three local txns (insert+update+delete) rebased on top of schema transform
2879
+ await pullChanges(localTxn);
2880
+ t.local.clearCaches();
2881
+ // Element should be gone (last operation was delete)
2882
+ chai.expect(() => t.getElementProps(t.local, tempElementId)).to.throw(`element not found`, "Element should be deleted after rebase");
2883
+ // Schema should be at v02
2884
+ const schema = t.local.getSchemaProps("TestDomain");
2885
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after rebase");
2886
+ });
2887
+ it("O2: local insert + delete of one element, plus insert + update of another → incoming schema → both correct", async () => {
2888
+ t = await TestIModel.initialize("O2InsertDeleteInsertUpdate");
2889
+ const farTxn = startTestTxn(t.far, "O2 far");
2890
+ const localTxn = startTestTxn(t.local, "O2 local");
2891
+ // Far imports trivial schema, pushes
2892
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2893
+ await pushChanges(farTxn, "far trivial schema import");
2894
+ // Local: txn1 - insert temporary element (will be deleted) + persistent element
2895
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2896
+ const tempId = t.insertElement(localTxn, "TestDomain:C", { propA: "temp_a", propC: "temp_c" });
2897
+ const persistId = t.insertElement(localTxn, "TestDomain:D", { propA: "persist_a", propD: "persist_d" });
2898
+ localTxn.saveChanges("local txn1 insert two elements");
2899
+ // Local: txn2 - update persistent element
2900
+ t.updateElement(localTxn, persistId, { propA: "persist_updated_a" });
2901
+ localTxn.saveChanges("local txn2 update persist element");
2902
+ // Local: txn3 - delete temporary element
2903
+ localTxn.deleteElement(tempId);
2904
+ localTxn.saveChanges("local txn3 delete temp element");
2905
+ // Local pulls: all three txns rebased on top of far's trivial schema
2906
+ await pullChanges(localTxn);
2907
+ t.local.clearCaches();
2908
+ // Temp element should be gone
2909
+ chai.expect(() => t.getElementProps(t.local, tempId)).to.throw(`element not found`, "Element should be deleted after rebase");
2910
+ // Persist element should have the update
2911
+ const persistElem = t.getElementProps(t.local, persistId);
2912
+ chai.expect(persistElem.propA).to.equal("persist_updated_a", "Persist element propA should be updated");
2913
+ chai.expect(persistElem.propD).to.equal("persist_d", "Persist element propD should be preserved");
2914
+ const schema = t.local.getSchemaProps("TestDomain");
2915
+ chai.expect(schema.version).to.equal("01.00.01", "Schema should be at v01");
2916
+ });
2917
+ it("O3: schema txn sandwiched between data txns; incoming data change → all local operations preserved in correct order", async () => {
2918
+ // Pattern: local txn1 (data) → txn2 (schema) → txn3 (data using new schema property)
2919
+ // Incoming: far has a data change on a different element.
2920
+ // Expected: txn1 data, schema, and txn3 data all reinstated correctly.
2921
+ t = await TestIModel.initialize("O3SchemaSandwichedBetweenData");
2922
+ let farTxn = startTestTxn(t.far, "O3 far");
2923
+ let localTxn = startTestTxn(t.local, "O3 local");
2924
+ // Create shared elements on far and push; both pull
2925
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2926
+ const sharedId = t.insertElement(farTxn, "TestDomain:C", { propA: "shared_a", propC: "shared_c" });
2927
+ farTxn.saveChanges("create shared element");
2928
+ await pushChanges(farTxn, "create shared element");
2929
+ farTxn = startTestTxn(t.far, "O3 far 2");
2930
+ await pullChanges(localTxn);
2931
+ localTxn = startTestTxn(t.local, "O3 local 2");
2932
+ // Far updates shared element and pushes (data-only incoming)
2933
+ await t.far.locks.acquireLocks({ exclusive: sharedId });
2934
+ t.updateElement(farTxn, sharedId, { propC: "far_updated_c" });
2935
+ farTxn.saveChanges("far update shared element");
2936
+ await pushChanges(farTxn, "far data update");
2937
+ // Local txn1: insert new element
2938
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2939
+ const newId1 = t.insertElement(localTxn, "TestDomain:C", { propA: "new_a", propC: "new_c" });
2940
+ localTxn.saveChanges("local txn3 insert element with PropC2");
2941
+ // Local txn2: import schema (adds PropC2)
2942
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
2943
+ // Local txn3: insert new element using the new PropC2 property
2944
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
2945
+ const newId2 = t.insertElement(localTxn, "TestDomain:C", { propA: "new_a", propC: "new_c", propC2: "new_c2_value" });
2946
+ localTxn.saveChanges("local txn3 insert element with PropC2");
2947
+ // Local pulls: txn1+schema+txn3 rebased on top of far's data update
2948
+ await pullChanges(localTxn);
2949
+ t.local.clearCaches();
2950
+ // Shared element should have far's propC update
2951
+ const sharedElem = t.getElementProps(t.local, sharedId);
2952
+ chai.expect(sharedElem.propA).to.equal("shared_a", "Initial propA update should be preserved");
2953
+ chai.expect(sharedElem.propC).to.equal("far_updated_c", "Far propC update should be applied");
2954
+ // Two New element with PropC2 should exist
2955
+ const newElem1 = t.getElementProps(t.local, newId1);
2956
+ chai.expect(newElem1.propA).to.equal("new_a", "New element propA should be preserved");
2957
+ chai.expect(newElem1.propC).to.equal("new_c", "New element propC should be preserved through schema rebase");
2958
+ const newElem2 = t.getElementProps(t.local, newId2);
2959
+ chai.expect(newElem2.propA).to.equal("new_a", "New element propA should be preserved");
2960
+ chai.expect(newElem2.propC2).to.equal("new_c2_value", "New element PropC2 should be preserved through schema rebase");
2961
+ const schema = t.local.getSchemaProps("TestDomain");
2962
+ chai.expect(schema.version).to.equal("01.00.01", "Schema should be at v01");
2963
+ });
2964
+ it("O4: five local data transactions each touching a different element; incoming transforming schema → all five preserved", async () => {
2965
+ // Stress-tests the capturePatchInstances + reinstatement path with 5 separate data txns.
2966
+ t = await TestIModel.initialize("O4FiveDataTxnsTransformingSchema");
2967
+ let farTxn = startTestTxn(t.far, "O4 far");
2968
+ let localTxn = startTestTxn(t.local, "O4 local");
2969
+ // Create five elements on far and push; both pull
2970
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
2971
+ const ids = [];
2972
+ for (let i = 0; i < 5; i++) {
2973
+ ids.push(t.insertElement(farTxn, "TestDomain:C", { propA: `initial_a_${i}`, propC: `initial_c_${i}` }));
2974
+ }
2975
+ farTxn.saveChanges("create five elements");
2976
+ await pushChanges(farTxn, "create five elements");
2977
+ farTxn = startTestTxn(t.far, "O4 far 2");
2978
+ await pullChanges(localTxn);
2979
+ localTxn = startTestTxn(t.local, "O4 local 2");
2980
+ // Far imports transforming schema (moves PropC to A) and pushes
2981
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
2982
+ await pushChanges(farTxn, "far transforming schema");
2983
+ // Local makes five separate data txns, one per element
2984
+ for (let i = 0; i < 5; i++) {
2985
+ await t.local.locks.acquireLocks({ exclusive: ids[i] });
2986
+ t.updateElement(localTxn, ids[i], { propA: `local_updated_a_${i}`, propC: `local_updated_c_${i}` });
2987
+ localTxn.saveChanges(`local txn${i + 1} update element ${i}`);
2988
+ }
2989
+ // Local pulls: all five data txns rebased on top of transforming schema
2990
+ await pullChanges(localTxn);
2991
+ t.local.clearCaches();
2992
+ // All five elements should have their local updates preserved
2993
+ for (let i = 0; i < 5; i++) {
2994
+ const elem = t.getElementProps(t.local, ids[i]);
2995
+ chai.expect(elem.propA).to.equal(`local_updated_a_${i}`, `Element ${i} propA should be preserved`);
2996
+ chai.expect(elem.propC).to.equal(`local_updated_c_${i}`, `Element ${i} propC should be preserved after transform`);
2997
+ }
2998
+ const schema = t.local.getSchemaProps("TestDomain");
2999
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after transforming rebase");
3000
+ });
3001
+ });
3002
+ /**
3003
+ * Cleanup and folder lifecycle edge cases.
3004
+ * Tests that rebase folder state is correctly managed in unusual lifecycle scenarios.
3005
+ */
3006
+ describe("Semantic Rebase - Cleanup and Folder Lifecycle", function () {
3007
+ this.timeout(60000);
3008
+ let t;
3009
+ before(async () => {
3010
+ await TestUtils.shutdownBackend();
3011
+ await TestUtils.startBackend({ useSemanticRebase: true });
3012
+ });
3013
+ afterEach(() => {
3014
+ if (t) {
3015
+ t.shutdown();
3016
+ t = undefined;
3017
+ }
3018
+ });
3019
+ after(async () => {
3020
+ await TestUtils.shutdownBackend();
3021
+ await TestUtils.startBackend();
3022
+ });
3023
+ it("M1: schema folder cleaned up on push; subsequent pull creates no leftover folders", async () => {
3024
+ t = await TestIModel.initialize("M1SchemaFolderCleanupOnPush");
3025
+ let farTxn = startTestTxn(t.far, "M1 far");
3026
+ let localTxn = startTestTxn(t.local, "M1 local");
3027
+ // Far pushes data
3028
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
3029
+ const elementId = t.insertElement(farTxn, "TestDomain:C", { propA: "initial_a", propC: "initial_c" });
3030
+ farTxn.saveChanges("far create element");
3031
+ await pushChanges(farTxn, "far create element");
3032
+ farTxn = startTestTxn(t.far, "M1 far 2");
3033
+ await pullChanges(localTxn);
3034
+ localTxn = startTestTxn(t.local, "M1 local 2");
3035
+ // Far pushes another update
3036
+ await t.far.locks.acquireLocks({ exclusive: elementId });
3037
+ t.updateElement(farTxn, elementId, { propA: "far_updated_a" });
3038
+ farTxn.saveChanges("far update element");
3039
+ await pushChanges(farTxn, "far update element");
3040
+ // Local imports schema (creates schema folder)
3041
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
3042
+ const schemaTxnProps = t.local.txns.getLastSavedTxnProps();
3043
+ chai.expect(schemaTxnProps).to.not.be.undefined;
3044
+ chai.expect(t.checkIfFolderExists(t.local, schemaTxnProps.id, true)).to.be.true;
3045
+ // Local pulls (rebase: local schema onto far's data update)
3046
+ await pullChanges(localTxn);
3047
+ // Schema folder should still exist (local schema wins and is pending push)
3048
+ chai.expect(t.checkIfFolderExists(t.local, schemaTxnProps.id, true)).to.be.true;
3049
+ chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.true;
3050
+ // Local pushes: all rebase folders should be cleaned up
3051
+ localTxn = startTestTxn(t.local, "M1 local after pull");
3052
+ await pushChanges(localTxn, "local push schema");
3053
+ chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false;
3054
+ // Schema folder for the pushed txn is now gone
3055
+ chai.expect(t.checkIfFolderExists(t.local, schemaTxnProps.id, true)).to.be.false;
3056
+ // Pull again (nothing new): no new folders should appear
3057
+ localTxn = startTestTxn(t.local, "M1 local after push");
3058
+ await pullChanges(localTxn);
3059
+ chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false;
3060
+ });
3061
+ it("M3: multiple successive push/pull cycles preserve rebase folder invariants throughout", async () => {
3062
+ // Cycle 1: local schema → pull (no-op schema) → push → verify clean
3063
+ // Cycle 2: far schema → local data → pull (local data rebased onto far schema) → push → verify clean
3064
+ t = await TestIModel.initialize("M3SuccessivePushPullCycles");
3065
+ let farTxn = startTestTxn(t.far, "M3 far");
3066
+ let localTxn = startTestTxn(t.local, "M3 local");
3067
+ // --- Cycle 1: Local schema, nothing on far ---
3068
+ // Local imports v01, pulls (nothing incoming), pushes
3069
+ await importSchemaStrings(localTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
3070
+ const cycle1TxnProps = t.local.txns.getLastSavedTxnProps();
3071
+ chai.expect(t.checkIfFolderExists(t.local, cycle1TxnProps.id, true)).to.be.true;
3072
+ await pullChanges(localTxn); // nothing incoming → no rebase
3073
+ chai.expect(t.checkIfFolderExists(t.local, cycle1TxnProps.id, true)).to.be.true; // still pending push
3074
+ localTxn = startTestTxn(t.local, "M3 local after cycle1 pull");
3075
+ await pushChanges(localTxn, "local push schema v01");
3076
+ chai.expect(t.checkIfFolderExists(t.local, cycle1TxnProps.id, true)).to.be.false; // cleaned on push
3077
+ chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false;
3078
+ // --- Cycle 2: Far pushes schema v02, local creates data element ---
3079
+ await pullChanges(farTxn); // far pulls v01
3080
+ farTxn = startTestTxn(t.far, "M3 far 2");
3081
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
3082
+ await pushChanges(farTxn, "far push schema v02");
3083
+ localTxn = startTestTxn(t.local, "M3 local cycle2");
3084
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
3085
+ const cycle2ElemId = t.insertElement(localTxn, "TestDomain:C", { propA: "cycle2_a", propC: "cycle2_c" });
3086
+ localTxn.saveChanges("local create cycle2 element");
3087
+ chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // data-only local, schema incoming
3088
+ // Actually because far has a schema change incoming, semantic rebase WILL be used here
3089
+ // The data folder will be created on the fly and removed after rebase
3090
+ await pullChanges(localTxn);
3091
+ chai.expect(t.checkifRebaseFolderExists(t.local)).to.be.false; // data folder removed after rebase
3092
+ t.local.clearCaches();
3093
+ const cycle2Elem = t.getElementProps(t.local, cycle2ElemId);
3094
+ chai.expect(cycle2Elem.propA).to.equal("cycle2_a", "Cycle 2 element should be preserved");
3095
+ const schema = t.local.getSchemaProps("TestDomain");
3096
+ chai.expect(schema.version).to.equal("01.00.02", "Schema should be at v02 after cycle 2");
3097
+ });
3098
+ });
3099
+ describe("Semantic Rebase - Multi-Pull Verification", function () {
3100
+ this.timeout(90000);
3101
+ let t;
3102
+ before(async () => {
3103
+ await TestUtils.shutdownBackend();
3104
+ await TestUtils.startBackend({ useSemanticRebase: true });
3105
+ });
3106
+ afterEach(() => {
3107
+ if (t) {
3108
+ t.shutdown();
3109
+ t = undefined;
3110
+ }
3111
+ });
3112
+ after(async () => {
3113
+ await TestUtils.shutdownBackend();
3114
+ await TestUtils.startBackend();
3115
+ });
3116
+ // ──────────────────────────────────────────────────────────────────────────
3117
+ // R2: Three consecutive pulls, each triggering rebase through escalating schema
3118
+ // changes (trivial → trivial → transforming).
3119
+ // Full ECSql snapshot taken before and after every pull.
3120
+ // ──────────────────────────────────────────────────────────────────────────
3121
+ it("R2: three consecutive pulls through escalating schema changes; full ECSql snapshot after each", async () => {
3122
+ t = await TestIModel.initialize("R2ThreePullsEscalatingSchema");
3123
+ let farTxn = startTestTxn(t.far, "R2 far");
3124
+ let localTxn = startTestTxn(t.local, "R2 local");
3125
+ // ── Phase 0: shared setup ─────────────────────────────────────────────────
3126
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
3127
+ const c1Id = t.insertElement(farTxn, "TestDomain:C", { propA: "c1_a_init", propC: "c1_c_init" });
3128
+ const c2Id = t.insertElement(farTxn, "TestDomain:C", { propA: "c2_a_init", propC: "c2_c_init" });
3129
+ const d1Id = t.insertElement(farTxn, "TestDomain:D", { propA: "d1_a_init", propD: "d1_d_init" });
3130
+ farTxn.saveChanges("create three shared elements");
3131
+ await pushChanges(farTxn, "create shared elements");
3132
+ farTxn = startTestTxn(t.far, "R2 far 2");
3133
+ await pullChanges(localTxn);
3134
+ localTxn = startTestTxn(t.local, "R2 local 2");
3135
+ // ── Round 1: far: schema v01 (PropC2) + update c1; local: insert r1Elem ──
3136
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
3137
+ await t.far.locks.acquireLocks({ exclusive: c1Id });
3138
+ t.updateElement(farTxn, c1Id, { propA: "c1_a_r1" });
3139
+ farTxn.saveChanges("far r1 update c1");
3140
+ await pushChanges(farTxn, "R2 far round1");
3141
+ farTxn = startTestTxn(t.far, "R2 far 3");
3142
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
3143
+ const r1ElemId = t.insertElement(localTxn, "TestDomain:C", { propA: "r1_a", propC: "r1_c" });
3144
+ localTxn.saveChanges("local r1 insert r1Elem");
3145
+ // Pull #1
3146
+ await pullChanges(localTxn);
3147
+ t.local.clearCaches({ instanceCachesOnly: true });
3148
+ // c1: far's propA update applied; propC unchanged
3149
+ const c1After1 = t.getElementProps(t.local, c1Id);
3150
+ chai.expect(c1After1.propA).to.equal("c1_a_r1", "c1 propA should be updated by far after pull #1");
3151
+ chai.expect(c1After1.propC).to.equal("c1_c_init", "c1 propC should be unchanged after pull #1");
3152
+ // c2: unchanged
3153
+ const c2After1 = t.getElementProps(t.local, c2Id);
3154
+ chai.expect(c2After1.propA).to.equal("c2_a_init", "c2 propA should be unchanged after pull #1");
3155
+ chai.expect(c2After1.propC).to.equal("c2_c_init", "c2 propC should be unchanged after pull #1");
3156
+ // r1Elem: insert preserved with same ECInstanceId
3157
+ const r1After1 = t.getElementProps(t.local, r1ElemId);
3158
+ chai.expect(r1After1.propA).to.equal("r1_a", "r1Elem propA should be preserved after pull #1 rebase");
3159
+ chai.expect(r1After1.propC).to.equal("r1_c", "r1Elem propC should be preserved after pull #1 rebase");
3160
+ chai.expect(r1After1.id).to.equal(r1ElemId, "r1ElemId ECInstanceId must be stable after pull #1");
3161
+ // d1: unchanged
3162
+ const d1After1 = t.getElementProps(t.local, d1Id);
3163
+ chai.expect(d1After1.propA).to.equal("d1_a_init", "d1 propA should be unchanged after pull #1");
3164
+ chai.expect(d1After1.propD).to.equal("d1_d_init", "d1 propD should be unchanged after pull #1");
3165
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.01", "Schema v01 after pull #1");
3166
+ // ── Round 2: far: schema v02 (PropD2) + update d1; local: update r1Elem ─
3167
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
3168
+ await t.far.locks.acquireLocks({ exclusive: d1Id });
3169
+ t.updateElement(farTxn, d1Id, { propA: "d1_a_r2" });
3170
+ farTxn.saveChanges("far r2 update d1");
3171
+ await pushChanges(farTxn, "R2 far round2");
3172
+ farTxn = startTestTxn(t.far, "R2 far 4");
3173
+ localTxn = startTestTxn(t.local, "R2 local r2 update");
3174
+ await t.local.locks.acquireLocks({ exclusive: r1ElemId });
3175
+ t.updateElement(localTxn, r1ElemId, { propA: "r1_a_updated" });
3176
+ localTxn.saveChanges("local r2 update r1Elem");
3177
+ // Pull #2
3178
+ await pullChanges(localTxn);
3179
+ t.local.clearCaches({ instanceCachesOnly: true });
3180
+ // c1: propA from pull #1; unchanged this round
3181
+ const c1After2 = t.getElementProps(t.local, c1Id);
3182
+ chai.expect(c1After2.propA).to.equal("c1_a_r1", "c1 propA should remain from pull #1 after pull #2");
3183
+ chai.expect(c1After2.propC).to.equal("c1_c_init", "c1 propC should be unchanged after pull #2");
3184
+ // c2: unchanged
3185
+ const c2After2 = t.getElementProps(t.local, c2Id);
3186
+ chai.expect(c2After2.propA).to.equal("c2_a_init", "c2 propA should be unchanged after pull #2");
3187
+ // r1Elem: propA update must survive rebase
3188
+ const r1After2 = t.getElementProps(t.local, r1ElemId);
3189
+ chai.expect(r1After2.propA).to.equal("r1_a_updated", "r1Elem propA update should survive pull #2 rebase");
3190
+ chai.expect(r1After2.propC).to.equal("r1_c", "r1Elem propC should be unchanged after pull #2 rebase");
3191
+ chai.expect(r1After2.id).to.equal(r1ElemId, "r1ElemId ECInstanceId must be stable after pull #2");
3192
+ // d1: far's propA update applied
3193
+ const d1After2 = t.getElementProps(t.local, d1Id);
3194
+ chai.expect(d1After2.propA).to.equal("d1_a_r2", "d1 propA should be updated by far after pull #2");
3195
+ chai.expect(d1After2.propD).to.equal("d1_d_init", "d1 propD should be unchanged after pull #2");
3196
+ chai.expect(d1After2.id).to.equal(d1Id, "d1Id ECInstanceId must be stable after pull #2");
3197
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.02", "Schema v02 after pull #2");
3198
+ // ── Round 3: far: schema v03 (transforming: moves PropC to A) + update c2;
3199
+ // local: insert r3Elem ──────────────────────────────────────────
3200
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x03MovePropCAndD]);
3201
+ await t.far.locks.acquireLocks({ exclusive: c2Id });
3202
+ t.updateElement(farTxn, c2Id, { propA: "c2_a_r3" });
3203
+ farTxn.saveChanges("far r3 update c2");
3204
+ await pushChanges(farTxn, "R2 far round3");
3205
+ localTxn = startTestTxn(t.local, "R2 local r3 insert");
3206
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
3207
+ const r3ElemId = t.insertElement(localTxn, "TestDomain:D", { propA: "r3_a" });
3208
+ localTxn.saveChanges("local r3 insert r3Elem");
3209
+ // Pull #3 — transforming schema rebase
3210
+ await pullChanges(localTxn);
3211
+ t.local.clearCaches({ instanceCachesOnly: true });
3212
+ // c1: propA from pull #1; PropC moved to A so query it as propC still accessible via A
3213
+ const c1After3 = t.getElementProps(t.local, c1Id);
3214
+ chai.expect(c1After3.propA).to.equal("c1_a_r1", "c1 propA should remain from pull #1 after pull #3");
3215
+ // c2: far's propA update from round 3
3216
+ const c2After3 = t.getElementProps(t.local, c2Id);
3217
+ chai.expect(c2After3.propA).to.equal("c2_a_r3", "c2 propA should be updated by far after pull #3");
3218
+ // r1Elem: propA update from round 2 must survive transforming rebase
3219
+ const r1After3 = t.getElementProps(t.local, r1ElemId);
3220
+ chai.expect(r1After3.propA).to.equal("r1_a_updated", "r1Elem propA update should survive transforming pull #3 rebase");
3221
+ chai.expect(r1After3.id).to.equal(r1ElemId, "r1ElemId ECInstanceId must be stable after pull #3");
3222
+ // d1: propA from round 2
3223
+ const d1After3 = t.getElementProps(t.local, d1Id);
3224
+ chai.expect(d1After3.propA).to.equal("d1_a_r2", "d1 propA should remain from pull #2 after pull #3");
3225
+ chai.expect(d1After3.id).to.equal(d1Id, "d1Id ECInstanceId must be stable after pull #3");
3226
+ // r3Elem: local insert preserved
3227
+ const r3After3 = t.getElementProps(t.local, r3ElemId);
3228
+ chai.expect(r3After3.propA).to.equal("r3_a", "r3Elem propA should be preserved after pull #3 rebase");
3229
+ chai.expect(r3After3.id).to.equal(r3ElemId, "r3ElemId ECInstanceId must be stable after pull #3");
3230
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.03", "Schema v03 (MovePropCAndD) after pull #3");
3231
+ });
3232
+ // ──────────────────────────────────────────────────────────────────────────
3233
+ // R3: Multi-pull with insert, update, and delete in different rounds.
3234
+ // Two pulls each rebase. Element lifecycle verified at every stage with
3235
+ // ECSqlReader (ECInstanceId, className, domain props).
3236
+ // ──────────────────────────────────────────────────────────────────────────
3237
+ it("R3: two pulls with insert/update/delete across rounds; element lifecycle verified", async () => {
3238
+ t = await TestIModel.initialize("R3MultiPullInsertUpdateDelete");
3239
+ let farTxn = startTestTxn(t.far, "R3 far");
3240
+ let localTxn = startTestTxn(t.local, "R3 local");
3241
+ // ── Phase 0: shared elements ──────────────────────────────────────────────
3242
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
3243
+ const sharedC = t.insertElement(farTxn, "TestDomain:C", { propA: "sc_a_init", propC: "sc_c_init" });
3244
+ const sharedD = t.insertElement(farTxn, "TestDomain:D", { propA: "sd_a_init", propD: "sd_d_init" });
3245
+ farTxn.saveChanges("create shared elements");
3246
+ await pushChanges(farTxn, "shared elements");
3247
+ farTxn = startTestTxn(t.far, "R3 far 2");
3248
+ await pullChanges(localTxn);
3249
+ localTxn = startTestTxn(t.local, "R3 local 2");
3250
+ // ── Round 1: far: schema v01 + update sharedC.propA ──────────────────────
3251
+ // local: insert ephemeral C + insert persistent D + delete ephemeral
3252
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
3253
+ await t.far.locks.acquireLocks({ exclusive: sharedC });
3254
+ t.updateElement(farTxn, sharedC, { propA: "sc_a_r1" });
3255
+ farTxn.saveChanges("far r1 update sharedC");
3256
+ await pushChanges(farTxn, "R3 far round1");
3257
+ farTxn = startTestTxn(t.far, "R3 far 3");
3258
+ // Local txn #1: insert ephemeral element (will be deleted before pull #1)
3259
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
3260
+ const ephemeralId = t.insertElement(localTxn, "TestDomain:C", { propA: "eph_a", propC: "eph_c" });
3261
+ localTxn.saveChanges("local r1 insert ephemeral");
3262
+ // Local txn #2: insert persistent D element
3263
+ const persistDId = t.insertElement(localTxn, "TestDomain:D", { propA: "pd_a_init", propD: "pd_d_init" });
3264
+ localTxn.saveChanges("local r1 insert persistD");
3265
+ // Local txn #3: delete the ephemeral element
3266
+ localTxn.deleteElement(ephemeralId);
3267
+ localTxn.saveChanges("local r1 delete ephemeral");
3268
+ // Pull #1: rebase insert-persistD + delete-ephemeral txns onto incoming schema v01 + sharedC update
3269
+ await pullChanges(localTxn);
3270
+ chai.expect(() => t.getElementProps(t.local, ephemeralId)).to.throw(`element not found`, "Ephemeral element should be deleted after pull #1");
3271
+ const elementProps = t.getElementProps(t.local, persistDId);
3272
+ chai.expect(elementProps.propA).to.equal("pd_a_init", "Persistent D propA should be preserved after pull #1");
3273
+ chai.expect(elementProps.propD).to.equal("pd_d_init", "Persistent D propD should be preserved after pull #1");
3274
+ const sharedCAfter1 = t.getElementProps(t.local, sharedC);
3275
+ chai.expect(sharedCAfter1.propA).to.equal("sc_a_r1", "sharedC propA should be updated by far after pull #1");
3276
+ chai.expect(sharedCAfter1.propC).to.equal("sc_c_init", "sharedC propC should be unchanged after pull #1");
3277
+ chai.expect(sharedCAfter1.propC2).to.be.undefined;
3278
+ const sharedDAfter1 = t.getElementProps(t.local, sharedD);
3279
+ chai.expect(sharedDAfter1.propA).to.equal("sd_a_init", "sharedD propA should be unchanged after pull #1");
3280
+ chai.expect(sharedDAfter1.propD).to.equal("sd_d_init", "sharedD propD should be unchanged after pull #1");
3281
+ });
3282
+ // ──────────────────────────────────────────────────────────────────────────
3283
+ // R4: Three consecutive pulls where local never pushes.
3284
+ // Each round: local makes multiple data txns, far pushes a schema change.
3285
+ // Complete ECSql verification of all elements after all three pulls.
3286
+ // ──────────────────────────────────────────────────────────────────────────
3287
+ it("R4: three pulls without push; local accumulates txns; all elements verified after each", async () => {
3288
+ t = await TestIModel.initialize("R4ThreePullsNoPush");
3289
+ let farTxn = startTestTxn(t.far, "R4 far");
3290
+ let localTxn = startTestTxn(t.local, "R4 local");
3291
+ // ── Phase 0: create 2 C elements and 1 D element on far, both pull ────────
3292
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
3293
+ const baseC1 = t.insertElement(farTxn, "TestDomain:C", { propA: "bc1_a", propC: "bc1_c" });
3294
+ const baseC2 = t.insertElement(farTxn, "TestDomain:C", { propA: "bc2_a", propC: "bc2_c" });
3295
+ const baseD1 = t.insertElement(farTxn, "TestDomain:D", { propA: "bd1_a", propD: "bd1_d" });
3296
+ farTxn.saveChanges("create base elements");
3297
+ await pushChanges(farTxn, "base elements");
3298
+ farTxn = startTestTxn(t.far, "R4 far 2");
3299
+ await pullChanges(localTxn);
3300
+ localTxn = startTestTxn(t.local, "R4 local 2");
3301
+ // ── Round 1: far: trivial schema v01 (PropC2 additive)
3302
+ // local: TWO data txns – update baseC1 + insert localC1 ─────────
3303
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
3304
+ await pushChanges(farTxn, "R4 far schema v01");
3305
+ farTxn = startTestTxn(t.far, "R4 far 3");
3306
+ // Local data txn A: update baseC1
3307
+ await t.local.locks.acquireLocks({ exclusive: baseC1 });
3308
+ t.updateElement(localTxn, baseC1, { propA: "bc1_a_loc_r1" });
3309
+ localTxn.saveChanges("local r1 update baseC1");
3310
+ // Local data txn B: insert localC1
3311
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
3312
+ const localC1 = t.insertElement(localTxn, "TestDomain:C", { propA: "lc1_a_r1", propC: "lc1_c_r1" });
3313
+ localTxn.saveChanges("local r1 insert localC1");
3314
+ // Pull #1
3315
+ await pullChanges(localTxn);
3316
+ t.local.clearCaches({ instanceCachesOnly: true });
3317
+ // baseC1: local propA update preserved
3318
+ const bc1After1 = t.getElementProps(t.local, baseC1);
3319
+ chai.expect(bc1After1.propA).to.equal("bc1_a_loc_r1", "baseC1 propA update should survive pull #1 rebase");
3320
+ chai.expect(bc1After1.propC).to.equal("bc1_c", "baseC1 propC should be unchanged after pull #1");
3321
+ // baseC2: unchanged
3322
+ const bc2After1 = t.getElementProps(t.local, baseC2);
3323
+ chai.expect(bc2After1.propA).to.equal("bc2_a", "baseC2 propA should be unchanged after pull #1");
3324
+ chai.expect(bc2After1.propC).to.equal("bc2_c", "baseC2 propC should be unchanged after pull #1");
3325
+ // localC1: insert preserved with same ECInstanceId
3326
+ const lc1After1 = t.getElementProps(t.local, localC1);
3327
+ chai.expect(lc1After1.propA).to.equal("lc1_a_r1", "localC1 propA should be preserved after pull #1 rebase");
3328
+ chai.expect(lc1After1.propC).to.equal("lc1_c_r1", "localC1 propC should be preserved after pull #1 rebase");
3329
+ chai.expect(lc1After1.id).to.equal(localC1, "localC1 ECInstanceId must be stable after pull #1");
3330
+ // baseD1: unchanged
3331
+ const bd1After1 = t.getElementProps(t.local, baseD1);
3332
+ chai.expect(bd1After1.propA).to.equal("bd1_a", "baseD1 propA should be unchanged after pull #1");
3333
+ chai.expect(bd1After1.propD).to.equal("bd1_d", "baseD1 propD should be unchanged after pull #1");
3334
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.01", "Schema v01 after pull #1");
3335
+ // ── Round 2: far: trivial schema v02 (PropD2 additive) + update baseD1
3336
+ // local: update localC1.propA + insert localD1 ──────────────────
3337
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02AddPropD2]);
3338
+ await t.far.locks.acquireLocks({ exclusive: baseD1 });
3339
+ t.updateElement(farTxn, baseD1, { propA: "bd1_a_r2" });
3340
+ farTxn.saveChanges("far r2 update baseD1");
3341
+ await pushChanges(farTxn, "R4 far schema v02 + update baseD1");
3342
+ farTxn = startTestTxn(t.far, "R4 far 4");
3343
+ localTxn = startTestTxn(t.local, "R4 local r2");
3344
+ await t.local.locks.acquireLocks({ exclusive: localC1 });
3345
+ t.updateElement(localTxn, localC1, { propA: "lc1_a_r2_upd" });
3346
+ localTxn.saveChanges("local r2 update localC1");
3347
+ await t.local.locks.acquireLocks({ shared: t.drawingModelId });
3348
+ const localD1 = t.insertElement(localTxn, "TestDomain:D", { propA: "ld1_a_r2", propD: "ld1_d_r2" });
3349
+ localTxn.saveChanges("local r2 insert localD1");
3350
+ // Pull #2
3351
+ await pullChanges(localTxn);
3352
+ t.local.clearCaches({ instanceCachesOnly: true });
3353
+ // baseC1: propA from round 1 unchanged
3354
+ const bc1After2 = t.getElementProps(t.local, baseC1);
3355
+ chai.expect(bc1After2.propA).to.equal("bc1_a_loc_r1", "baseC1 propA should remain from round 1 after pull #2");
3356
+ chai.expect(bc1After2.propC).to.equal("bc1_c", "baseC1 propC should be unchanged after pull #2");
3357
+ // localC1: propA update must survive
3358
+ const lc1After2 = t.getElementProps(t.local, localC1);
3359
+ chai.expect(lc1After2.propA).to.equal("lc1_a_r2_upd", "localC1 propA update should survive pull #2 rebase");
3360
+ chai.expect(lc1After2.propC).to.equal("lc1_c_r1", "localC1 propC should be unchanged after pull #2 rebase");
3361
+ chai.expect(lc1After2.id).to.equal(localC1, "localC1 ECInstanceId must be stable after pull #2");
3362
+ // baseD1: far's propA update applied
3363
+ const bd1After2 = t.getElementProps(t.local, baseD1);
3364
+ chai.expect(bd1After2.propA).to.equal("bd1_a_r2", "baseD1 propA should be updated by far after pull #2");
3365
+ // localD1: insert preserved
3366
+ const ld1After2 = t.getElementProps(t.local, localD1);
3367
+ chai.expect(ld1After2.propA).to.equal("ld1_a_r2", "localD1 propA should be preserved after pull #2 rebase");
3368
+ chai.expect(ld1After2.propD).to.equal("ld1_d_r2", "localD1 propD should be preserved after pull #2 rebase");
3369
+ chai.expect(ld1After2.id).to.equal(localD1, "localD1 ECInstanceId must be stable after pull #2");
3370
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.02", "Schema v02 after pull #2");
3371
+ // ── Round 3: far: transforming schema (moves PropC to A) + update baseC2.propA
3372
+ // local: delete localC1 only.
3373
+ //
3374
+ // NOTE: local does NOT attempt to lock baseC2 here. After far exclusively locked
3375
+ // baseC2 and pushed (releasing the lock at a newer changeset index),
3376
+ // `LocalHub.doesBriefcaseRequirePullBeforeLock` would throw PullIsRequired for any
3377
+ // lock request on baseC2 from local (still behind on changesets). The test therefore
3378
+ // only exercises local deleting one of its own elements (localC1), which local itself
3379
+ // inserted and locked — those locks have lastExclusiveReleaseChangesetIndex = undefined,
3380
+ // so no pull is required before re-acquiring them.
3381
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x03MovePropCAndD]);
3382
+ await t.far.locks.acquireLocks({ exclusive: baseC2 });
3383
+ t.updateElement(farTxn, baseC2, { propA: "bc2_a_r3" });
3384
+ farTxn.saveChanges("far r3 update baseC2");
3385
+ await pushChanges(farTxn, "R4 far round3");
3386
+ localTxn = startTestTxn(t.local, "R4 local r3 delete");
3387
+ await t.local.locks.acquireLocks({ exclusive: localC1 });
3388
+ localTxn.deleteElement(localC1);
3389
+ localTxn.saveChanges("local r3 delete localC1");
3390
+ // Pull #3 — transforming schema rebase
3391
+ await pullChanges(localTxn);
3392
+ // baseC1: propA from round 1 unchanged
3393
+ const bc1After3 = t.getElementProps(t.local, baseC1);
3394
+ chai.expect(bc1After3.propA).to.equal("bc1_a_loc_r1", "baseC1 propA should remain from round 1 after pull #3");
3395
+ chai.expect(bc1After3.propC).to.equal("bc1_c", "baseC1 propC should be unchanged after pull #3");
3396
+ // baseC2: propA updated by far in round 3; PropC moved to A but still accessible via propC query
3397
+ const bc2After3 = t.getElementProps(t.local, baseC2);
3398
+ chai.expect(bc2After3.propA).to.equal("bc2_a_r3", "baseC2 propA should be changed after pull #3");
3399
+ chai.expect(bc2After3.propC).to.equal("bc2_c", "baseC2 propC should be unchanged after pull #3");
3400
+ // localC1: must be deleted
3401
+ chai.expect(() => t.getElementProps(t.local, localC1)).to.throw(`element not found`, "localC1 should be deleted after pull #3");
3402
+ // baseD1: far's propA update applied
3403
+ const bd1After3 = t.getElementProps(t.local, baseD1);
3404
+ chai.expect(bd1After3.propA).to.equal("bd1_a_r2", "baseD1 propA should be updated by far after pull #3");
3405
+ // localD1: insert preserved
3406
+ const ld1After3 = t.getElementProps(t.local, localD1);
3407
+ chai.expect(ld1After3.propA).to.equal("ld1_a_r2", "localD1 propA should be preserved after pull #3 rebase");
3408
+ chai.expect(ld1After3.propD).to.equal("ld1_d_r2", "localD1 propD should be preserved after pull #3 rebase");
3409
+ chai.expect(ld1After2.id).to.equal(localD1, "localD1 ECInstanceId must be stable after pull #3");
3410
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.03", "Schema v02 after pull #3");
3411
+ });
3412
+ // ──────────────────────────────────────────────────────────────────────────
3413
+ // R5: Two consecutive pulls where local never pushes.
3414
+ // Each round: local makes multiple data txns, far pushes a schema change.
3415
+ // Complete ECSql verification of all elements after all three pulls.
3416
+ // ──────────────────────────────────────────────────────────────────────────
3417
+ it("R5: two consecutive pulls each trigger rebase with local changes; ECInstanceId/className/props verified after each", async () => {
3418
+ t = await TestIModel.initialize("R5TwoPullsEachRebase");
3419
+ let farTxn = startTestTxn(t.far, "R5 far");
3420
+ let localTxn = startTestTxn(t.local, "R5 local");
3421
+ // ── Phase 0: create shared elements on far, both pull to sync ────────────
3422
+ await t.far.locks.acquireLocks({ shared: t.drawingModelId });
3423
+ const cElemId = t.insertElement(farTxn, "TestDomain:C", { propA: "c_a_init", propC: "c_c_init" });
3424
+ farTxn.saveChanges("create shared elements");
3425
+ await pushChanges(farTxn, "create shared elements");
3426
+ farTxn = startTestTxn(t.far, "R5 far 2");
3427
+ await pullChanges(localTxn);
3428
+ localTxn = startTestTxn(t.local, "R5 local 2");
3429
+ await t.local.locks.acquireLocks({ exclusive: cElemId });
3430
+ t.updateElement(localTxn, cElemId, { propC: "c_c_local" });
3431
+ localTxn.saveChanges("local update to cElemId.propC");
3432
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x01AddPropC2]);
3433
+ await pushChanges(farTxn, "far schema v01");
3434
+ farTxn = startTestTxn(t.far, "R1 far 3");
3435
+ await pullChanges(localTxn);
3436
+ t.local.clearCaches({ instanceCachesOnly: true });
3437
+ const elementAfterSecondPull = t.getElementProps(t.local, cElemId);
3438
+ chai.expect(elementAfterSecondPull).to.not.be.undefined;
3439
+ chai.expect(elementAfterSecondPull.propC).to.equal("c_c_local", "Local update to cElemId.propC should survive first pull's rebase");
3440
+ chai.expect(elementAfterSecondPull.propA).to.equal("c_a_init", "Far's update to cElemId.propA should be applied after first pull");
3441
+ chai.expect(elementAfterSecondPull.propC2).to.be.undefined;
3442
+ await importSchemaStrings(farTxn, [TestIModel.schemas.v01x00x02MovePropCToA]);
3443
+ await pushChanges(farTxn, "far schema v02");
3444
+ farTxn = startTestTxn(t.far, "R1 far 4");
3445
+ await pullChanges(localTxn);
3446
+ t.local.clearCaches({ instanceCachesOnly: true });
3447
+ const elementAfterThirdPull = t.getElementProps(t.local, cElemId);
3448
+ chai.expect(elementAfterThirdPull).to.not.be.undefined;
3449
+ chai.expect(elementAfterThirdPull.propC).to.equal("c_c_local", "Local update to cElemId.propC should survive second pull's rebase");
3450
+ chai.expect(elementAfterThirdPull.propA).to.equal("c_a_init", "Far's update to cElemId.propA should be applied after second pull");
3451
+ chai.expect(elementAfterThirdPull.propC2).to.be.undefined;
3452
+ chai.expect(t.local.getSchemaProps("TestDomain").version).to.equal("01.00.02", "Schema must be v02 after pull #3");
3453
+ });
3454
+ });
1281
3455
  //# sourceMappingURL=SemanticRebase.test.js.map