@itwin/core-backend 5.15.0-dev.15 → 5.15.0-dev.17
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 +11 -1
- package/lib/cjs/CloudSqlite.d.ts +15 -0
- package/lib/cjs/CloudSqlite.d.ts.map +1 -1
- package/lib/cjs/CloudSqlite.js.map +1 -1
- package/lib/cjs/EditTxn.d.ts +5 -0
- package/lib/cjs/EditTxn.d.ts.map +1 -1
- package/lib/cjs/EditTxn.js +30 -10
- package/lib/cjs/EditTxn.js.map +1 -1
- package/lib/cjs/GeoCoordConfig.d.ts +1 -0
- package/lib/cjs/GeoCoordConfig.d.ts.map +1 -1
- package/lib/cjs/GeoCoordConfig.js +12 -8
- package/lib/cjs/GeoCoordConfig.js.map +1 -1
- package/lib/cjs/IModelDb.d.ts +14 -6
- package/lib/cjs/IModelDb.d.ts.map +1 -1
- package/lib/cjs/IModelDb.js +34 -4
- package/lib/cjs/IModelDb.js.map +1 -1
- package/lib/cjs/TxnManager.d.ts.map +1 -1
- package/lib/cjs/TxnManager.js +5 -1
- package/lib/cjs/TxnManager.js.map +1 -1
- package/lib/cjs/ViewStateHydrator.d.ts.map +1 -1
- package/lib/cjs/ViewStateHydrator.js.map +1 -1
- package/lib/cjs/internal/Symbols.d.ts +3 -0
- package/lib/cjs/internal/Symbols.d.ts.map +1 -1
- package/lib/cjs/internal/Symbols.js +4 -1
- package/lib/cjs/internal/Symbols.js.map +1 -1
- package/lib/cjs/internal/workspace/WorkspaceImpl.d.ts.map +1 -1
- package/lib/cjs/internal/workspace/WorkspaceImpl.js +20 -6
- package/lib/cjs/internal/workspace/WorkspaceImpl.js.map +1 -1
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts +6 -6
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
- package/lib/cjs/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/esm/CloudSqlite.d.ts +15 -0
- package/lib/esm/CloudSqlite.d.ts.map +1 -1
- package/lib/esm/CloudSqlite.js.map +1 -1
- package/lib/esm/EditTxn.d.ts +5 -0
- package/lib/esm/EditTxn.d.ts.map +1 -1
- package/lib/esm/EditTxn.js +32 -12
- package/lib/esm/EditTxn.js.map +1 -1
- package/lib/esm/GeoCoordConfig.d.ts +1 -0
- package/lib/esm/GeoCoordConfig.d.ts.map +1 -1
- package/lib/esm/GeoCoordConfig.js +12 -8
- package/lib/esm/GeoCoordConfig.js.map +1 -1
- package/lib/esm/IModelDb.d.ts +14 -6
- package/lib/esm/IModelDb.d.ts.map +1 -1
- package/lib/esm/IModelDb.js +36 -6
- package/lib/esm/IModelDb.js.map +1 -1
- package/lib/esm/TxnManager.d.ts.map +1 -1
- package/lib/esm/TxnManager.js +6 -2
- package/lib/esm/TxnManager.js.map +1 -1
- package/lib/esm/ViewStateHydrator.d.ts.map +1 -1
- package/lib/esm/ViewStateHydrator.js.map +1 -1
- package/lib/esm/internal/Symbols.d.ts +3 -0
- package/lib/esm/internal/Symbols.d.ts.map +1 -1
- package/lib/esm/internal/Symbols.js +3 -0
- package/lib/esm/internal/Symbols.js.map +1 -1
- package/lib/esm/internal/workspace/WorkspaceImpl.d.ts.map +1 -1
- package/lib/esm/internal/workspace/WorkspaceImpl.js +20 -6
- package/lib/esm/internal/workspace/WorkspaceImpl.js.map +1 -1
- package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts +6 -6
- package/lib/esm/rpc-impl/IModelReadRpcImpl.d.ts.map +1 -1
- package/lib/esm/rpc-impl/IModelReadRpcImpl.js.map +1 -1
- package/lib/esm/test/imodel/IModelGeolocation.test.js +8 -6
- package/lib/esm/test/imodel/IModelGeolocation.test.js.map +1 -1
- package/lib/esm/test/imodel/IModelProps.test.d.ts +2 -0
- package/lib/esm/test/imodel/IModelProps.test.d.ts.map +1 -0
- package/lib/esm/test/imodel/IModelProps.test.js +411 -0
- package/lib/esm/test/imodel/IModelProps.test.js.map +1 -0
- package/lib/esm/test/standalone/EditTxn.test.js +72 -1
- package/lib/esm/test/standalone/EditTxn.test.js.map +1 -1
- package/lib/esm/test/standalone/GeoCoordConfig.test.d.ts +2 -0
- package/lib/esm/test/standalone/GeoCoordConfig.test.d.ts.map +1 -0
- package/lib/esm/test/standalone/GeoCoordConfig.test.js +158 -0
- package/lib/esm/test/standalone/GeoCoordConfig.test.js.map +1 -0
- package/package.json +17 -17
|
@@ -0,0 +1,411 @@
|
|
|
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 { IModelStatus, Logger, OpenMode, UnexpectedErrors } from "@itwin/core-bentley";
|
|
6
|
+
import { Cartographic, EcefLocation, GeographicCRS } from "@itwin/core-common";
|
|
7
|
+
import { Range3d } from "@itwin/core-geometry";
|
|
8
|
+
import { expect } from "chai";
|
|
9
|
+
import * as sinon from "sinon";
|
|
10
|
+
import { BackendLoggerCategory, EditTxn, getAvailableVerticalCoordinateReferenceSystems, IModelJsFs, StandaloneDb, withEditTxn } from "../../core-backend";
|
|
11
|
+
import { _nativeDb, _refreshIModelProps } from "../../internal/Symbols";
|
|
12
|
+
import { IModelTestUtils } from "../IModelTestUtils";
|
|
13
|
+
import { TestUtils } from "../TestUtils";
|
|
14
|
+
import { getIModelError } from "./IModelTestFixtures";
|
|
15
|
+
describe("iModel property updates", () => {
|
|
16
|
+
let iModel;
|
|
17
|
+
let fileName;
|
|
18
|
+
const initialGcs = { horizontalCRS: { id: "LL84" }, verticalCRS: { id: "ELLIPSOID" } };
|
|
19
|
+
const changedGcs = {
|
|
20
|
+
...initialGcs,
|
|
21
|
+
additionalTransform: { helmert2DWithZOffset: { translationX: 1, translationY: 2, translationZ: 3, rotDeg: 0, scale: 1 } },
|
|
22
|
+
};
|
|
23
|
+
const extents = Range3d.createXYZXYZ(-10, -10, -10, 10, 10, 10);
|
|
24
|
+
const changedGcsJson = new GeographicCRS(changedGcs).toJSON();
|
|
25
|
+
before(async () => {
|
|
26
|
+
await TestUtils.shutdownBackend();
|
|
27
|
+
await TestUtils.startBackend({ loadGcsWorkspaces: true });
|
|
28
|
+
});
|
|
29
|
+
after(async () => {
|
|
30
|
+
await TestUtils.shutdownBackend();
|
|
31
|
+
await TestUtils.startBackend();
|
|
32
|
+
});
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
fileName = IModelTestUtils.prepareOutputFile("IModelProps", "IModelProps.bim");
|
|
35
|
+
iModel = StandaloneDb.createEmpty(fileName, { rootSubject: { name: "IModelProps" }, enableTransactions: true });
|
|
36
|
+
withEditTxn(iModel, (txn) => {
|
|
37
|
+
iModel.setGeographicCoordinateSystem(initialGcs);
|
|
38
|
+
txn.updateIModelProps();
|
|
39
|
+
});
|
|
40
|
+
expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem?.verticalCRS?.id).to.equal("ELLIPSOID");
|
|
41
|
+
});
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
sinon.restore();
|
|
44
|
+
if (iModel.isOpen)
|
|
45
|
+
iModel.close();
|
|
46
|
+
IModelJsFs.removeSync(fileName);
|
|
47
|
+
});
|
|
48
|
+
it("omits unchanged GCS data from general, extents, and ECEF updates", () => {
|
|
49
|
+
const update = sinon.spy(iModel[_nativeDb], "updateIModelProps");
|
|
50
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
51
|
+
iModel.setGeographicCoordinateSystem(initialGcs);
|
|
52
|
+
const ecef = EcefLocation.createFromCartographicOrigin(Cartographic.fromDegrees({ longitude: 0, latitude: 0, height: 0 }));
|
|
53
|
+
withEditTxn(iModel, (txn) => {
|
|
54
|
+
txn.updateIModelProps();
|
|
55
|
+
txn.updateProjectExtents(extents);
|
|
56
|
+
txn.updateEcefLocation(ecef);
|
|
57
|
+
});
|
|
58
|
+
expect(update.callCount).to.equal(3);
|
|
59
|
+
for (const call of update.getCalls())
|
|
60
|
+
expect(call.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
61
|
+
expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem).to.deep.equal(originalGcs);
|
|
62
|
+
expect(iModel[_nativeDb].getIModelProps().projectExtents).to.deep.equal(extents.toJSON());
|
|
63
|
+
});
|
|
64
|
+
it("preserves full named vertical metadata without asking native code to resolve it again", () => {
|
|
65
|
+
const namedGcs = {
|
|
66
|
+
horizontalCRS: { id: "LL84" },
|
|
67
|
+
verticalCRS: { id: "GEOID", crsName: "EGM96 height" },
|
|
68
|
+
};
|
|
69
|
+
withEditTxn(iModel, (txn) => {
|
|
70
|
+
iModel.setGeographicCoordinateSystem(namedGcs);
|
|
71
|
+
txn.updateIModelProps();
|
|
72
|
+
});
|
|
73
|
+
iModel[_refreshIModelProps]();
|
|
74
|
+
expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal("EGM96 height");
|
|
75
|
+
const property = { namespace: "dgn_Db", name: "DgnGCSVerticalCRS" };
|
|
76
|
+
const storedDefinition = iModel.queryFilePropertyString(property);
|
|
77
|
+
expect(storedDefinition).to.be.a("string").and.to.contain('"transforms"');
|
|
78
|
+
const nativeUpdate = iModel[_nativeDb].updateIModelProps.bind(iModel[_nativeDb]);
|
|
79
|
+
const update = sinon.stub(iModel[_nativeDb], "updateIModelProps").callsFake((props) => {
|
|
80
|
+
if (props.geographicCoordinateSystem)
|
|
81
|
+
throw new Error("GCS dictionary unavailable");
|
|
82
|
+
nativeUpdate(props);
|
|
83
|
+
});
|
|
84
|
+
withEditTxn(iModel, (txn) => {
|
|
85
|
+
txn.updateIModelProps();
|
|
86
|
+
txn.updateProjectExtents(extents);
|
|
87
|
+
txn.updateEcefLocation(EcefLocation.createFromCartographicOrigin(Cartographic.fromDegrees({ longitude: 0, latitude: 0, height: 0 })));
|
|
88
|
+
});
|
|
89
|
+
expect(update.callCount).to.equal(3);
|
|
90
|
+
for (const call of update.getCalls())
|
|
91
|
+
expect(call.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
92
|
+
expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);
|
|
93
|
+
iModel.close();
|
|
94
|
+
iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);
|
|
95
|
+
expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal("EGM96 height");
|
|
96
|
+
expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);
|
|
97
|
+
});
|
|
98
|
+
it("preserves an inline named vertical definition that is absent from the dictionary", () => {
|
|
99
|
+
const crsName = "EditTxn custom height";
|
|
100
|
+
expect(getAvailableVerticalCoordinateReferenceSystems().some((entry) => entry.crsName === crsName)).to.be.false;
|
|
101
|
+
const customGcs = {
|
|
102
|
+
horizontalCRS: { id: "LL84" },
|
|
103
|
+
verticalCRS: {
|
|
104
|
+
id: "ELLIPSOID",
|
|
105
|
+
crsName,
|
|
106
|
+
datumName: crsName,
|
|
107
|
+
type: "ELLIPSOID",
|
|
108
|
+
units: "Meter",
|
|
109
|
+
extent: { southWest: { latitude: -90, longitude: -180 }, northEast: { latitude: 90, longitude: 180 } },
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
withEditTxn(iModel, () => {
|
|
113
|
+
iModel[_nativeDb].updateIModelProps({ ...iModel.toJSON(), geographicCoordinateSystem: customGcs });
|
|
114
|
+
});
|
|
115
|
+
iModel[_refreshIModelProps]();
|
|
116
|
+
expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal(crsName);
|
|
117
|
+
const property = { namespace: "dgn_Db", name: "DgnGCSVerticalCRS" };
|
|
118
|
+
const storedDefinition = iModel.queryFilePropertyString(property);
|
|
119
|
+
expect(storedDefinition).to.be.a("string").and.to.contain(crsName);
|
|
120
|
+
withEditTxn(iModel, (txn) => {
|
|
121
|
+
txn.updateIModelProps();
|
|
122
|
+
txn.updateProjectExtents(extents);
|
|
123
|
+
txn.updateEcefLocation(EcefLocation.createFromCartographicOrigin(Cartographic.fromDegrees({ longitude: 0, latitude: 0, height: 0 })));
|
|
124
|
+
});
|
|
125
|
+
expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);
|
|
126
|
+
iModel.close();
|
|
127
|
+
iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);
|
|
128
|
+
expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal(crsName);
|
|
129
|
+
expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);
|
|
130
|
+
});
|
|
131
|
+
it("writes a pending GCS change through convenience methods only once", () => {
|
|
132
|
+
const update = sinon.spy(iModel[_nativeDb], "updateIModelProps");
|
|
133
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
134
|
+
withEditTxn(iModel, (txn) => {
|
|
135
|
+
txn.updateProjectExtents(extents);
|
|
136
|
+
txn.updateIModelProps();
|
|
137
|
+
txn.saveChanges();
|
|
138
|
+
txn.updateProjectExtents(extents);
|
|
139
|
+
});
|
|
140
|
+
expect(update.firstCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);
|
|
141
|
+
expect(update.secondCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
142
|
+
expect(update.thirdCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
143
|
+
expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem?.additionalTransform).to.deep.equal(changedGcs.additionalTransform);
|
|
144
|
+
});
|
|
145
|
+
it("restores the GCS and comparison baseline after abandoning an update", () => {
|
|
146
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
147
|
+
expect(originalGcs).not.to.equal(undefined);
|
|
148
|
+
const txn = new EditTxn(iModel, "abandon GCS update");
|
|
149
|
+
txn.start();
|
|
150
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
151
|
+
txn.updateIModelProps();
|
|
152
|
+
txn.abandonChanges();
|
|
153
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());
|
|
154
|
+
const update = sinon.spy(iModel[_nativeDb], "updateIModelProps");
|
|
155
|
+
txn.updateProjectExtents(extents);
|
|
156
|
+
expect(update.firstCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
157
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
158
|
+
txn.updateIModelProps();
|
|
159
|
+
expect(update.secondCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);
|
|
160
|
+
txn.end("abandon");
|
|
161
|
+
});
|
|
162
|
+
it("does not treat a rejected native update as persisted", () => {
|
|
163
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
164
|
+
const nativeUpdate = iModel[_nativeDb].updateIModelProps.bind(iModel[_nativeDb]);
|
|
165
|
+
const update = sinon.stub(iModel[_nativeDb], "updateIModelProps");
|
|
166
|
+
update.onFirstCall().throws(new Error("GCS update rejected"));
|
|
167
|
+
update.onSecondCall().callsFake(nativeUpdate);
|
|
168
|
+
withEditTxn(iModel, (txn) => {
|
|
169
|
+
expect(() => txn.updateIModelProps()).to.throw("GCS update rejected");
|
|
170
|
+
txn.updateIModelProps();
|
|
171
|
+
});
|
|
172
|
+
expect(update.firstCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);
|
|
173
|
+
expect(update.secondCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);
|
|
174
|
+
expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem?.additionalTransform).to.deep.equal(changedGcs.additionalTransform);
|
|
175
|
+
});
|
|
176
|
+
it("rejects reentrant explicit and implicit writes while restoring an abandoned GCS", () => {
|
|
177
|
+
sinon.stub(EditTxn, "implicitWriteEnforcement").value("allow");
|
|
178
|
+
const unexpectedError = sinon.spy(UnexpectedErrors, "handle");
|
|
179
|
+
const originalProps = iModel[_nativeDb].getIModelProps();
|
|
180
|
+
const failure = new Error("abandon this edit");
|
|
181
|
+
const listener = sinon.spy(() => {
|
|
182
|
+
expect(() => iModel.getIndirectTxn().updateIModelProps()).to.throw("Cannot write while restoring the geographic coordinate system");
|
|
183
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- Exercise the legacy implicit write path.
|
|
184
|
+
expect(() => iModel.updateIModelProps()).to.throw("Cannot write while restoring the geographic coordinate system");
|
|
185
|
+
});
|
|
186
|
+
expect(() => withEditTxn(iModel, (txn) => {
|
|
187
|
+
iModel.rootSubject = { name: "Abandoned root subject" };
|
|
188
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
189
|
+
txn.updateIModelProps();
|
|
190
|
+
iModel.onGeographicCoordinateSystemChanged.addListener(listener);
|
|
191
|
+
throw failure;
|
|
192
|
+
})).to.throw(failure);
|
|
193
|
+
expect(listener.calledOnce).to.be.true;
|
|
194
|
+
expect(unexpectedError.called).to.be.false;
|
|
195
|
+
expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;
|
|
196
|
+
expect(iModel[_nativeDb].getIModelProps().rootSubject).to.deep.equal(originalProps.rootSubject);
|
|
197
|
+
iModel.close();
|
|
198
|
+
iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);
|
|
199
|
+
expect(iModel.rootSubject).to.deep.equal(originalProps.rootSubject);
|
|
200
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalProps.geographicCoordinateSystem).toJSON());
|
|
201
|
+
});
|
|
202
|
+
it("releases the transaction and write guard when a rollback listener throws", () => {
|
|
203
|
+
const unexpectedError = sinon.stub(UnexpectedErrors, "handle");
|
|
204
|
+
const txn = new EditTxn(iModel, "rollback listener");
|
|
205
|
+
txn.start();
|
|
206
|
+
iModel.rootSubject = { name: "Abandoned root subject" };
|
|
207
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
208
|
+
txn.updateIModelProps();
|
|
209
|
+
const listener = sinon.spy(() => iModel.getIndirectTxn().updateIModelProps());
|
|
210
|
+
const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);
|
|
211
|
+
try {
|
|
212
|
+
txn.end("abandon");
|
|
213
|
+
expect(listener.threw()).to.be.true;
|
|
214
|
+
expect(listener.firstCall.exception).to.have.property("message", "Cannot write while restoring the geographic coordinate system");
|
|
215
|
+
expect(unexpectedError.calledOnceWithExactly(listener.firstCall.exception)).to.be.true;
|
|
216
|
+
expect(txn.isActive).to.be.false;
|
|
217
|
+
expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
removeListener();
|
|
221
|
+
}
|
|
222
|
+
withEditTxn(iModel, (nextTxn) => nextTxn.saveFileProperty({ namespace: "test", name: "after-rollback" }, "allowed"));
|
|
223
|
+
iModel.close();
|
|
224
|
+
iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);
|
|
225
|
+
expect(iModel.rootSubject.name).to.equal("IModelProps");
|
|
226
|
+
expect(iModel.queryFilePropertyString({ namespace: "test", name: "after-rollback" })).to.equal("allowed");
|
|
227
|
+
});
|
|
228
|
+
it("rejects scoped transaction activation during undo abandonment without retaining ownership", () => {
|
|
229
|
+
sinon.stub(EditTxn, "implicitWriteEnforcement").value("allow");
|
|
230
|
+
const unexpectedError = sinon.stub(UnexpectedErrors, "handle");
|
|
231
|
+
iModel[_nativeDb].deleteAllTxns();
|
|
232
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
233
|
+
iModel.getIndirectTxn().updateIModelProps();
|
|
234
|
+
const edit = sinon.spy((txn) => txn.updateIModelProps());
|
|
235
|
+
const listener = sinon.spy(() => withEditTxn(iModel, edit));
|
|
236
|
+
const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);
|
|
237
|
+
try {
|
|
238
|
+
expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.NothingToUndo);
|
|
239
|
+
expect(listener.calledOnce).to.be.true;
|
|
240
|
+
expect(unexpectedError.calledOnceWithExactly(listener.firstCall.exception)).to.be.true;
|
|
241
|
+
expect(listener.firstCall.exception).to.have.property("message", "Cannot write while restoring the geographic coordinate system");
|
|
242
|
+
expect(edit.called).to.be.false;
|
|
243
|
+
expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
removeListener();
|
|
247
|
+
}
|
|
248
|
+
withEditTxn(iModel, (txn) => txn.saveFileProperty({ namespace: "test", name: "after-listener-start" }, "allowed"));
|
|
249
|
+
expect(iModel.queryFilePropertyString({ namespace: "test", name: "after-listener-start" })).to.equal("allowed");
|
|
250
|
+
});
|
|
251
|
+
for (const endTxn of [false, true]) {
|
|
252
|
+
it(`${endTxn ? "releases" : "retains"} transaction ownership when a rollback notification propagates`, () => {
|
|
253
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
254
|
+
const txn = new EditTxn(iModel, "propagating rollback listener");
|
|
255
|
+
txn.start();
|
|
256
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
257
|
+
txn.updateIModelProps();
|
|
258
|
+
const listener = sinon.spy(() => txn.updateIModelProps());
|
|
259
|
+
const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);
|
|
260
|
+
const previousHandler = UnexpectedErrors.setHandler(UnexpectedErrors.reThrowImmediate);
|
|
261
|
+
try {
|
|
262
|
+
expect(() => endTxn ? txn.end("abandon") : txn.abandonChanges()).to.throw("Cannot write while restoring the geographic coordinate system");
|
|
263
|
+
expect(listener.calledOnce).to.be.true;
|
|
264
|
+
expect(txn.isActive).to.equal(!endTxn);
|
|
265
|
+
expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;
|
|
266
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
UnexpectedErrors.setHandler(previousHandler);
|
|
270
|
+
removeListener();
|
|
271
|
+
}
|
|
272
|
+
if (!endTxn)
|
|
273
|
+
txn.end("save");
|
|
274
|
+
withEditTxn(iModel, (nextTxn) => nextTxn.saveFileProperty({ namespace: "test", name: "after-propagated-notification" }, "allowed"));
|
|
275
|
+
expect(iModel.queryFilePropertyString({ namespace: "test", name: "after-propagated-notification" })).to.equal("allowed");
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
for (const asynchronous of [false, true]) {
|
|
279
|
+
it(`preserves the ${asynchronous ? "asynchronous" : "synchronous"} callback error when a rollback notification propagates`, async () => {
|
|
280
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
281
|
+
const failure = new Error("initiating edit failure");
|
|
282
|
+
const logError = sinon.spy(Logger, "logError");
|
|
283
|
+
let activeTxn;
|
|
284
|
+
let removeListener;
|
|
285
|
+
const listener = sinon.spy(() => iModel.getIndirectTxn().updateIModelProps());
|
|
286
|
+
const edit = (txn) => {
|
|
287
|
+
activeTxn = txn;
|
|
288
|
+
iModel.rootSubject = { name: "Abandoned root subject" };
|
|
289
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
290
|
+
txn.updateIModelProps();
|
|
291
|
+
removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);
|
|
292
|
+
throw failure;
|
|
293
|
+
};
|
|
294
|
+
const previousHandler = UnexpectedErrors.setHandler(UnexpectedErrors.reThrowImmediate);
|
|
295
|
+
try {
|
|
296
|
+
if (asynchronous) {
|
|
297
|
+
const error = await withEditTxn(iModel, async (txn) => {
|
|
298
|
+
await Promise.resolve();
|
|
299
|
+
edit(txn);
|
|
300
|
+
}).catch((err) => err);
|
|
301
|
+
expect(error).to.equal(failure);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
expect(() => withEditTxn(iModel, edit)).to.throw(failure);
|
|
305
|
+
}
|
|
306
|
+
expect(listener.calledOnce).to.be.true;
|
|
307
|
+
expect(listener.firstCall.exception).to.have.property("message", "Cannot write while restoring the geographic coordinate system");
|
|
308
|
+
expect(logError.calledOnceWithExactly(BackendLoggerCategory.IModelDb, listener.firstCall.exception)).to.be.true;
|
|
309
|
+
expect(activeTxn?.isActive).to.be.false;
|
|
310
|
+
expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;
|
|
311
|
+
}
|
|
312
|
+
finally {
|
|
313
|
+
UnexpectedErrors.setHandler(previousHandler);
|
|
314
|
+
removeListener?.();
|
|
315
|
+
}
|
|
316
|
+
withEditTxn(iModel, (txn) => txn.saveFileProperty({ namespace: "test", name: "after-failed-scope" }, "allowed"));
|
|
317
|
+
iModel.close();
|
|
318
|
+
iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);
|
|
319
|
+
expect(iModel.rootSubject.name).to.equal("IModelProps");
|
|
320
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());
|
|
321
|
+
expect(iModel.queryFilePropertyString({ namespace: "test", name: "after-failed-scope" })).to.equal("allowed");
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
it("refreshes the GCS and its baseline after undo", () => {
|
|
325
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
326
|
+
withEditTxn(iModel, (txn) => {
|
|
327
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
328
|
+
txn.updateIModelProps();
|
|
329
|
+
});
|
|
330
|
+
expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.Success);
|
|
331
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());
|
|
332
|
+
const update = sinon.spy(iModel[_nativeDb], "updateIModelProps");
|
|
333
|
+
withEditTxn(iModel, (txn) => txn.updateProjectExtents(extents));
|
|
334
|
+
expect(update.firstCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
335
|
+
expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem).to.deep.equal(originalGcs);
|
|
336
|
+
});
|
|
337
|
+
it("refreshes the GCS and its baseline after redo", () => {
|
|
338
|
+
withEditTxn(iModel, (txn) => {
|
|
339
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
340
|
+
txn.updateIModelProps();
|
|
341
|
+
});
|
|
342
|
+
const persistedGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
343
|
+
expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.Success);
|
|
344
|
+
expect(iModel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
|
|
345
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(persistedGcs).toJSON());
|
|
346
|
+
const update = sinon.spy(iModel[_nativeDb], "updateIModelProps");
|
|
347
|
+
withEditTxn(iModel, (txn) => txn.updateProjectExtents(extents));
|
|
348
|
+
expect(update.firstCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
349
|
+
});
|
|
350
|
+
it("refreshes an abandoned GCS even when there is nothing to undo", () => {
|
|
351
|
+
iModel[_nativeDb].deleteAllTxns();
|
|
352
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
353
|
+
const txn = new EditTxn(iModel, "undo unsaved GCS");
|
|
354
|
+
txn.start();
|
|
355
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
356
|
+
txn.updateIModelProps();
|
|
357
|
+
expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.NothingToUndo);
|
|
358
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());
|
|
359
|
+
txn.end("abandon");
|
|
360
|
+
});
|
|
361
|
+
it("refreshes an abandoned GCS even when asynchronous redo fails", async () => {
|
|
362
|
+
iModel[_nativeDb].deleteAllTxns();
|
|
363
|
+
const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;
|
|
364
|
+
const txn = new EditTxn(iModel, "redo unsaved GCS");
|
|
365
|
+
txn.start();
|
|
366
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
367
|
+
txn.updateIModelProps();
|
|
368
|
+
const error = await getIModelError(iModel.txns.reinstateTxnAsync());
|
|
369
|
+
expect(error?.errorNumber).to.equal(IModelStatus.NothingToRedo);
|
|
370
|
+
expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());
|
|
371
|
+
txn.end("abandon");
|
|
372
|
+
});
|
|
373
|
+
it("rejects implicit listener writes during undo and redo cache restoration", () => {
|
|
374
|
+
sinon.stub(EditTxn, "implicitWriteEnforcement").value("allow");
|
|
375
|
+
const unexpectedError = sinon.spy(UnexpectedErrors, "handle");
|
|
376
|
+
withEditTxn(iModel, (txn) => {
|
|
377
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
378
|
+
txn.updateIModelProps();
|
|
379
|
+
});
|
|
380
|
+
const listener = sinon.spy(() => {
|
|
381
|
+
expect(() => iModel.getIndirectTxn().updateIModelProps()).to.throw("Cannot write while restoring the geographic coordinate system");
|
|
382
|
+
});
|
|
383
|
+
const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);
|
|
384
|
+
try {
|
|
385
|
+
expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.Success);
|
|
386
|
+
expect(iModel.txns.reinstateTxn()).to.equal(IModelStatus.Success);
|
|
387
|
+
expect(listener.callCount).to.equal(2);
|
|
388
|
+
expect(unexpectedError.called).to.be.false;
|
|
389
|
+
expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;
|
|
390
|
+
}
|
|
391
|
+
finally {
|
|
392
|
+
removeListener();
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
it("uses the reloaded GCS as its baseline", () => {
|
|
396
|
+
iModel.close();
|
|
397
|
+
iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);
|
|
398
|
+
const update = sinon.spy(iModel[_nativeDb], "updateIModelProps");
|
|
399
|
+
withEditTxn(iModel, (txn) => txn.updateIModelProps());
|
|
400
|
+
expect(update.firstCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
401
|
+
withEditTxn(iModel, (txn) => {
|
|
402
|
+
iModel.setGeographicCoordinateSystem(changedGcs);
|
|
403
|
+
txn.updateIModelProps();
|
|
404
|
+
});
|
|
405
|
+
iModel[_refreshIModelProps]();
|
|
406
|
+
update.resetHistory();
|
|
407
|
+
withEditTxn(iModel, (txn) => txn.updateProjectExtents(extents));
|
|
408
|
+
expect(update.firstCall.args[0]).not.to.have.property("geographicCoordinateSystem");
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
//# sourceMappingURL=IModelProps.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IModelProps.test.js","sourceRoot":"","sources":["../../../../src/test/imodel/IModelProps.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 { IModelStatus, Logger, OpenMode, UnexpectedErrors } from \"@itwin/core-bentley\";\r\nimport { Cartographic, EcefLocation, GeographicCRS, GeographicCRSProps } from \"@itwin/core-common\";\r\nimport { Range3d } from \"@itwin/core-geometry\";\r\nimport { expect } from \"chai\";\r\nimport * as sinon from \"sinon\";\r\nimport { BackendLoggerCategory, EditTxn, getAvailableVerticalCoordinateReferenceSystems, IModelJsFs, StandaloneDb, withEditTxn } from \"../../core-backend\";\r\nimport { _nativeDb, _refreshIModelProps } from \"../../internal/Symbols\";\r\nimport { IModelTestUtils } from \"../IModelTestUtils\";\r\nimport { TestUtils } from \"../TestUtils\";\r\nimport { getIModelError } from \"./IModelTestFixtures\";\r\n\r\ndescribe(\"iModel property updates\", () => {\r\n let iModel: StandaloneDb;\r\n let fileName: string;\r\n const initialGcs: GeographicCRSProps = { horizontalCRS: { id: \"LL84\" }, verticalCRS: { id: \"ELLIPSOID\" } };\r\n const changedGcs: GeographicCRSProps = {\r\n ...initialGcs,\r\n additionalTransform: { helmert2DWithZOffset: { translationX: 1, translationY: 2, translationZ: 3, rotDeg: 0, scale: 1 } },\r\n };\r\n const extents = Range3d.createXYZXYZ(-10, -10, -10, 10, 10, 10);\r\n const changedGcsJson = new GeographicCRS(changedGcs).toJSON();\r\n\r\n before(async () => {\r\n await TestUtils.shutdownBackend();\r\n await TestUtils.startBackend({ loadGcsWorkspaces: true });\r\n });\r\n\r\n after(async () => {\r\n await TestUtils.shutdownBackend();\r\n await TestUtils.startBackend();\r\n });\r\n\r\n beforeEach(() => {\r\n fileName = IModelTestUtils.prepareOutputFile(\"IModelProps\", \"IModelProps.bim\");\r\n iModel = StandaloneDb.createEmpty(fileName, { rootSubject: { name: \"IModelProps\" }, enableTransactions: true });\r\n withEditTxn(iModel, (txn) => {\r\n iModel.setGeographicCoordinateSystem(initialGcs);\r\n txn.updateIModelProps();\r\n });\r\n expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem?.verticalCRS?.id).to.equal(\"ELLIPSOID\");\r\n });\r\n\r\n afterEach(() => {\r\n sinon.restore();\r\n if (iModel.isOpen)\r\n iModel.close();\r\n IModelJsFs.removeSync(fileName);\r\n });\r\n\r\n it(\"omits unchanged GCS data from general, extents, and ECEF updates\", () => {\r\n const update = sinon.spy(iModel[_nativeDb], \"updateIModelProps\");\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n iModel.setGeographicCoordinateSystem(initialGcs);\r\n const ecef = EcefLocation.createFromCartographicOrigin(Cartographic.fromDegrees({ longitude: 0, latitude: 0, height: 0 }));\r\n withEditTxn(iModel, (txn) => {\r\n txn.updateIModelProps();\r\n txn.updateProjectExtents(extents);\r\n txn.updateEcefLocation(ecef);\r\n });\r\n expect(update.callCount).to.equal(3);\r\n for (const call of update.getCalls())\r\n expect(call.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n\r\n expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem).to.deep.equal(originalGcs);\r\n expect(iModel[_nativeDb].getIModelProps().projectExtents).to.deep.equal(extents.toJSON());\r\n });\r\n\r\n it(\"preserves full named vertical metadata without asking native code to resolve it again\", () => {\r\n const namedGcs: GeographicCRSProps = {\r\n horizontalCRS: { id: \"LL84\" },\r\n verticalCRS: { id: \"GEOID\", crsName: \"EGM96 height\" },\r\n };\r\n withEditTxn(iModel, (txn) => {\r\n iModel.setGeographicCoordinateSystem(namedGcs);\r\n txn.updateIModelProps();\r\n });\r\n iModel[_refreshIModelProps]();\r\n expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal(\"EGM96 height\");\r\n const property = { namespace: \"dgn_Db\", name: \"DgnGCSVerticalCRS\" };\r\n const storedDefinition = iModel.queryFilePropertyString(property);\r\n expect(storedDefinition).to.be.a(\"string\").and.to.contain('\"transforms\"');\r\n const nativeUpdate = iModel[_nativeDb].updateIModelProps.bind(iModel[_nativeDb]);\r\n const update = sinon.stub(iModel[_nativeDb], \"updateIModelProps\").callsFake((props) => {\r\n if (props.geographicCoordinateSystem)\r\n throw new Error(\"GCS dictionary unavailable\");\r\n nativeUpdate(props);\r\n });\r\n withEditTxn(iModel, (txn) => {\r\n txn.updateIModelProps();\r\n txn.updateProjectExtents(extents);\r\n txn.updateEcefLocation(EcefLocation.createFromCartographicOrigin(Cartographic.fromDegrees({ longitude: 0, latitude: 0, height: 0 })));\r\n });\r\n expect(update.callCount).to.equal(3);\r\n for (const call of update.getCalls())\r\n expect(call.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);\r\n\r\n iModel.close();\r\n iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);\r\n expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal(\"EGM96 height\");\r\n expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);\r\n });\r\n\r\n it(\"preserves an inline named vertical definition that is absent from the dictionary\", () => {\r\n const crsName = \"EditTxn custom height\";\r\n expect(getAvailableVerticalCoordinateReferenceSystems().some((entry) => entry.crsName === crsName)).to.be.false;\r\n const customGcs = {\r\n horizontalCRS: { id: \"LL84\" },\r\n verticalCRS: {\r\n id: \"ELLIPSOID\" as const,\r\n crsName,\r\n datumName: crsName,\r\n type: \"ELLIPSOID\",\r\n units: \"Meter\",\r\n extent: { southWest: { latitude: -90, longitude: -180 }, northEast: { latitude: 90, longitude: 180 } },\r\n },\r\n };\r\n withEditTxn(iModel, () => {\r\n iModel[_nativeDb].updateIModelProps({ ...iModel.toJSON(), geographicCoordinateSystem: customGcs });\r\n });\r\n iModel[_refreshIModelProps]();\r\n expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal(crsName);\r\n const property = { namespace: \"dgn_Db\", name: \"DgnGCSVerticalCRS\" };\r\n const storedDefinition = iModel.queryFilePropertyString(property);\r\n expect(storedDefinition).to.be.a(\"string\").and.to.contain(crsName);\r\n withEditTxn(iModel, (txn) => {\r\n txn.updateIModelProps();\r\n txn.updateProjectExtents(extents);\r\n txn.updateEcefLocation(EcefLocation.createFromCartographicOrigin(Cartographic.fromDegrees({ longitude: 0, latitude: 0, height: 0 })));\r\n });\r\n expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);\r\n iModel.close();\r\n iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);\r\n expect(iModel.geographicCoordinateSystem?.verticalCRS?.crsName).to.equal(crsName);\r\n expect(iModel.queryFilePropertyString(property)).to.equal(storedDefinition);\r\n });\r\n\r\n it(\"writes a pending GCS change through convenience methods only once\", () => {\r\n const update = sinon.spy(iModel[_nativeDb], \"updateIModelProps\");\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n withEditTxn(iModel, (txn) => {\r\n txn.updateProjectExtents(extents);\r\n txn.updateIModelProps();\r\n txn.saveChanges();\r\n txn.updateProjectExtents(extents);\r\n });\r\n expect(update.firstCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);\r\n expect(update.secondCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n expect(update.thirdCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem?.additionalTransform).to.deep.equal(changedGcs.additionalTransform);\r\n });\r\n\r\n it(\"restores the GCS and comparison baseline after abandoning an update\", () => {\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n expect(originalGcs).not.to.equal(undefined);\r\n const txn = new EditTxn(iModel, \"abandon GCS update\");\r\n txn.start();\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n txn.abandonChanges();\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());\r\n const update = sinon.spy(iModel[_nativeDb], \"updateIModelProps\");\r\n txn.updateProjectExtents(extents);\r\n expect(update.firstCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n expect(update.secondCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);\r\n txn.end(\"abandon\");\r\n });\r\n\r\n it(\"does not treat a rejected native update as persisted\", () => {\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n const nativeUpdate = iModel[_nativeDb].updateIModelProps.bind(iModel[_nativeDb]);\r\n const update = sinon.stub(iModel[_nativeDb], \"updateIModelProps\");\r\n update.onFirstCall().throws(new Error(\"GCS update rejected\"));\r\n update.onSecondCall().callsFake(nativeUpdate);\r\n withEditTxn(iModel, (txn) => {\r\n expect(() => txn.updateIModelProps()).to.throw(\"GCS update rejected\");\r\n txn.updateIModelProps();\r\n });\r\n expect(update.firstCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);\r\n expect(update.secondCall.args[0].geographicCoordinateSystem).to.deep.equal(changedGcsJson);\r\n expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem?.additionalTransform).to.deep.equal(changedGcs.additionalTransform);\r\n });\r\n\r\n it(\"rejects reentrant explicit and implicit writes while restoring an abandoned GCS\", () => {\r\n sinon.stub(EditTxn, \"implicitWriteEnforcement\").value(\"allow\");\r\n const unexpectedError = sinon.spy(UnexpectedErrors, \"handle\");\r\n const originalProps = iModel[_nativeDb].getIModelProps();\r\n const failure = new Error(\"abandon this edit\");\r\n const listener = sinon.spy(() => {\r\n expect(() => iModel.getIndirectTxn().updateIModelProps()).to.throw(\"Cannot write while restoring the geographic coordinate system\");\r\n // eslint-disable-next-line @typescript-eslint/no-deprecated -- Exercise the legacy implicit write path.\r\n expect(() => iModel.updateIModelProps()).to.throw(\"Cannot write while restoring the geographic coordinate system\");\r\n });\r\n expect(() => withEditTxn(iModel, (txn) => {\r\n iModel.rootSubject = { name: \"Abandoned root subject\" };\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n iModel.onGeographicCoordinateSystemChanged.addListener(listener);\r\n throw failure;\r\n })).to.throw(failure);\r\n expect(listener.calledOnce).to.be.true;\r\n expect(unexpectedError.called).to.be.false;\r\n expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;\r\n expect(iModel[_nativeDb].getIModelProps().rootSubject).to.deep.equal(originalProps.rootSubject);\r\n iModel.close();\r\n iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);\r\n expect(iModel.rootSubject).to.deep.equal(originalProps.rootSubject);\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalProps.geographicCoordinateSystem).toJSON());\r\n });\r\n\r\n it(\"releases the transaction and write guard when a rollback listener throws\", () => {\r\n const unexpectedError = sinon.stub(UnexpectedErrors, \"handle\");\r\n const txn = new EditTxn(iModel, \"rollback listener\");\r\n txn.start();\r\n iModel.rootSubject = { name: \"Abandoned root subject\" };\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n const listener = sinon.spy(() => iModel.getIndirectTxn().updateIModelProps());\r\n const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);\r\n try {\r\n txn.end(\"abandon\");\r\n expect(listener.threw()).to.be.true;\r\n expect(listener.firstCall.exception).to.have.property(\"message\", \"Cannot write while restoring the geographic coordinate system\");\r\n expect(unexpectedError.calledOnceWithExactly(listener.firstCall.exception)).to.be.true;\r\n expect(txn.isActive).to.be.false;\r\n expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;\r\n } finally {\r\n removeListener();\r\n }\r\n withEditTxn(iModel, (nextTxn) => nextTxn.saveFileProperty({ namespace: \"test\", name: \"after-rollback\" }, \"allowed\"));\r\n iModel.close();\r\n iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);\r\n expect(iModel.rootSubject.name).to.equal(\"IModelProps\");\r\n expect(iModel.queryFilePropertyString({ namespace: \"test\", name: \"after-rollback\" })).to.equal(\"allowed\");\r\n });\r\n it(\"rejects scoped transaction activation during undo abandonment without retaining ownership\", () => {\r\n sinon.stub(EditTxn, \"implicitWriteEnforcement\").value(\"allow\");\r\n const unexpectedError = sinon.stub(UnexpectedErrors, \"handle\");\r\n iModel[_nativeDb].deleteAllTxns();\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n iModel.getIndirectTxn().updateIModelProps();\r\n const edit = sinon.spy((txn: EditTxn) => txn.updateIModelProps());\r\n const listener = sinon.spy(() => withEditTxn(iModel, edit));\r\n const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);\r\n try {\r\n expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.NothingToUndo);\r\n expect(listener.calledOnce).to.be.true;\r\n expect(unexpectedError.calledOnceWithExactly(listener.firstCall.exception)).to.be.true;\r\n expect(listener.firstCall.exception).to.have.property(\"message\", \"Cannot write while restoring the geographic coordinate system\");\r\n expect(edit.called).to.be.false;\r\n expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;\r\n } finally {\r\n removeListener();\r\n }\r\n withEditTxn(iModel, (txn) => txn.saveFileProperty({ namespace: \"test\", name: \"after-listener-start\" }, \"allowed\"));\r\n expect(iModel.queryFilePropertyString({ namespace: \"test\", name: \"after-listener-start\" })).to.equal(\"allowed\");\r\n });\r\n\r\n for (const endTxn of [false, true]) {\r\n it(`${endTxn ? \"releases\" : \"retains\"} transaction ownership when a rollback notification propagates`, () => {\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n const txn = new EditTxn(iModel, \"propagating rollback listener\");\r\n txn.start();\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n const listener = sinon.spy(() => txn.updateIModelProps());\r\n const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);\r\n const previousHandler = UnexpectedErrors.setHandler(UnexpectedErrors.reThrowImmediate);\r\n try {\r\n expect(() => endTxn ? txn.end(\"abandon\") : txn.abandonChanges()).to.throw(\"Cannot write while restoring the geographic coordinate system\");\r\n expect(listener.calledOnce).to.be.true;\r\n expect(txn.isActive).to.equal(!endTxn);\r\n expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());\r\n } finally {\r\n UnexpectedErrors.setHandler(previousHandler);\r\n removeListener();\r\n }\r\n if (!endTxn)\r\n txn.end(\"save\");\r\n withEditTxn(iModel, (nextTxn) => nextTxn.saveFileProperty({ namespace: \"test\", name: \"after-propagated-notification\" }, \"allowed\"));\r\n expect(iModel.queryFilePropertyString({ namespace: \"test\", name: \"after-propagated-notification\" })).to.equal(\"allowed\");\r\n });\r\n }\r\n\r\n for (const asynchronous of [false, true]) {\r\n it(`preserves the ${asynchronous ? \"asynchronous\" : \"synchronous\"} callback error when a rollback notification propagates`, async () => {\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n const failure = new Error(\"initiating edit failure\");\r\n const logError = sinon.spy(Logger, \"logError\");\r\n let activeTxn: EditTxn | undefined;\r\n let removeListener: (() => void) | undefined;\r\n const listener = sinon.spy(() => iModel.getIndirectTxn().updateIModelProps());\r\n const edit = (txn: EditTxn) => {\r\n activeTxn = txn;\r\n iModel.rootSubject = { name: \"Abandoned root subject\" };\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);\r\n throw failure;\r\n };\r\n const previousHandler = UnexpectedErrors.setHandler(UnexpectedErrors.reThrowImmediate);\r\n try {\r\n if (asynchronous) {\r\n const error = await withEditTxn(iModel, async (txn) => {\r\n await Promise.resolve();\r\n edit(txn);\r\n }).catch((err: unknown) => err);\r\n expect(error).to.equal(failure);\r\n } else {\r\n expect(() => withEditTxn(iModel, edit)).to.throw(failure);\r\n }\r\n expect(listener.calledOnce).to.be.true;\r\n expect(listener.firstCall.exception).to.have.property(\"message\", \"Cannot write while restoring the geographic coordinate system\");\r\n expect(logError.calledOnceWithExactly(BackendLoggerCategory.IModelDb, listener.firstCall.exception)).to.be.true;\r\n expect(activeTxn?.isActive).to.be.false;\r\n expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;\r\n } finally {\r\n UnexpectedErrors.setHandler(previousHandler);\r\n removeListener?.();\r\n }\r\n withEditTxn(iModel, (txn) => txn.saveFileProperty({ namespace: \"test\", name: \"after-failed-scope\" }, \"allowed\"));\r\n iModel.close();\r\n iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);\r\n expect(iModel.rootSubject.name).to.equal(\"IModelProps\");\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());\r\n expect(iModel.queryFilePropertyString({ namespace: \"test\", name: \"after-failed-scope\" })).to.equal(\"allowed\");\r\n });\r\n }\r\n\r\n it(\"refreshes the GCS and its baseline after undo\", () => {\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n withEditTxn(iModel, (txn) => {\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n });\r\n expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.Success);\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());\r\n const update = sinon.spy(iModel[_nativeDb], \"updateIModelProps\");\r\n withEditTxn(iModel, (txn) => txn.updateProjectExtents(extents));\r\n expect(update.firstCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n expect(iModel[_nativeDb].getIModelProps().geographicCoordinateSystem).to.deep.equal(originalGcs);\r\n });\r\n\r\n it(\"refreshes the GCS and its baseline after redo\", () => {\r\n withEditTxn(iModel, (txn) => {\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n });\r\n const persistedGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.Success);\r\n expect(iModel.txns.reinstateTxn()).to.equal(IModelStatus.Success);\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(persistedGcs).toJSON());\r\n const update = sinon.spy(iModel[_nativeDb], \"updateIModelProps\");\r\n withEditTxn(iModel, (txn) => txn.updateProjectExtents(extents));\r\n expect(update.firstCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n });\r\n\r\n it(\"refreshes an abandoned GCS even when there is nothing to undo\", () => {\r\n iModel[_nativeDb].deleteAllTxns();\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n const txn = new EditTxn(iModel, \"undo unsaved GCS\");\r\n txn.start();\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.NothingToUndo);\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());\r\n txn.end(\"abandon\");\r\n });\r\n\r\n it(\"refreshes an abandoned GCS even when asynchronous redo fails\", async () => {\r\n iModel[_nativeDb].deleteAllTxns();\r\n const originalGcs = iModel[_nativeDb].getIModelProps().geographicCoordinateSystem;\r\n const txn = new EditTxn(iModel, \"redo unsaved GCS\");\r\n txn.start();\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n const error = await getIModelError(iModel.txns.reinstateTxnAsync());\r\n expect(error?.errorNumber).to.equal(IModelStatus.NothingToRedo);\r\n expect(iModel.geographicCoordinateSystem?.toJSON()).to.deep.equal(new GeographicCRS(originalGcs).toJSON());\r\n txn.end(\"abandon\");\r\n });\r\n\r\n it(\"rejects implicit listener writes during undo and redo cache restoration\", () => {\r\n sinon.stub(EditTxn, \"implicitWriteEnforcement\").value(\"allow\");\r\n const unexpectedError = sinon.spy(UnexpectedErrors, \"handle\");\r\n withEditTxn(iModel, (txn) => {\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n });\r\n const listener = sinon.spy(() => {\r\n expect(() => iModel.getIndirectTxn().updateIModelProps()).to.throw(\"Cannot write while restoring the geographic coordinate system\");\r\n });\r\n const removeListener = iModel.onGeographicCoordinateSystemChanged.addListener(listener);\r\n try {\r\n expect(iModel.txns.reverseTxns(1)).to.equal(IModelStatus.Success);\r\n expect(iModel.txns.reinstateTxn()).to.equal(IModelStatus.Success);\r\n expect(listener.callCount).to.equal(2);\r\n expect(unexpectedError.called).to.be.false;\r\n expect(iModel[_nativeDb].hasUnsavedChanges()).to.be.false;\r\n } finally {\r\n removeListener();\r\n }\r\n });\r\n\r\n it(\"uses the reloaded GCS as its baseline\", () => {\r\n iModel.close();\r\n iModel = StandaloneDb.openFile(fileName, OpenMode.ReadWrite);\r\n const update = sinon.spy(iModel[_nativeDb], \"updateIModelProps\");\r\n withEditTxn(iModel, (txn) => txn.updateIModelProps());\r\n expect(update.firstCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n withEditTxn(iModel, (txn) => {\r\n iModel.setGeographicCoordinateSystem(changedGcs);\r\n txn.updateIModelProps();\r\n });\r\n iModel[_refreshIModelProps]();\r\n update.resetHistory();\r\n withEditTxn(iModel, (txn) => txn.updateProjectExtents(extents));\r\n expect(update.firstCall.args[0]).not.to.have.property(\"geographicCoordinateSystem\");\r\n });\r\n});\r\n"]}
|
|
@@ -6,7 +6,8 @@ import { Logger, OpenMode } from "@itwin/core-bentley";
|
|
|
6
6
|
import { expect } from "chai";
|
|
7
7
|
import * as sinon from "sinon";
|
|
8
8
|
import { EditTxnError } from "@itwin/core-common";
|
|
9
|
-
import { EditTxn, IModelJsFs, StandaloneDb } from "../../core-backend";
|
|
9
|
+
import { BackendLoggerCategory, EditTxn, IModelJsFs, StandaloneDb, withEditTxn } from "../../core-backend";
|
|
10
|
+
import { _nativeDb } from "../../internal/Symbols";
|
|
10
11
|
import { IModelTestUtils } from "../IModelTestUtils";
|
|
11
12
|
class TestEditTxn extends EditTxn {
|
|
12
13
|
constructor(iModel) {
|
|
@@ -177,6 +178,76 @@ describe("EditTxn", () => {
|
|
|
177
178
|
second.end("abandon");
|
|
178
179
|
expect(second.isActive).to.be.false;
|
|
179
180
|
});
|
|
181
|
+
for (const mode of ["save", "abandon"]) {
|
|
182
|
+
it(`retains transaction ownership when native ${mode} fails`, () => {
|
|
183
|
+
const txn = new TestEditTxn(iModel);
|
|
184
|
+
txn.start();
|
|
185
|
+
txn.writeFileProperty("pending", "value");
|
|
186
|
+
const failure = new Error(`native ${mode} failed`);
|
|
187
|
+
const nativeOperation = sinon.stub(iModel[_nativeDb], mode === "save" ? "saveChanges" : "abandonChanges").throws(failure);
|
|
188
|
+
expect(() => txn.end(mode)).to.throw(failure);
|
|
189
|
+
expect(txn.isActive).to.be.true;
|
|
190
|
+
expect(iModel.txns.hasUnsavedChanges).to.be.true;
|
|
191
|
+
nativeOperation.restore();
|
|
192
|
+
txn.end("abandon");
|
|
193
|
+
expect(txn.isActive).to.be.false;
|
|
194
|
+
expect(iModel.queryFilePropertyString({ name: "pending", namespace: "EditTxnTest" })).to.be.undefined;
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
it("preserves the callback error and transaction ownership when scoped native abandonment fails", () => {
|
|
198
|
+
const failure = new Error("callback failure");
|
|
199
|
+
const abandonFailure = new Error("native abandonment failed");
|
|
200
|
+
const nativeOperation = sinon.stub(iModel[_nativeDb], "abandonChanges").throws(abandonFailure);
|
|
201
|
+
const logError = sinon.spy(Logger, "logError");
|
|
202
|
+
let activeTxn;
|
|
203
|
+
expect(() => withEditTxn(iModel, (txn) => {
|
|
204
|
+
activeTxn = txn;
|
|
205
|
+
txn.saveFileProperty({ name: "pending", namespace: "EditTxnTest" }, "value");
|
|
206
|
+
throw failure;
|
|
207
|
+
})).to.throw(failure);
|
|
208
|
+
expect(activeTxn?.isActive).to.be.true;
|
|
209
|
+
expect(iModel.txns.hasUnsavedChanges).to.be.true;
|
|
210
|
+
expect(logError.calledOnceWithExactly(BackendLoggerCategory.IModelDb, abandonFailure)).to.be.true;
|
|
211
|
+
nativeOperation.restore();
|
|
212
|
+
activeTxn?.end("abandon");
|
|
213
|
+
expect(iModel.queryFilePropertyString({ name: "pending", namespace: "EditTxnTest" })).to.be.undefined;
|
|
214
|
+
});
|
|
215
|
+
for (const asynchronous of [false, true]) {
|
|
216
|
+
it(`preserves the ${asynchronous ? "asynchronous" : "synchronous"} callback error when rollback and error logging both fail`, async () => {
|
|
217
|
+
const failure = new Error("callback failure");
|
|
218
|
+
const abandonFailure = new Error("native abandonment failed");
|
|
219
|
+
const loggingFailure = new Error("error logging failed");
|
|
220
|
+
const nativeOperation = sinon.stub(iModel[_nativeDb], "abandonChanges").throws(abandonFailure);
|
|
221
|
+
const logError = sinon.stub(Logger, "logError").throws(loggingFailure);
|
|
222
|
+
let activeTxn;
|
|
223
|
+
const edit = (txn) => {
|
|
224
|
+
activeTxn = txn;
|
|
225
|
+
txn.saveFileProperty({ name: "pending", namespace: "EditTxnTest" }, "value");
|
|
226
|
+
throw failure;
|
|
227
|
+
};
|
|
228
|
+
try {
|
|
229
|
+
if (asynchronous) {
|
|
230
|
+
const error = await withEditTxn(iModel, async (txn) => {
|
|
231
|
+
await Promise.resolve();
|
|
232
|
+
edit(txn);
|
|
233
|
+
}).catch((err) => err);
|
|
234
|
+
expect(error).to.equal(failure);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
expect(() => withEditTxn(iModel, edit)).to.throw(failure);
|
|
238
|
+
}
|
|
239
|
+
expect(logError.calledOnceWithExactly(BackendLoggerCategory.IModelDb, abandonFailure)).to.be.true;
|
|
240
|
+
expect(logError.firstCall.exception).to.equal(loggingFailure);
|
|
241
|
+
expect(activeTxn?.isActive).to.be.true;
|
|
242
|
+
expect(iModel.txns.hasUnsavedChanges).to.be.true;
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
nativeOperation.restore();
|
|
246
|
+
activeTxn?.end("abandon");
|
|
247
|
+
}
|
|
248
|
+
expect(iModel.queryFilePropertyString({ name: "pending", namespace: "EditTxnTest" })).to.be.undefined;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
180
251
|
it("uses the default description unless save is given an override", () => {
|
|
181
252
|
const txn = new EditTxn(iModel, "default description");
|
|
182
253
|
txn.start();
|