@itwin/core-backend 5.11.0-dev.9 → 5.12.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.
- package/CHANGELOG.md +13 -1
- package/lib/cjs/BlobContainerService.d.ts +14 -1
- package/lib/cjs/BlobContainerService.d.ts.map +1 -1
- package/lib/cjs/BlobContainerService.js.map +1 -1
- package/lib/cjs/BriefcaseManager.d.ts +3 -1
- package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
- package/lib/cjs/BriefcaseManager.js +3 -3
- package/lib/cjs/BriefcaseManager.js.map +1 -1
- package/lib/cjs/CloudSqlite.d.ts +1 -1
- package/lib/cjs/CloudSqlite.d.ts.map +1 -1
- package/lib/cjs/CloudSqlite.js +4 -1
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/DisplayStyle.d.ts.map +1 -1
- package/lib/cjs/DisplayStyle.js +4 -8
- package/lib/cjs/DisplayStyle.js.map +1 -1
- package/lib/cjs/EditTxn.d.ts +56 -1
- package/lib/cjs/EditTxn.d.ts.map +1 -1
- package/lib/cjs/EditTxn.js +138 -0
- package/lib/cjs/EditTxn.js.map +1 -1
- package/lib/cjs/GeoCoordConfig.d.ts +6 -0
- package/lib/cjs/GeoCoordConfig.d.ts.map +1 -1
- package/lib/cjs/GeoCoordConfig.js +6 -0
- package/lib/cjs/GeoCoordConfig.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +40 -0
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +18 -0
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/IModelHost.d.ts +5 -3
- package/lib/cjs/IModelHost.d.ts.map +1 -1
- package/lib/cjs/IModelHost.js.map +1 -1
- package/lib/cjs/NativeHost.js +1 -1
- package/lib/cjs/NativeHost.js.map +1 -1
- package/lib/cjs/workspace/SettingsEditor.d.ts +4 -9
- package/lib/cjs/workspace/SettingsEditor.d.ts.map +1 -1
- package/lib/cjs/workspace/SettingsEditor.js +31 -15
- package/lib/cjs/workspace/SettingsEditor.js.map +1 -1
- package/lib/esm/BlobContainerService.d.ts +14 -1
- package/lib/esm/BlobContainerService.d.ts.map +1 -1
- package/lib/esm/BlobContainerService.js.map +1 -1
- package/lib/esm/BriefcaseManager.d.ts +3 -1
- package/lib/esm/BriefcaseManager.d.ts.map +1 -1
- package/lib/esm/BriefcaseManager.js +3 -3
- package/lib/esm/BriefcaseManager.js.map +1 -1
- package/lib/esm/CloudSqlite.d.ts +1 -1
- package/lib/esm/CloudSqlite.d.ts.map +1 -1
- package/lib/esm/CloudSqlite.js +4 -1
- package/lib/esm/CloudSqlite.js.map +1 -1
- package/lib/esm/DisplayStyle.d.ts.map +1 -1
- package/lib/esm/DisplayStyle.js +4 -8
- package/lib/esm/DisplayStyle.js.map +1 -1
- package/lib/esm/EditTxn.d.ts +56 -1
- package/lib/esm/EditTxn.d.ts.map +1 -1
- package/lib/esm/EditTxn.js +140 -2
- package/lib/esm/EditTxn.js.map +1 -1
- package/lib/esm/GeoCoordConfig.d.ts +6 -0
- package/lib/esm/GeoCoordConfig.d.ts.map +1 -1
- package/lib/esm/GeoCoordConfig.js +6 -0
- package/lib/esm/GeoCoordConfig.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +40 -0
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +18 -0
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/IModelHost.d.ts +5 -3
- package/lib/esm/IModelHost.d.ts.map +1 -1
- package/lib/esm/IModelHost.js.map +1 -1
- package/lib/esm/NativeHost.js +1 -1
- package/lib/esm/NativeHost.js.map +1 -1
- package/lib/esm/test/TestUtils.d.ts +17 -2
- package/lib/esm/test/TestUtils.d.ts.map +1 -1
- package/lib/esm/test/TestUtils.js +21 -2
- package/lib/esm/test/TestUtils.js.map +1 -1
- package/lib/esm/test/annotations/TextAnnotation.test.js +81 -16
- package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js +4 -2
- package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECDb.test.js +1 -1
- package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
- package/lib/esm/test/ecdb/ECSqlStatement.test.js +8 -2
- package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
- package/lib/esm/test/hubaccess/CodeSwap.test.d.ts +2 -0
- package/lib/esm/test/hubaccess/CodeSwap.test.d.ts.map +1 -0
- package/lib/esm/test/hubaccess/CodeSwap.test.js +431 -0
- package/lib/esm/test/hubaccess/CodeSwap.test.js.map +1 -0
- package/lib/esm/test/imodel/IModel.test.js +10 -2
- package/lib/esm/test/imodel/IModel.test.js.map +1 -1
- package/lib/esm/test/standalone/ChangeElementParentModel.test.d.ts +2 -0
- package/lib/esm/test/standalone/ChangeElementParentModel.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/ChangeElementParentModel.test.js +460 -0
- package/lib/esm/test/standalone/ChangeElementParentModel.test.js.map +1 -0
- package/lib/esm/test/standalone/ChangesetReader.test.js +3 -0
- package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
- package/lib/esm/test/standalone/CloudSqlite.test.d.ts +2 -0
- package/lib/esm/test/standalone/CloudSqlite.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/CloudSqlite.test.js +43 -0
- package/lib/esm/test/standalone/CloudSqlite.test.js.map +1 -0
- package/lib/esm/test/standalone/IModelWrite.test.d.ts.map +1 -1
- package/lib/esm/test/standalone/IModelWrite.test.js +2 -33
- package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
- package/lib/esm/test/standalone/ITwinWorkspace.test.js +118 -1
- package/lib/esm/test/standalone/ITwinWorkspace.test.js.map +1 -1
- package/lib/esm/test/standalone/Workspace.test.js +2 -2
- package/lib/esm/test/standalone/Workspace.test.js.map +1 -1
- package/lib/esm/workspace/SettingsEditor.d.ts +4 -9
- package/lib/esm/workspace/SettingsEditor.d.ts.map +1 -1
- package/lib/esm/workspace/SettingsEditor.js +31 -15
- package/lib/esm/workspace/SettingsEditor.js.map +1 -1
- package/package.json +16 -16
- package/lib/esm/test/misc/GeoServices.test.d.ts +0 -2
- package/lib/esm/test/misc/GeoServices.test.d.ts.map +0 -1
- package/lib/esm/test/misc/GeoServices.test.js +0 -918
- package/lib/esm/test/misc/GeoServices.test.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeSwap.test.d.ts","sourceRoot":"","sources":["../../../../src/test/hubaccess/CodeSwap.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,431 @@
|
|
|
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 * as chai from "chai";
|
|
6
|
+
import * as chaiAsPromised from "chai-as-promised";
|
|
7
|
+
import { Code, CodeScopeSpec, IModel, SubCategoryAppearance } from "@itwin/core-common";
|
|
8
|
+
import { HubWrappers, IModelTestUtils, KnownTestLocations } from "..";
|
|
9
|
+
import { ChannelControl, DrawingCategory } from "../../core-backend";
|
|
10
|
+
import { HubMock } from "../../internal/HubMock";
|
|
11
|
+
import { withEditTxn } from "../../EditTxn";
|
|
12
|
+
chai.use(chaiAsPromised);
|
|
13
|
+
describe("Code value management: null, swap, undo/redo, and cross-briefcase pull", function () {
|
|
14
|
+
this.timeout(60000);
|
|
15
|
+
it("swap codeValues between two elements sharing the same codeSpec and codeScope, verify undo/redo in a single transaction", async () => {
|
|
16
|
+
HubMock.startup("CodeSwapTest", KnownTestLocations.outputDir);
|
|
17
|
+
let b1;
|
|
18
|
+
try {
|
|
19
|
+
// --- Setup iModel ---
|
|
20
|
+
const iModelId = await HubMock.createNewIModel({
|
|
21
|
+
accessToken: "user1",
|
|
22
|
+
iTwinId: HubMock.iTwinId,
|
|
23
|
+
iModelName: "CodeSwapTest",
|
|
24
|
+
description: "CodeSwapTest",
|
|
25
|
+
});
|
|
26
|
+
b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
|
|
27
|
+
b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
28
|
+
// --- Create a drawing model + category and a CodeSpec ---
|
|
29
|
+
let drawingModelId;
|
|
30
|
+
let drawingCategoryId;
|
|
31
|
+
let codeSpecId;
|
|
32
|
+
let codeScopeId; // will be the drawing model
|
|
33
|
+
await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
|
|
34
|
+
withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
|
|
35
|
+
const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
|
|
36
|
+
const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
|
|
37
|
+
drawingModelId = modelId;
|
|
38
|
+
let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
|
|
39
|
+
if (undefined === catId)
|
|
40
|
+
catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
|
|
41
|
+
drawingCategoryId = catId;
|
|
42
|
+
codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
|
|
43
|
+
codeScopeId = drawingModelId;
|
|
44
|
+
});
|
|
45
|
+
await b1.pushChanges({ description: "setup" });
|
|
46
|
+
// --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---
|
|
47
|
+
let elem1Id;
|
|
48
|
+
let elem2Id;
|
|
49
|
+
await b1.locks.acquireLocks({ shared: drawingModelId });
|
|
50
|
+
withEditTxn(b1, "insert two coded elements", (txn) => {
|
|
51
|
+
const elem1Props = {
|
|
52
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
53
|
+
model: drawingModelId,
|
|
54
|
+
category: drawingCategoryId,
|
|
55
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
|
|
56
|
+
};
|
|
57
|
+
const elem2Props = {
|
|
58
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
59
|
+
model: drawingModelId,
|
|
60
|
+
category: drawingCategoryId,
|
|
61
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" }),
|
|
62
|
+
};
|
|
63
|
+
elem1Id = txn.insertElement(elem1Props);
|
|
64
|
+
elem2Id = txn.insertElement(elem2Props);
|
|
65
|
+
});
|
|
66
|
+
await b1.pushChanges({ description: "insert two elements with CODE_A and CODE_B" });
|
|
67
|
+
// Verify initial state
|
|
68
|
+
let e1 = b1.elements.getElementProps(elem1Id);
|
|
69
|
+
let e2 = b1.elements.getElementProps(elem2Id);
|
|
70
|
+
chai.expect(e1.code.spec).to.equal(codeSpecId);
|
|
71
|
+
chai.expect(e1.code.scope).to.equal(codeScopeId);
|
|
72
|
+
chai.expect(e1.code.value).to.equal("CODE_A");
|
|
73
|
+
chai.expect(e2.code.spec).to.equal(codeSpecId);
|
|
74
|
+
chai.expect(e2.code.scope).to.equal(codeScopeId);
|
|
75
|
+
chai.expect(e2.code.value).to.equal("CODE_B");
|
|
76
|
+
await b1.locks.acquireLocks({ exclusive: [elem2Id, elem1Id] });
|
|
77
|
+
withEditTxn(b1, "swap code values", (txn) => {
|
|
78
|
+
const props = b1.elements.getElementProps(elem1Id);
|
|
79
|
+
props.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "" });
|
|
80
|
+
txn.updateElement(props);
|
|
81
|
+
const props1 = b1.elements.getElementProps(elem1Id);
|
|
82
|
+
const props2 = b1.elements.getElementProps(elem2Id);
|
|
83
|
+
props2.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" });
|
|
84
|
+
props1.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" });
|
|
85
|
+
txn.updateElement(props2);
|
|
86
|
+
txn.updateElement(props1);
|
|
87
|
+
});
|
|
88
|
+
b1.clearCaches();
|
|
89
|
+
// Verify after swap2
|
|
90
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
91
|
+
e2 = b1.elements.getElementProps(elem2Id);
|
|
92
|
+
chai.expect(e1.code.value).to.equal("CODE_B");
|
|
93
|
+
chai.expect(e2.code.value).to.equal("CODE_A");
|
|
94
|
+
// --- Undo (reverse txn2: the swap) ---
|
|
95
|
+
chai.expect(b1.txns.isUndoPossible).to.be.true;
|
|
96
|
+
b1.txns.reverseTxns(1);
|
|
97
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
98
|
+
e2 = b1.elements.getElementProps(elem2Id);
|
|
99
|
+
chai.expect(e1.code.value).to.equal("CODE_A");
|
|
100
|
+
chai.expect(e2.code.value).to.equal("CODE_B");
|
|
101
|
+
// --- Redo (reinstate txn2: the swap) ---
|
|
102
|
+
chai.expect(b1.txns.isRedoPossible).to.be.true;
|
|
103
|
+
b1.txns.reinstateTxn();
|
|
104
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
105
|
+
e2 = b1.elements.getElementProps(elem2Id);
|
|
106
|
+
chai.expect(e1.code.value).to.equal("CODE_B");
|
|
107
|
+
chai.expect(e2.code.value).to.equal("CODE_A");
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
b1?.close();
|
|
111
|
+
HubMock.shutdown();
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
it("insert two elements with same codeSpecId and codeScopeId, null first code, swap codes, undo/redo, push", async () => {
|
|
115
|
+
HubMock.startup("CodeSwapTest", KnownTestLocations.outputDir);
|
|
116
|
+
let b1;
|
|
117
|
+
let b2;
|
|
118
|
+
try {
|
|
119
|
+
// --- Setup iModel ---
|
|
120
|
+
const iModelId = await HubMock.createNewIModel({
|
|
121
|
+
accessToken: "user1",
|
|
122
|
+
iTwinId: HubMock.iTwinId,
|
|
123
|
+
iModelName: "CodeSwapTest",
|
|
124
|
+
description: "CodeSwapTest",
|
|
125
|
+
});
|
|
126
|
+
b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
|
|
127
|
+
b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
128
|
+
b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user2", iTwinId: HubMock.iTwinId, iModelId });
|
|
129
|
+
b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
130
|
+
// --- Create a drawing model + category and a CodeSpec ---
|
|
131
|
+
let drawingModelId;
|
|
132
|
+
let drawingCategoryId;
|
|
133
|
+
let codeSpecId;
|
|
134
|
+
let codeScopeId; // will be the drawing model
|
|
135
|
+
await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
|
|
136
|
+
withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
|
|
137
|
+
const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
|
|
138
|
+
const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
|
|
139
|
+
drawingModelId = modelId;
|
|
140
|
+
let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
|
|
141
|
+
if (undefined === catId)
|
|
142
|
+
catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
|
|
143
|
+
drawingCategoryId = catId;
|
|
144
|
+
codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
|
|
145
|
+
codeScopeId = drawingModelId;
|
|
146
|
+
});
|
|
147
|
+
await b1.pushChanges({ description: "setup" });
|
|
148
|
+
await b2.pullChanges();
|
|
149
|
+
// --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---
|
|
150
|
+
let elem1Id;
|
|
151
|
+
let elem2Id;
|
|
152
|
+
await b1.locks.acquireLocks({ shared: drawingModelId });
|
|
153
|
+
withEditTxn(b1, "insert two coded elements", (txn) => {
|
|
154
|
+
const elem1Props = {
|
|
155
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
156
|
+
model: drawingModelId,
|
|
157
|
+
category: drawingCategoryId,
|
|
158
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
|
|
159
|
+
};
|
|
160
|
+
const elem2Props = {
|
|
161
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
162
|
+
model: drawingModelId,
|
|
163
|
+
category: drawingCategoryId,
|
|
164
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" }),
|
|
165
|
+
};
|
|
166
|
+
elem1Id = txn.insertElement(elem1Props);
|
|
167
|
+
elem2Id = txn.insertElement(elem2Props);
|
|
168
|
+
});
|
|
169
|
+
await b1.pushChanges({ description: "insert two elements with CODE_A and CODE_B" });
|
|
170
|
+
await b2.pullChanges();
|
|
171
|
+
// Verify initial state
|
|
172
|
+
let e1 = b1.elements.getElementProps(elem1Id);
|
|
173
|
+
let e2 = b1.elements.getElementProps(elem2Id);
|
|
174
|
+
chai.expect(e1.code.spec).to.equal(codeSpecId);
|
|
175
|
+
chai.expect(e1.code.scope).to.equal(codeScopeId);
|
|
176
|
+
chai.expect(e1.code.value).to.equal("CODE_A");
|
|
177
|
+
chai.expect(e2.code.spec).to.equal(codeSpecId);
|
|
178
|
+
chai.expect(e2.code.scope).to.equal(codeScopeId);
|
|
179
|
+
chai.expect(e2.code.value).to.equal("CODE_B");
|
|
180
|
+
// --- Step 1 (txn1): Set codeValue of elem1 to empty (null-equivalent) ---
|
|
181
|
+
await b1.locks.acquireLocks({ exclusive: [elem1Id] });
|
|
182
|
+
withEditTxn(b1, "set elem1 code to null", (txn) => {
|
|
183
|
+
const props = b1.elements.getElementProps(elem1Id);
|
|
184
|
+
props.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "" });
|
|
185
|
+
txn.updateElement(props);
|
|
186
|
+
});
|
|
187
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
188
|
+
chai.expect(e1.code.value).to.satisfy((v) => v === undefined || v === "");
|
|
189
|
+
// --- Step 2 (txn2): Swap codeValues — elem1 gets CODE_B, elem2 gets CODE_A ---
|
|
190
|
+
// elem1's intermediate empty code value avoids a uniqueness conflict while assigning CODE_A to elem2.
|
|
191
|
+
await b1.locks.acquireLocks({ exclusive: [elem2Id] });
|
|
192
|
+
withEditTxn(b1, "swap code values", (txn) => {
|
|
193
|
+
const props1 = b1.elements.getElementProps(elem1Id);
|
|
194
|
+
const props2 = b1.elements.getElementProps(elem2Id);
|
|
195
|
+
props2.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" });
|
|
196
|
+
props1.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" });
|
|
197
|
+
txn.updateElement(props2);
|
|
198
|
+
txn.updateElement(props1);
|
|
199
|
+
});
|
|
200
|
+
b1.clearCaches();
|
|
201
|
+
// Verify after swap
|
|
202
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
203
|
+
e2 = b1.elements.getElementProps(elem2Id);
|
|
204
|
+
chai.expect(e1.code.value).to.equal("CODE_B");
|
|
205
|
+
chai.expect(e2.code.value).to.equal("CODE_A");
|
|
206
|
+
// --- Undo (reverse txn2: the swap) ---
|
|
207
|
+
chai.expect(b1.txns.isUndoPossible).to.be.true;
|
|
208
|
+
b1.txns.reverseTxns(1);
|
|
209
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
210
|
+
e2 = b1.elements.getElementProps(elem2Id);
|
|
211
|
+
chai.expect(e1.code.value).to.satisfy((v) => v === undefined || v === "");
|
|
212
|
+
chai.expect(e2.code.value).to.equal("CODE_B");
|
|
213
|
+
// --- Redo (reinstate txn2: the swap) ---
|
|
214
|
+
chai.expect(b1.txns.isRedoPossible).to.be.true;
|
|
215
|
+
b1.txns.reinstateTxn();
|
|
216
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
217
|
+
e2 = b1.elements.getElementProps(elem2Id);
|
|
218
|
+
chai.expect(e1.code.value).to.equal("CODE_B");
|
|
219
|
+
chai.expect(e2.code.value).to.equal("CODE_A");
|
|
220
|
+
// --- Push changes ---
|
|
221
|
+
await b1.pushChanges({ description: "null elem1 code and swap code values" });
|
|
222
|
+
await b2.pullChanges();
|
|
223
|
+
// No local txns remain after push
|
|
224
|
+
chai.expect(b1.txns.isUndoPossible).to.be.false;
|
|
225
|
+
chai.expect(b1.txns.isRedoPossible).to.be.false;
|
|
226
|
+
// Verify b2 sees the final swapped code values after pulling
|
|
227
|
+
const b2e1 = b2.elements.getElementProps(elem1Id);
|
|
228
|
+
const b2e2 = b2.elements.getElementProps(elem2Id);
|
|
229
|
+
chai.expect(b2e1.code.value).to.equal("CODE_B");
|
|
230
|
+
chai.expect(b2e2.code.value).to.equal("CODE_A");
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
b1?.close();
|
|
234
|
+
b2?.close();
|
|
235
|
+
HubMock.shutdown();
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
it("delete element with existing code and insert new element with same code, verify undo/redo, push, and b2 sees final state after pull", async () => {
|
|
239
|
+
HubMock.startup("CodeDeleteInsertTest", KnownTestLocations.outputDir);
|
|
240
|
+
let b1;
|
|
241
|
+
let b2;
|
|
242
|
+
try {
|
|
243
|
+
// --- Setup iModel ---
|
|
244
|
+
const iModelId = await HubMock.createNewIModel({
|
|
245
|
+
accessToken: "user1",
|
|
246
|
+
iTwinId: HubMock.iTwinId,
|
|
247
|
+
iModelName: "CodeDeleteInsertTest",
|
|
248
|
+
description: "CodeDeleteInsertTest",
|
|
249
|
+
});
|
|
250
|
+
b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
|
|
251
|
+
b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
252
|
+
b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user2", iTwinId: HubMock.iTwinId, iModelId });
|
|
253
|
+
b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
254
|
+
// --- Create a drawing model + category and a CodeSpec ---
|
|
255
|
+
let drawingModelId;
|
|
256
|
+
let drawingCategoryId;
|
|
257
|
+
let codeSpecId;
|
|
258
|
+
let codeScopeId;
|
|
259
|
+
await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
|
|
260
|
+
withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
|
|
261
|
+
const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
|
|
262
|
+
const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
|
|
263
|
+
drawingModelId = modelId;
|
|
264
|
+
let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
|
|
265
|
+
if (undefined === catId)
|
|
266
|
+
catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
|
|
267
|
+
drawingCategoryId = catId;
|
|
268
|
+
codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
|
|
269
|
+
codeScopeId = drawingModelId;
|
|
270
|
+
});
|
|
271
|
+
await b1.pushChanges({ description: "setup" });
|
|
272
|
+
await b2.pullChanges();
|
|
273
|
+
// --- Insert an element with a code ---
|
|
274
|
+
let elem1Id;
|
|
275
|
+
await b1.locks.acquireLocks({ shared: drawingModelId });
|
|
276
|
+
withEditTxn(b1, "insert element with CODE_A", (txn) => {
|
|
277
|
+
const elem1Props = {
|
|
278
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
279
|
+
model: drawingModelId,
|
|
280
|
+
category: drawingCategoryId,
|
|
281
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
|
|
282
|
+
};
|
|
283
|
+
elem1Id = txn.insertElement(elem1Props);
|
|
284
|
+
});
|
|
285
|
+
await b1.pushChanges({ description: "insert element with CODE_A" });
|
|
286
|
+
await b2.pullChanges();
|
|
287
|
+
// Verify initial state
|
|
288
|
+
let e1 = b1.elements.getElementProps(elem1Id);
|
|
289
|
+
chai.expect(e1.code.spec).to.equal(codeSpecId);
|
|
290
|
+
chai.expect(e1.code.scope).to.equal(codeScopeId);
|
|
291
|
+
chai.expect(e1.code.value).to.equal("CODE_A");
|
|
292
|
+
// --- Delete elem1 and insert a new element with the same code ---
|
|
293
|
+
let newElemId;
|
|
294
|
+
await b1.locks.acquireLocks({ exclusive: [elem1Id] });
|
|
295
|
+
withEditTxn(b1, "delete elem1 and insert new element with same code", (txn) => {
|
|
296
|
+
txn.deleteElement(elem1Id);
|
|
297
|
+
const newElemProps = {
|
|
298
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
299
|
+
model: drawingModelId,
|
|
300
|
+
category: drawingCategoryId,
|
|
301
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
|
|
302
|
+
};
|
|
303
|
+
newElemId = txn.insertElement(newElemProps);
|
|
304
|
+
});
|
|
305
|
+
b1.clearCaches();
|
|
306
|
+
// Verify after delete + insert
|
|
307
|
+
chai.expect(() => b1.elements.getElementProps(elem1Id)).to.throw();
|
|
308
|
+
const newElem = b1.elements.getElementProps(newElemId);
|
|
309
|
+
chai.expect(newElem.code.value).to.equal("CODE_A");
|
|
310
|
+
// --- Undo (reverse the delete + insert) ---
|
|
311
|
+
chai.expect(b1.txns.isUndoPossible).to.be.true;
|
|
312
|
+
b1.txns.reverseTxns(1);
|
|
313
|
+
e1 = b1.elements.getElementProps(elem1Id);
|
|
314
|
+
chai.expect(e1.code.value).to.equal("CODE_A");
|
|
315
|
+
chai.expect(() => b1.elements.getElementProps(newElemId)).to.throw();
|
|
316
|
+
// --- Redo (reinstate the delete + insert) ---
|
|
317
|
+
chai.expect(b1.txns.isRedoPossible).to.be.true;
|
|
318
|
+
b1.txns.reinstateTxn();
|
|
319
|
+
chai.expect(() => b1.elements.getElementProps(elem1Id)).to.throw();
|
|
320
|
+
const redoneElem = b1.elements.getElementProps(newElemId);
|
|
321
|
+
chai.expect(redoneElem.code.value).to.equal("CODE_A");
|
|
322
|
+
// --- Push changes ---
|
|
323
|
+
await b1.pushChanges({ description: "delete elem1 and insert new element with same code" });
|
|
324
|
+
await b2.pullChanges();
|
|
325
|
+
// No local txns remain after push
|
|
326
|
+
chai.expect(b1.txns.isUndoPossible).to.be.false;
|
|
327
|
+
chai.expect(b1.txns.isRedoPossible).to.be.false;
|
|
328
|
+
// Verify b2 sees the final state after pulling
|
|
329
|
+
chai.expect(() => b2.elements.getElementProps(elem1Id)).to.throw();
|
|
330
|
+
const b2NewElem = b2.elements.getElementProps(newElemId);
|
|
331
|
+
chai.expect(b2NewElem.code.value).to.equal("CODE_A");
|
|
332
|
+
}
|
|
333
|
+
finally {
|
|
334
|
+
b1?.close();
|
|
335
|
+
b2?.close();
|
|
336
|
+
HubMock.shutdown();
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
it("single-transaction code swap reverts to old behaviour (swap not applied) when noUpdateLoop is true", async () => {
|
|
340
|
+
HubMock.startup("CodeSwapNoUpdateLoopTest", KnownTestLocations.outputDir);
|
|
341
|
+
let b1;
|
|
342
|
+
let b2;
|
|
343
|
+
try {
|
|
344
|
+
// --- Setup iModel ---
|
|
345
|
+
const iModelId = await HubMock.createNewIModel({
|
|
346
|
+
accessToken: "user1",
|
|
347
|
+
iTwinId: HubMock.iTwinId,
|
|
348
|
+
iModelName: "CodeSwapNoUpdateLoopTest",
|
|
349
|
+
description: "CodeSwapNoUpdateLoopTest",
|
|
350
|
+
});
|
|
351
|
+
b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
|
|
352
|
+
b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
353
|
+
b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user2", iTwinId: HubMock.iTwinId, iModelId });
|
|
354
|
+
b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
|
|
355
|
+
let drawingModelId;
|
|
356
|
+
let drawingCategoryId;
|
|
357
|
+
let codeSpecId;
|
|
358
|
+
let codeScopeId;
|
|
359
|
+
await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
|
|
360
|
+
withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
|
|
361
|
+
const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
|
|
362
|
+
const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
|
|
363
|
+
drawingModelId = modelId;
|
|
364
|
+
let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
|
|
365
|
+
if (undefined === catId)
|
|
366
|
+
catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
|
|
367
|
+
drawingCategoryId = catId;
|
|
368
|
+
codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
|
|
369
|
+
codeScopeId = drawingModelId;
|
|
370
|
+
});
|
|
371
|
+
await b1.pushChanges({ description: "setup" });
|
|
372
|
+
await b2.pullChanges();
|
|
373
|
+
let elem1Id;
|
|
374
|
+
let elem2Id;
|
|
375
|
+
await b1.locks.acquireLocks({ shared: drawingModelId });
|
|
376
|
+
withEditTxn(b1, "insert two coded elements", (txn) => {
|
|
377
|
+
const elem1Props = {
|
|
378
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
379
|
+
model: drawingModelId,
|
|
380
|
+
category: drawingCategoryId,
|
|
381
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
|
|
382
|
+
};
|
|
383
|
+
const elem2Props = {
|
|
384
|
+
classFullName: "BisCore:DrawingGraphic",
|
|
385
|
+
model: drawingModelId,
|
|
386
|
+
category: drawingCategoryId,
|
|
387
|
+
code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" }),
|
|
388
|
+
};
|
|
389
|
+
elem1Id = txn.insertElement(elem1Props);
|
|
390
|
+
elem2Id = txn.insertElement(elem2Props);
|
|
391
|
+
});
|
|
392
|
+
await b1.pushChanges({ description: "insert two elements with CODE_A and CODE_B" });
|
|
393
|
+
await b2.pullChanges();
|
|
394
|
+
// --- Single-transaction swap: null elem1, then swap codes ---
|
|
395
|
+
await b1.locks.acquireLocks({ exclusive: [elem2Id, elem1Id] });
|
|
396
|
+
withEditTxn(b1, "single-txn swap", (txn) => {
|
|
397
|
+
// Step 1: null out elem1's code to free the uniqueness slot
|
|
398
|
+
const props = b1.elements.getElementProps(elem1Id);
|
|
399
|
+
props.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "" });
|
|
400
|
+
txn.updateElement(props);
|
|
401
|
+
// Step 2: assign CODE_A to elem2, CODE_B to elem1
|
|
402
|
+
const props1 = b1.elements.getElementProps(elem1Id);
|
|
403
|
+
const props2 = b1.elements.getElementProps(elem2Id);
|
|
404
|
+
props2.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" });
|
|
405
|
+
props1.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" });
|
|
406
|
+
txn.updateElement(props2);
|
|
407
|
+
txn.updateElement(props1);
|
|
408
|
+
});
|
|
409
|
+
// Verify b1 has the swapped state
|
|
410
|
+
b1.clearCaches();
|
|
411
|
+
const b1e1 = b1.elements.getElementProps(elem1Id);
|
|
412
|
+
const b1e2 = b1.elements.getElementProps(elem2Id);
|
|
413
|
+
chai.expect(b1e1.code.value).to.equal("CODE_B");
|
|
414
|
+
chai.expect(b1e2.code.value).to.equal("CODE_A");
|
|
415
|
+
await b1.pushChanges({ description: "single-txn code swap" });
|
|
416
|
+
await b2.pullChanges({ noUpdateLoop: true });
|
|
417
|
+
b2.clearCaches();
|
|
418
|
+
const b2e1 = b2.elements.getElementProps(elem1Id);
|
|
419
|
+
const b2e2 = b2.elements.getElementProps(elem2Id);
|
|
420
|
+
// Old behaviour: swap did NOT land — b2 still sees the original codes
|
|
421
|
+
chai.expect(b2e1.code.value).to.equal("CODE_A");
|
|
422
|
+
chai.expect(b2e2.code.value).to.equal("CODE_B");
|
|
423
|
+
}
|
|
424
|
+
finally {
|
|
425
|
+
b1?.close();
|
|
426
|
+
b2?.close();
|
|
427
|
+
HubMock.shutdown();
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
//# sourceMappingURL=CodeSwap.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CodeSwap.test.js","sourceRoot":"","sources":["../../../../src/test/hubaccess/CodeSwap.test.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n\nimport * as chai from \"chai\";\nimport * as chaiAsPromised from \"chai-as-promised\";\nimport { Suite } from \"mocha\";\nimport { Id64String } from \"@itwin/core-bentley\";\nimport { Code, CodeScopeSpec, GeometricElement2dProps, IModel, SubCategoryAppearance } from \"@itwin/core-common\";\nimport { HubWrappers, IModelTestUtils, KnownTestLocations } from \"..\";\nimport { BriefcaseDb, ChannelControl, DrawingCategory } from \"../../core-backend\";\nimport { HubMock } from \"../../internal/HubMock\";\nimport { withEditTxn } from \"../../EditTxn\";\n\nchai.use(chaiAsPromised);\n\ndescribe(\"Code value management: null, swap, undo/redo, and cross-briefcase pull\", function (this: Suite) {\n this.timeout(60000);\n\n it(\"swap codeValues between two elements sharing the same codeSpec and codeScope, verify undo/redo in a single transaction\", async () => {\n HubMock.startup(\"CodeSwapTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeSwapTest\",\n description: \"CodeSwapTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n // --- Create a drawing model + category and a CodeSpec ---\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String; // will be the drawing model\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n\n // --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---\n let elem1Id: Id64String;\n let elem2Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert two coded elements\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n const elem2Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n elem2Id = txn.insertElement(elem2Props);\n });\n\n await b1.pushChanges({ description: \"insert two elements with CODE_A and CODE_B\" });\n\n // Verify initial state\n let e1 = b1.elements.getElementProps(elem1Id!);\n let e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.spec).to.equal(codeSpecId!);\n chai.expect(e1.code.scope).to.equal(codeScopeId!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(e2.code.spec).to.equal(codeSpecId!);\n chai.expect(e2.code.scope).to.equal(codeScopeId!);\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n await b1.locks.acquireLocks({ exclusive: [elem2Id!, elem1Id!] });\n withEditTxn(b1, \"swap code values\", (txn) => {\n const props = b1!.elements.getElementProps(elem1Id!);\n props.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"\" });\n txn.updateElement(props);\n\n const props1 = b1!.elements.getElementProps(elem1Id!);\n const props2 = b1!.elements.getElementProps(elem2Id!);\n\n props2.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" });\n props1.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" });\n\n txn.updateElement(props2);\n txn.updateElement(props1);\n });\n b1.clearCaches();\n // Verify after swap2\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n\n // --- Undo (reverse txn2: the swap) ---\n chai.expect(b1.txns.isUndoPossible).to.be.true;\n b1.txns.reverseTxns(1);\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n // --- Redo (reinstate txn2: the swap) ---\n chai.expect(b1.txns.isRedoPossible).to.be.true;\n b1.txns.reinstateTxn();\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n\n } finally {\n b1?.close();\n HubMock.shutdown();\n }\n });\n it(\"insert two elements with same codeSpecId and codeScopeId, null first code, swap codes, undo/redo, push\", async () => {\n HubMock.startup(\"CodeSwapTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n let b2: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeSwapTest\",\n description: \"CodeSwapTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user2\", iTwinId: HubMock.iTwinId, iModelId });\n b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n // --- Create a drawing model + category and a CodeSpec ---\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String; // will be the drawing model\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n await b2.pullChanges();\n\n // --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---\n let elem1Id: Id64String;\n let elem2Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert two coded elements\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n const elem2Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n elem2Id = txn.insertElement(elem2Props);\n });\n\n await b1.pushChanges({ description: \"insert two elements with CODE_A and CODE_B\" });\n await b2.pullChanges();\n\n // Verify initial state\n let e1 = b1.elements.getElementProps(elem1Id!);\n let e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.spec).to.equal(codeSpecId!);\n chai.expect(e1.code.scope).to.equal(codeScopeId!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(e2.code.spec).to.equal(codeSpecId!);\n chai.expect(e2.code.scope).to.equal(codeScopeId!);\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n // --- Step 1 (txn1): Set codeValue of elem1 to empty (null-equivalent) ---\n await b1.locks.acquireLocks({ exclusive: [elem1Id!] });\n withEditTxn(b1, \"set elem1 code to null\", (txn) => {\n const props = b1!.elements.getElementProps(elem1Id!);\n props.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"\" });\n txn.updateElement(props);\n });\n\n e1 = b1.elements.getElementProps(elem1Id!);\n chai.expect(e1.code.value).to.satisfy((v: string | undefined) => v === undefined || v === \"\");\n\n // --- Step 2 (txn2): Swap codeValues — elem1 gets CODE_B, elem2 gets CODE_A ---\n // elem1's intermediate empty code value avoids a uniqueness conflict while assigning CODE_A to elem2.\n await b1.locks.acquireLocks({ exclusive: [elem2Id!] });\n withEditTxn(b1, \"swap code values\", (txn) => {\n const props1 = b1!.elements.getElementProps(elem1Id!);\n const props2 = b1!.elements.getElementProps(elem2Id!);\n\n props2.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" });\n props1.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" });\n\n txn.updateElement(props2);\n txn.updateElement(props1);\n });\n b1.clearCaches();\n // Verify after swap\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n\n // --- Undo (reverse txn2: the swap) ---\n chai.expect(b1.txns.isUndoPossible).to.be.true;\n b1.txns.reverseTxns(1);\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.satisfy((v: string | undefined) => v === undefined || v === \"\");\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n // --- Redo (reinstate txn2: the swap) ---\n chai.expect(b1.txns.isRedoPossible).to.be.true;\n b1.txns.reinstateTxn();\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n // --- Push changes ---\n await b1.pushChanges({ description: \"null elem1 code and swap code values\" });\n await b2.pullChanges();\n\n // No local txns remain after push\n chai.expect(b1.txns.isUndoPossible).to.be.false;\n chai.expect(b1.txns.isRedoPossible).to.be.false;\n\n // Verify b2 sees the final swapped code values after pulling\n const b2e1 = b2.elements.getElementProps(elem1Id!);\n const b2e2 = b2.elements.getElementProps(elem2Id!);\n chai.expect(b2e1.code.value).to.equal(\"CODE_B\");\n chai.expect(b2e2.code.value).to.equal(\"CODE_A\");\n\n } finally {\n b1?.close();\n b2?.close();\n HubMock.shutdown();\n }\n });\n it(\"delete element with existing code and insert new element with same code, verify undo/redo, push, and b2 sees final state after pull\", async () => {\n HubMock.startup(\"CodeDeleteInsertTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n let b2: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeDeleteInsertTest\",\n description: \"CodeDeleteInsertTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user2\", iTwinId: HubMock.iTwinId, iModelId });\n b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n // --- Create a drawing model + category and a CodeSpec ---\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String;\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n await b2.pullChanges();\n\n // --- Insert an element with a code ---\n let elem1Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert element with CODE_A\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n });\n\n await b1.pushChanges({ description: \"insert element with CODE_A\" });\n await b2.pullChanges();\n\n // Verify initial state\n let e1 = b1.elements.getElementProps(elem1Id!);\n chai.expect(e1.code.spec).to.equal(codeSpecId!);\n chai.expect(e1.code.scope).to.equal(codeScopeId!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n\n // --- Delete elem1 and insert a new element with the same code ---\n let newElemId: Id64String;\n\n await b1.locks.acquireLocks({ exclusive: [elem1Id!] });\n withEditTxn(b1, \"delete elem1 and insert new element with same code\", (txn) => {\n txn.deleteElement(elem1Id!);\n\n const newElemProps: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n newElemId = txn.insertElement(newElemProps);\n });\n\n b1.clearCaches();\n\n // Verify after delete + insert\n chai.expect(() => b1!.elements.getElementProps(elem1Id!)).to.throw();\n const newElem = b1.elements.getElementProps(newElemId!);\n chai.expect(newElem.code.value).to.equal(\"CODE_A\");\n\n // --- Undo (reverse the delete + insert) ---\n chai.expect(b1.txns.isUndoPossible).to.be.true;\n b1.txns.reverseTxns(1);\n\n e1 = b1.elements.getElementProps(elem1Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(() => b1!.elements.getElementProps(newElemId!)).to.throw();\n\n // --- Redo (reinstate the delete + insert) ---\n chai.expect(b1.txns.isRedoPossible).to.be.true;\n b1.txns.reinstateTxn();\n\n chai.expect(() => b1!.elements.getElementProps(elem1Id!)).to.throw();\n const redoneElem = b1.elements.getElementProps(newElemId!);\n chai.expect(redoneElem.code.value).to.equal(\"CODE_A\");\n\n // --- Push changes ---\n await b1.pushChanges({ description: \"delete elem1 and insert new element with same code\" });\n await b2.pullChanges();\n\n // No local txns remain after push\n chai.expect(b1.txns.isUndoPossible).to.be.false;\n chai.expect(b1.txns.isRedoPossible).to.be.false;\n\n // Verify b2 sees the final state after pulling\n chai.expect(() => b2!.elements.getElementProps(elem1Id!)).to.throw();\n const b2NewElem = b2.elements.getElementProps(newElemId!);\n chai.expect(b2NewElem.code.value).to.equal(\"CODE_A\");\n\n } finally {\n b1?.close();\n b2?.close();\n HubMock.shutdown();\n }\n });\n\n it(\"single-transaction code swap reverts to old behaviour (swap not applied) when noUpdateLoop is true\", async () => {\n HubMock.startup(\"CodeSwapNoUpdateLoopTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n let b2: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeSwapNoUpdateLoopTest\",\n description: \"CodeSwapNoUpdateLoopTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user2\", iTwinId: HubMock.iTwinId, iModelId });\n b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String;\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n await b2.pullChanges();\n\n let elem1Id: Id64String;\n let elem2Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert two coded elements\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n const elem2Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n elem2Id = txn.insertElement(elem2Props);\n });\n\n await b1.pushChanges({ description: \"insert two elements with CODE_A and CODE_B\" });\n await b2.pullChanges();\n\n // --- Single-transaction swap: null elem1, then swap codes ---\n await b1.locks.acquireLocks({ exclusive: [elem2Id!, elem1Id!] });\n withEditTxn(b1, \"single-txn swap\", (txn) => {\n // Step 1: null out elem1's code to free the uniqueness slot\n const props = b1!.elements.getElementProps(elem1Id!);\n props.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"\" });\n txn.updateElement(props);\n\n // Step 2: assign CODE_A to elem2, CODE_B to elem1\n const props1 = b1!.elements.getElementProps(elem1Id!);\n const props2 = b1!.elements.getElementProps(elem2Id!);\n props2.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" });\n props1.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" });\n txn.updateElement(props2);\n txn.updateElement(props1);\n });\n\n // Verify b1 has the swapped state\n b1.clearCaches();\n const b1e1 = b1.elements.getElementProps(elem1Id!);\n const b1e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(b1e1.code.value).to.equal(\"CODE_B\");\n chai.expect(b1e2.code.value).to.equal(\"CODE_A\");\n\n await b1.pushChanges({ description: \"single-txn code swap\" });\n\n // Pull with noUpdateLoop: true — this uses the old (pre-fix) apply path.\n // The unique-index conflict handler skips the intermediate null step,\n // so the swap is silently dropped and b2 retains the original codes.\n // noUpdateLoop is intentionally not in the public PullChangesArgs type.\n type InternalPullArgs = Parameters<BriefcaseDb[\"pullChanges\"]>[0] & { noUpdateLoop?: boolean };\n await b2.pullChanges({ noUpdateLoop: true } as InternalPullArgs);\n\n b2.clearCaches();\n const b2e1 = b2.elements.getElementProps(elem1Id!);\n const b2e2 = b2.elements.getElementProps(elem2Id!);\n // Old behaviour: swap did NOT land — b2 still sees the original codes\n chai.expect(b2e1.code.value).to.equal(\"CODE_A\");\n chai.expect(b2e2.code.value).to.equal(\"CODE_B\");\n\n } finally {\n b1?.close();\n b2?.close();\n HubMock.shutdown();\n }\n });\n});\n"]}
|
|
@@ -68,7 +68,7 @@ import { BriefcaseDb } from "../../IModelDb";
|
|
|
68
68
|
import { HubMock } from "../../internal/HubMock";
|
|
69
69
|
import { KnownTestLocations } from "../KnownTestLocations";
|
|
70
70
|
import { IModelTestUtils } from "../IModelTestUtils";
|
|
71
|
-
import { DisableNativeAssertions } from "../TestUtils";
|
|
71
|
+
import { DisableNativeAssertions, TestUtils } from "../TestUtils";
|
|
72
72
|
import { samplePngTexture } from "../imageData";
|
|
73
73
|
import { performance } from "perf_hooks";
|
|
74
74
|
import { _cache, _hubAccess, _instanceKeyCache } from "../../internal/Symbols";
|
|
@@ -106,6 +106,11 @@ describe("iModel", () => {
|
|
|
106
106
|
let originalEnv;
|
|
107
107
|
before(async () => {
|
|
108
108
|
originalEnv = { ...process.env };
|
|
109
|
+
// Several tests in this suite (ECEF location, geolocation, and coordinate reprojection) rely on
|
|
110
|
+
// GCS data loaded from cloud workspaces, so restart the backend with GCS workspaces enabled
|
|
111
|
+
// (the test harness disables them by default to avoid network calls).
|
|
112
|
+
await TestUtils.shutdownBackend();
|
|
113
|
+
await TestUtils.startBackend({ loadGcsWorkspaces: true });
|
|
109
114
|
IModelTestUtils.registerTestBimSchema();
|
|
110
115
|
imodel1 = await generateTestSnapshot("test.bim", "test.bim");
|
|
111
116
|
imodel2 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "CompatibilityTestSeed.bim"), IModelTestUtils.resolveAssetFile("CompatibilityTestSeed.bim"));
|
|
@@ -113,13 +118,16 @@ describe("iModel", () => {
|
|
|
113
118
|
imodel4 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "GetSetAutoHandledArrayProperties.bim"), IModelTestUtils.resolveAssetFile("GetSetAutoHandledArrayProperties.bim"));
|
|
114
119
|
imodel5 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "mirukuru.ibim"), IModelTestUtils.resolveAssetFile("mirukuru.ibim"));
|
|
115
120
|
});
|
|
116
|
-
after(() => {
|
|
121
|
+
after(async () => {
|
|
117
122
|
process.env = originalEnv;
|
|
118
123
|
imodel1.close();
|
|
119
124
|
imodel2.close();
|
|
120
125
|
imodel3.close();
|
|
121
126
|
imodel4.close();
|
|
122
127
|
imodel5.close();
|
|
128
|
+
// Restore the default test backend so subsequent test suites aren't left with GCS enabled.
|
|
129
|
+
await TestUtils.shutdownBackend();
|
|
130
|
+
await TestUtils.startBackend();
|
|
123
131
|
});
|
|
124
132
|
afterEach(() => {
|
|
125
133
|
sinon.restore();
|