@itwin/core-backend 5.13.0-dev.9 → 5.14.0-dev.2

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 (235) hide show
  1. package/CHANGELOG.md +33 -1
  2. package/lib/cjs/Category.d.ts +4 -4
  3. package/lib/cjs/Category.js.map +1 -1
  4. package/lib/cjs/ChangesetECAdaptor.d.ts +5 -0
  5. package/lib/cjs/ChangesetECAdaptor.d.ts.map +1 -1
  6. package/lib/cjs/ChangesetECAdaptor.js +13 -3
  7. package/lib/cjs/ChangesetECAdaptor.js.map +1 -1
  8. package/lib/cjs/CodeSpecs.d.ts +3 -3
  9. package/lib/cjs/CodeSpecs.js.map +1 -1
  10. package/lib/cjs/DisplayStyle.d.ts +2 -2
  11. package/lib/cjs/DisplayStyle.js.map +1 -1
  12. package/lib/cjs/EditTxn.d.ts.map +1 -1
  13. package/lib/cjs/EditTxn.js +1 -0
  14. package/lib/cjs/EditTxn.js.map +1 -1
  15. package/lib/cjs/Element.d.ts +48 -12
  16. package/lib/cjs/Element.d.ts.map +1 -1
  17. package/lib/cjs/Element.js +120 -24
  18. package/lib/cjs/Element.js.map +1 -1
  19. package/lib/cjs/IModelDb.d.ts +30 -15
  20. package/lib/cjs/IModelDb.d.ts.map +1 -1
  21. package/lib/cjs/IModelDb.js +51 -15
  22. package/lib/cjs/IModelDb.js.map +1 -1
  23. package/lib/cjs/Model.d.ts +5 -5
  24. package/lib/cjs/Model.d.ts.map +1 -1
  25. package/lib/cjs/Model.js.map +1 -1
  26. package/lib/cjs/PropertyStore.js +1 -1
  27. package/lib/cjs/PropertyStore.js.map +1 -1
  28. package/lib/cjs/SchemaSync.d.ts +37 -9
  29. package/lib/cjs/SchemaSync.d.ts.map +1 -1
  30. package/lib/cjs/SchemaSync.js +125 -6
  31. package/lib/cjs/SchemaSync.js.map +1 -1
  32. package/lib/cjs/SynchronousChannel.d.ts +62 -0
  33. package/lib/cjs/SynchronousChannel.d.ts.map +1 -0
  34. package/lib/cjs/SynchronousChannel.js +11 -0
  35. package/lib/cjs/SynchronousChannel.js.map +1 -0
  36. package/lib/cjs/Texture.d.ts +1 -1
  37. package/lib/cjs/Texture.js.map +1 -1
  38. package/lib/cjs/TxnManager.d.ts +44 -4
  39. package/lib/cjs/TxnManager.d.ts.map +1 -1
  40. package/lib/cjs/TxnManager.js +72 -14
  41. package/lib/cjs/TxnManager.js.map +1 -1
  42. package/lib/cjs/ViewDefinition.d.ts +6 -6
  43. package/lib/cjs/ViewDefinition.d.ts.map +1 -1
  44. package/lib/cjs/ViewDefinition.js.map +1 -1
  45. package/lib/cjs/annotations/LeaderGeometry.d.ts +22 -7
  46. package/lib/cjs/annotations/LeaderGeometry.d.ts.map +1 -1
  47. package/lib/cjs/annotations/LeaderGeometry.js +104 -36
  48. package/lib/cjs/annotations/LeaderGeometry.js.map +1 -1
  49. package/lib/cjs/annotations/TextAnnotationElement.d.ts +1 -1
  50. package/lib/cjs/annotations/TextAnnotationElement.d.ts.map +1 -1
  51. package/lib/cjs/annotations/TextAnnotationElement.js +25 -10
  52. package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
  53. package/lib/cjs/core-backend.d.ts +1 -0
  54. package/lib/cjs/core-backend.d.ts.map +1 -1
  55. package/lib/cjs/core-backend.js +1 -0
  56. package/lib/cjs/core-backend.js.map +1 -1
  57. package/lib/cjs/internal/IntegrityCheck.d.ts +22 -0
  58. package/lib/cjs/internal/IntegrityCheck.d.ts.map +1 -1
  59. package/lib/cjs/internal/IntegrityCheck.js +13 -0
  60. package/lib/cjs/internal/IntegrityCheck.js.map +1 -1
  61. package/lib/cjs/internal/NoReservations.d.ts +6 -0
  62. package/lib/cjs/internal/NoReservations.d.ts.map +1 -0
  63. package/lib/cjs/internal/NoReservations.js +21 -0
  64. package/lib/cjs/internal/NoReservations.js.map +1 -0
  65. package/lib/cjs/internal/SchemaSyncReservations.d.ts +4 -0
  66. package/lib/cjs/internal/SchemaSyncReservations.d.ts.map +1 -0
  67. package/lib/cjs/internal/SchemaSyncReservations.js +118 -0
  68. package/lib/cjs/internal/SchemaSyncReservations.js.map +1 -0
  69. package/lib/cjs/internal/Symbols.d.ts +1 -0
  70. package/lib/cjs/internal/Symbols.d.ts.map +1 -1
  71. package/lib/cjs/internal/Symbols.js +2 -1
  72. package/lib/cjs/internal/Symbols.js.map +1 -1
  73. package/lib/esm/Category.d.ts +4 -4
  74. package/lib/esm/Category.js.map +1 -1
  75. package/lib/esm/ChangesetECAdaptor.d.ts +5 -0
  76. package/lib/esm/ChangesetECAdaptor.d.ts.map +1 -1
  77. package/lib/esm/ChangesetECAdaptor.js +13 -3
  78. package/lib/esm/ChangesetECAdaptor.js.map +1 -1
  79. package/lib/esm/CodeSpecs.d.ts +3 -3
  80. package/lib/esm/CodeSpecs.js.map +1 -1
  81. package/lib/esm/DisplayStyle.d.ts +2 -2
  82. package/lib/esm/DisplayStyle.js.map +1 -1
  83. package/lib/esm/EditTxn.d.ts.map +1 -1
  84. package/lib/esm/EditTxn.js +1 -0
  85. package/lib/esm/EditTxn.js.map +1 -1
  86. package/lib/esm/Element.d.ts +48 -12
  87. package/lib/esm/Element.d.ts.map +1 -1
  88. package/lib/esm/Element.js +121 -25
  89. package/lib/esm/Element.js.map +1 -1
  90. package/lib/esm/IModelDb.d.ts +30 -15
  91. package/lib/esm/IModelDb.d.ts.map +1 -1
  92. package/lib/esm/IModelDb.js +52 -16
  93. package/lib/esm/IModelDb.js.map +1 -1
  94. package/lib/esm/Model.d.ts +5 -5
  95. package/lib/esm/Model.d.ts.map +1 -1
  96. package/lib/esm/Model.js.map +1 -1
  97. package/lib/esm/PropertyStore.js +1 -1
  98. package/lib/esm/PropertyStore.js.map +1 -1
  99. package/lib/esm/SchemaSync.d.ts +37 -9
  100. package/lib/esm/SchemaSync.d.ts.map +1 -1
  101. package/lib/esm/SchemaSync.js +127 -8
  102. package/lib/esm/SchemaSync.js.map +1 -1
  103. package/lib/esm/SynchronousChannel.d.ts +62 -0
  104. package/lib/esm/SynchronousChannel.d.ts.map +1 -0
  105. package/lib/esm/SynchronousChannel.js +9 -0
  106. package/lib/esm/SynchronousChannel.js.map +1 -0
  107. package/lib/esm/Texture.d.ts +1 -1
  108. package/lib/esm/Texture.js.map +1 -1
  109. package/lib/esm/TxnManager.d.ts +44 -4
  110. package/lib/esm/TxnManager.d.ts.map +1 -1
  111. package/lib/esm/TxnManager.js +72 -14
  112. package/lib/esm/TxnManager.js.map +1 -1
  113. package/lib/esm/ViewDefinition.d.ts +6 -6
  114. package/lib/esm/ViewDefinition.d.ts.map +1 -1
  115. package/lib/esm/ViewDefinition.js.map +1 -1
  116. package/lib/esm/annotations/LeaderGeometry.d.ts +22 -7
  117. package/lib/esm/annotations/LeaderGeometry.d.ts.map +1 -1
  118. package/lib/esm/annotations/LeaderGeometry.js +104 -36
  119. package/lib/esm/annotations/LeaderGeometry.js.map +1 -1
  120. package/lib/esm/annotations/TextAnnotationElement.d.ts +1 -1
  121. package/lib/esm/annotations/TextAnnotationElement.d.ts.map +1 -1
  122. package/lib/esm/annotations/TextAnnotationElement.js +25 -10
  123. package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
  124. package/lib/esm/core-backend.d.ts +1 -0
  125. package/lib/esm/core-backend.d.ts.map +1 -1
  126. package/lib/esm/core-backend.js +1 -0
  127. package/lib/esm/core-backend.js.map +1 -1
  128. package/lib/esm/internal/IntegrityCheck.d.ts +22 -0
  129. package/lib/esm/internal/IntegrityCheck.d.ts.map +1 -1
  130. package/lib/esm/internal/IntegrityCheck.js +13 -0
  131. package/lib/esm/internal/IntegrityCheck.js.map +1 -1
  132. package/lib/esm/internal/NoReservations.d.ts +6 -0
  133. package/lib/esm/internal/NoReservations.d.ts.map +1 -0
  134. package/lib/esm/internal/NoReservations.js +18 -0
  135. package/lib/esm/internal/NoReservations.js.map +1 -0
  136. package/lib/esm/internal/SchemaSyncReservations.d.ts +4 -0
  137. package/lib/esm/internal/SchemaSyncReservations.d.ts.map +1 -0
  138. package/lib/esm/internal/SchemaSyncReservations.js +115 -0
  139. package/lib/esm/internal/SchemaSyncReservations.js.map +1 -0
  140. package/lib/esm/internal/Symbols.d.ts +1 -0
  141. package/lib/esm/internal/Symbols.d.ts.map +1 -1
  142. package/lib/esm/internal/Symbols.js +1 -0
  143. package/lib/esm/internal/Symbols.js.map +1 -1
  144. package/lib/esm/test/IModelTestUtils.d.ts +1 -1
  145. package/lib/esm/test/IModelTestUtils.d.ts.map +1 -1
  146. package/lib/esm/test/IModelTestUtils.js +8 -3
  147. package/lib/esm/test/IModelTestUtils.js.map +1 -1
  148. package/lib/esm/test/annotations/LeaderGeometry.test.js +178 -2
  149. package/lib/esm/test/annotations/LeaderGeometry.test.js.map +1 -1
  150. package/lib/esm/test/annotations/TextAnnotation.test.js +48 -18
  151. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  152. package/lib/esm/test/ecdb/ECSqlStatement.test.js +177 -0
  153. package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
  154. package/lib/esm/test/ecdb/QueryReaders.test.js +2 -16
  155. package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
  156. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js +2 -2
  157. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
  158. package/lib/esm/test/element/ElementRoundTrip.test.js +80 -2
  159. package/lib/esm/test/element/ElementRoundTrip.test.js.map +1 -1
  160. package/lib/esm/test/imodel/IModelECSql.test.d.ts +2 -0
  161. package/lib/esm/test/imodel/IModelECSql.test.d.ts.map +1 -0
  162. package/lib/esm/test/imodel/IModelECSql.test.js +367 -0
  163. package/lib/esm/test/imodel/IModelECSql.test.js.map +1 -0
  164. package/lib/esm/test/imodel/IModelElements.test.d.ts +2 -0
  165. package/lib/esm/test/imodel/IModelElements.test.d.ts.map +1 -0
  166. package/lib/esm/test/imodel/IModelElements.test.js +969 -0
  167. package/lib/esm/test/imodel/IModelElements.test.js.map +1 -0
  168. package/lib/esm/test/imodel/IModelGeolocation.test.d.ts +2 -0
  169. package/lib/esm/test/imodel/IModelGeolocation.test.d.ts.map +1 -0
  170. package/lib/esm/test/imodel/IModelGeolocation.test.js +539 -0
  171. package/lib/esm/test/imodel/IModelGeolocation.test.js.map +1 -0
  172. package/lib/esm/test/imodel/IModelLifecycle.test.d.ts +2 -0
  173. package/lib/esm/test/imodel/IModelLifecycle.test.d.ts.map +1 -0
  174. package/lib/esm/test/imodel/IModelLifecycle.test.js +499 -0
  175. package/lib/esm/test/imodel/IModelLifecycle.test.js.map +1 -0
  176. package/lib/esm/test/imodel/IModelMetadata.test.d.ts +2 -0
  177. package/lib/esm/test/imodel/IModelMetadata.test.d.ts.map +1 -0
  178. package/lib/esm/test/imodel/IModelMetadata.test.js +274 -0
  179. package/lib/esm/test/imodel/IModelMetadata.test.js.map +1 -0
  180. package/lib/esm/test/imodel/IModelModels.test.d.ts +2 -0
  181. package/lib/esm/test/imodel/IModelModels.test.d.ts.map +1 -0
  182. package/lib/esm/test/imodel/IModelModels.test.js +357 -0
  183. package/lib/esm/test/imodel/IModelModels.test.js.map +1 -0
  184. package/lib/esm/test/imodel/IModelRelationships.test.d.ts +2 -0
  185. package/lib/esm/test/imodel/IModelRelationships.test.d.ts.map +1 -0
  186. package/lib/esm/test/imodel/IModelRelationships.test.js +407 -0
  187. package/lib/esm/test/imodel/IModelRelationships.test.js.map +1 -0
  188. package/lib/esm/test/imodel/IModelTestFixtures.d.ts +53 -0
  189. package/lib/esm/test/imodel/IModelTestFixtures.d.ts.map +1 -0
  190. package/lib/esm/test/imodel/IModelTestFixtures.js +107 -0
  191. package/lib/esm/test/imodel/IModelTestFixtures.js.map +1 -0
  192. package/lib/esm/test/imodel/IModelViews.test.d.ts +2 -0
  193. package/lib/esm/test/imodel/IModelViews.test.d.ts.map +1 -0
  194. package/lib/esm/test/imodel/IModelViews.test.js +199 -0
  195. package/lib/esm/test/imodel/IModelViews.test.js.map +1 -0
  196. package/lib/esm/test/index.d.ts +0 -1
  197. package/lib/esm/test/index.d.ts.map +1 -1
  198. package/lib/esm/test/index.js +0 -1
  199. package/lib/esm/test/index.js.map +1 -1
  200. package/lib/esm/test/standalone/ChangesetReader.test.js +8 -26
  201. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  202. package/lib/esm/test/standalone/DgnDomainMergeConflict.test.d.ts +2 -0
  203. package/lib/esm/test/standalone/DgnDomainMergeConflict.test.d.ts.map +1 -0
  204. package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js +263 -0
  205. package/lib/esm/test/standalone/DgnDomainMergeConflict.test.js.map +1 -0
  206. package/lib/esm/test/standalone/GeometryStream.test.js +1 -3
  207. package/lib/esm/test/standalone/GeometryStream.test.js.map +1 -1
  208. package/lib/esm/test/standalone/IModelWrite.test.d.ts.map +1 -1
  209. package/lib/esm/test/standalone/IModelWrite.test.js +78 -1
  210. package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
  211. package/lib/esm/test/standalone/IntegrityCheck.test.js +29 -10
  212. package/lib/esm/test/standalone/IntegrityCheck.test.js.map +1 -1
  213. package/lib/esm/test/standalone/SQliteChangesetReaderAndChangesetECAdaptor.test.js +23 -0
  214. package/lib/esm/test/standalone/SQliteChangesetReaderAndChangesetECAdaptor.test.js.map +1 -1
  215. package/lib/esm/test/standalone/SchemaSyncDb.test.d.ts +2 -0
  216. package/lib/esm/test/standalone/SchemaSyncDb.test.d.ts.map +1 -0
  217. package/lib/esm/test/standalone/SchemaSyncDb.test.js +221 -0
  218. package/lib/esm/test/standalone/SchemaSyncDb.test.js.map +1 -0
  219. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.d.ts +2 -0
  220. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.d.ts.map +1 -0
  221. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js +459 -0
  222. package/lib/esm/test/standalone/SchemaSyncElementReservation.test.js.map +1 -0
  223. package/lib/esm/test/standalone/SnapshotDb.test.js +1 -0
  224. package/lib/esm/test/standalone/SnapshotDb.test.js.map +1 -1
  225. package/lib/esm/test/standalone/TxnManager.test.js +108 -8
  226. package/lib/esm/test/standalone/TxnManager.test.js.map +1 -1
  227. package/package.json +14 -17
  228. package/lib/esm/test/RevisionUtility.d.ts +0 -33
  229. package/lib/esm/test/RevisionUtility.d.ts.map +0 -1
  230. package/lib/esm/test/RevisionUtility.js +0 -52
  231. package/lib/esm/test/RevisionUtility.js.map +0 -1
  232. package/lib/esm/test/imodel/IModel.test.d.ts +0 -2
  233. package/lib/esm/test/imodel/IModel.test.d.ts.map +0 -1
  234. package/lib/esm/test/imodel/IModel.test.js +0 -3277
  235. package/lib/esm/test/imodel/IModel.test.js.map +0 -1
@@ -0,0 +1,459 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ import { Id64, OpenMode } from "@itwin/core-bentley";
6
+ import { BriefcaseIdValue, ElementReservationError, IModel } from "@itwin/core-common";
7
+ import { expect } from "chai";
8
+ import * as sinon from "sinon";
9
+ import { IModelJsFs, SchemaSync, StandaloneDb } from "../../core-backend";
10
+ import { DrawingCategory } from "../../Category";
11
+ import { _nativeDb, _onReservedElementInsert } from "../../internal/Symbols";
12
+ import { EditTxn, withEditTxn } from "../../EditTxn";
13
+ import { IModelTestUtils } from "../IModelTestUtils";
14
+ /** Assert that `promise` rejects with an `ElementReservationError` carrying the given key. */
15
+ async function expectReservationError(promise, key) {
16
+ try {
17
+ await promise;
18
+ }
19
+ catch (err) {
20
+ expect(ElementReservationError.isError(err, key), `expected ElementReservationError '${key}', got: ${JSON.stringify(err)}`).to.be.true;
21
+ return;
22
+ }
23
+ expect.fail(`expected promise to reject with ElementReservationError '${key}'`);
24
+ }
25
+ const fedGuidA = "8b33a6ec-1a6f-4ae2-8ad4-c426276d1f30";
26
+ const fedGuidB = "1f0e5b96-1234-4abc-9def-0123456789ab";
27
+ describe("SchemaSync element reservation", () => {
28
+ let iModel;
29
+ let iModelFileName;
30
+ let schemaDb;
31
+ let schemaDbFileName;
32
+ beforeEach(() => {
33
+ iModelFileName = IModelTestUtils.prepareOutputFile("SchemaSyncElementReservation", "element-reservation.bim");
34
+ schemaDbFileName = IModelTestUtils.prepareOutputFile("SchemaSyncElementReservation", "element-reservation-sync.db");
35
+ iModel = StandaloneDb.createEmpty(iModelFileName, { rootSubject: { name: "SchemaSyncElementReservation" } });
36
+ SchemaSync.SchemaSyncDb.createNewDb(schemaDbFileName);
37
+ schemaDb = new SchemaSync.SchemaSyncDb();
38
+ schemaDb.openDb(schemaDbFileName, OpenMode.ReadWrite);
39
+ });
40
+ afterEach(() => {
41
+ sinon.restore();
42
+ if (schemaDb.isOpen)
43
+ schemaDb.closeDb();
44
+ if (iModel.isOpen)
45
+ iModel.close();
46
+ IModelJsFs.removeSync(iModelFileName);
47
+ IModelJsFs.removeSync(schemaDbFileName);
48
+ });
49
+ // `SchemaSyncReservations` only ever obtains its access object via `SchemaSync.getCloudAccess`, so we
50
+ // stub that single seam to hand back a mock that delegates every operation to the already-open
51
+ // `schemaDb`. `withLockedDb` / `writeLocker` mirror the real commit-on-success, abandon-on-error
52
+ // semantics without any CloudContainer.
53
+ function stubCloudAccess() {
54
+ // This is the minimal `SchemaSync.CloudAccess` surface that `SchemaSyncReservations` actually consumes.
55
+ const access = {
56
+ synchronizeWithCloud() { },
57
+ close() { },
58
+ getUri: () => `${schemaDbFileName}?vfs=test`,
59
+ container: { hasLocalChanges: false }, // these tests use local-only SchemaSyncDb, no need to mock a real CloudContainer
60
+ reader: {
61
+ findReservedElement: (fedGuid) => schemaDb.findReservedElement(fedGuid),
62
+ },
63
+ writeLocker: {
64
+ reserveElements: async (ids) => {
65
+ try {
66
+ await schemaDb.reserveElements(ids);
67
+ schemaDb.saveChanges();
68
+ }
69
+ catch (err) {
70
+ schemaDb.abandonChanges();
71
+ throw err;
72
+ }
73
+ },
74
+ },
75
+ withLockedDb: async (_args, operation) => {
76
+ try {
77
+ const result = await operation();
78
+ schemaDb.saveChanges();
79
+ return result;
80
+ }
81
+ catch (err) {
82
+ schemaDb.abandonChanges();
83
+ throw err;
84
+ }
85
+ },
86
+ };
87
+ sinon.stub(SchemaSync, "getCloudAccess").resolves(access);
88
+ return access;
89
+ }
90
+ function enableSchemaSync() {
91
+ sinon.stub(iModel[_nativeDb], "schemaSyncEnabled").returns(true);
92
+ }
93
+ async function setupSchemaSyncReservations() {
94
+ const access = stubCloudAccess();
95
+ enableSchemaSync();
96
+ await iModel.initializeSharedElementReservations();
97
+ return access;
98
+ }
99
+ function readAllRows() {
100
+ return schemaDb.withSqliteStatement("SELECT federationGuid, elementId, ecClassId, codeSpecId, codeScope, codeValue FROM reserved_elements ORDER BY elementId", (stmt) => {
101
+ const rows = [];
102
+ while (stmt.nextRow()) {
103
+ rows.push({
104
+ federationGuid: stmt.getValueGuid(0),
105
+ elementId: stmt.getValueId(1),
106
+ ecClassId: stmt.getValueId(2),
107
+ codeSpecId: stmt.getValueId(3),
108
+ codeScope: stmt.getValueId(4),
109
+ codeValue: stmt.getValueString(5),
110
+ });
111
+ }
112
+ return rows;
113
+ });
114
+ }
115
+ // eslint-disable-next-line @typescript-eslint/dot-notation
116
+ const readNextLocalId = () => schemaDb["getNextReservedElementLocalId"]();
117
+ const nonEmptyCode = (value) => ({ spec: "0x1", scope: "0x2", value });
118
+ describe("reservation control initialization", () => {
119
+ it("uses NoReservations when SchemaSync is disabled", async () => {
120
+ await iModel.initializeSharedElementReservations();
121
+ expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.false;
122
+ await expect(iModel.reservations.reserveElements({
123
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
124
+ })).to.be.fulfilled;
125
+ });
126
+ it("initializes SchemaSync-backed reservations after initializeForIModel", async () => {
127
+ let schemaSyncEnabled = false;
128
+ stubCloudAccess();
129
+ sinon.stub(iModel[_nativeDb], "schemaSyncEnabled").callsFake(() => schemaSyncEnabled);
130
+ sinon.stub(iModel[_nativeDb], "schemaSyncInit").callsFake(() => {
131
+ schemaSyncEnabled = true;
132
+ });
133
+ sinon.stub(iModel, "acquireSchemaLock").resolves();
134
+ sinon.stub(iModel, "pullChanges").resolves();
135
+ sinon.stub(iModel, "pushChanges").resolves();
136
+ await iModel.initializeSharedElementReservations();
137
+ expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.false;
138
+ const previousEnforcement = EditTxn.implicitWriteEnforcement;
139
+ EditTxn.implicitWriteEnforcement = "allow";
140
+ try {
141
+ await SchemaSync.initializeForIModel({
142
+ iModel,
143
+ containerProps: {
144
+ baseUri: "https://example.invalid",
145
+ containerId: "element-reservation",
146
+ storageType: "azure",
147
+ },
148
+ });
149
+ }
150
+ finally {
151
+ EditTxn.implicitWriteEnforcement = previousEnforcement;
152
+ }
153
+ await iModel.reservations.reserveElements({
154
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
155
+ });
156
+ expect(readAllRows()).to.have.lengthOf(1);
157
+ expect(readAllRows()[0].federationGuid).to.equal(fedGuidA);
158
+ });
159
+ it("reports needsElementReservation only for valid unreserved guids when SchemaSync is enabled", async () => {
160
+ await setupSchemaSyncReservations();
161
+ expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.true;
162
+ expect(() => iModel.reservations.needsElementReservation("not-a-guid")).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "invalid-reservation"));
163
+ await iModel.reservations.reserveElements({
164
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
165
+ });
166
+ expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.false;
167
+ expect(iModel.reservations.needsElementReservation(fedGuidB)).to.be.true;
168
+ });
169
+ });
170
+ describe("reserveElements", () => {
171
+ it("reserves a single ID and advances the counter", async () => {
172
+ await setupSchemaSyncReservations();
173
+ await iModel.reservations.reserveElements({
174
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:TypeDefinitionElement", code: nonEmptyCode("TD-A") }],
175
+ });
176
+ const rows = readAllRows();
177
+ expect(rows).to.have.lengthOf(1);
178
+ const elementId = rows[0].elementId;
179
+ expect(elementId).to.equal(Id64.fromLocalAndBriefcaseIds(1, BriefcaseIdValue.SchemaSyncElementReserved));
180
+ expect(rows[0].federationGuid).to.equal(fedGuidA);
181
+ expect(rows[0].elementId).to.equal(elementId);
182
+ expect(rows[0].codeValue).to.equal("TD-A");
183
+ expect(readNextLocalId()).to.equal(2);
184
+ });
185
+ it("reserves a non-definition element with an explicit federationGuid", async () => {
186
+ await setupSchemaSyncReservations();
187
+ // A Subject is *not* a DefinitionElement; under the broadened rule it is still reservable.
188
+ await iModel.reservations.reserveElements({
189
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:Subject" }],
190
+ });
191
+ const rows = readAllRows();
192
+ expect(rows).to.have.lengthOf(1);
193
+ expect(rows[0].federationGuid).to.equal(fedGuidA);
194
+ expect(rows[0].codeValue).to.equal(""); // empty code
195
+ });
196
+ it("reserves two IDs for categories and advances the counter", async () => {
197
+ await setupSchemaSyncReservations();
198
+ await iModel.reservations.reserveElements({
199
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
200
+ });
201
+ const rows = readAllRows();
202
+ expect(rows).to.have.lengthOf(1);
203
+ const elementId = rows[0].elementId;
204
+ expect(elementId).to.equal(Id64.fromLocalAndBriefcaseIds(1, BriefcaseIdValue.SchemaSyncElementReserved));
205
+ expect(rows[0].federationGuid).to.equal(fedGuidA);
206
+ expect(rows[0].elementId).to.equal(elementId);
207
+ expect(rows[0].codeValue).to.equal("Cat-A");
208
+ expect(readNextLocalId()).to.equal(3); // +2 because category inserts always trigger a second insert for default subcategory
209
+ });
210
+ it("is idempotent when reserving the identical identity twice", async () => {
211
+ await setupSchemaSyncReservations();
212
+ const args = { elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }] };
213
+ await iModel.reservations.reserveElements(args);
214
+ await iModel.reservations.reserveElements(args);
215
+ expect(readAllRows()).to.have.lengthOf(1);
216
+ });
217
+ it("throws reservation-conflict when the same non-empty code is reserved for different guids (in one batch)", async () => {
218
+ await setupSchemaSyncReservations();
219
+ await expect(iModel.reservations.reserveElements({
220
+ elements: [
221
+ { federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-X") },
222
+ { federationGuid: fedGuidB, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-X") },
223
+ ],
224
+ })).to.be.rejected;
225
+ expect(readAllRows()).to.be.empty;
226
+ });
227
+ it("throws reservation-conflict when the same non-empty code is reserved for a different guid (across batches)", async () => {
228
+ await setupSchemaSyncReservations();
229
+ await iModel.reservations.reserveElements({
230
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-X") }],
231
+ });
232
+ await expect(iModel.reservations.reserveElements({
233
+ elements: [{ federationGuid: fedGuidB, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-X") }],
234
+ })).to.be.rejected;
235
+ expect(readAllRows()).to.have.lengthOf(1);
236
+ });
237
+ it("aggregates validation errors and reserves nothing on failure", async () => {
238
+ await setupSchemaSyncReservations();
239
+ await expectReservationError(iModel.reservations.reserveElements({
240
+ elements: [
241
+ { federationGuid: "not-a-guid", classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat") },
242
+ { federationGuid: fedGuidA, classFullName: "BisCore:NonexistentClassXYZ", code: nonEmptyCode("Cat") },
243
+ { federationGuid: fedGuidB, classFullName: "BisCore:DrawingCategory", code: { spec: "not-an-id", scope: IModel.dictionaryId, value: "Cat" } },
244
+ ],
245
+ }), "invalid-reservation");
246
+ expect(readAllRows()).to.be.empty;
247
+ expect(readNextLocalId()).to.equal(1);
248
+ });
249
+ it("rejects entries with a missing federationGuid", async () => {
250
+ await setupSchemaSyncReservations();
251
+ await expectReservationError(iModel.reservations.reserveElements({
252
+ elements: [{ classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat") }],
253
+ }), "invalid-reservation");
254
+ expect(readAllRows()).to.be.empty;
255
+ });
256
+ it("rolls back counter and rows when the locked-db write is abandoned", async () => {
257
+ const syncAccess = await setupSchemaSyncReservations();
258
+ // First reserve advances the counter to 2 and commits.
259
+ await iModel.reservations.reserveElements({
260
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:TypeDefinitionElement", code: nonEmptyCode("TD-A") }],
261
+ });
262
+ expect(readNextLocalId()).to.equal(2);
263
+ // Simulate something going wrong with the upload where the db write is abandoned instead of committed.
264
+ syncAccess.writeLocker.reserveElements = async (ids) => {
265
+ try {
266
+ await schemaDb.reserveElements(ids);
267
+ }
268
+ finally {
269
+ schemaDb.abandonChanges();
270
+ }
271
+ };
272
+ // The second reserve allocates + inserts a new row, then the write is abandoned.
273
+ await iModel.reservations.reserveElements({
274
+ elements: [{ federationGuid: fedGuidB, classFullName: "BisCore:TypeDefinitionElement", code: nonEmptyCode("TD-B") }],
275
+ });
276
+ // The new row should not be visible, and the counter file-property must remain at "2".
277
+ const rows = readAllRows();
278
+ expect(rows.map((r) => r.federationGuid)).to.deep.equal([fedGuidA]);
279
+ expect(readNextLocalId()).to.equal(2);
280
+ });
281
+ });
282
+ describe("Reservations _onReservedElementInsert hook", () => {
283
+ it("is a no-op when SchemaSync is not enabled", () => {
284
+ // schemaSyncEnabled returns false by default.
285
+ const arg = {
286
+ iModel,
287
+ props: {
288
+ classFullName: "BisCore:DrawingCategory",
289
+ federationGuid: fedGuidA,
290
+ model: IModel.dictionaryId,
291
+ code: nonEmptyCode("Cat-A"),
292
+ },
293
+ options: {},
294
+ };
295
+ expect(() => iModel.reservations[_onReservedElementInsert](arg)).not.to.throw();
296
+ expect(arg.props.id).to.be.undefined;
297
+ expect(arg.options.forceUseId).to.be.undefined;
298
+ });
299
+ it("is a no-op when the schema lock is held", async () => {
300
+ await setupSchemaSyncReservations();
301
+ sinon.stub(iModel, "holdsSchemaLock").get(() => true);
302
+ expect(() => iModel.reservations[_onReservedElementInsert]({
303
+ iModel,
304
+ props: { classFullName: "BisCore:DrawingCategory" },
305
+ options: {},
306
+ })).not.to.throw();
307
+ });
308
+ it("throws 'reservation-not-found' when reserve has never been called", async () => {
309
+ await setupSchemaSyncReservations();
310
+ expect(() => iModel.reservations[_onReservedElementInsert]({
311
+ iModel,
312
+ props: {
313
+ classFullName: "BisCore:DrawingCategory",
314
+ federationGuid: fedGuidA,
315
+ model: IModel.dictionaryId,
316
+ code: nonEmptyCode("Cat-A"),
317
+ },
318
+ options: {},
319
+ })).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "reservation-not-found"));
320
+ });
321
+ it("throws 'reservation-not-found' for an unreserved federationGuid", async () => {
322
+ await setupSchemaSyncReservations();
323
+ // Reserve some other identity so the read handle is initialized.
324
+ await iModel.reservations.reserveElements({
325
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
326
+ });
327
+ expect(() => iModel.reservations[_onReservedElementInsert]({
328
+ iModel,
329
+ props: {
330
+ classFullName: "BisCore:DrawingCategory",
331
+ federationGuid: fedGuidB,
332
+ model: IModel.dictionaryId,
333
+ code: nonEmptyCode("Cat-B"),
334
+ },
335
+ options: {},
336
+ })).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "reservation-not-found"));
337
+ });
338
+ it("throws when federationGuid is present but malformed", async () => {
339
+ await setupSchemaSyncReservations();
340
+ await iModel.reservations.reserveElements({
341
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
342
+ });
343
+ expect(() => iModel.reservations[_onReservedElementInsert]({
344
+ iModel,
345
+ props: {
346
+ classFullName: "BisCore:DrawingCategory",
347
+ federationGuid: "not-a-valid-guid",
348
+ model: IModel.dictionaryId,
349
+ code: nonEmptyCode("Cat-A"),
350
+ },
351
+ options: {},
352
+ })).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "invalid-reservation"));
353
+ });
354
+ it("throws when SchemaSync container has local changes", async () => {
355
+ const syncAccess = await setupSchemaSyncReservations();
356
+ syncAccess.container.hasLocalChanges = true;
357
+ expect(() => iModel.reservations[_onReservedElementInsert]({
358
+ iModel,
359
+ props: {
360
+ classFullName: "BisCore:DrawingCategory",
361
+ federationGuid: fedGuidA,
362
+ model: IModel.dictionaryId,
363
+ code: nonEmptyCode("Cat-A"),
364
+ },
365
+ options: {},
366
+ })).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "container-has-local-changes"));
367
+ });
368
+ it("sets props.id and options.forceUseId when the reservation matches", async () => {
369
+ await setupSchemaSyncReservations();
370
+ await iModel.reservations.reserveElements({
371
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
372
+ });
373
+ const expectedId = readAllRows()[0].elementId;
374
+ const props = {
375
+ classFullName: "BisCore:DrawingCategory",
376
+ federationGuid: fedGuidA,
377
+ model: IModel.dictionaryId,
378
+ code: nonEmptyCode("Cat-A"),
379
+ };
380
+ const options = {};
381
+ iModel.reservations[_onReservedElementInsert]({ iModel, props, options });
382
+ expect(props.id).to.equal(expectedId);
383
+ expect(options.forceUseId).to.be.true;
384
+ });
385
+ it("throws when the insert's class does not match the reserved class", async () => {
386
+ await setupSchemaSyncReservations();
387
+ await iModel.reservations.reserveElements({
388
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
389
+ });
390
+ expect(() => iModel.reservations[_onReservedElementInsert]({
391
+ iModel,
392
+ props: {
393
+ classFullName: "BisCore:SpatialCategory",
394
+ federationGuid: fedGuidA,
395
+ model: IModel.dictionaryId,
396
+ code: nonEmptyCode("Cat-A"),
397
+ },
398
+ options: {},
399
+ })).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "reservation-conflict"));
400
+ });
401
+ it("throws when the insert's code does not match the reserved code", async () => {
402
+ await setupSchemaSyncReservations();
403
+ await iModel.reservations.reserveElements({
404
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: nonEmptyCode("Cat-A") }],
405
+ });
406
+ expect(() => iModel.reservations[_onReservedElementInsert]({
407
+ iModel,
408
+ props: {
409
+ classFullName: "BisCore:DrawingCategory",
410
+ federationGuid: fedGuidA,
411
+ model: IModel.dictionaryId,
412
+ code: nonEmptyCode("Cat-DIFFERENT"),
413
+ },
414
+ options: {},
415
+ })).to.throw().that.satisfies((err) => ElementReservationError.isError(err, "reservation-conflict"));
416
+ });
417
+ it("applies reserved element id during a real DrawingCategory insert", async () => {
418
+ await setupSchemaSyncReservations();
419
+ const categoryName = "ReservedEndToEndCategory";
420
+ const categoryCode = DrawingCategory.createCode(iModel, IModel.dictionaryId, categoryName);
421
+ await iModel.reservations.reserveElements({
422
+ elements: [{ federationGuid: fedGuidA, classFullName: "BisCore:DrawingCategory", code: categoryCode }],
423
+ });
424
+ const expectedId = readAllRows()[0].elementId;
425
+ const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);
426
+ category.federationGuid = fedGuidA;
427
+ const insertedId = withEditTxn(iModel, (txn) => category.insert(txn));
428
+ expect(insertedId).to.equal(expectedId);
429
+ expect(Id64.getBriefcaseId(insertedId)).to.equal(BriefcaseIdValue.SchemaSyncElementReserved);
430
+ });
431
+ it("inserts freely (no reservation) when no federationGuid is set", async () => {
432
+ await setupSchemaSyncReservations();
433
+ const categoryName = "UnreservedNoGuidCategory";
434
+ // No federationGuid on the element => ordinary element => not gated => inserts normally.
435
+ const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);
436
+ const insertedId = withEditTxn(iModel, (txn) => category.insert(txn));
437
+ expect(Id64.getBriefcaseId(insertedId)).to.not.equal(BriefcaseIdValue.SchemaSyncElementReserved);
438
+ });
439
+ it("bypasses the reservation check when skipReservationCheck is set", async () => {
440
+ await setupSchemaSyncReservations();
441
+ const categoryName = "SkippedCategory";
442
+ const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);
443
+ category.federationGuid = fedGuidA; // explicit fedGuid but no reservation exists
444
+ const props = category.toJSON();
445
+ const insertedId = withEditTxn(iModel, (txn) => txn.insertElement(props, { skipReservationCheck: true }));
446
+ expect(Id64.getBriefcaseId(insertedId)).to.not.equal(BriefcaseIdValue.SchemaSyncElementReserved);
447
+ expect(iModel.elements.getElement(insertedId).federationGuid).to.equal(fedGuidA);
448
+ });
449
+ it("throws reservation-not-found for a real insert of an unreserved element with an explicit fedGuid", async () => {
450
+ await setupSchemaSyncReservations();
451
+ const categoryName = "UnreservedGuidCategory";
452
+ const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);
453
+ category.federationGuid = fedGuidB; // explicit fedGuid, never reserved
454
+ expect(() => withEditTxn(iModel, (txn) => category.insert(txn)))
455
+ .to.throw().that.satisfies((err) => ElementReservationError.isError(err, "reservation-not-found"));
456
+ });
457
+ });
458
+ });
459
+ //# sourceMappingURL=SchemaSyncElementReservation.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SchemaSyncElementReservation.test.js","sourceRoot":"","sources":["../../../../src/test/standalone/SchemaSyncElementReservation.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\r\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\r\n * See LICENSE.md in the project root for license terms and full copyright notice.\r\n *--------------------------------------------------------------------------------------------*/\r\n\r\nimport { Id64, OpenMode } from \"@itwin/core-bentley\";\r\nimport { BriefcaseIdValue, ElementReservationError, IModel } from \"@itwin/core-common\";\r\nimport { expect } from \"chai\";\r\nimport * as sinon from \"sinon\";\r\nimport { CloudSqlite, IModelJsFs, SchemaSync, StandaloneDb } from \"../../core-backend\";\r\nimport { DrawingCategory } from \"../../Category\";\r\nimport { _nativeDb, _onReservedElementInsert } from \"../../internal/Symbols\";\r\nimport { EditTxn, withEditTxn } from \"../../EditTxn\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\n\r\n/** Assert that `promise` rejects with an `ElementReservationError` carrying the given key. */\r\nasync function expectReservationError(promise: Promise<unknown>, key: ElementReservationError.Key): Promise<void> {\r\n try {\r\n await promise;\r\n } catch (err) {\r\n expect(ElementReservationError.isError(err, key), `expected ElementReservationError '${key}', got: ${JSON.stringify(err)}`).to.be.true;\r\n return;\r\n }\r\n expect.fail(`expected promise to reject with ElementReservationError '${key}'`);\r\n}\r\n\r\nconst fedGuidA = \"8b33a6ec-1a6f-4ae2-8ad4-c426276d1f30\";\r\nconst fedGuidB = \"1f0e5b96-1234-4abc-9def-0123456789ab\";\r\n\r\ndescribe(\"SchemaSync element reservation\", () => {\r\n let iModel: StandaloneDb;\r\n let iModelFileName: string;\r\n let schemaDb: SchemaSync.SchemaSyncDb;\r\n let schemaDbFileName: string;\r\n\r\n beforeEach(() => {\r\n iModelFileName = IModelTestUtils.prepareOutputFile(\"SchemaSyncElementReservation\", \"element-reservation.bim\");\r\n schemaDbFileName = IModelTestUtils.prepareOutputFile(\"SchemaSyncElementReservation\", \"element-reservation-sync.db\");\r\n iModel = StandaloneDb.createEmpty(iModelFileName, { rootSubject: { name: \"SchemaSyncElementReservation\" } });\r\n SchemaSync.SchemaSyncDb.createNewDb(schemaDbFileName);\r\n schemaDb = new SchemaSync.SchemaSyncDb();\r\n schemaDb.openDb(schemaDbFileName, OpenMode.ReadWrite);\r\n });\r\n\r\n afterEach(() => {\r\n sinon.restore();\r\n if (schemaDb.isOpen)\r\n schemaDb.closeDb();\r\n if (iModel.isOpen)\r\n iModel.close();\r\n\r\n IModelJsFs.removeSync(iModelFileName);\r\n IModelJsFs.removeSync(schemaDbFileName);\r\n });\r\n\r\n // `SchemaSyncReservations` only ever obtains its access object via `SchemaSync.getCloudAccess`, so we\r\n // stub that single seam to hand back a mock that delegates every operation to the already-open\r\n // `schemaDb`. `withLockedDb` / `writeLocker` mirror the real commit-on-success, abandon-on-error\r\n // semantics without any CloudContainer.\r\n function stubCloudAccess() {\r\n // This is the minimal `SchemaSync.CloudAccess` surface that `SchemaSyncReservations` actually consumes.\r\n const access = {\r\n synchronizeWithCloud() { },\r\n close() { },\r\n getUri: () => `${schemaDbFileName}?vfs=test`,\r\n container: { hasLocalChanges: false } as CloudSqlite.CloudContainer, // these tests use local-only SchemaSyncDb, no need to mock a real CloudContainer\r\n reader: {\r\n findReservedElement: (fedGuid) => schemaDb.findReservedElement(fedGuid),\r\n },\r\n writeLocker: {\r\n reserveElements: async (ids) => {\r\n try {\r\n await schemaDb.reserveElements(ids);\r\n schemaDb.saveChanges();\r\n } catch (err) {\r\n schemaDb.abandonChanges();\r\n throw err;\r\n }\r\n },\r\n },\r\n withLockedDb: async (_args, operation) => {\r\n try {\r\n const result = await operation();\r\n schemaDb.saveChanges();\r\n return result;\r\n } catch (err) {\r\n schemaDb.abandonChanges();\r\n throw err;\r\n }\r\n },\r\n } satisfies Partial<SchemaSync.CloudAccess>;\r\n sinon.stub(SchemaSync, \"getCloudAccess\").resolves(access as SchemaSync.CloudAccess);\r\n return access;\r\n }\r\n\r\n function enableSchemaSync(): void {\r\n sinon.stub(iModel[_nativeDb], \"schemaSyncEnabled\").returns(true);\r\n }\r\n\r\n async function setupSchemaSyncReservations() {\r\n const access = stubCloudAccess();\r\n enableSchemaSync();\r\n await iModel.initializeSharedElementReservations();\r\n return access;\r\n }\r\n\r\n function readAllRows(): Array<{ federationGuid: string, elementId: string, ecClassId: string, codeSpecId: string, codeScope: string, codeValue: string }> {\r\n return schemaDb.withSqliteStatement(\r\n \"SELECT federationGuid, elementId, ecClassId, codeSpecId, codeScope, codeValue FROM reserved_elements ORDER BY elementId\",\r\n (stmt) => {\r\n const rows: Array<{ federationGuid: string, elementId: string, ecClassId: string, codeSpecId: string, codeScope: string, codeValue: string }> = [];\r\n while (stmt.nextRow()) {\r\n rows.push({\r\n federationGuid: stmt.getValueGuid(0),\r\n elementId: stmt.getValueId(1),\r\n ecClassId: stmt.getValueId(2),\r\n codeSpecId: stmt.getValueId(3),\r\n codeScope: stmt.getValueId(4),\r\n codeValue: stmt.getValueString(5),\r\n });\r\n }\r\n return rows;\r\n },\r\n );\r\n }\r\n\r\n // eslint-disable-next-line @typescript-eslint/dot-notation\r\n const readNextLocalId = () => schemaDb[\"getNextReservedElementLocalId\"]();\r\n const nonEmptyCode = (value: string) => ({ spec: \"0x1\", scope: \"0x2\", value });\r\n\r\n describe(\"reservation control initialization\", () => {\r\n it(\"uses NoReservations when SchemaSync is disabled\", async () => {\r\n await iModel.initializeSharedElementReservations();\r\n expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.false;\r\n await expect(iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n })).to.be.fulfilled;\r\n });\r\n\r\n it(\"initializes SchemaSync-backed reservations after initializeForIModel\", async () => {\r\n let schemaSyncEnabled = false;\r\n stubCloudAccess();\r\n sinon.stub(iModel[_nativeDb], \"schemaSyncEnabled\").callsFake(() => schemaSyncEnabled);\r\n sinon.stub(iModel[_nativeDb], \"schemaSyncInit\").callsFake(() => {\r\n schemaSyncEnabled = true;\r\n });\r\n sinon.stub(iModel, \"acquireSchemaLock\").resolves();\r\n sinon.stub(iModel, \"pullChanges\").resolves();\r\n sinon.stub(iModel, \"pushChanges\").resolves();\r\n\r\n await iModel.initializeSharedElementReservations();\r\n expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.false;\r\n\r\n const previousEnforcement = EditTxn.implicitWriteEnforcement;\r\n EditTxn.implicitWriteEnforcement = \"allow\";\r\n try {\r\n await SchemaSync.initializeForIModel({\r\n iModel,\r\n containerProps: {\r\n baseUri: \"https://example.invalid\",\r\n containerId: \"element-reservation\",\r\n storageType: \"azure\",\r\n },\r\n });\r\n } finally {\r\n EditTxn.implicitWriteEnforcement = previousEnforcement;\r\n }\r\n\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n expect(readAllRows()).to.have.lengthOf(1);\r\n expect(readAllRows()[0].federationGuid).to.equal(fedGuidA);\r\n });\r\n\r\n it(\"reports needsElementReservation only for valid unreserved guids when SchemaSync is enabled\", async () => {\r\n await setupSchemaSyncReservations();\r\n\r\n expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.true;\r\n expect(() => iModel.reservations.needsElementReservation(\"not-a-guid\")).to.throw().that.satisfies(\r\n (err: unknown) => ElementReservationError.isError(err, \"invalid-reservation\")\r\n );\r\n\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n\r\n expect(iModel.reservations.needsElementReservation(fedGuidA)).to.be.false;\r\n expect(iModel.reservations.needsElementReservation(fedGuidB)).to.be.true;\r\n });\r\n });\r\n\r\n describe(\"reserveElements\", () => {\r\n it(\"reserves a single ID and advances the counter\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:TypeDefinitionElement\", code: nonEmptyCode(\"TD-A\") }],\r\n });\r\n\r\n const rows = readAllRows();\r\n expect(rows).to.have.lengthOf(1);\r\n const elementId = rows[0].elementId;\r\n expect(elementId).to.equal(Id64.fromLocalAndBriefcaseIds(1, BriefcaseIdValue.SchemaSyncElementReserved));\r\n\r\n expect(rows[0].federationGuid).to.equal(fedGuidA);\r\n expect(rows[0].elementId).to.equal(elementId);\r\n expect(rows[0].codeValue).to.equal(\"TD-A\");\r\n expect(readNextLocalId()).to.equal(2);\r\n });\r\n\r\n it(\"reserves a non-definition element with an explicit federationGuid\", async () => {\r\n await setupSchemaSyncReservations();\r\n // A Subject is *not* a DefinitionElement; under the broadened rule it is still reservable.\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:Subject\" }],\r\n });\r\n const rows = readAllRows();\r\n expect(rows).to.have.lengthOf(1);\r\n expect(rows[0].federationGuid).to.equal(fedGuidA);\r\n expect(rows[0].codeValue).to.equal(\"\"); // empty code\r\n });\r\n\r\n it(\"reserves two IDs for categories and advances the counter\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n\r\n const rows = readAllRows();\r\n expect(rows).to.have.lengthOf(1);\r\n const elementId = rows[0].elementId;\r\n expect(elementId).to.equal(Id64.fromLocalAndBriefcaseIds(1, BriefcaseIdValue.SchemaSyncElementReserved));\r\n\r\n expect(rows[0].federationGuid).to.equal(fedGuidA);\r\n expect(rows[0].elementId).to.equal(elementId);\r\n expect(rows[0].codeValue).to.equal(\"Cat-A\");\r\n expect(readNextLocalId()).to.equal(3); // +2 because category inserts always trigger a second insert for default subcategory\r\n });\r\n\r\n it(\"is idempotent when reserving the identical identity twice\", async () => {\r\n await setupSchemaSyncReservations();\r\n const args = { elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }] };\r\n await iModel.reservations.reserveElements(args);\r\n await iModel.reservations.reserveElements(args);\r\n expect(readAllRows()).to.have.lengthOf(1);\r\n });\r\n\r\n it(\"throws reservation-conflict when the same non-empty code is reserved for different guids (in one batch)\", async () => {\r\n await setupSchemaSyncReservations();\r\n await expect(iModel.reservations.reserveElements({\r\n elements: [\r\n { federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-X\") },\r\n { federationGuid: fedGuidB, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-X\") },\r\n ],\r\n })).to.be.rejected;\r\n expect(readAllRows()).to.be.empty;\r\n });\r\n\r\n it(\"throws reservation-conflict when the same non-empty code is reserved for a different guid (across batches)\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-X\") }],\r\n });\r\n await expect(iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidB, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-X\") }],\r\n })).to.be.rejected;\r\n expect(readAllRows()).to.have.lengthOf(1);\r\n });\r\n\r\n it(\"aggregates validation errors and reserves nothing on failure\", async () => {\r\n await setupSchemaSyncReservations();\r\n await expectReservationError(iModel.reservations.reserveElements({\r\n elements: [\r\n { federationGuid: \"not-a-guid\", classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat\") },\r\n { federationGuid: fedGuidA, classFullName: \"BisCore:NonexistentClassXYZ\", code: nonEmptyCode(\"Cat\") },\r\n { federationGuid: fedGuidB, classFullName: \"BisCore:DrawingCategory\", code: { spec: \"not-an-id\", scope: IModel.dictionaryId, value: \"Cat\" } },\r\n ],\r\n }), \"invalid-reservation\");\r\n expect(readAllRows()).to.be.empty;\r\n expect(readNextLocalId()).to.equal(1);\r\n });\r\n\r\n it(\"rejects entries with a missing federationGuid\", async () => {\r\n await setupSchemaSyncReservations();\r\n await expectReservationError(iModel.reservations.reserveElements({\r\n elements: [{ classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat\") } as any],\r\n }), \"invalid-reservation\");\r\n expect(readAllRows()).to.be.empty;\r\n });\r\n\r\n it(\"rolls back counter and rows when the locked-db write is abandoned\", async () => {\r\n const syncAccess = await setupSchemaSyncReservations();\r\n // First reserve advances the counter to 2 and commits.\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:TypeDefinitionElement\", code: nonEmptyCode(\"TD-A\") }],\r\n });\r\n expect(readNextLocalId()).to.equal(2);\r\n\r\n // Simulate something going wrong with the upload where the db write is abandoned instead of committed.\r\n syncAccess.writeLocker.reserveElements = async (ids) => {\r\n try {\r\n await schemaDb.reserveElements(ids);\r\n } finally {\r\n schemaDb.abandonChanges();\r\n }\r\n };\r\n\r\n // The second reserve allocates + inserts a new row, then the write is abandoned.\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidB, classFullName: \"BisCore:TypeDefinitionElement\", code: nonEmptyCode(\"TD-B\") }],\r\n });\r\n\r\n // The new row should not be visible, and the counter file-property must remain at \"2\".\r\n const rows = readAllRows();\r\n expect(rows.map((r) => r.federationGuid)).to.deep.equal([fedGuidA]);\r\n expect(readNextLocalId()).to.equal(2);\r\n });\r\n });\r\n\r\n describe(\"Reservations _onReservedElementInsert hook\", () => {\r\n it(\"is a no-op when SchemaSync is not enabled\", () => {\r\n // schemaSyncEnabled returns false by default.\r\n const arg = {\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: fedGuidA,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-A\"),\r\n },\r\n options: {},\r\n };\r\n expect(() => iModel.reservations[_onReservedElementInsert](arg)).not.to.throw();\r\n expect((arg.props as any).id).to.be.undefined;\r\n expect((arg.options as any).forceUseId).to.be.undefined;\r\n });\r\n\r\n it(\"is a no-op when the schema lock is held\", async () => {\r\n await setupSchemaSyncReservations();\r\n sinon.stub(iModel, \"holdsSchemaLock\").get(() => true);\r\n\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: { classFullName: \"BisCore:DrawingCategory\" },\r\n options: {},\r\n } as any)).not.to.throw();\r\n });\r\n\r\n it(\"throws 'reservation-not-found' when reserve has never been called\", async () => {\r\n await setupSchemaSyncReservations();\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: fedGuidA,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-A\"),\r\n },\r\n options: {},\r\n })).to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"reservation-not-found\"));\r\n });\r\n\r\n it(\"throws 'reservation-not-found' for an unreserved federationGuid\", async () => {\r\n await setupSchemaSyncReservations();\r\n // Reserve some other identity so the read handle is initialized.\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: fedGuidB,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-B\"),\r\n },\r\n options: {},\r\n })).to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"reservation-not-found\"));\r\n });\r\n\r\n it(\"throws when federationGuid is present but malformed\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: \"not-a-valid-guid\",\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-A\"),\r\n },\r\n options: {},\r\n })).to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"invalid-reservation\"));\r\n });\r\n\r\n it(\"throws when SchemaSync container has local changes\", async () => {\r\n const syncAccess = await setupSchemaSyncReservations();\r\n (syncAccess.container as any).hasLocalChanges = true;\r\n\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: fedGuidA,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-A\"),\r\n },\r\n options: {},\r\n })).to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"container-has-local-changes\"));\r\n });\r\n\r\n it(\"sets props.id and options.forceUseId when the reservation matches\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n const expectedId = readAllRows()[0].elementId;\r\n\r\n const props: any = {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: fedGuidA,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-A\"),\r\n };\r\n const options: any = {};\r\n iModel.reservations[_onReservedElementInsert]({ iModel, props, options });\r\n expect(props.id).to.equal(expectedId);\r\n expect(options.forceUseId).to.be.true;\r\n });\r\n\r\n it(\"throws when the insert's class does not match the reserved class\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:SpatialCategory\",\r\n federationGuid: fedGuidA,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-A\"),\r\n },\r\n options: {},\r\n })).to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"reservation-conflict\"));\r\n });\r\n\r\n it(\"throws when the insert's code does not match the reserved code\", async () => {\r\n await setupSchemaSyncReservations();\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: nonEmptyCode(\"Cat-A\") }],\r\n });\r\n expect(() => iModel.reservations[_onReservedElementInsert]({\r\n iModel,\r\n props: {\r\n classFullName: \"BisCore:DrawingCategory\",\r\n federationGuid: fedGuidA,\r\n model: IModel.dictionaryId,\r\n code: nonEmptyCode(\"Cat-DIFFERENT\"),\r\n },\r\n options: {},\r\n })).to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"reservation-conflict\"));\r\n });\r\n\r\n it(\"applies reserved element id during a real DrawingCategory insert\", async () => {\r\n await setupSchemaSyncReservations();\r\n const categoryName = \"ReservedEndToEndCategory\";\r\n const categoryCode = DrawingCategory.createCode(iModel, IModel.dictionaryId, categoryName);\r\n await iModel.reservations.reserveElements({\r\n elements: [{ federationGuid: fedGuidA, classFullName: \"BisCore:DrawingCategory\", code: categoryCode }],\r\n });\r\n\r\n const expectedId = readAllRows()[0].elementId;\r\n const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);\r\n category.federationGuid = fedGuidA;\r\n const insertedId = withEditTxn(iModel, (txn) => category.insert(txn));\r\n expect(insertedId).to.equal(expectedId);\r\n expect(Id64.getBriefcaseId(insertedId)).to.equal(BriefcaseIdValue.SchemaSyncElementReserved);\r\n });\r\n\r\n it(\"inserts freely (no reservation) when no federationGuid is set\", async () => {\r\n await setupSchemaSyncReservations();\r\n const categoryName = \"UnreservedNoGuidCategory\";\r\n // No federationGuid on the element => ordinary element => not gated => inserts normally.\r\n const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);\r\n const insertedId = withEditTxn(iModel, (txn) => category.insert(txn));\r\n expect(Id64.getBriefcaseId(insertedId)).to.not.equal(BriefcaseIdValue.SchemaSyncElementReserved);\r\n });\r\n\r\n it(\"bypasses the reservation check when skipReservationCheck is set\", async () => {\r\n await setupSchemaSyncReservations();\r\n const categoryName = \"SkippedCategory\";\r\n const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);\r\n category.federationGuid = fedGuidA; // explicit fedGuid but no reservation exists\r\n const props = category.toJSON();\r\n const insertedId = withEditTxn(iModel, (txn) => txn.insertElement(props, { skipReservationCheck: true }));\r\n expect(Id64.getBriefcaseId(insertedId)).to.not.equal(BriefcaseIdValue.SchemaSyncElementReserved);\r\n expect(iModel.elements.getElement(insertedId).federationGuid).to.equal(fedGuidA);\r\n });\r\n\r\n it(\"throws reservation-not-found for a real insert of an unreserved element with an explicit fedGuid\", async () => {\r\n await setupSchemaSyncReservations();\r\n const categoryName = \"UnreservedGuidCategory\";\r\n const category = DrawingCategory.create(iModel, IModel.dictionaryId, categoryName);\r\n category.federationGuid = fedGuidB; // explicit fedGuid, never reserved\r\n expect(() => withEditTxn(iModel, (txn) => category.insert(txn)))\r\n .to.throw().that.satisfies((err: unknown) => ElementReservationError.isError(err, \"reservation-not-found\"));\r\n });\r\n });\r\n});\r\n"]}
@@ -41,6 +41,7 @@ describe("SnapshotDb.refreshContainerForRpc", () => {
41
41
  getITwinId: () => iTwinId,
42
42
  getCurrentChangeset: () => changeset,
43
43
  hasUnsavedChanges: () => false,
44
+ schemaSyncEnabled: () => false,
44
45
  setIModelDb: () => { },
45
46
  closeIModel: () => { },
46
47
  restartDefaultTxn: () => { },